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