| /* Routines for name->symbol lookups in GDB. | 
 |     | 
 |    Copyright (C) 2003-2022 Free Software Foundation, Inc. | 
 |  | 
 |    Contributed by David Carlton <carlton@bactrian.org> and by Kealia, | 
 |    Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include <ctype.h> | 
 | #include "gdb_obstack.h" | 
 | #include "symtab.h" | 
 | #include "buildsym.h" | 
 | #include "dictionary.h" | 
 | #include "safe-ctype.h" | 
 | #include <unordered_map> | 
 | #include "language.h" | 
 |  | 
 | /* This file implements dictionaries, which are tables that associate | 
 |    symbols to names.  They are represented by an opaque type 'struct | 
 |    dictionary'.  That type has various internal implementations, which | 
 |    you can choose between depending on what properties you need | 
 |    (e.g. fast lookup, order-preserving, expandable). | 
 |  | 
 |    Each dictionary starts with a 'virtual function table' that | 
 |    contains the functions that actually implement the various | 
 |    operations that dictionaries provide.  (Note, however, that, for | 
 |    the sake of client code, we also provide some functions that can be | 
 |    implemented generically in terms of the functions in the vtable.) | 
 |  | 
 |    To add a new dictionary implementation <impl>, what you should do | 
 |    is: | 
 |  | 
 |    * Add a new element DICT_<IMPL> to dict_type. | 
 |     | 
 |    * Create a new structure dictionary_<impl>.  If your new | 
 |    implementation is a variant of an existing one, make sure that | 
 |    their structs have the same initial data members.  Define accessor | 
 |    macros for your new data members. | 
 |  | 
 |    * Implement all the functions in dict_vector as static functions, | 
 |    whose name is the same as the corresponding member of dict_vector | 
 |    plus _<impl>.  You don't have to do this for those members where | 
 |    you can reuse existing generic functions | 
 |    (e.g. add_symbol_nonexpandable, free_obstack) or in the case where | 
 |    your new implementation is a variant of an existing implementation | 
 |    and where the variant doesn't affect the member function in | 
 |    question. | 
 |  | 
 |    * Define a static const struct dict_vector dict_<impl>_vector. | 
 |  | 
 |    * Define a function dict_create_<impl> to create these | 
 |    gizmos.  Add its declaration to dictionary.h. | 
 |  | 
 |    To add a new operation <op> on all existing implementations, what | 
 |    you should do is: | 
 |  | 
 |    * Add a new member <op> to struct dict_vector. | 
 |  | 
 |    * If there is useful generic behavior <op>, define a static | 
 |    function <op>_something_informative that implements that behavior. | 
 |    (E.g. add_symbol_nonexpandable, free_obstack.) | 
 |  | 
 |    * For every implementation <impl> that should have its own specific | 
 |    behavior for <op>, define a static function <op>_<impl> | 
 |    implementing it. | 
 |  | 
 |    * Modify all existing dict_vector_<impl>'s to include the appropriate | 
 |    member. | 
 |  | 
 |    * Define a function dict_<op> that looks up <op> in the dict_vector | 
 |    and calls the appropriate function.  Add a declaration for | 
 |    dict_<op> to dictionary.h.  */ | 
 |  | 
 | /* An enum representing the various implementations of dictionaries. | 
 |    Used only for debugging.  */ | 
 |  | 
 | enum dict_type | 
 |   { | 
 |     /* Symbols are stored in a fixed-size hash table.  */ | 
 |     DICT_HASHED, | 
 |     /* Symbols are stored in an expandable hash table.  */ | 
 |     DICT_HASHED_EXPANDABLE, | 
 |     /* Symbols are stored in a fixed-size array.  */ | 
 |     DICT_LINEAR, | 
 |     /* Symbols are stored in an expandable array.  */ | 
 |     DICT_LINEAR_EXPANDABLE | 
 |   }; | 
 |  | 
 | /* The virtual function table.  */ | 
 |  | 
 | struct dict_vector | 
 | { | 
 |   /* The type of the dictionary.  This is only here to make debugging | 
 |      a bit easier; it's not actually used.  */ | 
 |   enum dict_type type; | 
 |   /* The function to free a dictionary.  */ | 
 |   void (*free) (struct dictionary *dict); | 
 |   /* Add a symbol to a dictionary, if possible.  */ | 
 |   void (*add_symbol) (struct dictionary *dict, struct symbol *sym); | 
 |   /* Iterator functions.  */ | 
 |   struct symbol *(*iterator_first) (const struct dictionary *dict, | 
 | 				    struct dict_iterator *iterator); | 
 |   struct symbol *(*iterator_next) (struct dict_iterator *iterator); | 
 |   /* Functions to iterate over symbols with a given name.  */ | 
 |   struct symbol *(*iter_match_first) (const struct dictionary *dict, | 
 | 				      const lookup_name_info &name, | 
 | 				      struct dict_iterator *iterator); | 
 |   struct symbol *(*iter_match_next) (const lookup_name_info &name, | 
 | 				     struct dict_iterator *iterator); | 
 |   /* A size function, for maint print symtabs.  */ | 
 |   int (*size) (const struct dictionary *dict); | 
 | }; | 
 |  | 
 | /* Now comes the structs used to store the data for different | 
 |    implementations.  If two implementations have data in common, put | 
 |    the common data at the top of their structs, ordered in the same | 
 |    way.  */ | 
 |  | 
 | struct dictionary_hashed | 
 | { | 
 |   int nbuckets; | 
 |   struct symbol **buckets; | 
 | }; | 
 |  | 
 | struct dictionary_hashed_expandable | 
 | { | 
 |   /* How many buckets we currently have.  */ | 
 |   int nbuckets; | 
 |   struct symbol **buckets; | 
 |   /* How many syms we currently have; we need this so we will know | 
 |      when to add more buckets.  */ | 
 |   int nsyms; | 
 | }; | 
 |  | 
 | struct dictionary_linear | 
 | { | 
 |   int nsyms; | 
 |   struct symbol **syms; | 
 | }; | 
 |  | 
 | struct dictionary_linear_expandable | 
 | { | 
 |   /* How many symbols we currently have.  */ | 
 |   int nsyms; | 
 |   struct symbol **syms; | 
 |   /* How many symbols we can store before needing to reallocate.  */ | 
 |   int capacity; | 
 | }; | 
 |  | 
 | /* And now, the star of our show.  */ | 
 |  | 
 | struct dictionary | 
 | { | 
 |   const struct language_defn *language; | 
 |   const struct dict_vector *vector; | 
 |   union | 
 |   { | 
 |     struct dictionary_hashed hashed; | 
 |     struct dictionary_hashed_expandable hashed_expandable; | 
 |     struct dictionary_linear linear; | 
 |     struct dictionary_linear_expandable linear_expandable; | 
 |   } | 
 |   data; | 
 | }; | 
 |  | 
 | /* Accessor macros.  */ | 
 |  | 
 | #define DICT_VECTOR(d)			(d)->vector | 
 | #define DICT_LANGUAGE(d)                (d)->language | 
 |  | 
 | /* These can be used for DICT_HASHED_EXPANDABLE, too.  */ | 
 |  | 
 | #define DICT_HASHED_NBUCKETS(d)		(d)->data.hashed.nbuckets | 
 | #define DICT_HASHED_BUCKETS(d)		(d)->data.hashed.buckets | 
 | #define DICT_HASHED_BUCKET(d,i)		DICT_HASHED_BUCKETS (d) [i] | 
 |  | 
 | #define DICT_HASHED_EXPANDABLE_NSYMS(d)	(d)->data.hashed_expandable.nsyms | 
 |  | 
 | /* These can be used for DICT_LINEAR_EXPANDABLEs, too.  */ | 
 |  | 
 | #define DICT_LINEAR_NSYMS(d)		(d)->data.linear.nsyms | 
 | #define DICT_LINEAR_SYMS(d)		(d)->data.linear.syms | 
 | #define DICT_LINEAR_SYM(d,i)		DICT_LINEAR_SYMS (d) [i] | 
 |  | 
 | #define DICT_LINEAR_EXPANDABLE_CAPACITY(d) \ | 
 | 		(d)->data.linear_expandable.capacity | 
 |  | 
 | /* The initial size of a DICT_*_EXPANDABLE dictionary.  */ | 
 |  | 
 | #define DICT_EXPANDABLE_INITIAL_CAPACITY 10 | 
 |  | 
 | /* This calculates the number of buckets we'll use in a hashtable, | 
 |    given the number of symbols that it will contain.  */ | 
 |  | 
 | #define DICT_HASHTABLE_SIZE(n)	((n)/5 + 1) | 
 |  | 
 | /* Accessor macros for dict_iterators; they're here rather than | 
 |    dictionary.h because code elsewhere should treat dict_iterators as | 
 |    opaque.  */ | 
 |  | 
 | /* The dictionary that the iterator is associated to.  */ | 
 | #define DICT_ITERATOR_DICT(iter)		(iter)->dict | 
 | /* For linear dictionaries, the index of the last symbol returned; for | 
 |    hashed dictionaries, the bucket of the last symbol returned.  */ | 
 | #define DICT_ITERATOR_INDEX(iter)		(iter)->index | 
 | /* For hashed dictionaries, this points to the last symbol returned; | 
 |    otherwise, this is unused.  */ | 
 | #define DICT_ITERATOR_CURRENT(iter)		(iter)->current | 
 |  | 
 | /* Declarations of functions for vectors.  */ | 
 |  | 
 | /* Functions that might work across a range of dictionary types.  */ | 
 |  | 
 | static void add_symbol_nonexpandable (struct dictionary *dict, | 
 | 				      struct symbol *sym); | 
 |  | 
 | static void free_obstack (struct dictionary *dict); | 
 |  | 
 | /* Functions for DICT_HASHED and DICT_HASHED_EXPANDABLE | 
 |    dictionaries.  */ | 
 |  | 
 | static struct symbol *iterator_first_hashed (const struct dictionary *dict, | 
 | 					     struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iterator_next_hashed (struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iter_match_first_hashed (const struct dictionary *dict, | 
 | 					       const lookup_name_info &name, | 
 | 					      struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iter_match_next_hashed (const lookup_name_info &name, | 
 | 					      struct dict_iterator *iterator); | 
 |  | 
 | /* Functions only for DICT_HASHED.  */ | 
 |  | 
 | static int size_hashed (const struct dictionary *dict); | 
 |  | 
 | /* Functions only for DICT_HASHED_EXPANDABLE.  */ | 
 |  | 
 | static void free_hashed_expandable (struct dictionary *dict); | 
 |  | 
 | static void add_symbol_hashed_expandable (struct dictionary *dict, | 
 | 					  struct symbol *sym); | 
 |  | 
 | static int size_hashed_expandable (const struct dictionary *dict); | 
 |  | 
 | /* Functions for DICT_LINEAR and DICT_LINEAR_EXPANDABLE | 
 |    dictionaries.  */ | 
 |  | 
 | static struct symbol *iterator_first_linear (const struct dictionary *dict, | 
 | 					     struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iterator_next_linear (struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iter_match_first_linear (const struct dictionary *dict, | 
 | 					       const lookup_name_info &name, | 
 | 					       struct dict_iterator *iterator); | 
 |  | 
 | static struct symbol *iter_match_next_linear (const lookup_name_info &name, | 
 | 					      struct dict_iterator *iterator); | 
 |  | 
 | static int size_linear (const struct dictionary *dict); | 
 |  | 
 | /* Functions only for DICT_LINEAR_EXPANDABLE.  */ | 
 |  | 
 | static void free_linear_expandable (struct dictionary *dict); | 
 |  | 
 | static void add_symbol_linear_expandable (struct dictionary *dict, | 
 | 					  struct symbol *sym); | 
 |  | 
 | /* Various vectors that we'll actually use.  */ | 
 |  | 
 | static const struct dict_vector dict_hashed_vector = | 
 |   { | 
 |     DICT_HASHED,			/* type */ | 
 |     free_obstack,			/* free */ | 
 |     add_symbol_nonexpandable,		/* add_symbol */ | 
 |     iterator_first_hashed,		/* iterator_first */ | 
 |     iterator_next_hashed,		/* iterator_next */ | 
 |     iter_match_first_hashed,		/* iter_name_first */ | 
 |     iter_match_next_hashed,		/* iter_name_next */ | 
 |     size_hashed,			/* size */ | 
 |   }; | 
 |  | 
 | static const struct dict_vector dict_hashed_expandable_vector = | 
 |   { | 
 |     DICT_HASHED_EXPANDABLE,		/* type */ | 
 |     free_hashed_expandable,		/* free */ | 
 |     add_symbol_hashed_expandable,	/* add_symbol */ | 
 |     iterator_first_hashed,		/* iterator_first */ | 
 |     iterator_next_hashed,		/* iterator_next */ | 
 |     iter_match_first_hashed,		/* iter_name_first */ | 
 |     iter_match_next_hashed,		/* iter_name_next */ | 
 |     size_hashed_expandable,		/* size */ | 
 |   }; | 
 |  | 
 | static const struct dict_vector dict_linear_vector = | 
 |   { | 
 |     DICT_LINEAR,			/* type */ | 
 |     free_obstack,			/* free */ | 
 |     add_symbol_nonexpandable,		/* add_symbol */ | 
 |     iterator_first_linear,		/* iterator_first */ | 
 |     iterator_next_linear,		/* iterator_next */ | 
 |     iter_match_first_linear,		/* iter_name_first */ | 
 |     iter_match_next_linear,		/* iter_name_next */ | 
 |     size_linear,			/* size */ | 
 |   }; | 
 |  | 
 | static const struct dict_vector dict_linear_expandable_vector = | 
 |   { | 
 |     DICT_LINEAR_EXPANDABLE,		/* type */ | 
 |     free_linear_expandable,		/* free */ | 
 |     add_symbol_linear_expandable,	/* add_symbol */ | 
 |     iterator_first_linear,		/* iterator_first */ | 
 |     iterator_next_linear,		/* iterator_next */ | 
 |     iter_match_first_linear,		/* iter_name_first */ | 
 |     iter_match_next_linear,		/* iter_name_next */ | 
 |     size_linear,			/* size */ | 
 |   }; | 
 |  | 
 | /* Declarations of helper functions (i.e. ones that don't go into | 
 |    vectors).  */ | 
 |  | 
 | static struct symbol *iterator_hashed_advance (struct dict_iterator *iter); | 
 |  | 
 | static void insert_symbol_hashed (struct dictionary *dict, | 
 | 				  struct symbol *sym); | 
 |  | 
 | static void expand_hashtable (struct dictionary *dict); | 
 |  | 
 | /* The creation functions.  */ | 
 |  | 
 | /* Create a hashed dictionary of a given language.  */ | 
 |  | 
 | static struct dictionary * | 
 | dict_create_hashed (struct obstack *obstack, | 
 | 		    enum language language, | 
 | 		    const std::vector<symbol *> &symbol_list) | 
 | { | 
 |   /* Allocate the dictionary.  */ | 
 |   struct dictionary *retval = XOBNEW (obstack, struct dictionary); | 
 |   DICT_VECTOR (retval) = &dict_hashed_vector; | 
 |   DICT_LANGUAGE (retval) = language_def (language); | 
 |  | 
 |   /* Allocate space for symbols.  */ | 
 |   int nsyms = symbol_list.size (); | 
 |   int nbuckets = DICT_HASHTABLE_SIZE (nsyms); | 
 |   DICT_HASHED_NBUCKETS (retval) = nbuckets; | 
 |   struct symbol **buckets = XOBNEWVEC (obstack, struct symbol *, nbuckets); | 
 |   memset (buckets, 0, nbuckets * sizeof (struct symbol *)); | 
 |   DICT_HASHED_BUCKETS (retval) = buckets; | 
 |  | 
 |   /* Now fill the buckets.  */ | 
 |   for (const auto &sym : symbol_list) | 
 |     insert_symbol_hashed (retval, sym); | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* Create an expandable hashed dictionary of a given language.  */ | 
 |  | 
 | static struct dictionary * | 
 | dict_create_hashed_expandable (enum language language) | 
 | { | 
 |   struct dictionary *retval = XNEW (struct dictionary); | 
 |  | 
 |   DICT_VECTOR (retval) = &dict_hashed_expandable_vector; | 
 |   DICT_LANGUAGE (retval) = language_def (language); | 
 |   DICT_HASHED_NBUCKETS (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY; | 
 |   DICT_HASHED_BUCKETS (retval) = XCNEWVEC (struct symbol *, | 
 | 					   DICT_EXPANDABLE_INITIAL_CAPACITY); | 
 |   DICT_HASHED_EXPANDABLE_NSYMS (retval) = 0; | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* Create a linear dictionary of a given language.  */ | 
 |  | 
 | static struct dictionary * | 
 | dict_create_linear (struct obstack *obstack, | 
 | 		    enum language language, | 
 | 		    const std::vector<symbol *> &symbol_list) | 
 | { | 
 |   struct dictionary *retval = XOBNEW (obstack, struct dictionary); | 
 |   DICT_VECTOR (retval) = &dict_linear_vector; | 
 |   DICT_LANGUAGE (retval) = language_def (language); | 
 |  | 
 |   /* Allocate space for symbols.  */ | 
 |   int nsyms = symbol_list.size (); | 
 |   DICT_LINEAR_NSYMS (retval) = nsyms; | 
 |   struct symbol **syms = XOBNEWVEC (obstack, struct symbol *, nsyms); | 
 |   DICT_LINEAR_SYMS (retval) = syms; | 
 |  | 
 |   /* Now fill in the symbols.  */ | 
 |   int idx = nsyms - 1; | 
 |   for (const auto &sym : symbol_list) | 
 |     syms[idx--] = sym; | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* Create an expandable linear dictionary of a given language.  */ | 
 |  | 
 | static struct dictionary * | 
 | dict_create_linear_expandable (enum language language) | 
 | { | 
 |   struct dictionary *retval = XNEW (struct dictionary); | 
 |  | 
 |   DICT_VECTOR (retval) = &dict_linear_expandable_vector; | 
 |   DICT_LANGUAGE (retval) = language_def (language); | 
 |   DICT_LINEAR_NSYMS (retval) = 0; | 
 |   DICT_LINEAR_EXPANDABLE_CAPACITY (retval) = DICT_EXPANDABLE_INITIAL_CAPACITY; | 
 |   DICT_LINEAR_SYMS (retval) | 
 |     = XNEWVEC (struct symbol *, DICT_LINEAR_EXPANDABLE_CAPACITY (retval)); | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* The functions providing the dictionary interface.  */ | 
 |  | 
 | /* Free the memory used by a dictionary that's not on an obstack.  (If | 
 |    any.)  */ | 
 |  | 
 | static void | 
 | dict_free (struct dictionary *dict) | 
 | { | 
 |   (DICT_VECTOR (dict))->free (dict); | 
 | } | 
 |  | 
 | /* Add SYM to DICT.  DICT had better be expandable.  */ | 
 |  | 
 | static void | 
 | dict_add_symbol (struct dictionary *dict, struct symbol *sym) | 
 | { | 
 |   (DICT_VECTOR (dict))->add_symbol (dict, sym); | 
 | } | 
 |  | 
 | /* Utility to add a list of symbols to a dictionary. | 
 |    DICT must be an expandable dictionary.  */ | 
 |  | 
 | static void | 
 | dict_add_pending (struct dictionary *dict, | 
 | 		  const std::vector<symbol *> &symbol_list) | 
 | { | 
 |   /* Preserve ordering by reversing the list.  */ | 
 |   for (auto sym = symbol_list.rbegin (); sym != symbol_list.rend (); ++sym) | 
 |     dict_add_symbol (dict, *sym); | 
 | } | 
 |  | 
 | /* Initialize ITERATOR to point at the first symbol in DICT, and | 
 |    return that first symbol, or NULL if DICT is empty.  */ | 
 |  | 
 | static struct symbol * | 
 | dict_iterator_first (const struct dictionary *dict, | 
 | 		     struct dict_iterator *iterator) | 
 | { | 
 |   return (DICT_VECTOR (dict))->iterator_first (dict, iterator); | 
 | } | 
 |  | 
 | /* Advance ITERATOR, and return the next symbol, or NULL if there are | 
 |    no more symbols.  */ | 
 |  | 
 | static struct symbol * | 
 | dict_iterator_next (struct dict_iterator *iterator) | 
 | { | 
 |   return (DICT_VECTOR (DICT_ITERATOR_DICT (iterator))) | 
 |     ->iterator_next (iterator); | 
 | } | 
 |  | 
 | static struct symbol * | 
 | dict_iter_match_first (const struct dictionary *dict, | 
 | 		       const lookup_name_info &name, | 
 | 		       struct dict_iterator *iterator) | 
 | { | 
 |   return (DICT_VECTOR (dict))->iter_match_first (dict, name, iterator); | 
 | } | 
 |  | 
 | static struct symbol * | 
 | dict_iter_match_next (const lookup_name_info &name, | 
 | 		      struct dict_iterator *iterator) | 
 | { | 
 |   return (DICT_VECTOR (DICT_ITERATOR_DICT (iterator))) | 
 |     ->iter_match_next (name, iterator); | 
 | } | 
 |  | 
 | static int | 
 | dict_size (const struct dictionary *dict) | 
 | { | 
 |   return (DICT_VECTOR (dict))->size (dict); | 
 | } | 
 |   | 
 | /* Now come functions (well, one function, currently) that are | 
 |    implemented generically by means of the vtable.  Typically, they're | 
 |    rarely used.  */ | 
 |  | 
 |  | 
 | /* The functions implementing the dictionary interface.  */ | 
 |  | 
 | /* Generic functions, where appropriate.  */ | 
 |  | 
 | static void | 
 | free_obstack (struct dictionary *dict) | 
 | { | 
 |   /* Do nothing!  */ | 
 | } | 
 |  | 
 | static void | 
 | add_symbol_nonexpandable (struct dictionary *dict, struct symbol *sym) | 
 | { | 
 |   internal_error (__FILE__, __LINE__, | 
 | 		  _("dict_add_symbol: non-expandable dictionary")); | 
 | } | 
 |  | 
 | /* Functions for DICT_HASHED and DICT_HASHED_EXPANDABLE.  */ | 
 |  | 
 | static struct symbol * | 
 | iterator_first_hashed (const struct dictionary *dict, | 
 | 		       struct dict_iterator *iterator) | 
 | { | 
 |   DICT_ITERATOR_DICT (iterator) = dict; | 
 |   DICT_ITERATOR_INDEX (iterator) = -1; | 
 |   return iterator_hashed_advance (iterator); | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iterator_next_hashed (struct dict_iterator *iterator) | 
 | { | 
 |   struct symbol *next; | 
 |  | 
 |   next = DICT_ITERATOR_CURRENT (iterator)->hash_next; | 
 |    | 
 |   if (next == NULL) | 
 |     return iterator_hashed_advance (iterator); | 
 |   else | 
 |     { | 
 |       DICT_ITERATOR_CURRENT (iterator) = next; | 
 |       return next; | 
 |     } | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iterator_hashed_advance (struct dict_iterator *iterator) | 
 | { | 
 |   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator); | 
 |   int nbuckets = DICT_HASHED_NBUCKETS (dict); | 
 |   int i; | 
 |  | 
 |   for (i = DICT_ITERATOR_INDEX (iterator) + 1; i < nbuckets; ++i) | 
 |     { | 
 |       struct symbol *sym = DICT_HASHED_BUCKET (dict, i); | 
 |        | 
 |       if (sym != NULL) | 
 | 	{ | 
 | 	  DICT_ITERATOR_INDEX (iterator) = i; | 
 | 	  DICT_ITERATOR_CURRENT (iterator) = sym; | 
 | 	  return sym; | 
 | 	} | 
 |     } | 
 |  | 
 |   return NULL; | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iter_match_first_hashed (const struct dictionary *dict, | 
 | 			 const lookup_name_info &name, | 
 | 			 struct dict_iterator *iterator) | 
 | { | 
 |   const language_defn *lang = DICT_LANGUAGE (dict); | 
 |   unsigned int hash_index = (name.search_name_hash (lang->la_language) | 
 | 			     % DICT_HASHED_NBUCKETS (dict)); | 
 |   symbol_name_matcher_ftype *matches_name | 
 |     = lang->get_symbol_name_matcher (name); | 
 |   struct symbol *sym; | 
 |  | 
 |   DICT_ITERATOR_DICT (iterator) = dict; | 
 |  | 
 |   /* Loop through the symbols in the given bucket, breaking when SYM | 
 |      first matches.  If SYM never matches, it will be set to NULL; | 
 |      either way, we have the right return value.  */ | 
 |    | 
 |   for (sym = DICT_HASHED_BUCKET (dict, hash_index); | 
 |        sym != NULL; | 
 |        sym = sym->hash_next) | 
 |     { | 
 |       /* Warning: the order of arguments to compare matters!  */ | 
 |       if (matches_name (sym->search_name (), name, NULL)) | 
 | 	break; | 
 |     } | 
 |  | 
 |   DICT_ITERATOR_CURRENT (iterator) = sym; | 
 |   return sym; | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iter_match_next_hashed (const lookup_name_info &name, | 
 | 			struct dict_iterator *iterator) | 
 | { | 
 |   const language_defn *lang = DICT_LANGUAGE (DICT_ITERATOR_DICT (iterator)); | 
 |   symbol_name_matcher_ftype *matches_name | 
 |     = lang->get_symbol_name_matcher (name); | 
 |   struct symbol *next; | 
 |  | 
 |   for (next = DICT_ITERATOR_CURRENT (iterator)->hash_next; | 
 |        next != NULL; | 
 |        next = next->hash_next) | 
 |     { | 
 |       if (matches_name (next->search_name (), name, NULL)) | 
 | 	break; | 
 |     } | 
 |  | 
 |   DICT_ITERATOR_CURRENT (iterator) = next; | 
 |  | 
 |   return next; | 
 | } | 
 |  | 
 | /* Insert SYM into DICT.  */ | 
 |  | 
 | static void | 
 | insert_symbol_hashed (struct dictionary *dict, | 
 | 		      struct symbol *sym) | 
 | { | 
 |   unsigned int hash_index; | 
 |   unsigned int hash; | 
 |   struct symbol **buckets = DICT_HASHED_BUCKETS (dict); | 
 |  | 
 |   /* We don't want to insert a symbol into a dictionary of a different | 
 |      language.  The two may not use the same hashing algorithm.  */ | 
 |   gdb_assert (sym->language () == DICT_LANGUAGE (dict)->la_language); | 
 |  | 
 |   hash = search_name_hash (sym->language (), sym->search_name ()); | 
 |   hash_index = hash % DICT_HASHED_NBUCKETS (dict); | 
 |   sym->hash_next = buckets[hash_index]; | 
 |   buckets[hash_index] = sym; | 
 | } | 
 |  | 
 | static int | 
 | size_hashed (const struct dictionary *dict) | 
 | { | 
 |   return DICT_HASHED_NBUCKETS (dict); | 
 | } | 
 |  | 
 | /* Functions only for DICT_HASHED_EXPANDABLE.  */ | 
 |  | 
 | static void | 
 | free_hashed_expandable (struct dictionary *dict) | 
 | { | 
 |   xfree (DICT_HASHED_BUCKETS (dict)); | 
 |   xfree (dict); | 
 | } | 
 |  | 
 | static void | 
 | add_symbol_hashed_expandable (struct dictionary *dict, | 
 | 			      struct symbol *sym) | 
 | { | 
 |   int nsyms = ++DICT_HASHED_EXPANDABLE_NSYMS (dict); | 
 |  | 
 |   if (DICT_HASHTABLE_SIZE (nsyms) > DICT_HASHED_NBUCKETS (dict)) | 
 |     expand_hashtable (dict); | 
 |  | 
 |   insert_symbol_hashed (dict, sym); | 
 |   DICT_HASHED_EXPANDABLE_NSYMS (dict) = nsyms; | 
 | } | 
 |  | 
 | static int | 
 | size_hashed_expandable (const struct dictionary *dict) | 
 | { | 
 |   return DICT_HASHED_EXPANDABLE_NSYMS (dict); | 
 | } | 
 |  | 
 | static void | 
 | expand_hashtable (struct dictionary *dict) | 
 | { | 
 |   int old_nbuckets = DICT_HASHED_NBUCKETS (dict); | 
 |   struct symbol **old_buckets = DICT_HASHED_BUCKETS (dict); | 
 |   int new_nbuckets = 2 * old_nbuckets + 1; | 
 |   struct symbol **new_buckets = XCNEWVEC (struct symbol *, new_nbuckets); | 
 |   int i; | 
 |  | 
 |   DICT_HASHED_NBUCKETS (dict) = new_nbuckets; | 
 |   DICT_HASHED_BUCKETS (dict) = new_buckets; | 
 |  | 
 |   for (i = 0; i < old_nbuckets; ++i) | 
 |     { | 
 |       struct symbol *sym, *next_sym; | 
 |  | 
 |       sym = old_buckets[i]; | 
 |       if (sym != NULL)  | 
 | 	{ | 
 | 	  for (next_sym = sym->hash_next; | 
 | 	       next_sym != NULL; | 
 | 	       next_sym = sym->hash_next) | 
 | 	    { | 
 | 	      insert_symbol_hashed (dict, sym); | 
 | 	      sym = next_sym; | 
 | 	    } | 
 |  | 
 | 	  insert_symbol_hashed (dict, sym); | 
 | 	} | 
 |     } | 
 |  | 
 |   xfree (old_buckets); | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | unsigned int | 
 | language_defn::search_name_hash (const char *string0) const | 
 | { | 
 |   /* The Ada-encoded version of a name P1.P2...Pn has either the form | 
 |      P1__P2__...Pn<suffix> or _ada_P1__P2__...Pn<suffix> (where the Pi | 
 |      are lower-cased identifiers).  The <suffix> (which can be empty) | 
 |      encodes additional information about the denoted entity.  This | 
 |      routine hashes such names to msymbol_hash_iw(Pn).  It actually | 
 |      does this for a superset of both valid Pi and of <suffix>, but  | 
 |      in other cases it simply returns msymbol_hash_iw(STRING0).  */ | 
 |  | 
 |   const char *string; | 
 |   unsigned int hash; | 
 |  | 
 |   string = string0; | 
 |   if (*string == '_') | 
 |     { | 
 |       if (startswith (string, "_ada_")) | 
 | 	string += 5; | 
 |       else | 
 | 	return msymbol_hash_iw (string0); | 
 |     } | 
 |  | 
 |   hash = 0; | 
 |   while (*string) | 
 |     { | 
 |       switch (*string) | 
 | 	{ | 
 | 	case '$': | 
 | 	case '.': | 
 | 	case 'X': | 
 | 	  if (string0 == string) | 
 | 	    return msymbol_hash_iw (string0); | 
 | 	  else | 
 | 	    return hash; | 
 | 	case ' ': | 
 | 	case '(': | 
 | 	  return msymbol_hash_iw (string0); | 
 | 	case '_': | 
 | 	  if (string[1] == '_' && string != string0) | 
 | 	    { | 
 | 	      int c = string[2]; | 
 |  | 
 | 	      if (c == 'B' && string[3] == '_') | 
 | 		{ | 
 | 		  for (string += 4; ISDIGIT (*string); ++string) | 
 | 		    ; | 
 | 		  continue; | 
 | 		} | 
 |  | 
 | 	      if ((c < 'a' || c > 'z') && c != 'O') | 
 | 		return hash; | 
 | 	      hash = 0; | 
 | 	      string += 2; | 
 | 	      continue; | 
 | 	    } | 
 | 	  break; | 
 | 	case 'T': | 
 | 	  /* Ignore "TKB" suffixes. | 
 |  | 
 | 	     These are used by Ada for subprograms implementing a task body. | 
 | 	     For instance for a task T inside package Pck, the name of the | 
 | 	     subprogram implementing T's body is `pck__tTKB'.  We need to | 
 | 	     ignore the "TKB" suffix because searches for this task body | 
 | 	     subprogram are going to be performed using `pck__t' (the encoded | 
 | 	     version of the natural name `pck.t').  */ | 
 | 	  if (strcmp (string, "TKB") == 0) | 
 | 	    return hash; | 
 | 	  break; | 
 | 	} | 
 |  | 
 |       hash = SYMBOL_HASH_NEXT (hash, *string); | 
 |       string += 1; | 
 |     } | 
 |   return hash; | 
 | } | 
 |  | 
 | /* Functions for DICT_LINEAR and DICT_LINEAR_EXPANDABLE.  */ | 
 |  | 
 | static struct symbol * | 
 | iterator_first_linear (const struct dictionary *dict, | 
 | 		       struct dict_iterator *iterator) | 
 | { | 
 |   DICT_ITERATOR_DICT (iterator) = dict; | 
 |   DICT_ITERATOR_INDEX (iterator) = 0; | 
 |   return DICT_LINEAR_NSYMS (dict) ? DICT_LINEAR_SYM (dict, 0) : NULL; | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iterator_next_linear (struct dict_iterator *iterator) | 
 | { | 
 |   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator); | 
 |  | 
 |   if (++DICT_ITERATOR_INDEX (iterator) >= DICT_LINEAR_NSYMS (dict)) | 
 |     return NULL; | 
 |   else | 
 |     return DICT_LINEAR_SYM (dict, DICT_ITERATOR_INDEX (iterator)); | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iter_match_first_linear (const struct dictionary *dict, | 
 | 			 const lookup_name_info &name, | 
 | 			 struct dict_iterator *iterator) | 
 | { | 
 |   DICT_ITERATOR_DICT (iterator) = dict; | 
 |   DICT_ITERATOR_INDEX (iterator) = -1; | 
 |  | 
 |   return iter_match_next_linear (name, iterator); | 
 | } | 
 |  | 
 | static struct symbol * | 
 | iter_match_next_linear (const lookup_name_info &name, | 
 | 			struct dict_iterator *iterator) | 
 | { | 
 |   const struct dictionary *dict = DICT_ITERATOR_DICT (iterator); | 
 |   const language_defn *lang = DICT_LANGUAGE (dict); | 
 |   symbol_name_matcher_ftype *matches_name | 
 |     = lang->get_symbol_name_matcher (name); | 
 |  | 
 |   int i, nsyms = DICT_LINEAR_NSYMS (dict); | 
 |   struct symbol *sym, *retval = NULL; | 
 |  | 
 |   for (i = DICT_ITERATOR_INDEX (iterator) + 1; i < nsyms; ++i) | 
 |     { | 
 |       sym = DICT_LINEAR_SYM (dict, i); | 
 |  | 
 |       if (matches_name (sym->search_name (), name, NULL)) | 
 | 	{ | 
 | 	  retval = sym; | 
 | 	  break; | 
 | 	} | 
 |     } | 
 |  | 
 |   DICT_ITERATOR_INDEX (iterator) = i; | 
 |    | 
 |   return retval; | 
 | } | 
 |  | 
 | static int | 
 | size_linear (const struct dictionary *dict) | 
 | { | 
 |   return DICT_LINEAR_NSYMS (dict); | 
 | } | 
 |  | 
 | /* Functions only for DICT_LINEAR_EXPANDABLE.  */ | 
 |  | 
 | static void | 
 | free_linear_expandable (struct dictionary *dict) | 
 | { | 
 |   xfree (DICT_LINEAR_SYMS (dict)); | 
 |   xfree (dict); | 
 | } | 
 |  | 
 |  | 
 | static void | 
 | add_symbol_linear_expandable (struct dictionary *dict, | 
 | 			      struct symbol *sym) | 
 | { | 
 |   int nsyms = ++DICT_LINEAR_NSYMS (dict); | 
 |  | 
 |   /* Do we have enough room?  If not, grow it.  */ | 
 |   if (nsyms > DICT_LINEAR_EXPANDABLE_CAPACITY (dict)) | 
 |     { | 
 |       DICT_LINEAR_EXPANDABLE_CAPACITY (dict) *= 2; | 
 |       DICT_LINEAR_SYMS (dict) | 
 | 	= XRESIZEVEC (struct symbol *, DICT_LINEAR_SYMS (dict), | 
 | 		      DICT_LINEAR_EXPANDABLE_CAPACITY (dict)); | 
 |     } | 
 |  | 
 |   DICT_LINEAR_SYM (dict, nsyms - 1) = sym; | 
 | } | 
 |  | 
 | /* Multi-language dictionary support.  */ | 
 |  | 
 | /* The structure describing a multi-language dictionary.  */ | 
 |  | 
 | struct multidictionary | 
 | { | 
 |   /* An array of dictionaries, one per language.  All dictionaries | 
 |      must be of the same type.  This should be free'd for expandable | 
 |      dictionary types.  */ | 
 |   struct dictionary **dictionaries; | 
 |  | 
 |   /* The number of language dictionaries currently allocated. | 
 |      Only used for expandable dictionaries.  */ | 
 |   unsigned short n_allocated_dictionaries; | 
 | }; | 
 |  | 
 | /* A hasher for enum language.  Injecting this into std is a convenience | 
 |    when using unordered_map with C++11.  */ | 
 |  | 
 | namespace std | 
 | { | 
 |   template<> struct hash<enum language> | 
 |   { | 
 |     typedef enum language argument_type; | 
 |     typedef std::size_t result_type; | 
 |  | 
 |     result_type operator() (const argument_type &l) const noexcept | 
 |     { | 
 |       return static_cast<result_type> (l); | 
 |     } | 
 |   }; | 
 | } /* namespace std */ | 
 |  | 
 | /* A helper function to collate symbols on the pending list by language.  */ | 
 |  | 
 | static std::unordered_map<enum language, std::vector<symbol *>> | 
 | collate_pending_symbols_by_language (const struct pending *symbol_list) | 
 | { | 
 |   std::unordered_map<enum language, std::vector<symbol *>> nsyms; | 
 |  | 
 |   for (const pending *list_counter = symbol_list; | 
 |        list_counter != nullptr; list_counter = list_counter->next) | 
 |     { | 
 |       for (int i = list_counter->nsyms - 1; i >= 0; --i) | 
 | 	{ | 
 | 	  enum language language = list_counter->symbol[i]->language (); | 
 | 	  nsyms[language].push_back (list_counter->symbol[i]); | 
 | 	} | 
 |     } | 
 |  | 
 |   return nsyms; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct multidictionary * | 
 | mdict_create_hashed (struct obstack *obstack, | 
 | 		     const struct pending *symbol_list) | 
 | { | 
 |   struct multidictionary *retval | 
 |     = XOBNEW (obstack, struct multidictionary); | 
 |   std::unordered_map<enum language, std::vector<symbol *>> nsyms | 
 |     = collate_pending_symbols_by_language (symbol_list); | 
 |  | 
 |   /* Loop over all languages and create/populate dictionaries.  */ | 
 |   retval->dictionaries | 
 |     = XOBNEWVEC (obstack, struct dictionary *, nsyms.size ()); | 
 |   retval->n_allocated_dictionaries = nsyms.size (); | 
 |  | 
 |   int idx = 0; | 
 |   for (const auto &pair : nsyms) | 
 |     { | 
 |       enum language language = pair.first; | 
 |       std::vector<symbol *> symlist = pair.second; | 
 |  | 
 |       retval->dictionaries[idx++] | 
 | 	= dict_create_hashed (obstack, language, symlist); | 
 |     } | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct multidictionary * | 
 | mdict_create_hashed_expandable (enum language language) | 
 | { | 
 |   struct multidictionary *retval = XNEW (struct multidictionary); | 
 |  | 
 |   /* We have no symbol list to populate, but we create an empty | 
 |      dictionary of the requested language to populate later.  */ | 
 |   retval->n_allocated_dictionaries = 1; | 
 |   retval->dictionaries = XNEW (struct dictionary *); | 
 |   retval->dictionaries[0] = dict_create_hashed_expandable (language); | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct multidictionary * | 
 | mdict_create_linear (struct obstack *obstack, | 
 | 		     const struct pending *symbol_list) | 
 | { | 
 |   struct multidictionary *retval | 
 |     = XOBNEW (obstack, struct multidictionary); | 
 |   std::unordered_map<enum language, std::vector<symbol *>> nsyms | 
 |     = collate_pending_symbols_by_language (symbol_list); | 
 |  | 
 |   /* Loop over all languages and create/populate dictionaries.  */ | 
 |   retval->dictionaries | 
 |     = XOBNEWVEC (obstack, struct dictionary *, nsyms.size ()); | 
 |   retval->n_allocated_dictionaries = nsyms.size (); | 
 |  | 
 |   int idx = 0; | 
 |   for (const auto &pair : nsyms) | 
 |     { | 
 |       enum language language = pair.first; | 
 |       std::vector<symbol *> symlist = pair.second; | 
 |  | 
 |       retval->dictionaries[idx++] | 
 | 	= dict_create_linear (obstack, language, symlist); | 
 |     } | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct multidictionary * | 
 | mdict_create_linear_expandable (enum language language) | 
 | { | 
 |   struct multidictionary *retval = XNEW (struct multidictionary); | 
 |  | 
 |   /* We have no symbol list to populate, but we create an empty | 
 |      dictionary to populate later.  */ | 
 |   retval->n_allocated_dictionaries = 1; | 
 |   retval->dictionaries = XNEW (struct dictionary *); | 
 |   retval->dictionaries[0] = dict_create_linear_expandable (language); | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | void | 
 | mdict_free (struct multidictionary *mdict) | 
 | { | 
 |   /* Grab the type of dictionary being used.  */ | 
 |   enum dict_type type = mdict->dictionaries[0]->vector->type; | 
 |  | 
 |   /* Loop over all dictionaries and free them.  */ | 
 |   for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx) | 
 |     dict_free (mdict->dictionaries[idx]); | 
 |  | 
 |   /* Free the dictionary list, if needed.  */ | 
 |   switch (type) | 
 |     { | 
 |     case DICT_HASHED: | 
 |     case DICT_LINEAR: | 
 |       /* Memory was allocated on an obstack when created.  */ | 
 |       break; | 
 |  | 
 |     case DICT_HASHED_EXPANDABLE: | 
 |     case DICT_LINEAR_EXPANDABLE: | 
 |       xfree (mdict->dictionaries); | 
 |       break; | 
 |     } | 
 | } | 
 |  | 
 | /* Helper function to find the dictionary associated with LANGUAGE | 
 |    or NULL if there is no dictionary of that language.  */ | 
 |  | 
 | static struct dictionary * | 
 | find_language_dictionary (const struct multidictionary *mdict, | 
 | 			  enum language language) | 
 | { | 
 |   for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx) | 
 |     { | 
 |       if (DICT_LANGUAGE (mdict->dictionaries[idx])->la_language == language) | 
 | 	return mdict->dictionaries[idx]; | 
 |     } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | /* Create a new language dictionary for LANGUAGE and add it to the | 
 |    multidictionary MDICT's list of dictionaries.  If MDICT is not | 
 |    based on expandable dictionaries, this function throws an | 
 |    internal error.  */ | 
 |  | 
 | static struct dictionary * | 
 | create_new_language_dictionary (struct multidictionary *mdict, | 
 | 				enum language language) | 
 | { | 
 |   struct dictionary *retval = nullptr; | 
 |  | 
 |   /* We use the first dictionary entry to decide what create function | 
 |      to call.  Not optimal but sufficient.  */ | 
 |   gdb_assert (mdict->dictionaries[0] != nullptr); | 
 |   switch (mdict->dictionaries[0]->vector->type) | 
 |     { | 
 |     case DICT_HASHED: | 
 |     case DICT_LINEAR: | 
 |       internal_error (__FILE__, __LINE__, | 
 | 		      _("create_new_language_dictionary: attempted to expand " | 
 | 			"non-expandable multidictionary")); | 
 |  | 
 |     case DICT_HASHED_EXPANDABLE: | 
 |       retval = dict_create_hashed_expandable (language); | 
 |       break; | 
 |  | 
 |     case DICT_LINEAR_EXPANDABLE: | 
 |       retval = dict_create_linear_expandable (language); | 
 |       break; | 
 |     } | 
 |  | 
 |   /* Grow the dictionary vector and save the new dictionary.  */ | 
 |   mdict->dictionaries | 
 |     = (struct dictionary **) xrealloc (mdict->dictionaries, | 
 | 				       (++mdict->n_allocated_dictionaries | 
 | 					* sizeof (struct dictionary *))); | 
 |   mdict->dictionaries[mdict->n_allocated_dictionaries - 1] = retval; | 
 |  | 
 |   return retval; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | void | 
 | mdict_add_symbol (struct multidictionary *mdict, struct symbol *sym) | 
 | { | 
 |   struct dictionary *dict | 
 |     = find_language_dictionary (mdict, sym->language ()); | 
 |  | 
 |   if (dict == nullptr) | 
 |     { | 
 |       /* SYM is of a new language that we haven't previously seen. | 
 | 	 Create a new dictionary for it.  */ | 
 |       dict = create_new_language_dictionary (mdict, sym->language ()); | 
 |     } | 
 |  | 
 |   dict_add_symbol (dict, sym); | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | void | 
 | mdict_add_pending (struct multidictionary *mdict, | 
 | 		   const struct pending *symbol_list) | 
 | { | 
 |   std::unordered_map<enum language, std::vector<symbol *>> nsyms | 
 |     = collate_pending_symbols_by_language (symbol_list); | 
 |  | 
 |   for (const auto &pair : nsyms) | 
 |     { | 
 |       enum language language = pair.first; | 
 |       std::vector<symbol *> symlist = pair.second; | 
 |       struct dictionary *dict = find_language_dictionary (mdict, language); | 
 |  | 
 |       if (dict == nullptr) | 
 | 	{ | 
 | 	  /* The language was not previously seen.  Create a new dictionary | 
 | 	     for it.  */ | 
 | 	  dict = create_new_language_dictionary (mdict, language); | 
 | 	} | 
 |  | 
 |       dict_add_pending (dict, symlist); | 
 |     } | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct symbol * | 
 | mdict_iterator_first (const multidictionary *mdict, | 
 | 		      struct mdict_iterator *miterator) | 
 | { | 
 |   miterator->mdict = mdict; | 
 |   miterator->current_idx = 0; | 
 |  | 
 |   for (unsigned short idx = miterator->current_idx; | 
 |        idx < mdict->n_allocated_dictionaries; ++idx) | 
 |     { | 
 |       struct symbol *result | 
 | 	= dict_iterator_first (mdict->dictionaries[idx], &miterator->iterator); | 
 |  | 
 |       if (result != nullptr) | 
 | 	{ | 
 | 	  miterator->current_idx = idx; | 
 | 	  return result; | 
 | 	} | 
 |     } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct symbol * | 
 | mdict_iterator_next (struct mdict_iterator *miterator) | 
 | { | 
 |   struct symbol *result = dict_iterator_next (&miterator->iterator); | 
 |  | 
 |   if (result != nullptr) | 
 |     return result; | 
 |  | 
 |   /* The current dictionary had no matches -- move to the next | 
 |      dictionary, if any.  */ | 
 |   for (unsigned short idx = ++miterator->current_idx; | 
 |        idx < miterator->mdict->n_allocated_dictionaries; ++idx) | 
 |     { | 
 |       result | 
 | 	= dict_iterator_first (miterator->mdict->dictionaries[idx], | 
 | 			       &miterator->iterator); | 
 |       if (result != nullptr) | 
 | 	{ | 
 | 	  miterator->current_idx = idx; | 
 | 	  return result; | 
 | 	} | 
 |     } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct symbol * | 
 | mdict_iter_match_first (const struct multidictionary *mdict, | 
 | 			const lookup_name_info &name, | 
 | 			struct mdict_iterator *miterator) | 
 | { | 
 |   miterator->mdict = mdict; | 
 |   miterator->current_idx = 0; | 
 |  | 
 |   for (unsigned short idx = miterator->current_idx; | 
 |        idx < mdict->n_allocated_dictionaries; ++idx) | 
 |     { | 
 |       struct symbol *result | 
 | 	= dict_iter_match_first (mdict->dictionaries[idx], name, | 
 | 				 &miterator->iterator); | 
 |  | 
 |       if (result != nullptr) | 
 | 	return result; | 
 |     } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | struct symbol * | 
 | mdict_iter_match_next (const lookup_name_info &name, | 
 | 		       struct mdict_iterator *miterator) | 
 | { | 
 |   /* Search the current dictionary.  */ | 
 |   struct symbol *result = dict_iter_match_next (name, &miterator->iterator); | 
 |  | 
 |   if (result != nullptr) | 
 |     return result; | 
 |  | 
 |   /* The current dictionary had no matches -- move to the next | 
 |      dictionary, if any.  */ | 
 |   for (unsigned short idx = ++miterator->current_idx; | 
 |        idx < miterator->mdict->n_allocated_dictionaries; ++idx) | 
 |     { | 
 |       result | 
 | 	= dict_iter_match_first (miterator->mdict->dictionaries[idx], | 
 | 				 name, &miterator->iterator); | 
 |       if (result != nullptr) | 
 | 	{ | 
 | 	  miterator->current_idx = idx; | 
 | 	  return result; | 
 | 	} | 
 |     } | 
 |  | 
 |   return nullptr; | 
 | } | 
 |  | 
 | /* See dictionary.h.  */ | 
 |  | 
 | int | 
 | mdict_size (const struct multidictionary *mdict) | 
 | { | 
 |   int size = 0; | 
 |  | 
 |   for (unsigned short idx = 0; idx < mdict->n_allocated_dictionaries; ++idx) | 
 |     size += dict_size (mdict->dictionaries[idx]); | 
 |  | 
 |   return size; | 
 | } |