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