• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
hashString(const void * key)69 static unsigned int hashString(const void *key)
70 {
71   return ~pcrcComputeString(key);
72 }
73 
PHashTableCreate(PHashTableArgs * args,const LCHAR * memTag,PHashTable ** table)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 
PHashTableDestroy(PHashTable * table)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 
PHashTableGetSize(PHashTable * table,size_t * size)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 
getEntry(PHashTable * table,const void * key,unsigned int hashCode,unsigned int idx)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 
removeEntry(PHashTableEntry * entry)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 
PHashTableGetValue(PHashTable * table,const void * key,void ** value)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 
PHashTableContainsKey(PHashTable * table,const void * key,ESR_BOOL * exists)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 
PHashTableGetEntry(PHashTable * table,const void * key,PHashTableEntry ** entry)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 
PHashTableRehash(PHashTable * table)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 
PHashTablePutValue(PHashTable * table,const void * key,const void * value,void ** oldValue)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 
PHashTableRemoveValue(PHashTable * table,const void * key,void ** oldValue)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 
PHashTableEntryGetKeyValue(PHashTableEntry * entry,void ** key,void ** value)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  **/
PHashTableEntrySetValue(PHashTableEntry * entry,const void * value,void ** oldValue)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  **/
PHashTableEntryRemove(PHashTableEntry * entry)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 
iteratorAdvance(PHashTable * table,PHashTableEntry * entry)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 
PHashTableEntryGetFirst(PHashTable * table,PHashTableEntry ** entry)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  **/
PHashTableEntryAdvance(PHashTableEntry ** entry)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