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
36struct 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
47typedef struct PHashTableEntryBlock_t
48{
49  PHashTableEntry entries[ALLOC_SIZE];
50  struct PHashTableEntryBlock_t *next;
51}
52PHashTableEntryBlock;
53
54
55struct 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
69static unsigned int hashString(const void *key)
70{
71  return ~pcrcComputeString(key);
72}
73
74ESR_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
129ESR_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
149ESR_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
159static 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
190static 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
208ESR_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
231ESR_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;
251CLEANUP:
252  return rc;
253}
254
255ESR_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
274static 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
338ESR_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
422ESR_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;
453CLEANUP:
454  return rc;
455}
456
457ESR_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 **/
475ESR_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 **/
492ESR_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
502static 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
537ESR_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 **/
553ESR_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