💾 Archived View for gmi.noulin.net › gitRepositories › hashtable › file › hashtable.h.gmi captured on 2024-07-09 at 02:19:37. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
hashtable.h (54232B)
1 #pragma once 2 3 /** 4 * \file 5 * 6 * Chained hash table 7 * 8 * NOTE: The macros in this file are for creating hash table functions wrapping the macros. 9 * I don't advise using these directly because of the risk of memory leaks and 10 * wrong computations. 11 * The leaks and wrong computations happen because the parameters to the macros 12 * are evaluated multiple times. 13 * Example: 14 * hashTbAdd(ahashtable, strdup("akey"), value) causes a leak because strdup is 15 * evaluted twice. 16 * 17 * NOTE: This package doesn't provide the hash functions, install the hashfunctions package to 18 * get some hash functions. 19 * 20 * This hash table has 2 single linked lists in each bucket, 21 * the performance is still good with a load factor of 2. 22 * 23 * The maximum number of buckets is 2^32 and and the maximum number (key, value) nodes 2^32 ( > 40 GB RAM) 24 * 25 * The size (bucket count) increases in power of 2. 26 * 27 * The status of the macros is saved (hashtableVar).res and is set to true when the operation is success and 28 * false when the operation is failure. 29 * 30 * Use hashmap when there are more than 50 elements in the dictionary 31 * 32 * Hash table memory usage (for key type char* and value type u32): 33 * Empty hashmap size: 2744 bytes 34 * Size depending on element count (with load factor 2): 184 + 42 * count 35 * 36 * It takes in average 4 times more memory than smallDict 37 * hashmap = 4 * smallDict RAM 38 * 39 * Lookup time: 40 * fnv hash get: 440ns (300 000 000 keys in hash table - kaby lake CPU) 41 * 42 * This hash table has better performance than smallDict with 50 elements 43 * 44 * 45 * The types handled by the hash table (key, value and hash types) 46 * are defined with hashTbT or hashNodeT and hashtableT. 47 * 48 * The hashTb* are short and convenient macros for the hashtable* macros. 49 * With the hashTb* macros there is no need to specify the hash, cmp and free functions. 50 * 51 * Before using the hashTb* macros, define HASHFUNC CMPFUNC and FREEFUNC. 52 * 53 * *Node Macros: 54 * The *Node macros handle the nodes in hash table and save time by not running the hash function. 55 * These macros are useful when the hash function is expensive (like siphash). 56 * 57 * The hash table has to be defined with hashNodeT and hashtableT to be able to use the *Node macros. 58 * The *Node macros use the context type defined by hashNodeT, the context type is named: 59 * 60 * context_typeName 61 * 62 * The key and value in the node is accessed with: 63 * node.key 64 * node.value 65 * 66 * HASHFUNC is a function with prototype: 67 * hashType hashFunc(keyType key); 68 * 69 * CMPFUNC compares keys and is a function with prototype: 70 * int compare(keyType k1, keyType k2); 71 * Return 0 when the keys are equal, 1 otherwise 72 * 73 * FREEFUNC frees key and value is a function with prototype: 74 * void freeKV(keyType* key, valueType* value); 75 * NOTE: the free function gets a pointer to the key and value which is useful when iterating on the nodes 76 * (for example, when keyType is int, set key to -1, iterate on the aHashnode dArray and ignore the node 77 * when the key is -1) 78 * 79 * See the 'node macros' section below for details on usage scenarios of the *Node macros. 80 * 81 * With the iterator, it is possible to loop on all nodes. Nodes can be deleted and added while the iteration 82 * is ongoing. 83 * 84 * The iterator macros are: hashTbIterStart, hashTbIterNext, hashTbIterKey, hashTbIterValue. 85 * 86 * To start the iteration call hashTbIterStart, check (name).res for status and 87 * to get the next nodes call hashTbIterNext until (name).res is false. 88 * 89 * hashTbIterKey and hashTbIterValue provide key and value for current node in iteration. 90 * 91 * Example: 92 * hashTbT(hasht, char*, u32, u32); 93 * 94 * #define HASHFUNC strHashFNV1A 95 * #define CMPFUNC strcmp 96 * #define FREEFUNC freeNode 97 * 98 * void freeNode(char **key, u32* UNUSED value) { 99 * //logI("free key %x\n", key); 100 * free(key); 101 * } 102 * 103 * hasht h; 104 * hashtableInit(h, 128, 2, 1); 105 * 106 * hashTbAdd(h, "a key", 12); 107 * 108 * u32 result = 0; 109 * hashTbGet(h, "a key", result); 110 * 111 * // iteration on all elements 112 * hashTbIterStart(h) 113 * while (h.res) { 114 * put(hashTbIterKey(h)); 115 * hashTbIterNext(h); 116 * } 117 * 118 * hashTbDel(h, "a key"); 119 * 120 * hashTbFree(h); 121 */ 122 123 124 #include "libsheepyObject.h" 125 126 /** 127 * function declarations for tailored hash table 128 * 129 * - define hash table types 130 * - declare prototypes 131 * 132 * this macro is placed most of the time in a header file 133 * 134 * the Hash table type is defined as: 135 * 'typeName't 136 * 137 * the node in the hash table is defined as: 138 * 'typeName'Nodet 139 * 140 * the node context is defined as: 141 * context_'typeName'Nodet 142 * 143 * the function names are: 144 * function'suffix' 145 * 146 * the scope parameter is the function scope and can be 'empty' for global functions, 'static' for local functions, ... 147 * 148 * the declared functions are: 149 * init, empty, free, add, addOrFind, find, get, getKey, del, seed, addOrFindNode 150 * setKeyNode, getNode, updateNodeContext, unlink, freeUnlinked, delNode 151 * 152 * 153 * RETURN VALUES: 154 * add, setKeyNode, updateNodeContext: true success, false failure 155 * addOrFind, addOrFindNode: non NULL success, NULL failure. 156 * *isnew is set to true when the returned value pointer is in a new node. 157 * find, getNode, unlink: non NULL success, NULL failure 158 * 159 * the ctx pointer parameters must be allocated before the function calls 160 * the isnew pointer must point to an existing bool variable 161 * 162 * 163 * Example: 164 * hashTbDef(, m3232, M32, u32, u32, u32); // declare hash table type m3232t with u32 keys, u32 values and u32 hash 165 * 166 * m3232Nodet *node; 167 * context_m3232Nodet ctx; 168 * bool isnew; 169 * 170 * m3232t h; // declares a hash table 171 * 172 */ 173 #define hashTbDef(scope, typeName, suffix, keyType, valueType, hashType) \ 174 hashNodeT(typeName##Nodet, keyType, valueType, hashType);\ 175 hashtableT(typeName##t, typeName##Nodet);\ 176 scope void init##suffix(typeName##t *map);\ 177 scope void empty##suffix(typeName##t *map);\ 178 scope void free##suffix(typeName##t *map);\ 179 scope bool add##suffix(typeName##t *map, keyType key, valueType value);\ 180 scope valueType* addOrFind##suffix(typeName##t *map, keyType key, bool *isnew);\ 181 scope valueType* find##suffix(typeName##t *map, keyType key);\ 182 scope valueType get##suffix(typeName##t *map, keyType key);\ 183 scope keyType getKey##suffix(typeName##t *map, keyType key);\ 184 scope void del##suffix(typeName##t *map, keyType key);\ 185 scope u8* seed##suffix(typeName##t *map);\ 186 scope typeName##Nodet* addOrFindNode##suffix(typeName##t *map, keyType key, bool *isnew, context_##typeName##Nodet *ctx);\ 187 scope bool setKeyNode##suffix(typeName##t *map, context_##typeName##Nodet *ctx);\ 188 scope typeName##Nodet *getNode##suffix(typeName##t *map, keyType key, context_##typeName##Nodet *ctx);\ 189 scope bool updateNodeContext##suffix(typeName##t *map, context_##typeName##Nodet *ctx);\ 190 scope typeName##Nodet* unlink##suffix(typeName##t *map, keyType key);\ 191 scope void freeUnlinked##suffix(typeName##t *map, typeName##Nodet *node);\ 192 scope void delNode##suffix(typeName##t *map, context_##typeName##Nodet *ctx) 193 194 /** 195 * function implementations 196 * 197 * this macros implements the hash table functions: 198 * init, empty, free, add, addOrFind, find, get, getKey, del, seed, addOrFindNode 199 * setKeyNode, getNode, updateNodeContext, unlink, freeUnlinked, delNode 200 * 201 * this macro is placed most of the time in a source file 202 * 203 * the hash function, compare function and free function must be implemented before this macro 204 * HASHFUNC, CMPFUNC and FREEFUNC must be defined before this macro 205 * 206 * Example: 207 * hashTbDef(, m3232, M32, u32, u32, u32); // declare hash table type m3232t with u32 keys, u32 values and u32 hash 208 * 209 * int cmp(u32 k1, u32 k2) { 210 * return k1 == k2 ? 0 : 1; 211 * } 212 * 213 * void freeM(u32* k, u32* v) { 214 * } 215 * 216 * #define HASHFUNC u32Hash // hash function from the hashfunctions spm package 217 * #define CMPFUNC cmp 218 * #define FREEFUNC freeM 219 * 220 * hashTbFunctions(, m3232, M32, u32, u32); // implements global scope m3232t functions: *M32() 221 * 222 * #undef HASHFUNC 223 * #undef CMPFUNC 224 * #undef FREEFUNC 225 * 226 * m3232t h; // declares a hash table 227 * 228 * initM32(&h); 229 * addM32(&h, 1, 1); 230 * 231 */ 232 #define hashTbFunctions(scope, typeName, suffix, keyType, valueType)\ 233 scope void init##suffix(typeName##t *map) {\ 234 hashTbInit(*map, 64, 2, 1);\ 235 }\ 236 scope void empty##suffix(typeName##t *map) {\ 237 hashTbEmpty(*map);\ 238 }\ 239 scope void free##suffix(typeName##t *map) {\ 240 hashTbFree(*map);\ 241 }\ 242 scope bool add##suffix(typeName##t *map, keyType key, valueType value) {\ 243 hashTbAdd(*map, key, value);\ 244 return map->res;\ 245 }\ 246 scope valueType* addOrFind##suffix(typeName##t *map, keyType key, bool *isnew) {\ 247 valueType* r = NULL;\ 248 hashTbAddOrFind(*map, key, r, *isnew);\ 249 return r;\ 250 }\ 251 scope valueType* find##suffix(typeName##t *map, keyType key) {\ 252 valueType *r = NULL;\ 253 hashTbFind(*map, key, r);\ 254 return r;\ 255 }\ 256 scope valueType get##suffix(typeName##t *map, keyType key) {\ 257 valueType r;\ 258 hashTbGet(*map, key, r);\ 259 return r;\ 260 }\ 261 scope keyType getKey##suffix(typeName##t *map, keyType key) {\ 262 keyType r;\ 263 hashTbGetKey(*map, key, r);\ 264 return r;\ 265 }\ 266 scope void del##suffix(typeName##t *map, keyType key) {\ 267 hashTbDel(*map, key);\ 268 }\ 269 scope u8* seed##suffix(typeName##t *map) {\ 270 u8 *r = hashTbSeed(*map);\ 271 return r;\ 272 }\ 273 scope typeName##Nodet* addOrFindNode##suffix(typeName##t *map, keyType key, bool *isnew, context_##typeName##Nodet *ctx) {\ 274 typeName##Nodet* r;\ 275 hashTbAddOrFindNode(*map, key, r, *ctx, *isnew);\ 276 return r;\ 277 }\ 278 scope bool setKeyNode##suffix(typeName##t *map, context_##typeName##Nodet *ctx) {\ 279 hashTbSetKeyNode(*map, *ctx);\ 280 return map->res;\ 281 }\ 282 scope typeName##Nodet *getNode##suffix(typeName##t *map, keyType key, context_##typeName##Nodet *ctx) {\ 283 typeName##Nodet *r = NULL;\ 284 hashTbGetNode(*map , key, r, *ctx);\ 285 return r;\ 286 }\ 287 scope bool updateNodeContext##suffix(typeName##t *map, context_##typeName##Nodet *ctx) {\ 288 hashTbUpdateNodeContext(*map, *ctx);\ 289 return map->res;\ 290 }\ 291 scope typeName##Nodet* unlink##suffix(typeName##t *map, keyType key) {\ 292 typeName##Nodet *r = NULL;\ 293 hashTbUnlinkNode(*map, key, r);\ 294 return r;\ 295 }\ 296 scope void freeUnlinked##suffix(typeName##t *map, typeName##Nodet *node) {\ 297 hashTbFreeUnlinkedNode(*map, node);\ 298 }\ 299 scope void delNode##suffix(typeName##t *map, context_##typeName##Nodet *ctx) {\ 300 hashTbDelNode(*map, *ctx);\ 301 } 302 303 304 /** initialize hash table */ 305 #define hashTbInit(HT, SIZE, NUMERATOR, DENOMINATOR) hashtableInit(HT, SIZE, NUMERATOR, DENOMINATOR) 306 307 /** remove all key,value pairs in hash table */ 308 #define hashTbEmpty(HT) hashtableEmpty(HT, FREEFUNC) 309 310 /** free hash table */ 311 #define hashTbFree(HT) hashtableFree(HT, FREEFUNC) 312 313 /** insert new (key,value), fail when the key already exists */ 314 #define hashTbAdd(HT, K, V) hashtableAdd(HT, K, V, HASHFUNC, CMPFUNC) 315 316 /** insert/find key, return value address in VPOINTER and ISNEW */ 317 #define hashTbAddOrFind(HT, K, VPOINTER, ISNEW) hashtableAddOrFind(HT, K, VPOINTER, ISNEW, HASHFUNC, CMPFUNC) 318 319 /** find key, return value address in VPOINTER */ 320 #define hashTbFind(HT, K, VPOINTER) hashtableFind(HT, K, VPOINTER, HASHFUNC, CMPFUNC) 321 322 /** get value */ 323 #define hashTbGet(HT, K, R) hashtableGet(HT, K, R, HASHFUNC, CMPFUNC) 324 325 /** get key */ 326 #define hashTbGetKey(HT, K, R) hashtableGetKey(HT, K, R, HASHFUNC, CMPFUNC) 327 328 /** remove key, value */ 329 #define hashTbDel(HT, K) hashtableDel(HT, K, HASHFUNC, CMPFUNC, FREEFUNC) 330 331 /** get random seed address */ 332 #define hashTbSeed(HT) hashtableSeed(HT) 333 334 /** create or reuse an exists node, return NODEPOINTER, NODECONTEXT and ISNEW */ 335 #define hashTbAddOrFindNode(HT, K, NODEPOINTER, NODECONTEXT, ISNEW) hashtableAddOrFindNode(HT, K, NODEPOINTER, NODECONTEXT, ISNEW, HASHFUNC, CMPFUNC) 336 337 /** change the key in a node */ 338 #define hashTbSetKeyNode(HT, NODECONTEXT) hashtableSetKeyNode(HT, NODECONTEXT, HASHFUNC, CMPFUNC) 339 340 /** get a node, return NODE and NODECONTEXT */ 341 #define hashTbGetNode(HT, K, NODE, NODECONTEXT) hashtableGetNode(HT, K, NODE, NODECONTEXT, HASHFUNC, CMPFUNC) 342 343 /** key in node */ 344 #define hashTbNodeKey(NODE) (NODE).key 345 346 /** value in node */ 347 #define hashTbNodeValue(NODE) (NODE).value 348 349 /** update node context after a rehash, return NODECONTEXT */ 350 #define hashTbUpdateNodeContext(HT, NODECONTEXT) hashtableUpdateNodeContext(HT, NODECONTEXT, HASHFUNC, CMPFUNC) 351 352 /** return a node and remove it from the hashtable, return NODE */ 353 #define hashTbUnlinkNode(HT, K, NODE) hashtableUnlinkNode(HT, K, NODE, HASHFUNC, CMPFUNC) 354 355 /** free an unlinked node */ 356 #define hashTbFreeUnlinkedNode(HT, NODE) hashtableFreeUnlinkedNode(HT, NODE, FREEFUNC) 357 358 /** delete a node */ 359 #define hashTbDelNode(HT, NODECONTEXT) hashtableDelNode(HT, NODECONTEXT, FREEFUNC) 360 361 /** start iteration on all nodes */ 362 #define hashTbIterStart(name) hashtableIterStart(name) 363 364 /** move to next node in iteration */ 365 #define hashTbIterNext(name) hashtableIterNext(name) 366 367 /** key of node in iteration */ 368 #define hashTbIterKey(name) hashtableIterKey(name) 369 370 /** value of node in iteration */ 371 #define hashTbIterValue(name) hashtableIterValue(name) 372 373 /** 374 * node definition for bucket single linked lists 375 * 376 * keyType, valueType and hashType can any valid type. 377 * 378 * context_typeName is defined and is needed for *Node macros 379 * 380 * In general, hashType is an uint between 32bits and 256bits 381 */ 382 #define hashNodeT(typeName, keyType, valueType, hashType)\ 383 typedef struct typeName typeName;\ 384 struct typeName {\ 385 keyType key;\ 386 typeName *next;\ 387 hashType hash;\ 388 u32 index;\ 389 valueType value;\ 390 };\ 391 typedef struct {\ 392 typeName *node;\ 393 typeName *prev;\ 394 u32 mhash;\ 395 u8 moreOrLess;\ 396 u32 size;\ 397 } TOKENPASTE(context_, typeName) /* concatenate strings with preprocessor */ 398 399 /** 400 * hash table definition 401 * typeName is the type of hash table defined. 402 * 403 * hashNodeT(nodeType) has to defined before this macro. 404 * 405 * Example: 406 * hashNodeT(hashSipNodet, char*, u32, u64); 407 * hashtableT(hashsipt, hashSipNodet); 408 * 409 * context_hashSipNodet is the node context type. 410 */ 411 #define hashtableT(typeName, nodeType)\ 412 dArrayT(UNIQVAR(aHashnodet), nodeType);\ 413 dArrayT(UNIQVAR(HNFreet), u32);\ 414 typedef struct {\ 415 nodeType* (*list)[][2]; /* buckets, 2 single linked lists */\ 416 nodeType node;\ 417 size_t count; /* node count */\ 418 u32 size; /* bucket count */\ 419 u32 szMask; /* mask for bucket indexing */\ 420 UNIQVAR(aHashnodet) aHashnode; /* node dynamic array */\ 421 UNIQVAR(HNFreet) HNFree; /* list of free nodes in aHashnode */\ 422 u8 hashseed[16]; /* random seed for siphash */\ 423 u32 loadfactor_numerator;\ 424 u32 loadfactor_denominator;\ 425 bool res; /* return value for macros */\ 426 bool newNodeInDArray; /* add, addOrFind and addOrFindNode set this flag: true an new node is pushed in aHashnode, false an existing array element is recycled */\ 427 u32 iterIdx; /* iterator bucket index */\ 428 u8 iterMoreOrLess; /* iterator list index */\ 429 nodeType* iterNode; /* iterator node - NULL when iteration is finished or not started */\ 430 nodeType* iterNext; /* iterator next node */\ 431 } typeName 432 433 /** 434 * hash table and hash node definition 435 * typeName is the type of hash table defined. 436 * 437 * keyType, valueType and hashType can any valid type. 438 * 439 * In general, hashType is an uint between 32bits and 256bits 440 */ 441 #define hashTbT(typeName, keyType, valueType, hashType)\ 442 hashNodeT(UNIQVAR(hashnodet), keyType, valueType, hashType);\ 443 hashtableT(typeName, UNIQVAR(hashnodet)) 444 445 /** 446 * fast log2 function for computing the bucket list size and mask 447 */ 448 u32 ilog2Hashtable(u32 value); 449 450 /** 451 * initialize the hash table 'name' 452 * 453 * sz is the initial size 454 * numerator and denominator are the load factor 455 * 456 * the random hash seed is generated 457 * 458 * this macro must be called before using the hash table 459 * 460 * \return 461 * (name).res true success, false failed 462 */ 463 #define hashtableInit(name, sz, numerator, denominator) do{\ 464 (name).size = sz;\ 465 if (sz <= 0) { (name).res = false; break;}\ 466 (name).loadfactor_numerator = numerator;\ 467 (name).loadfactor_denominator = denominator;\ 468 dArrayInit(&(name).aHashnode);\ 469 dArrayInit(&(name).HNFree);\ 470 setSoftwareRandom();\ 471 range(UNIQVAR(i),16) {\ 472 (name).hashseed[UNIQVAR(i)] = randomChoice(256);\ 473 }\ 474 (name).size = (1<<ilog2Hashtable(sz));\ 475 (name).szMask = (name).size - 1;\ 476 (name).list = malloc((name).size * 2 * sizeof(typeof((name).node)*));\ 477 if (!(name).list) /* alloc failed */ { (name).size = 0; (name).res = false; break;}\ 478 memset((name).list, 0, (name).size * 2 * sizeof(typeof((name).node)*));\ 479 (name).count = 0;\ 480 (name).res = true;\ 481 (name).newNodeInDArray = false;\ 482 (name).iterIdx = 0;\ 483 (name).iterMoreOrLess = 0;\ 484 (name).iterNode = NULL;\ 485 (name).iterNext = NULL;\ 486 } while(0) 487 488 /** Internal - for testing the hash table 489 * show the content of the buckets 490 */ 491 #define hashtableTest(name) do{\ 492 range(UNIQVAR(i), (name).size) {\ 493 /* free first linked list */\ 494 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(i)][0];\ 495 while (UNIQVAR(node)) {\ 496 logI("bucket %3d list 0 key %s", UNIQVAR(i), UNIQVAR(node)->key);\ 497 typeof((name).node) *UNIQVAR(next) = UNIQVAR(node)->next;\ 498 UNIQVAR(node) = UNIQVAR(next);\ 499 }\ 500 /* free second linked list */\ 501 UNIQVAR(node) = (*(name).list)[UNIQVAR(i)][1];\ 502 while (UNIQVAR(node)) {\ 503 logI("bucket %3d list 1 key %s", UNIQVAR(i), UNIQVAR(node)->key);\ 504 typeof((name).node) *UNIQVAR(next) = UNIQVAR(node)->next;\ 505 UNIQVAR(node) = UNIQVAR(next);\ 506 }\ 507 }\ 508 } while(0); 509 510 /** 511 * remove all key,value pairs in hash table 512 */ 513 #define hashtableEmpty(name, freeNodeFunc) do{\ 514 range(UNIQVAR(i), (name).size) {\ 515 /* free first linked list */\ 516 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(i)][0];\ 517 while (UNIQVAR(node)) {\ 518 typeof((name).node) *UNIQVAR(next) = UNIQVAR(node)->next;\ 519 freeNodeFunc(&UNIQVAR(node)->key, &UNIQVAR(node)->value);\ 520 UNIQVAR(node) = UNIQVAR(next);\ 521 }\ 522 /* free second linked list */\ 523 UNIQVAR(node) = (*(name).list)[UNIQVAR(i)][1];\ 524 while (UNIQVAR(node)) {\ 525 typeof((name).node) *UNIQVAR(next) = UNIQVAR(node)->next;\ 526 freeNodeFunc(&UNIQVAR(node)->key, &UNIQVAR(node)->value);\ 527 UNIQVAR(node) = UNIQVAR(next);\ 528 }\ 529 (*(name).list)[UNIQVAR(i)][0] = NULL;\ 530 (*(name).list)[UNIQVAR(i)][1] = NULL;\ 531 }\ 532 (name).count = 0;\ 533 } while(0); 534 535 /** 536 * empty hash table and free all buffers 537 * 538 * \return 539 * (name).res true success, false failed 540 */ 541 #define hashtableFree(name, freeNodeFunc) do{\ 542 (name).res = false;\ 543 if (!(name).size) /* size=0 invalid */ break;\ 544 hashtableEmpty(name, freeNodeFunc);\ 545 free((name).list);\ 546 dArrayFree(&(name).aHashnode);\ 547 dArrayFree(&(name).HNFree);\ 548 (name).list = NULL;\ 549 (name).count = 0;\ 550 (name).size = 0;\ 551 (name).res = true;\ 552 } while(0) 553 554 /** 555 * resize the hash table 556 * 557 * \return 558 * (name).res true success, false failed 559 */ 560 #define hashtableResize(name, sz) do{\ 561 u32 UNIQVAR(new_size) = (1<<ilog2Hashtable(sz));\ 562 (name).szMask = (name).size - 1;\ 563 if ((name).size == UNIQVAR(new_size)) goto UNIQVAR(msuccess);\ 564 typeof((name).node) *(*UNIQVAR(list))[][2] = malloc(UNIQVAR(new_size) * 2 * sizeof(typeof((name).node)*));\ 565 if (!UNIQVAR(list)) {(name).res = false; break;}\ 566 memset(UNIQVAR(list), 0, UNIQVAR(new_size) * 2 * sizeof(typeof((name).node)*));\ 567 range(UNIQVAR(i), (name).size) {\ 568 range(UNIQVAR(moreLessIdx), 2) {\ 569 for (typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(i)][UNIQVAR(moreLessIdx)]; UNIQVAR(node);) {\ 570 typeof((name).node) *UNIQVAR(next) = UNIQVAR(node)->next;\ 571 u32 UNIQVAR(mhash) = UNIQVAR(node)->hash & (name).szMask;\ 572 typeof((name).node) *UNIQVAR(search) = (*UNIQVAR(list))[UNIQVAR(mhash)][0];\ 573 typeof((name).node) *UNIQVAR(prev) = NULL;\ 574 u8 UNIQVAR(moreOrLess) = 0;\ 575 if (UNIQVAR(search)) {\ 576 if (UNIQVAR(node)->hash >= UNIQVAR(search)->hash) {\ 577 UNIQVAR(moreOrLess) = 0;\ 578 while (UNIQVAR(search) && UNIQVAR(node)->hash >= UNIQVAR(search)->hash) {\ 579 UNIQVAR(prev) = UNIQVAR(search);\ 580 UNIQVAR(search) = UNIQVAR(search)->next;\ 581 }\ 582 }\ 583 else {\ 584 UNIQVAR(moreOrLess) = 1;\ 585 UNIQVAR(search) = (*UNIQVAR(list))[UNIQVAR(mhash)][1];\ 586 while (UNIQVAR(search) && UNIQVAR(node)->hash <= UNIQVAR(search)->hash) {\ 587 UNIQVAR(prev) = UNIQVAR(search);\ 588 UNIQVAR(search) = UNIQVAR(search)->next;\ 589 }\ 590 }\ 591 }\ 592 UNIQVAR(node)->next = UNIQVAR(search);\ 593 if (UNIQVAR(prev)) {\ 594 UNIQVAR(prev)->next = UNIQVAR(node);\ 595 }\ 596 else {\ 597 (*UNIQVAR(list))[UNIQVAR(mhash)][UNIQVAR(moreOrLess)] = UNIQVAR(node);\ 598 }\ 599 UNIQVAR(node) = UNIQVAR(next);\ 600 }\ 601 }\ 602 }\ 603 free((name).list);\ 604 (name).list = UNIQVAR(list);\ 605 (name).size = UNIQVAR(new_size);\ 606 UNIQVAR(msuccess):\ 607 (name).res = true;\ 608 } while(0) 609 610 /** 611 * insert key, value 612 * 613 * fails when the key already exists 614 * 615 * \return 616 * (name).res true success, false failed 617 */ 618 #define hashtableAdd(name, k, v, hashFunc, cmpFunc) do{\ 619 if ((name).loadfactor_denominator * (name).count >= (name).loadfactor_numerator * (name).size) {\ 620 /* load factor too high */\ 621 hashtableResize(name, (name).count*2);\ 622 /*if (!(name).res) break; else (name).res = false;*/\ 623 }\ 624 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 625 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 626 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][0];\ 627 typeof((name).node) *UNIQVAR(prev) = NULL;\ 628 typeof((name).node) *UNIQVAR(add);\ 629 u8 UNIQVAR(moreOrLess) = 0;\ 630 if (UNIQVAR(node)) {\ 631 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 632 UNIQVAR(moreOrLess) = 0;\ 633 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 634 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 635 (name).newNodeInDArray = false;\ 636 (name).res = false; goto UNIQVAR(mreturn);\ 637 }\ 638 UNIQVAR(prev) = UNIQVAR(node);\ 639 UNIQVAR(node) = UNIQVAR(node)->next;\ 640 }\ 641 }\ 642 else {\ 643 UNIQVAR(moreOrLess) = 1;\ 644 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 645 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 646 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 647 (name).newNodeInDArray = false;\ 648 (name).res = false; goto UNIQVAR(mreturn);\ 649 }\ 650 UNIQVAR(prev) = UNIQVAR(node);\ 651 UNIQVAR(node) = UNIQVAR(node)->next;\ 652 }\ 653 }\ 654 }\ 655 if (dArrayIsEmpty(&(name).HNFree)) {\ 656 dArrayPush(&(name).aHashnode);\ 657 UNIQVAR(add) = dArrayLastPtr(&(name).aHashnode);\ 658 UNIQVAR(add)->index = dArrayLastIndex(&(name).aHashnode);\ 659 (name).newNodeInDArray = true;\ 660 }\ 661 else {\ 662 u32 UNIQVAR(idx) = dArrayLast(&(name).HNFree);\ 663 dArrayDelLast(&(name).HNFree);\ 664 UNIQVAR(add) = dArrayPtr(&(name).aHashnode, UNIQVAR(idx));\ 665 (name).newNodeInDArray = false;\ 666 }\ 667 UNIQVAR(add)->key = k;\ 668 UNIQVAR(add)->value = v;\ 669 UNIQVAR(add)->hash = UNIQVAR(hash);\ 670 if (UNIQVAR(prev)) UNIQVAR(prev)->next = UNIQVAR(add);\ 671 else (*(name).list)[UNIQVAR(mhash)][UNIQVAR(moreOrLess)] = UNIQVAR(add);\ 672 UNIQVAR(add)->next = UNIQVAR(node);\ 673 (name).count++;\ 674 (name).res = true;\ 675 UNIQVAR(mreturn):;\ 676 } while(0) 677 678 /** 679 * add or find key, get value pointer 680 * 681 * vPointer must be of type: 682 * valueType * 683 * 684 * isNew must be of type: 685 * bool 686 * 687 * \return 688 * vPointer address to value associated the key k 689 * isNew true when the node is new, false when the node already existed 690 * (name).res true success, false failed 691 */ 692 #define hashtableAddOrFind(name, k, vPointer, isNew, hashFunc, cmpFunc) do{\ 693 if ((name).loadfactor_denominator * (name).count >= (name).loadfactor_numerator * (name).size) {\ 694 /* load factor too high */\ 695 hashtableResize(name, (name).count*2);\ 696 /*if (!(name).res) break; else (name).res = false;*/\ 697 }\ 698 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 699 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 700 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][0];\ 701 typeof((name).node) *UNIQVAR(prev) = NULL;\ 702 typeof((name).node) *UNIQVAR(add);\ 703 u8 UNIQVAR(moreOrLess) = 0;\ 704 if (UNIQVAR(node)) {\ 705 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 706 UNIQVAR(moreOrLess) = 0;\ 707 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 708 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 709 vPointer = &(UNIQVAR(node)->value);\ 710 isNew = false;\ 711 (name).newNodeInDArray = false;\ 712 (name).res = true; goto UNIQVAR(mreturn);\ 713 }\ 714 UNIQVAR(prev) = UNIQVAR(node);\ 715 UNIQVAR(node) = UNIQVAR(node)->next;\ 716 }\ 717 }\ 718 else {\ 719 UNIQVAR(moreOrLess) = 1;\ 720 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 721 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 722 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 723 vPointer = &(UNIQVAR(node)->value);\ 724 isNew = false;\ 725 (name).newNodeInDArray = false;\ 726 (name).res = true; goto UNIQVAR(mreturn);\ 727 }\ 728 UNIQVAR(prev) = UNIQVAR(node);\ 729 UNIQVAR(node) = UNIQVAR(node)->next;\ 730 }\ 731 }\ 732 }\ 733 isNew = true;\ 734 if (dArrayIsEmpty(&(name).HNFree)) {\ 735 dArrayPush(&(name).aHashnode);\ 736 UNIQVAR(add) = dArrayLastPtr(&(name).aHashnode);\ 737 UNIQVAR(add)->index = dArrayLastIndex(&(name).aHashnode);\ 738 (name).newNodeInDArray = true;\ 739 }\ 740 else {\ 741 u32 UNIQVAR(idx) = dArrayLast(&(name).HNFree);\ 742 dArrayDelLast(&(name).HNFree);\ 743 UNIQVAR(add) = dArrayPtr(&(name).aHashnode, UNIQVAR(idx));\ 744 (name).newNodeInDArray = false;\ 745 }\ 746 UNIQVAR(add)->key = k;\ 747 vPointer = &(UNIQVAR(add)->value);\ 748 UNIQVAR(add)->hash = UNIQVAR(hash);\ 749 if (UNIQVAR(prev)) UNIQVAR(prev)->next = UNIQVAR(add);\ 750 else (*(name).list)[UNIQVAR(mhash)][UNIQVAR(moreOrLess)] = UNIQVAR(add);\ 751 UNIQVAR(add)->next = UNIQVAR(node);\ 752 (name).count++;\ 753 (name).res = true;\ 754 UNIQVAR(mreturn):;\ 755 } while(0) 756 757 /** 758 * find key, get value pointer 759 * 760 * vPointer must be of type: 761 * valueType * 762 * 763 * \return 764 * vPointer address to value associated the key k 765 * (name).res true success, false failed 766 */ 767 #define hashtableFind(name, k, vPointer, hashFunc, cmpFunc) do{\ 768 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 769 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 770 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 771 if (UNIQVAR(node)) {\ 772 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 773 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 774 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 775 vPointer = &UNIQVAR(node)->value;\ 776 (name).res = true;\ 777 goto UNIQVAR(mreturn);\ 778 }\ 779 UNIQVAR(node) = UNIQVAR(node)->next;\ 780 }\ 781 }\ 782 else {\ 783 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 784 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 785 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 786 vPointer = &UNIQVAR(node)->value;\ 787 (name).res = true;\ 788 goto UNIQVAR(mreturn);\ 789 }\ 790 UNIQVAR(node) = UNIQVAR(node)->next;\ 791 }\ 792 }\ 793 }\ 794 (name).res = false;\ 795 UNIQVAR(mreturn):;\ 796 } while(0) 797 798 /** 799 * get value 800 * 801 * \return 802 * (name).res true success, false failed 803 */ 804 #define hashtableGet(name, k, result, hashFunc, cmpFunc) do{\ 805 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 806 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 807 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 808 if (UNIQVAR(node)) {\ 809 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 810 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 811 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 812 result = UNIQVAR(node)->value;\ 813 (name).res = true;\ 814 goto UNIQVAR(mreturn);\ 815 }\ 816 UNIQVAR(node) = UNIQVAR(node)->next;\ 817 }\ 818 }\ 819 else {\ 820 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 821 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 822 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 823 result = UNIQVAR(node)->value;\ 824 (name).res = true;\ 825 goto UNIQVAR(mreturn);\ 826 }\ 827 UNIQVAR(node) = UNIQVAR(node)->next;\ 828 }\ 829 }\ 830 }\ 831 (name).res = false;\ 832 UNIQVAR(mreturn):;\ 833 } while(0) 834 835 /** 836 * get key 837 * 838 * \return 839 * (name).res true success, false failed 840 */ 841 #define hashtableGetKey(name, k, result, hashFunc, cmpFunc) do{\ 842 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 843 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 844 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 845 if (UNIQVAR(node)) {\ 846 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 847 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 848 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 849 result = UNIQVAR(node)->key;\ 850 (name).res = true;\ 851 goto UNIQVAR(mreturn);\ 852 }\ 853 UNIQVAR(node) = UNIQVAR(node)->next;\ 854 }\ 855 }\ 856 else {\ 857 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 858 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 859 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 860 result = UNIQVAR(node)->key;\ 861 (name).res = true;\ 862 goto UNIQVAR(mreturn);\ 863 }\ 864 UNIQVAR(node) = UNIQVAR(node)->next;\ 865 }\ 866 }\ 867 }\ 868 (name).res = false;\ 869 UNIQVAR(mreturn):;\ 870 } while(0) 871 872 /** 873 * remove key, value 874 * 875 * \return 876 * (name).res true success, false failed 877 */ 878 #define hashtableDel(name, k, hashFunc, cmpFunc, freeNodeFunc) do{\ 879 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 880 u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 881 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][0];\ 882 typeof((name).node) *UNIQVAR(prev) = NULL;\ 883 if (UNIQVAR(node)) {\ 884 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 885 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 886 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 887 freeNodeFunc(&UNIQVAR(node)->key, &UNIQVAR(node)->value);\ 888 hashtableInternalDelNode(name, UNIQVAR(node), UNIQVAR(mhash), UNIQVAR(prev), 0);\ 889 (name).res = true;\ 890 goto UNIQVAR(mreturn);\ 891 }\ 892 UNIQVAR(prev) = UNIQVAR(node);\ 893 UNIQVAR(node) = UNIQVAR(node)->next;\ 894 }\ 895 }\ 896 else {\ 897 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 898 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 899 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 900 freeNodeFunc(&UNIQVAR(node)->key, &UNIQVAR(node)->value);\ 901 hashtableInternalDelNode(name, UNIQVAR(node), UNIQVAR(mhash), UNIQVAR(prev), 1);\ 902 (name).res = true;\ 903 goto UNIQVAR(mreturn);\ 904 }\ 905 UNIQVAR(prev) = UNIQVAR(node);\ 906 UNIQVAR(node) = UNIQVAR(node)->next;\ 907 }\ 908 }\ 909 }\ 910 (name).res = false;\ 911 UNIQVAR(mreturn):;\ 912 } while(0) 913 914 // Internal macro 915 #define hashtableInternalDelNode(name, node, mhash, prev, moreOrLess) do{\ 916 hashtableInternalUnlink(name, node, mhash, prev, moreOrLess);\ 917 dArrayAppend(&(name).HNFree, node->index);\ 918 (name).count--;\ 919 } while(0) 920 921 /** 922 * get hash seed address for siphash 923 */ 924 #define hashtableSeed(name) ((name).hashseed) 925 926 927 928 /*************************************** 929 * node macros 930 * 931 * Usage scenarios: 932 * 933 * The getNode allow processing node data before reinsert or delete and running the hash function 934 * only once. 935 * 936 * Example: 937 * getNode 938 * use node data, change key 939 * setKeyNode 940 * 941 * same as 942 * get 943 * use node data, change key 944 * del original key 945 * add new key, value 946 * 947 * The unlink macro allow using data in a node before deleting it. 948 * The same operation can be done with Get and Del, using unlink runs the hash function only once. 949 * 950 * Example: 951 * unlinkNode 952 * use node data 953 * freeUnlinkedNode 954 * 955 * same as 956 * get 957 * use value 958 * del 959 * 960 ***************************************/ 961 962 /** 963 * add or find node 964 * 965 * nodePointer must be a pointer to hash node type 966 * hashNode *result; 967 * 968 * nodeContext must be a struct of type 969 * context_hashNode ctx; 970 * 971 * \return 972 * (name).res true success, false failed 973 */ 974 #define hashtableAddOrFindNode(name, k, nodePointer, nodeContext, isNew, hashFunc, cmpFunc) do{\ 975 if ((name).loadfactor_denominator * (name).count >= (name).loadfactor_numerator * (name).size) {\ 976 /* load factor too high */\ 977 hashtableResize(name, (name).count*2);\ 978 /*if (!(name).res) break; else (name).res = false;*/\ 979 }\ 980 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 981 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 982 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][0];\ 983 typeof((name).node) *UNIQVAR(prev) = NULL;\ 984 u8 UNIQVAR(moreOrLess) = 0;\ 985 if (UNIQVAR(node)) {\ 986 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 987 UNIQVAR(moreOrLess) = 0;\ 988 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 989 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 990 nodePointer = UNIQVAR(node);\ 991 (nodeContext).node = nodePointer;\ 992 (nodeContext).prev = UNIQVAR(prev);\ 993 (nodeContext).mhash = UNIQVAR(mhash);\ 994 (nodeContext).moreOrLess = UNIQVAR(moreOrLess);\ 995 (nodeContext).size = (name).size;\ 996 isNew = false;\ 997 (name).newNodeInDArray = false;\ 998 (name).res = true; goto UNIQVAR(mreturn);\ 999 }\ 1000 UNIQVAR(prev) = UNIQVAR(node);\ 1001 UNIQVAR(node) = UNIQVAR(node)->next;\ 1002 }\ 1003 }\ 1004 else {\ 1005 UNIQVAR(moreOrLess) = 1;\ 1006 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 1007 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 1008 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 1009 nodePointer = UNIQVAR(node);\ 1010 (nodeContext).node = nodePointer;\ 1011 (nodeContext).prev = UNIQVAR(prev);\ 1012 (nodeContext).mhash = UNIQVAR(mhash);\ 1013 (nodeContext).moreOrLess = UNIQVAR(moreOrLess);\ 1014 (nodeContext).size = (name).size;\ 1015 isNew = false;\ 1016 (name).newNodeInDArray = false;\ 1017 (name).res = true; goto UNIQVAR(mreturn);\ 1018 }\ 1019 UNIQVAR(prev) = UNIQVAR(node);\ 1020 UNIQVAR(node) = UNIQVAR(node)->next;\ 1021 }\ 1022 }\ 1023 }\ 1024 isNew = true;\ 1025 if (dArrayIsEmpty(&(name).HNFree)) {\ 1026 dArrayPush(&(name).aHashnode);\ 1027 nodePointer = dArrayLastPtr(&(name).aHashnode);\ 1028 nodePointer->index = dArrayLastIndex(&(name).aHashnode);\ 1029 (name).newNodeInDArray = true;\ 1030 }\ 1031 else {\ 1032 u32 UNIQVAR(idx) = dArrayLast(&(name).HNFree);\ 1033 dArrayDelLast(&(name).HNFree);\ 1034 nodePointer = dArrayPtr(&(name).aHashnode, UNIQVAR(idx));\ 1035 (name).newNodeInDArray = false;\ 1036 }\ 1037 nodePointer->key = k;\ 1038 nodePointer->hash = UNIQVAR(hash);\ 1039 if (UNIQVAR(prev)) UNIQVAR(prev)->next = nodePointer;\ 1040 else (*(name).list)[UNIQVAR(mhash)][UNIQVAR(moreOrLess)] = nodePointer;\ 1041 nodePointer->next = UNIQVAR(node);\ 1042 (name).count++;\ 1043 /* set context */\ 1044 (nodeContext).node = nodePointer;\ 1045 (nodeContext).prev = UNIQVAR(prev);\ 1046 (nodeContext).mhash = UNIQVAR(mhash);\ 1047 (nodeContext).moreOrLess = UNIQVAR(moreOrLess);\ 1048 (nodeContext).size = (name).size;\ 1049 (name).res = true;\ 1050 UNIQVAR(mreturn):;\ 1051 } while(0) 1052 1053 /** 1054 * set node to be used when the key is updated 1055 * unlink the node then 1056 * insert it in another bucket 1057 * 1058 * nodeContext must be a struct of type 1059 * context_hashNode ctx; 1060 * 1061 * The node context is updated and remains valid after this macro (no need to run updateNodeContext) 1062 * 1063 * When key,value pairs are added after getNode and the table is rehashed (the size changed), the node context should be updated with getNode before calling this macro 1064 * 1065 * The node is unlinked, so in case of failure, the node should be added with AddOrFindNode or the node should be freed with freeUnlinkedNode 1066 * 1067 * \return 1068 * (name).res true success, false failed 1069 */ 1070 #define hashtableSetKeyNode(name, nodeContext, hashFunc, cmpFunc) do{\ 1071 /* no need to resize because the node is unlinked and the key is rehashed */\ 1072 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc((nodeContext).node->key);\ 1073 if (UNIQVAR(hash) == (nodeContext).node->hash) /* the hash is identical to the previous one, we are done */{\ 1074 (name).res = true; goto UNIQVAR(mreturn);\ 1075 }\ 1076 hashtableInternalUnlinkNode(name, nodeContext);\ 1077 if (!(name).res) /* the table has been rehashed, the context is invalid */ goto UNIQVAR(mreturn);\ 1078 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 1079 (nodeContext).node->hash = UNIQVAR(hash);\ 1080 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][0];\ 1081 typeof((name).node) *UNIQVAR(prev) = NULL;\ 1082 u8 UNIQVAR(moreOrLess) = 0;\ 1083 if (UNIQVAR(node)) {\ 1084 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1085 UNIQVAR(moreOrLess) = 0;\ 1086 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1087 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (((nodeContext).node->key == UNIQVAR(node)->key) || (cmpFunc((nodeContext).node->key, UNIQVAR(node)->key) == 0))) {\ 1088 (name).res = false; goto UNIQVAR(mreturn);\ 1089 }\ 1090 UNIQVAR(prev) = UNIQVAR(node);\ 1091 UNIQVAR(node) = UNIQVAR(node)->next;\ 1092 }\ 1093 }\ 1094 else {\ 1095 UNIQVAR(moreOrLess) = 1;\ 1096 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 1097 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 1098 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (((nodeContext).node->key == UNIQVAR(node)->key) || (cmpFunc((nodeContext).node->key, UNIQVAR(node)->key) == 0))) {\ 1099 (name).res = false; goto UNIQVAR(mreturn);\ 1100 }\ 1101 UNIQVAR(prev) = UNIQVAR(node);\ 1102 UNIQVAR(node) = UNIQVAR(node)->next;\ 1103 }\ 1104 }\ 1105 }\ 1106 if (UNIQVAR(prev)) UNIQVAR(prev)->next = (nodeContext).node;\ 1107 else (*(name).list)[UNIQVAR(mhash)][UNIQVAR(moreOrLess)] = (nodeContext).node;\ 1108 (nodeContext).node->next = UNIQVAR(node);\ 1109 /* update context */\ 1110 (nodeContext).prev = UNIQVAR(prev);\ 1111 (nodeContext).mhash = UNIQVAR(mhash);\ 1112 (nodeContext).moreOrLess = UNIQVAR(moreOrLess);\ 1113 (nodeContext).size = (name).size;\ 1114 (name).res = true;\ 1115 UNIQVAR(mreturn):;\ 1116 } while(0) 1117 1118 // Internal macro, like Internal DelNode without free the node 1119 // the node is removed from the bucket 1120 #define hashtableInternalUnlinkNode(name, nodeContext) do{\ 1121 if ((nodeContext).size != (name).size) {\ 1122 /* the table has been rehashed since the nodeContext was filled in */\ 1123 /* update the context, run updateNodeContext with the original key */\ 1124 /* the original is not available in the context, since the key is probably updated */\ 1125 (name).res = false;\ 1126 break;\ 1127 }\ 1128 if ((nodeContext).prev) (nodeContext).prev->next = (nodeContext).node->next;\ 1129 else (*(name).list)[(nodeContext).mhash][(nodeContext).moreOrLess] = (nodeContext).node->next;\ 1130 if (((nodeContext).moreOrLess == 0) && !(*(name).list)[(nodeContext).mhash][0] && (*(name).list)[(nodeContext).mhash][1]) {\ 1131 /* the first linked list is empty, take the first element from the second linked list */\ 1132 (*(name).list)[(nodeContext).mhash][0] = (*(name).list)[(nodeContext).mhash][1];\ 1133 (*(name).list)[(nodeContext).mhash][1] = (*(name).list)[(nodeContext).mhash][1]->next;\ 1134 (*(name).list)[(nodeContext).mhash][0]->next = NULL;\ 1135 }\ 1136 (name).res = true;\ 1137 } while(0) 1138 1139 1140 /** 1141 * get node 1142 * 1143 * result must be a pointer to hash node type 1144 * hashNode *result; 1145 * 1146 * nodeContext must be a struct of type 1147 * context_hashNode ctx; 1148 * 1149 * \return 1150 * (name).res true success, false failed 1151 */ 1152 #define hashtableGetNode(name, k, result, nodeContext, hashFunc, cmpFunc) do{\ 1153 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 1154 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 1155 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 1156 typeof((name).node) *UNIQVAR(prev) = NULL;\ 1157 if (UNIQVAR(node)) {\ 1158 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1159 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1160 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 1161 result = UNIQVAR(node);\ 1162 (nodeContext).node = UNIQVAR(node);\ 1163 (nodeContext).prev = UNIQVAR(prev);\ 1164 (nodeContext).mhash = UNIQVAR(mhash);\ 1165 (nodeContext).moreOrLess = 0;\ 1166 (nodeContext).size = (name).size;\ 1167 (name).res = true;\ 1168 goto UNIQVAR(mreturn);\ 1169 }\ 1170 UNIQVAR(prev) = UNIQVAR(node);\ 1171 UNIQVAR(node) = UNIQVAR(node)->next;\ 1172 }\ 1173 }\ 1174 else {\ 1175 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 1176 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 1177 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 1178 result = UNIQVAR(node);\ 1179 (nodeContext).node = UNIQVAR(node);\ 1180 (nodeContext).prev = UNIQVAR(prev);\ 1181 (nodeContext).mhash = UNIQVAR(mhash);\ 1182 (nodeContext).moreOrLess = 1;\ 1183 (nodeContext).size = (name).size;\ 1184 (name).res = true;\ 1185 goto UNIQVAR(mreturn);\ 1186 }\ 1187 UNIQVAR(prev) = UNIQVAR(node);\ 1188 UNIQVAR(node) = UNIQVAR(node)->next;\ 1189 }\ 1190 }\ 1191 }\ 1192 (name).res = false;\ 1193 UNIQVAR(mreturn):;\ 1194 } while(0) 1195 1196 /** 1197 * update node context 1198 * 1199 * the node context must have been initialized with getNode or setKeyNode 1200 * 1201 * call this macro when the table has been rehashed after getNode or setKeyNode 1202 * 1203 * nodeContext must be a struct of type 1204 * context_hashNode ctx; 1205 * 1206 * \return 1207 * (name).res true success, false failed 1208 */ 1209 #define hashtableUpdateNodeContext(name, nodeContext, hashFunc, cmpFunc) do{\ 1210 const typeof((name).node.hash) UNIQVAR(hash) = (nodeContext).node->hash;\ 1211 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 1212 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 1213 typeof((name).node) *UNIQVAR(prev) = NULL;\ 1214 if (UNIQVAR(node)) {\ 1215 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1216 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1217 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (((nodeContext).node->key == UNIQVAR(node)->key) || (cmpFunc((nodeContext).node->key, UNIQVAR(node)->key) == 0))) {\ 1218 (nodeContext).prev = UNIQVAR(prev);\ 1219 (nodeContext).mhash = UNIQVAR(mhash);\ 1220 (nodeContext).moreOrLess = 0;\ 1221 (nodeContext).size = (name).size;\ 1222 (name).res = true;\ 1223 goto UNIQVAR(mreturn);\ 1224 }\ 1225 UNIQVAR(prev) = UNIQVAR(node);\ 1226 UNIQVAR(node) = UNIQVAR(node)->next;\ 1227 }\ 1228 }\ 1229 else {\ 1230 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 1231 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 1232 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (((nodeContext).node->key == UNIQVAR(node)->key) || (cmpFunc((nodeContext).node->key, UNIQVAR(node)->key) == 0))) {\ 1233 (nodeContext).prev = UNIQVAR(prev);\ 1234 (nodeContext).mhash = UNIQVAR(mhash);\ 1235 (nodeContext).moreOrLess = 1;\ 1236 (nodeContext).size = (name).size;\ 1237 (name).res = true;\ 1238 goto UNIQVAR(mreturn);\ 1239 }\ 1240 UNIQVAR(prev) = UNIQVAR(node);\ 1241 UNIQVAR(node) = UNIQVAR(node)->next;\ 1242 }\ 1243 }\ 1244 }\ 1245 (name).res = false;\ 1246 UNIQVAR(mreturn):;\ 1247 } while(0) 1248 1249 /** 1250 * unlink node 1251 * 1252 * result must be a pointer to hash node type 1253 * hashNode *result; 1254 * 1255 * result must be freed with freeUnlinkNode 1256 * 1257 * This macro allow using data in a node before deleting it. 1258 * The same operation can be done with Get and Del, using unlink runs the hash function only once. 1259 * 1260 * Example: 1261 * unlinkNode 1262 * use node data 1263 * freeUnlinkedNode 1264 * 1265 * same as 1266 * get 1267 * use value 1268 * del 1269 * 1270 * \return 1271 * result unlinked node 1272 * (name).res true success, false failed 1273 */ 1274 #define hashtableUnlinkNode(name, k, result, hashFunc, cmpFunc) do{\ 1275 const typeof((name).node.hash) UNIQVAR(hash) = hashFunc(k);\ 1276 const u32 UNIQVAR(mhash) = UNIQVAR(hash) & (name).szMask;\ 1277 typeof((name).node) *UNIQVAR(node) = (*(name).list)[UNIQVAR(hash) & (name).szMask][0];\ 1278 typeof((name).node) *UNIQVAR(prev) = NULL;\ 1279 if (UNIQVAR(node)) {\ 1280 if (UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1281 while (UNIQVAR(node) && UNIQVAR(hash) >= UNIQVAR(node)->hash) {\ 1282 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 1283 hashtableInternalUnlink(name, UNIQVAR(node), UNIQVAR(mhash), UNIQVAR(prev), 0);\ 1284 result = UNIQVAR(node);\ 1285 (name).res = true;\ 1286 goto UNIQVAR(mreturn);\ 1287 }\ 1288 UNIQVAR(prev) = UNIQVAR(node);\ 1289 UNIQVAR(node) = UNIQVAR(node)->next;\ 1290 }\ 1291 }\ 1292 else {\ 1293 UNIQVAR(node) = (*(name).list)[UNIQVAR(mhash)][1];\ 1294 while (UNIQVAR(node) && UNIQVAR(hash) <= UNIQVAR(node)->hash) {\ 1295 if (UNIQVAR(hash) == UNIQVAR(node)->hash && (cmpFunc(k, UNIQVAR(node)->key) == 0)) {\ 1296 hashtableInternalUnlink(name, UNIQVAR(node), UNIQVAR(mhash), UNIQVAR(prev), 1);\ 1297 result = UNIQVAR(node);\ 1298 (name).res = true;\ 1299 goto UNIQVAR(mreturn);\ 1300 }\ 1301 UNIQVAR(prev) = UNIQVAR(node);\ 1302 UNIQVAR(node) = UNIQVAR(node)->next;\ 1303 }\ 1304 }\ 1305 }\ 1306 (name).res = false;\ 1307 UNIQVAR(mreturn):;\ 1308 } while(0) 1309 1310 // Internal macro 1311 #define hashtableInternalUnlink(name, node, mhash, prev, moreOrLess) do{\ 1312 if (prev) prev->next = node->next;\ 1313 else (*(name).list)[mhash][moreOrLess] = node->next;\ 1314 if ((moreOrLess == 0) && !(*(name).list)[mhash][0] && (*(name).list)[mhash][1]) {\ 1315 /* the first linked list is empty, take the first element from the second linked list */\ 1316 (*(name).list)[mhash][0] = (*(name).list)[mhash][1];\ 1317 (*(name).list)[mhash][1] = (*(name).list)[mhash][1]->next;\ 1318 (*(name).list)[mhash][0]->next = NULL;\ 1319 }\ 1320 } while(0) 1321 1322 /** 1323 * free an unlinked node 1324 * 1325 * node must be a pointer to hash node type 1326 * hashNode *node; 1327 * 1328 * the node has to be first unlinked with the unlinkNode macro 1329 */ 1330 #define hashtableFreeUnlinkedNode(name, node, freeNodeFunc) do{\ 1331 freeNodeFunc(&(node)->key, &(node)->value);\ 1332 dArrayAppend(&(name).HNFree, (node)->index);\ 1333 (name).count--;\ 1334 } while(0) 1335 1336 /** 1337 * delete/remove node 1338 * 1339 * the node context must be already initialized with the other *Node macros 1340 * 1341 * if the node context is changed due to rehash, updateNodeContext has to be called before this macro 1342 */ 1343 // no status 1344 #define hashtableDelNode(name, nodeContext, freeNodeFunc) do{\ 1345 freeNodeFunc(&(nodeContext).node->key, &(nodeContext).node->value);\ 1346 hashtableInternalDelNode(name, (nodeContext).node, (nodeContext).mhash, (nodeContext).prev, (nodeContext).moreOrLess);\ 1347 } while(0) 1348 1349 /** 1350 * start iteration on all nodes 1351 * 1352 * To get the key and value of current node in iteration use: 1353 * hashtableIterKey and hashtableIterValue 1354 * 1355 * \return 1356 * (name).res = true when first node is found 1357 * (name).res = false when no node is found 1358 */ 1359 #define hashtableIterStart(name) procbegin\ 1360 if (!(name).count) {\ 1361 (name).iterNode = NULL;\ 1362 (name).res = false;\ 1363 goto UNIQVAR(end);\ 1364 }\ 1365 range(UNIQVAR(i), (name).size) {\ 1366 if ((*(name).list)[UNIQVAR(i)][0]) {\ 1367 (name).iterNode = (*(name).list)[UNIQVAR(i)][0];\ 1368 (name).iterIdx = UNIQVAR(i);\ 1369 (name).iterMoreOrLess = 0;\ 1370 goto UNIQVAR(foundFirstElement);\ 1371 }\ 1372 }\ 1373 /* no element found - should not happen because count == 0 */\ 1374 (name).iterNode = NULL;\ 1375 (name).res = false;\ 1376 goto UNIQVAR(end);\ 1377 UNIQVAR(foundFirstElement):\ 1378 hashtableInternalIterNext(name);\ 1379 (name).res = true;\ 1380 UNIQVAR(end):;\ 1381 procend 1382 1383 /** 1384 * move to next node in iteration 1385 * 1386 * Before call this macro, the iteration has to be started with 1387 * hashtableIterStart 1388 * 1389 * To get the key and value of current node in iteration use: 1390 * hashtableIterKey and hashtableIterValue 1391 * 1392 * \return 1393 * (name).res = true when next node is found 1394 * (name).res = false when no node is found (iteration is finished or not started, (name).iterNode is set to NULL) 1395 */ 1396 #define hashtableIterNext(name) procbegin\ 1397 if (!(name).iterNode) {\ 1398 /* iteration is finished or not started */\ 1399 (name).res = false;\ 1400 goto UNIQVAR(end);\ 1401 }\ 1402 (name).iterNode = (name).iterNext;\ 1403 if ((name).iterNode) {\ 1404 hashtableInternalIterNext(name);\ 1405 (name).res = true;\ 1406 goto UNIQVAR(end);\ 1407 }\ 1408 /* find next linked list or the iteration is finished */\ 1409 if ((name).iterMoreOrLess == 0) {\ 1410 /* check second linked list for iterIdx */\ 1411 (name).iterNode = (*(name).list)[(name).iterIdx][1];\ 1412 if ((name).iterNode) {\ 1413 (name).iterMoreOrLess = 1;\ 1414 goto UNIQVAR(foundElement);\ 1415 }\ 1416 }\ 1417 /* fixIterMoreOrLess == 1 or no node in second list */\ 1418 (name).iterIdx++;\ 1419 rangeFrom(UNIQVAR(i), (name).iterIdx, (name).size) {\ 1420 /* if there is an element at this index it is always in the first list */\ 1421 if ((*(name).list)[UNIQVAR(i)][0]) {\ 1422 (name).iterNode = (*(name).list)[UNIQVAR(i)][0];\ 1423 (name).iterIdx = UNIQVAR(i);\ 1424 (name).iterMoreOrLess = 0;\ 1425 goto UNIQVAR(foundElement);\ 1426 }\ 1427 }\ 1428 /* no next node found - iterNode is NULL */\ 1429 (name).res = false;\ 1430 goto UNIQVAR(end);\ 1431 UNIQVAR(foundElement):;\ 1432 hashtableInternalIterNext(name);\ 1433 (name).res = true;\ 1434 UNIQVAR(end):;\ 1435 procend 1436 1437 /** key of node in iteration */ 1438 #define hashtableIterKey(name) ((name).iterNode->key) 1439 1440 /** value of node in iteration */ 1441 #define hashtableIterValue(name) ((name).iterNode->value) 1442 1443 /** internal macro to find next node in iteration */ 1444 #define hashtableInternalIterNext(name) procbegin\ 1445 /* save next pointer to allow calls to delete current element */\ 1446 /* and still iterate through all elements*/\ 1447 if ((*(name).list)[(name).iterIdx][0] == (name).iterNode\ 1448 and !(name).iterNode->next\ 1449 and (*(name).list)[(name).iterIdx][1]) {\ 1450 /* check if current node is a unique node in first linked list */\ 1451 /* and there is a least one node in the second linked list. */\ 1452 (name).iterNext = (*(name).list)[(name).iterIdx][1];\ 1453 (name).iterMoreOrLess = 1;\ 1454 }\ 1455 else {\ 1456 (name).iterNext = (name).iterNode->next;\ 1457 }\ 1458 procend 1459 1460 1461 // the function in hashtable doesnt depend on libsheepy, no need to recompile when libsheepy version is changed 1462 #define isHashtableCompiledWithCurrentLisheepyVersion true