/**************************************************************** * * * Copyright 2001, 2012 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ /* Uniform Hash Implementation Hash table code supports the following data types as key: a) int4 b) int8 c) UINTPTR_T d) object code e) variable name. f) local process address (supported via define using either int4 or int8 as type Using pre-processor following four C files will expand five sets of routines. a) hashtab_int4.c b) hashtab_int8.c c) hashtab_addr.c d) hashtab_mname.c e) hashtab_objcode.c Restrictions : We assumed that no user of hash needs to add "key, value" pair where both are null. We examined that GT.M does not need to have such cases. We can add 0 as valid data for int4 and int8, however, it must always have non-zero value. We can add 0 length string as "key" in objcode, however, it must always have non-zero length value. We know object code cannot be 0 length, so even object source (key) is of 0 length, we are fine. GT.M cannot have 0 length mname. So "key" for mname cannot be 0 length. (If we want to remove above restriction, an extra field is needed for HT_ENT) */ #include "mdef.h" #include "gtm_malloc.h" /* For raise_gtmmemory_error() definition */ #include "bit_set.h" #include "gtmio.h" #include "have_crit.h" LITREF int ht_sizes[]; #define DEBUGHASHTABLE 0 #if defined(INT4_HASH) # define HT_KEY_T uint4 # define HT_ENT ht_ent_int4 # define HASH_TABLE hash_table_int4 # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_INT4(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_INT4 # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT4 # define HTENT_VALID HTENT_VALID_INT4 # define INIT_HASHTAB init_hashtab_int4 # define INIT_HASHTAB_INTL init_hashtab_intl_int4 # define EXPAND_HASHTAB expand_hashtab_int4 # define ADD_HASHTAB add_hashtab_int4 # define ADD_HASHTAB_INTL add_hashtab_intl_int4 # define LOOKUP_HASHTAB lookup_hashtab_int4 # define DELETE_HASHTAB_ENT delete_hashtab_ent_int4 # define DELETE_HASHTAB delete_hashtab_int4 # define FREE_HASHTAB free_hashtab_int4 # define REINITIALIZE_HASHTAB reinitialize_hashtab_int4 # define COMPACT_HASHTAB compact_hashtab_int4 #elif defined(INT8_HASH) # define HT_KEY_T gtm_uint64_t # define HT_ENT ht_ent_int8 # define HASH_TABLE hash_table_int8 # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_INT8(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_INT8 # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_INT8 # define HTENT_VALID HTENT_VALID_INT8 # define INIT_HASHTAB init_hashtab_int8 # define INIT_HASHTAB_INTL init_hashtab_intl_int8 # define EXPAND_HASHTAB expand_hashtab_int8 # define ADD_HASHTAB add_hashtab_int8 # define ADD_HASHTAB_INTL add_hashtab_intl_int8 # define LOOKUP_HASHTAB lookup_hashtab_int8 # define DELETE_HASHTAB_ENT delete_hashtab_ent_int8 # define DELETE_HASHTAB delete_hashtab_int8 # define FREE_HASHTAB free_hashtab_int8 # define REINITIALIZE_HASHTAB reinitialize_hashtab_int8 # define COMPACT_HASHTAB compact_hashtab_int8 #elif defined(ADDR_HASH) # define HT_KEY_T char * # define HT_ENT ht_ent_addr # define HASH_TABLE hash_table_addr # define HTENT_KEY_MATCH(tabent, hkey) ((tabent)->key == (*hkey)) # define FIND_HASH(hkey, hash) COMPUTE_HASH_ADDR(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_ADDR # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_ADDR # define HTENT_VALID HTENT_VALID_ADDR # define INIT_HASHTAB init_hashtab_addr # define INIT_HASHTAB_INTL init_hashtab_intl_addr # define EXPAND_HASHTAB expand_hashtab_addr # define ADD_HASHTAB add_hashtab_addr # define ADD_HASHTAB_INTL add_hashtab_intl_addr # define LOOKUP_HASHTAB lookup_hashtab_addr # define DELETE_HASHTAB_ENT delete_hashtab_ent_addr # define DELETE_HASHTAB delete_hashtab_addr # define FREE_HASHTAB free_hashtab_addr # define REINITIALIZE_HASHTAB reinitialize_hashtab_addr # define COMPACT_HASHTAB compact_hashtab_addr #elif defined(MNAME_HASH) # define HT_KEY_T mname_entry # define HT_ENT ht_ent_mname # define HASH_TABLE hash_table_mname # define HTENT_KEY_MATCH(tabent, hkey) \ ( ((tabent)->key.hash_code == (hkey)->hash_code) \ && ((tabent)->key.var_name.len == (hkey)->var_name.len) \ && (0 == memcmp((tabent)->key.var_name.addr, (hkey)->var_name.addr, (hkey)->var_name.len)) \ ) # define FIND_HASH(hkey, hash) {assert((hkey)->hash_code); hash = (hkey)->hash_code;} /* Note: FIND_HASH for mname does not compute hash_code. Callers must make sure it is already computed. * FIND_HASH for objcode or int4 or int8 computes hash code * for every function call of add or lookup or delete. */ # define HTENT_EMPTY HTENT_EMPTY_MNAME # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_MNAME # define HTENT_VALID HTENT_VALID_MNAME # define INIT_HASHTAB init_hashtab_mname # define INIT_HASHTAB_INTL init_hashtab_intl_mname # define EXPAND_HASHTAB expand_hashtab_mname # define ADD_HASHTAB add_hashtab_mname # define ADD_HASHTAB_INTL add_hashtab_intl_mname # define LOOKUP_HASHTAB lookup_hashtab_mname # define DELETE_HASHTAB_ENT delete_hashtab_ent_mname # define DELETE_HASHTAB delete_hashtab_mnamen # define FREE_HASHTAB free_hashtab_mname # define REINITIALIZE_HASHTAB reinitialize_hashtab_mname # define COMPACT_HASHTAB compact_hashtab_mname #elif defined(STRING_HASH) # define HT_KEY_T stringkey # define HT_ENT ht_ent_str # define HASH_TABLE hash_table_str # define HTENT_KEY_MATCH(tabent, hkey) \ (((tabent)->key.hash_code == (hkey)->hash_code) \ && ((tabent)->key.str.len == (hkey)->str.len) \ && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ ) # define FIND_HASH(hkey, hash) hash = (hkey)->hash_code /* Note: FIND_HASH for str does not compute hash_code. Callers must make sure it is already computed */ # define HTENT_EMPTY HTENT_EMPTY_STR # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_STR # define HTENT_VALID HTENT_VALID_STR # define INIT_HASHTAB init_hashtab_str # define INIT_HASHTAB_INTL init_hashtab_intl_str # define EXPAND_HASHTAB expand_hashtab_str # define ADD_HASHTAB add_hashtab_str # define ADD_HASHTAB_INTL add_hashtab_intl_str # define LOOKUP_HASHTAB lookup_hashtab_str # define DELETE_HASHTAB_ENT delete_hashtab_ent_str # define DELETE_HASHTAB delete_hashtab_str # define FREE_HASHTAB free_hashtab_str # define REINITIALIZE_HASHTAB reinitialize_hashtab_str # define COMPACT_HASHTAB compact_hashtab_str #elif defined (OBJCODE_HASH) # define HT_KEY_T icode_str # define HT_ENT ht_ent_objcode # define HASH_TABLE hash_table_objcode # define HTENT_KEY_MATCH(tabent, hkey) \ (((tabent)->key.code == (hkey)->code) \ && ((tabent)->key.str.len == (hkey)->str.len) \ && (0 == memcmp((tabent)->key.str.addr, (hkey)->str.addr, (hkey)->str.len)) \ ) # define FIND_HASH(hkey, hash) COMPUTE_HASH_OBJCODE(hkey, hash) # define HTENT_EMPTY HTENT_EMPTY_OBJCODE # define HTENT_MARK_EMPTY HTENT_MARK_EMPTY_OBJCODE # define HTENT_VALID HTENT_VALID_OBJCODE # define INIT_HASHTAB init_hashtab_objcode # define INIT_HASHTAB_INTL init_hashtab_intl_objcode # define EXPAND_HASHTAB expand_hashtab_objcode # define ADD_HASHTAB add_hashtab_objcode # define ADD_HASHTAB_INTL add_hashtab_intl_objcode # define LOOKUP_HASHTAB lookup_hashtab_objcode # define DELETE_HASHTAB_ENT delete_hashtab_ent_objcode # define DELETE_HASHTAB delete_hashtab_objcode # define FREE_HASHTAB free_hashtab_objcode # define REINITIALIZE_HASHTAB reinitialize_hashtab_objcode # define COMPACT_HASHTAB compact_hashtab_objcode #else #error undefined hash #endif #if DEBUGHASHTABLE /* Debug FPRINTF with pre and post requisite flushing of appropriate streams */ #define DBGHASHTAB(x) {flush_pio(); FPRINTF x; FFLUSH(stderr);} #else #define DBGHASHTAB(x) #endif /* We use DOUBLE HASHING algorithm. After first collision we calculate * rehash factor (rhfact) that is a function of the hash value (even though for * correctness purposes any number from 1 to prime-1 should be fine) to * avoid primary and secondary clustering. * The SET_REHASH_INDEX is actually equivalent to ht_index = (ht_index + rhfact) % prime; */ #define SET_REHASH_FACTOR(rhfact, hash, prime) (rhfact) = (1 + ((hash) % ((prime) - 1))) #define SET_REHASH_INDEX(ht_index, rhfact, prime) \ assert((rhfact) < (prime)); \ assert((ht_index) < (prime)); \ (ht_index) += (rhfact); \ if ((ht_index) >= (prime)) \ (ht_index) -= (prime); #define RETURN_IF_ADDED(table, tabent, hkey, value) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ { \ if (NULL != first_del_ent) \ tabent = first_del_ent; \ INSERT_HTENT(table, tabent, hkey, value); \ return TRUE; \ } \ if (!HTENT_MARK_DELETED(tabent)) \ { \ if (HTENT_KEY_MATCH(tabent, hkey)) \ return FALSE; /* valid and matched */ \ } else if (NULL == first_del_ent) \ first_del_ent = tabent; \ } #define RETURN_IF_LOOKUP_DONE(tabent, hkey) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ return NULL; \ if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ return tabent; /* valid and matched */ \ } #define DELETE_HTENT(table, tabent) \ { \ uint4 entry_num; \ sm_uc_ptr_t ptr; \ \ entry_num = (uint4)((tabent) - (table)->base); \ /* Compute offset into bitmap for this entry */ \ ptr = (table)->entry_passed_thru + (entry_num / BITS_PER_UCHAR); \ if ((1 << (entry_num & 7)) & *ptr) \ { \ (tabent)->value = HT_DELETED_ENTRY; \ (table)->del_count++; \ } else \ HTENT_MARK_EMPTY(tabent); \ (table)->count--; \ assert(((table)->count + (table)->del_count) <= (table)->size); \ } #define RETURN_IF_DELETED(table, tabent, hkey) \ { \ void *dummy; \ if (HTENT_EMPTY(tabent, void, dummy)) \ return FALSE; \ if (!HTENT_MARK_DELETED(tabent) && HTENT_KEY_MATCH(tabent, hkey)) \ { \ DELETE_HTENT(table, tabent); \ if (COMPACT_NEEDED(table)) \ COMPACT_HASHTAB(table); \ return TRUE; \ } \ } #define HT_FIELDS_COMMON_INIT(table) \ table->exp_trigger_size = (double)table->size * HT_LOAD_FACTOR / 100.0; \ table->cmp_trigger_size = (double)table->size * HT_REHASH_FACTOR / 100.0; \ table->count = table->del_count = 0; /* Prototypes */ void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table); STATICFNDCL void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table); void EXPAND_HASHTAB(HASH_TABLE *table, int minsize); boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr); STATICFNDCL boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, boolean_t changing_table_size); void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent); boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key); void FREE_HASHTAB(HASH_TABLE *table); void REINITIALIZE_HASHTAB(HASH_TABLE *table); void COMPACT_HASHTAB(HASH_TABLE *table); error_def(ERR_HTOFLOW); error_def(ERR_HTSHRINKFAIL); /* This is used by external callers to initially setup the hash table. */ void INIT_HASHTAB(HASH_TABLE *table, int minsize, boolean_t dont_compact, boolean_t dont_keep_spare_table) { int index; for (index = 0, table->initial_size = ht_sizes[index]; table->initial_size && table->initial_size < minsize; index++) table->initial_size = ht_sizes[index]; table->initial_size = table->initial_size ? table->initial_size : minsize; table->dont_compact = dont_compact; table->dont_keep_spare_table = dont_keep_spare_table; table->defer_base_release = FALSE; INIT_HASHTAB_INTL(table, minsize, NULL); } /* This routine initializes hash table. It must be called once before hashing can be used. Note that the ht_sizes array is defined in mtables.c. A NULL old_table pointer means that the table is being setup for the first time. */ STATICFNDEF void INIT_HASHTAB_INTL(HASH_TABLE *table, int minsize, HASH_TABLE *old_table) { unsigned int cur_ht_size, prior_size; int index; boolean_t dont_keep_spare_table; DBGHASHTAB((stderr, "INIT_HASHTAB:table(%lx) minsize(%d) old_table(%lx)\n", table, minsize, old_table)); /* If this is the first time the hash table is being initialized (old_table == NULL), then look up the * actual hash table size in ht_sizes based on the requested size (minsize). * * We dont want the hash table to shrink too fast so if we are changing the size of an existing hash table: * 1) if the requested size is not smaller than half of the previous size: * a) pick the actual size from ht_sizes based on the requested size. * 2) if the requested size is smaller than half of the previous size: * b) pick the previous entry (from the previous size (old_table->size) in ht_sizes. */ if ((NULL == old_table) || (minsize > (old_table->size / 2))) { for (index = 0, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < minsize; index++) cur_ht_size = ht_sizes[index]; } else /* don't shrink too fast ! */ { prior_size = ht_sizes[0]; for (index = 1, cur_ht_size = ht_sizes[index]; cur_ht_size && cur_ht_size < old_table->size; index++) { cur_ht_size = ht_sizes[index]; prior_size = ht_sizes[index-1]; } cur_ht_size = prior_size; cur_ht_size = (cur_ht_size > old_table->initial_size) ? cur_ht_size : old_table->initial_size; } if (cur_ht_size) { DBGHASHTAB((stderr, "INIT_HASHTAB:table size will be (%d) for table(%lx)\n", cur_ht_size, old_table?old_table:table)); table->base = NULL; table->spare_base = NULL; table->spare_base_size = 0; /* a fresh new hash table */ /* If this is is an initialization from a caller outside of the hash table implementation then * old_table == NULL since there is no previous hash table. In this case the external versions of * INIT_HASHTAB will setup table with values for dont_compact and dont_keep_spare_table. Otherwise, * we can use them from the old_table. */ dont_keep_spare_table = old_table ? old_table->dont_keep_spare_table:table->dont_keep_spare_table; if (!dont_keep_spare_table) { DBGHASHTAB((stderr, "INIT_HASHTAB: old_table(%lx)\n", old_table)); if (NULL != old_table) { DBGHASHTAB((stderr, "INIT_HASHTAB: cur_ht_size(%d), spare_base_size(%d)\n", cur_ht_size, old_table->spare_base_size)); if (old_table->spare_base_size == cur_ht_size) { /* We can use the spare table since it is the size we would have malloc'd */ table->base = old_table->spare_base; DBGHASHTAB((stderr, "INIT_HASHTAB: use spare table: base(%lx)\n", table->base)); /* We no longer have a spare */ old_table->spare_base = NULL; old_table->spare_base_size = 0; } else /* no luck on the reuse thing */ if (NULL != old_table->spare_base) /* so free it if it exists */ { DBGHASHTAB((stderr, "INIT_HASHTAB: table(%lx): free spare_base(%lx)\n", old_table, old_table->spare_base)); free(old_table->spare_base); old_table->spare_base = NULL; old_table->spare_base_size = 0; } } } if (NULL == table->base) { /* Let's make sure we have a HT_ENT table. We are here either thru dont_keep_spare_table, old_table == NULL, or wrong-sized spare */ table->base = (void *)malloc((cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); DBGHASHTAB((stderr, "INIT_HASHTAB: malloc a new table: table(%lx) base(%lx)\n", old_table?old_table:table, table->base)); } memset((char *)table->base, 0, (cur_ht_size * SIZEOF(HT_ENT)) + ROUND_UP(cur_ht_size, BITS_PER_UCHAR)); table->size = cur_ht_size; if (NULL != old_table) { table->initial_size = old_table->initial_size; table->dont_compact = old_table->dont_compact; table->dont_keep_spare_table = old_table->dont_keep_spare_table; table->defer_base_release = old_table->defer_base_release; } table->top = table->base + cur_ht_size; table->entry_passed_thru = (sm_uc_ptr_t) table->top; DBGHASHTAB((stderr, "INIT_HASHTAB: entry_passed_thru points to (%lx)\n", table->entry_passed_thru)); HT_FIELDS_COMMON_INIT(table); } else { DBGHASHTAB((stderr, "INIT_HASHTAB:HTOFLOW: minsize(%d) cur_ht_size(%d)\n", minsize, cur_ht_size)); send_msg(VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); rts_error(VARLSTCNT(3) ERR_HTOFLOW, 1, minsize); } } /* Description: Expand the hash table with at least minsize. This can do either expansion or compaction, which depends on old table size and minsize passed. It creates a new table and move old element to new table. It deallocate old table entries */ void EXPAND_HASHTAB(HASH_TABLE *table, int minsize) { HASH_TABLE newtable, temptab; HT_ENT *tabent, *topent, *dummy; boolean_t added; void *htval; CONDITION_HANDLER(hashtab_rehash_ch); ESTABLISH(hashtab_rehash_ch); DBGHASHTAB((stderr, "EXPAND_HASHTAB:ENTER: table: table(%lx) base (%lx), spare_base(%lx), spare_base_size(%d), \n", table, table->base, table->spare_base, table->spare_base_size)); /* The next line keeps the HP-UX Itanium compiler in pro happy. This initialization is done is INIT_HASHTAB_INTL* * but this line is placed here to appease the compiler. */ newtable.dont_keep_spare_table = table->dont_keep_spare_table; INIT_HASHTAB_INTL(&newtable, minsize, table); REVERT; if (0 < table->count) /* if no active entries then nothing to move */ for (tabent = table->base, topent = table->top; tabent < topent; tabent++) { if (HTENT_VALID(tabent, void, htval)) { /* Place location of new ht_ent entry into value location of existing ht entry */ added = ADD_HASHTAB_INTL(&newtable, &tabent->key, htval, (HT_ENT **)&tabent->value, TRUE); assert(added); } } if (!table->defer_base_release && table->dont_keep_spare_table) { DBGHASHTAB((stderr, "EXPAND_HASHTAB:free base (%lx) \n", table->base)); free(table->base); /* Deallocate old table entries */ } if (!table->dont_keep_spare_table) { temptab.spare_base = table->base; /* let's keep a spare in case we just have to clear the DELETED entries */ temptab.spare_base_size = table->size; } *table = newtable; if (table->dont_keep_spare_table) { table->spare_base = NULL; table->spare_base_size = 0; } else { table->spare_base = temptab.spare_base; /* let's keep a spare in case we just have to clear the DELETED entries */ table->spare_base_size = temptab.spare_base_size; } DBGHASHTAB((stderr, "EXPAND_HASHTAB:EXIT: table: table(%lx ) base (%lx), spare_base(%lx), spare_base_size(%d) \n", table, table->base, table->spare_base, table->spare_base_size)); } /* Description: Adds a key and corresponding value in hash table. If key is already present, return false. If key is not present, it adds the entry and returns true. As a side-effect tabent points to the matched entry or added entry */ /* This flavor is used by external caller (outside of the hash table implementation */ boolean_t ADD_HASHTAB(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr) { return ADD_HASHTAB_INTL(table, key, value, tabentptr, FALSE); } /* This flavor is used by internal callers, for example when adding entries during a change of hash table size. */ STATICFNDEF boolean_t ADD_HASHTAB_INTL(HASH_TABLE *table, HT_KEY_T *key, void *value, HT_ENT **tabentptr, boolean_t changing_table_size) { #ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; #else uint4 hash, ht_index, save_ht_index, prime, rhfact; #endif /* INT8_HASH */ HT_ENT *oldbase, *first_del_ent, *tabbase; if (!changing_table_size && (table->count >= table->exp_trigger_size)) { oldbase = table->base; EXPAND_HASHTAB(table, table->size + 1); if (oldbase == table->base) /* expansion failed */ { if (table->exp_trigger_size >= table->size) /* Note this error routine will use the memory error parameters recorded when the memory error was first raised by EXPAND_HASHTAB() above so the error will appear as if it had occurred during that expansion attempt. */ raise_gtmmemory_error(); table->exp_trigger_size = table->size; } } first_del_ent = NULL; tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = (int) (hash % prime); *tabentptr = tabbase + ht_index; RETURN_IF_ADDED(table, *tabentptr, key, value); /* We are here because collision happened. Do collision resolution */ # ifdef INT8_HASH assert(MAXUINT4 > ht_index); # endif bit_set(ht_index, table->entry_passed_thru); save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { *tabentptr = tabbase + ht_index; RETURN_IF_ADDED(table, *tabentptr, key, value); # ifdef INT8_HASH assert(MAXUINT4 > ht_index); # endif bit_set(ht_index, table->entry_passed_thru); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); /* All entries either deleted or used. No empty frame found */ if (NULL != first_del_ent) { /* There was a deleted one we could use - reuse the deleted frame */ *tabentptr = first_del_ent; INSERT_HTENT(table, *tabentptr, key, value); return TRUE; } GTMASSERT; return FALSE; /* to prevent warnings */ } /* * Returns pointer to the value corresponding to key, if found. * Otherwise, it returns null. */ void *LOOKUP_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) { # ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; # else uint4 hash, ht_index, save_ht_index, prime, rhfact; # endif /* INT8_HASH */ HT_ENT *tabent, *tabbase; tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = hash % prime; tabent = tabbase + ht_index; RETURN_IF_LOOKUP_DONE(tabent, key); /* We are here because collision happened. Do collision resolution */ save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { tabent = tabbase + ht_index; RETURN_IF_LOOKUP_DONE(tabent, key); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); return (void *)NULL; } /* Description: Deletes hash table entry from hash table (whether it was active or not). The function version is for callers outside of the hash table implementation. */ void DELETE_HASHTAB_ENT(HASH_TABLE *table, HT_ENT *tabent) { DELETE_HTENT(table, tabent); } /* * Returns TRUE if * 1) key is found and deleted successfully * or * 2) already key was marked deleted. * Otherwise, it returns FALSE * Deletion is done by marking value to HT_DELETED_ENTRY. * If there are too many deleted entry, we call expand_hashtab() to do the * compaction eliminating entries marked HT_DELETED_ENTRY * Compaction will save memory and also cause LOOKUP_HASHTAB to run faster. */ boolean_t DELETE_HASHTAB(HASH_TABLE *table, HT_KEY_T *key) { # ifdef INT8_HASH gtm_uint64_t hash, ht_index, save_ht_index, prime, rhfact; # else uint4 hash, ht_index, save_ht_index, prime, rhfact; # endif /* INT8_HASH */ HT_ENT *tabent, *tabbase; tabbase = &table->base[0]; prime = table->size; FIND_HASH(key, hash); ht_index = hash % prime; tabent = tabbase + ht_index; RETURN_IF_DELETED(table, tabent, key); /* We are here because collision happened. Do collision resolution */ save_ht_index = ht_index; SET_REHASH_FACTOR(rhfact, hash, prime); SET_REHASH_INDEX(ht_index, rhfact, prime); do { tabent = tabbase + ht_index; RETURN_IF_DELETED(table, tabent, key); SET_REHASH_INDEX(ht_index, rhfact, prime); } while(ht_index != save_ht_index); return FALSE; } /* * free memory occupied by hash table. */ void FREE_HASHTAB(HASH_TABLE *table) { if (table->base) { DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): base (%lx)\n", table, table->base)); free(table->base); } table->base = NULL; if (table->spare_base) { DBGHASHTAB((stderr, "FREE_HASHTAB:free table(%lx): spare_base (%lx)\n", table, table->spare_base)); free(table->spare_base); } table->spare_base = NULL; table->spare_base_size = 0; } /* * Returns TRUE, if key found and deleted successfully or already deleted. */ void REINITIALIZE_HASHTAB(HASH_TABLE *table) { memset((char *)table->base, 0, (table->size * SIZEOF(HT_ENT)) + ((table->size / BITS_PER_UCHAR) + 1)); HT_FIELDS_COMMON_INIT(table); } /* * Compact hashtable removing entries marked deleted. Note that this is necessary because * of the search algorithm in ADD_HASHTAB which needs to find if a key exists before it can * add a new entry. It keeps searching until it either finds the key, finds an empty (never * used) entry or until it searches the entire table. So we need to replentish the supply of * never used nodes. */ void COMPACT_HASHTAB(HASH_TABLE *table) { HT_ENT *oldbase; DBGHASHTAB((stderr, "COMPACT_HASHTAB: table(%lx)\n", table)); if (!table->dont_compact) { oldbase = (table)->base; EXPAND_HASHTAB(table, HT_REHASH_TABLE_SIZE(table)); if (oldbase == (table)->base) /* rehash failed */ { /* We will continue but performance will likely suffer */ send_msg(VARLSTCNT(1) ERR_HTSHRINKFAIL); (table)->cmp_trigger_size = (table)->size; } } }