Home | History | Annotate | Download | only in src
      1 /*---------------------------------------------------------------------------*
      2  *  phashtable.c  *
      3  *                                                                           *
      4  *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
      5  *                                                                           *
      6  *  Licensed under the Apache License, Version 2.0 (the 'License');          *
      7  *  you may not use this file except in compliance with the License.         *
      8  *                                                                           *
      9  *  You may obtain a copy of the License at                                  *
     10  *      http://www.apache.org/licenses/LICENSE-2.0                           *
     11  *                                                                           *
     12  *  Unless required by applicable law or agreed to in writing, software      *
     13  *  distributed under the License is distributed on an 'AS IS' BASIS,        *
     14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
     15  *  See the License for the specific language governing permissions and      *
     16  *  limitations under the License.                                           *
     17  *                                                                           *
     18  *---------------------------------------------------------------------------*/
     19 
     20 
     21 
     22 
     23 
     24 
     25 #include <string.h>
     26 
     27 #include "phashtable.h"
     28 #include "plog.h"
     29 #include "pmemory.h"
     30 #include "pstdio.h"
     31 
     32 //extern int strcmp(const char * s1,  const char * s2);
     33 
     34 #define ALLOC_SIZE 16
     35 
     36 struct PHashTableEntry_t
     37 {
     38   const void *key;
     39   const void *value;
     40   PHashTable *table;
     41   unsigned int idx;
     42   PHashTableEntry *next;
     43   PHashTableEntry *prev;
     44   unsigned int hashCode;
     45 };
     46 
     47 typedef struct PHashTableEntryBlock_t
     48 {
     49   PHashTableEntry entries[ALLOC_SIZE];
     50   struct PHashTableEntryBlock_t *next;
     51 }
     52 PHashTableEntryBlock;
     53 
     54 
     55 struct PHashTable_t
     56 {
     57   PHashTableArgs args;
     58   const LCHAR *memoryTag;
     59   unsigned int size;
     60   float maxLoadFactor;
     61   PHashTableEntry **entries;
     62   unsigned int threshold;
     63   PHashTableEntry *freeList;
     64   PHashTableEntryBlock *entryBlock;
     65 };
     66 
     67 #include "pcrc.h"
     68 
     69 static unsigned int hashString(const void *key)
     70 {
     71   return ~pcrcComputeString(key);
     72 }
     73 
     74 ESR_ReturnCode PHashTableCreate(PHashTableArgs *args,
     75                                 const LCHAR *memTag,
     76                                 PHashTable **table)
     77 {
     78   PHashTable *tmp;
     79   unsigned int i;
     80 
     81   if (table == NULL ||
     82       (args != NULL && args->maxLoadFactor <= 0.0))
     83     return ESR_INVALID_ARGUMENT;
     84 
     85 
     86   if ((tmp = NEW(PHashTable, memTag)) == NULL)
     87     return ESR_OUT_OF_MEMORY;
     88 
     89   if (args == NULL)
     90   {
     91     tmp->args.capacity = PHASH_TABLE_DEFAULT_CAPACITY;
     92     tmp->args.maxLoadFactor = PHASH_TABLE_DEFAULT_MAX_LOAD_FACTOR;
     93     tmp->args.hashFunction = PHASH_TABLE_DEFAULT_HASH_FUNCTION;
     94     tmp->args.compFunction = PHASH_TABLE_DEFAULT_COMP_FUNCTION;
     95   }
     96   else
     97   {
     98     memcpy(&tmp->args, args, sizeof(PHashTableArgs));
     99   }
    100   if (tmp->args.hashFunction == PHASH_TABLE_DEFAULT_HASH_FUNCTION)
    101     tmp->args.hashFunction = hashString;
    102 
    103   if (tmp->args.compFunction == PHASH_TABLE_DEFAULT_COMP_FUNCTION)
    104     tmp->args.compFunction = LSTRCMP;
    105 
    106   tmp->entries = NEW_ARRAY(PHashTableEntry *, tmp->args.capacity, memTag);
    107 
    108   if (tmp->entries == NULL)
    109   {
    110     FREE(tmp);
    111     return ESR_OUT_OF_MEMORY;
    112   }
    113 
    114   for (i = tmp->args.capacity; i > 0;)
    115   {
    116     tmp->entries[--i] = NULL;
    117   }
    118 
    119   tmp->memoryTag = memTag;
    120   tmp->size = 0;
    121   tmp->threshold = (unsigned int)(tmp->args.capacity * tmp->args.maxLoadFactor);
    122   tmp->freeList = NULL;
    123   tmp->entryBlock = NULL;
    124 
    125   *table = tmp;
    126   return ESR_SUCCESS;
    127 }
    128 
    129 ESR_ReturnCode PHashTableDestroy(PHashTable *table)
    130 {
    131   PHashTableEntryBlock *tmp, *block;
    132 
    133   if (table == NULL)
    134     return ESR_INVALID_ARGUMENT;
    135 
    136   block = table->entryBlock;
    137   while (block != NULL)
    138   {
    139     tmp = block->next;
    140     FREE(block);
    141     block = tmp;
    142   }
    143 
    144   FREE(table->entries);
    145   FREE(table);
    146   return ESR_SUCCESS;
    147 }
    148 
    149 ESR_ReturnCode PHashTableGetSize(PHashTable *table,
    150                                  size_t *size)
    151 {
    152   if (table == NULL || size == NULL)
    153     return ESR_INVALID_ARGUMENT;
    154 
    155   *size = table->size;
    156   return ESR_SUCCESS;
    157 }
    158 
    159 static PHashTableEntry *getEntry(PHashTable *table,
    160                                  const void *key,
    161                                  unsigned int hashCode,
    162                                  unsigned int idx)
    163 {
    164   PHashTableEntry *entry = table->entries[idx];
    165 
    166   if (key == NULL)
    167   {
    168     while (entry != NULL)
    169     {
    170       if (entry->key == NULL)
    171         return entry;
    172 
    173       entry = entry->next;
    174     }
    175   }
    176   else
    177   {
    178     while (entry != NULL)
    179     {
    180       if (entry->hashCode == hashCode && table->args.compFunction(key, entry->key) == 0)
    181         return entry;
    182 
    183       entry = entry->next;
    184     }
    185   }
    186 
    187   return NULL;
    188 }
    189 
    190 static void removeEntry(PHashTableEntry *entry)
    191 {
    192   if (entry->prev == NULL)
    193     entry->table->entries[entry->idx] = entry->next;
    194   else
    195     entry->prev->next = entry->next;
    196 
    197   if (entry->next != NULL)
    198     entry->next->prev = entry->prev;
    199 
    200   entry->table->size--;
    201 
    202   entry->next = entry->table->freeList;
    203   entry->table->freeList = entry;
    204   /* clean up entry for re-use. */
    205   entry->key = entry->value = NULL;
    206 }
    207 
    208 ESR_ReturnCode PHashTableGetValue(PHashTable *table, const void *key, void **value)
    209 {
    210   PHashTableEntry *entry;
    211   unsigned int hashCode;
    212   unsigned int idx;
    213 
    214   if (table == NULL || value == NULL)
    215     return ESR_INVALID_ARGUMENT;
    216 
    217   hashCode = table->args.hashFunction(key);
    218   idx = hashCode % table->args.capacity;
    219   if ((entry = getEntry(table, key, hashCode, idx)) != NULL)
    220   {
    221     *value = (void *) entry->value;
    222     return ESR_SUCCESS;
    223   }
    224   else
    225   {
    226     *value = NULL;
    227     return ESR_NO_MATCH_ERROR;
    228   }
    229 }
    230 
    231 ESR_ReturnCode PHashTableContainsKey(PHashTable *table, const void *key, ESR_BOOL* exists)
    232 {
    233   ESR_ReturnCode rc;
    234   PHashTableEntry* entry;
    235 
    236   if (table == NULL || exists == NULL)
    237   {
    238     rc = ESR_INVALID_ARGUMENT;
    239     PLogError(ESR_rc2str(rc));
    240     goto CLEANUP;
    241   }
    242   rc = PHashTableGetEntry(table, key, &entry);
    243   if (rc == ESR_SUCCESS)
    244     *exists = ESR_TRUE;
    245   else if (rc == ESR_NO_MATCH_ERROR)
    246     *exists = ESR_FALSE;
    247   else
    248     goto CLEANUP;
    249 
    250   return ESR_SUCCESS;
    251 CLEANUP:
    252   return rc;
    253 }
    254 
    255 ESR_ReturnCode PHashTableGetEntry(PHashTable *table, const void *key, PHashTableEntry **entry)
    256 {
    257   unsigned int hashCode;
    258   unsigned int idx;
    259   PHashTableEntry* result;
    260 
    261   if (table == NULL || entry == NULL)
    262     return ESR_INVALID_ARGUMENT;
    263 
    264   hashCode = table->args.hashFunction(key);
    265   idx = hashCode % table->args.capacity;
    266 
    267   result = getEntry(table, key, hashCode, idx);
    268   if (result == NULL)
    269     return ESR_NO_MATCH_ERROR;
    270   *entry = result;
    271   return ESR_SUCCESS;
    272 }
    273 
    274 static ESR_ReturnCode PHashTableRehash(PHashTable *table)
    275 {
    276   unsigned int i, idx;
    277   unsigned int oldCapacity = table->args.capacity;
    278   unsigned int newCapacity = ((oldCapacity << 1) | 0x01);
    279   PHashTableEntry *entry, *tmp, *next;
    280 
    281   PHashTableEntry **newEntries =
    282     (PHashTableEntry **)
    283     REALLOC(table->entries,
    284             sizeof(PHashTableEntry *) * newCapacity);
    285 
    286   if (newEntries == NULL)
    287     return ESR_OUT_OF_MEMORY;
    288 
    289   table->entries = newEntries;
    290   table->args.capacity = newCapacity;
    291   table->threshold = (unsigned int)(newCapacity * table->args.maxLoadFactor);
    292 
    293   for (i = oldCapacity; i < newCapacity; ++i)
    294   {
    295     table->entries[i] = NULL;
    296   }
    297 
    298   for (i = 0; i < oldCapacity; i++)
    299   {
    300     for (entry = table->entries[i]; entry != NULL;)
    301     {
    302       idx = entry->hashCode % newCapacity;
    303       if (idx != i)
    304       {
    305         /* Need to change location. */
    306         entry->idx = idx;
    307 
    308         next = entry->next;
    309 
    310         if (entry->prev != NULL)
    311           entry->prev->next = next;
    312         else
    313           table->entries[i] = next;
    314 
    315         if (next != NULL)
    316           next->prev = entry->prev;
    317 
    318         tmp = table->entries[idx];
    319         entry->next = tmp;
    320         entry->prev = NULL;
    321         if (tmp != NULL)
    322           tmp->prev = entry;
    323         table->entries[idx] = entry;
    324 
    325         entry = next;
    326       }
    327       else
    328       {
    329         /* Already in the right slot. */
    330         entry = entry->next;
    331       }
    332     }
    333   }
    334   return ESR_SUCCESS;
    335 }
    336 
    337 
    338 ESR_ReturnCode PHashTablePutValue(PHashTable *table,
    339                                   const void *key,
    340                                   const void *value,
    341                                   void **oldValue)
    342 {
    343   ESR_ReturnCode rc = ESR_SUCCESS;
    344   unsigned int hashCode, idx;
    345   PHashTableEntry *entry;
    346 
    347   if (table == NULL) return ESR_INVALID_ARGUMENT;
    348   hashCode = table->args.hashFunction(key);
    349   idx = hashCode % table->args.capacity;
    350 
    351   entry = getEntry(table, key, hashCode, idx);
    352   if (entry != NULL)
    353   {
    354     if (oldValue != NULL) *oldValue = (void *) entry->value;
    355     entry->value = value;
    356     return ESR_SUCCESS;
    357   }
    358 
    359   /* If we get here, we need to add a new entry.  But first, verify if we need
    360      to rehash. */
    361   if (table->size >= table->threshold)
    362   {
    363     if ((rc = PHashTableRehash(table)) != ESR_SUCCESS)
    364       return rc;
    365     idx = hashCode % table->args.capacity;
    366   }
    367 
    368   if (table->freeList == NULL)
    369   {
    370     /* Allocate a new block and put all entries on the free list. */
    371     PHashTableEntryBlock *block;
    372     int i;
    373 
    374     block = NEW(PHashTableEntryBlock, table->memoryTag);
    375     if (block == NULL)
    376       return ESR_OUT_OF_MEMORY;
    377 
    378     block->next = table->entryBlock;
    379     table->entryBlock = block;
    380 
    381     for (i = 0; i < ALLOC_SIZE - 1; ++i)
    382     {
    383       block->entries[i].next = &block->entries[i+1];
    384     }
    385     block->entries[ALLOC_SIZE-1].next = NULL;
    386 
    387     /* do not see any bug in following code. But on the VxWorks with optimization option -O3
    388       it produces wrong result: block->entries[0].next is correct but block->entries[1].next = NULL
    389       it causes lot of memory wastes.
    390     for (i = 0, entry = block->entries; i < ALLOC_SIZE - 1; ++i, ++entry)
    391     {
    392       entry->next = entry+1;
    393     }
    394     entry->next = table->freeList;
    395     */
    396 
    397     table->freeList = block->entries;
    398   }
    399 
    400   /* Get an entry from the freeList. */
    401   entry = table->freeList;
    402   table->freeList = entry->next;
    403 
    404   /* Initialize entry data structure. */
    405   entry->table = table;
    406   entry->idx = idx;
    407   entry->key = key;
    408   entry->value = value;
    409   entry->hashCode = hashCode;
    410   entry->next = table->entries[idx];
    411   entry->prev = NULL;
    412   if (entry->next != NULL)
    413     entry->next->prev = entry;
    414   table->entries[idx] = entry;
    415   table->size++;
    416 
    417   if (oldValue != NULL) *oldValue = NULL;
    418   return ESR_SUCCESS;
    419 }
    420 
    421 
    422 ESR_ReturnCode PHashTableRemoveValue(PHashTable *table,
    423                                      const void *key,
    424                                      void **oldValue)
    425 {
    426   unsigned int hashCode, idx;
    427   PHashTableEntry *entry;
    428   ESR_ReturnCode rc;
    429 
    430   if (table == NULL)
    431   {
    432     rc = ESR_INVALID_ARGUMENT;
    433     PLogError(ESR_rc2str(rc));
    434     goto CLEANUP;
    435   }
    436 
    437   hashCode = table->args.hashFunction(key);
    438   idx = hashCode % table->args.capacity;
    439 
    440   entry = getEntry(table, key, hashCode, idx);
    441   if (entry != NULL)
    442   {
    443     if (oldValue != NULL)
    444       *oldValue = (void*) entry->value;
    445     removeEntry(entry);
    446   }
    447   else
    448   {
    449     if (oldValue != NULL)
    450       *oldValue = NULL;
    451   }
    452   return ESR_SUCCESS;
    453 CLEANUP:
    454   return rc;
    455 }
    456 
    457 ESR_ReturnCode PHashTableEntryGetKeyValue(PHashTableEntry *entry,
    458     void **key,
    459     void **value)
    460 {
    461   if (entry == NULL) return ESR_INVALID_ARGUMENT;
    462 
    463   if (key != NULL) *key = (void *) entry->key;
    464   if (value != NULL) *value = (void *) entry->value;
    465   return ESR_SUCCESS;
    466 }
    467 
    468 
    469 /**
    470  * Sets the value associated with this entry.
    471  * @param entry The hashtable entry.
    472  * @param value The value to associate with the entry.
    473  * @param oldvalue If this pointer is non-NULL, it will be set to the previous value associated with this entry.
    474  **/
    475 ESR_ReturnCode PHashTableEntrySetValue(PHashTableEntry *entry,
    476                                        const void *value,
    477                                        void **oldValue)
    478 {
    479   if (entry == NULL) return ESR_INVALID_ARGUMENT;
    480 
    481   if (oldValue != NULL) *oldValue = (void *) entry->value;
    482   entry->value = value;
    483   return ESR_SUCCESS;
    484 }
    485 
    486 
    487 /**
    488  * Removes the entry from its hash table.
    489  *
    490  * @param entry The hashtable entry.
    491  **/
    492 ESR_ReturnCode PHashTableEntryRemove(PHashTableEntry *entry)
    493 {
    494   if (entry == NULL)
    495     return ESR_INVALID_ARGUMENT;
    496 
    497   removeEntry(entry);
    498 
    499   return ESR_SUCCESS;
    500 }
    501 
    502 static PHashTableEntry* iteratorAdvance(PHashTable *table, PHashTableEntry *entry)
    503 {
    504   unsigned int idx;
    505 
    506   if (entry != NULL)
    507   {
    508     idx = entry->idx;
    509     entry = entry->next;
    510     if (entry == NULL)
    511     {
    512       while (++idx < table->args.capacity)
    513       {
    514         if (table->entries[idx] != NULL)
    515         {
    516           entry = table->entries[idx];
    517           break;
    518         }
    519       }
    520     }
    521   }
    522   else
    523   {
    524     for (idx = 0; idx < table->args.capacity; ++idx)
    525     {
    526       if (table->entries[idx] != NULL)
    527       {
    528         entry = table->entries[idx];
    529         break;
    530       }
    531     }
    532   }
    533   return entry;
    534 }
    535 
    536 
    537 ESR_ReturnCode PHashTableEntryGetFirst(PHashTable *table, PHashTableEntry **entry)
    538 {
    539   if (table == NULL || entry == NULL)
    540     return ESR_INVALID_ARGUMENT;
    541 
    542   *entry = iteratorAdvance(table, NULL);
    543   return ESR_SUCCESS;
    544 }
    545 
    546 /**
    547  * Iterates on the next key and value.  Returns a NULL key when at the end of the hash table.
    548  *
    549  * @param iter The iterator on which the iteration is performed.
    550  * @param key Returns the key associated with the entry, cannot be NULL.
    551  * @param value If non-NULL, returns the value associated with the entry.
    552  **/
    553 ESR_ReturnCode PHashTableEntryAdvance(PHashTableEntry **entry)
    554 {
    555   if (entry == NULL || *entry == NULL)
    556     return ESR_INVALID_ARGUMENT;
    557 
    558   *entry = iteratorAdvance((*entry)->table, *entry);
    559   return ESR_SUCCESS;
    560 }
    561