blob: 5a6d3e41b580eab841e70b16679362342e42f9d3 [file] [log] [blame]
Lev Walkinadfcde22017-11-05 22:45:54 -08001/*
2 * Copyright (c) 2002-2005 Lev Walkin <vlm@lionet.info>. All rights reserved.
3 * Copyright (c) 2001-2004 Netli, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: genhash.c 447 2005-06-07 06:51:10Z vlm $
27 */
28/*
29 * Implementation of a hash data structure.
30 * This particular implementation is supposed to be space-efficient
31 * particularly in the case of tiny number of hash elements.
32 * It also has an aggressive hash buckets expanding technique, which allows
33 * to deal with increasing number of elements without a loss of search speed.
34 *
35 * Generally, one structure of type genhash_t is allocated per hash set.
36 * This structure is supposed to hold all information related to the current
37 * set, and also holds a tiny number of hash elements, when hash hasn't yet
38 * grown up. When the number of elements reaches some point, part of the
39 * genhash_t structure is reused to contain the pointers to the actual
40 * hash buckets and LRU (Least Recently Used) list's head and tail.
41 * Elements which were held inside genhash_t will be moved to the hash buckets.
42 *
43 * Said above effectively means two modes of operation: TINY and NORMAL.
44 * They can be distinguished by examining the h->numbuckets value, which
45 * is 0 for TINY and greater for NORMAL mode.
46 *
47 * In the TINY mode we use a lower part of the genhash_t structure
48 * (lower 32 bytes from 64 bytes of genhash_t) to hold up to IH_VALUE (4)
49 * key/value pairs.
50 *
51 * In the NORMAL mode we use the lower part of the genhash_t structure
52 * to hold a set of pointers, including a pointer to the hash buckets.
53 * We agressively expand hash buckets size when adding new elements
54 * to lower the number of key comparisons.
55 */
56
57#include <sys/types.h>
58#include <stdlib.h>
59#include <string.h>
60#include <assert.h>
Lev Walkin08661a62017-11-06 02:08:47 -080061#include <stddef.h>
Lev Walkinadfcde22017-11-05 22:45:54 -080062#include <errno.h>
63#include "genhash.h"
64
65/* 1M entries, 4M RAM */
66#define DEFAULT_MAXIMUM_HASH_BUCKETS_NUMBER (1024 * 1024)
67static int maximum_hash_buckets_number = DEFAULT_MAXIMUM_HASH_BUCKETS_NUMBER;
68
69/*
70 * A single hash element structure which binds a value to its key.
71 */
72typedef struct genhash_el_s {
73 unsigned int key_hash; /* Saved hash of the key */
74 void *key;
75 void *value;
76 struct genhash_el_s *hash_next; /* Collision list inside the bucket */
77 struct genhash_el_s *hash_prev;
78 struct genhash_el_s *lru_prev; /* Per-hash LRU list */
79 struct genhash_el_s *lru_next;
80} genhash_el;
81
82/*
83 * A hash structure with buckets etc.
84 */
85struct genhash_s {
86 int (*keycmpf) (const void *lkey1, const void *rkey2);
87 unsigned int (*keyhashf) (const void *key); /* hash function */
88 void (*keydestroyf) (void *key); /* key destructor */
89 void (*valuedestroyf) (void *value); /* value destructor */
90
91 int numelements; /* Total number of hash elements */
92 int numbuckets; /* 0 means "use _TINY" */
93 int lru_limit; /* Must be initialized explicitly */
94 genhash_iter_t *iters; /* Active iterators */
95
96 /* 32-byte boundary here */
97
98 union {
99#define IH_VALUES 4 /* Internally held key/value pairs for TINY mode */
100 struct _internal_tiny_s {
101 void *keys[IH_VALUES];
102 void *values[IH_VALUES];
103 } _TINY; /* 32-byte structure */
104 struct _internal_normal_s {
105 genhash_el *lru_head; /* LRU list head */
106 genhash_el *lru_tail; /* LRU list tail */
107 genhash_el **buckets; /* Hash buckets */
108 /* void *unused; */
109 } _NORMAL;
110 } un;
111#define tiny_keys un._TINY.keys
112#define tiny_values un._TINY.values
113#define lru_head un._NORMAL.lru_head
114#define lru_tail un._NORMAL.lru_tail
115#define buckets un._NORMAL.buckets
116};
117
118
119static int
120_genhash_normal_add(genhash_t *h, genhash_el *el, void *key, void *value);
121
122
123genhash_t *
124genhash_new(
125 int (*keycmpf) (const void *key1, const void *key2),
126 unsigned int (*keyhashf) (const void *key),
127 void (*keydestroyf) (void *key),
128 void (*valuedestroyf) (void *value)
129) {
130 genhash_t *h;
131
132 h = (genhash_t *)malloc(sizeof(genhash_t));
133 if (!h)
134 return NULL;
135
136 memset(h, 0, sizeof(genhash_t));
137
138 genhash_reinit(h, keycmpf, keyhashf, keydestroyf, valuedestroyf);
139
140 return h;
141}
142
143int
144genhash_reinit(
145 genhash_t *h,
146 int (*keycmpf) (const void *key1, const void *key2),
147 unsigned int (*keyhashf) (const void *key),
148 void (*keydestroyf) (void *key),
149 void (*valuedestroyf) (void *value)
150) {
151
152 assert(keycmpf && keyhashf);
153
154 h->keycmpf = keycmpf;
155 h->keyhashf = keyhashf;
156 h->keydestroyf = keydestroyf;
157 h->valuedestroyf = valuedestroyf;
158
159 return 0;
160}
161
162int
163genhash_count(genhash_t *h) {
164 if(h) {
165 return h->numelements;
166 } else {
167 return 0;
168 }
169}
170
171
172static void
173_remove_normal_hash_el(genhash_t *h, genhash_el *el) {
174 genhash_iter_t *iter;
175 void *kd_arg;
176 void *vd_arg;
177
178 /* Remove from the collision list */
179 if (el->hash_prev) {
180 if((el->hash_prev->hash_next = el->hash_next))
181 el->hash_next->hash_prev = el->hash_prev;
182
183 } else {
184 if((h->buckets[el->key_hash % h->numbuckets] = el->hash_next))
185 el->hash_next->hash_prev = NULL;
186 }
187
188 /* Remove from LRU list */
189 if(el->lru_prev) {
190 if((el->lru_prev->lru_next = el->lru_next))
191 el->lru_next->lru_prev = el->lru_prev;
192 else
193 h->lru_tail = el->lru_prev;
194 } else {
195 if(h->lru_head == el) {
196 if((h->lru_head = el->lru_next) == NULL)
197 h->lru_tail = NULL;
198 else
199 h->lru_head->lru_prev = NULL;
200 }
201 }
202
203 /* Remember key and value */
204 kd_arg = el->key;
205 vd_arg = el->value;
206
207 /* Move iterators off the element being deleted */
208 for(iter = h->iters; iter; iter = iter->iter_next) {
209 assert(iter->hash_ptr == h);
210 if(iter->un.location == el) {
211 iter->un.location = iter->order_lru_first
212 ? el->lru_prev : el->lru_next;
213 }
214 }
215
216 free(el);
217 h->numelements--;
218
219 /* Remove key and value */
220 if (h->keydestroyf) h->keydestroyf(kd_arg);
221 if (h->valuedestroyf) h->valuedestroyf(vd_arg);
222}
223
224static inline void
225_genhash_normal_el_move2top(genhash_t *h, genhash_el *el) {
226
227 /* Disable sorting if iterators are running */
228 if(h->iters) return;
229
230 /* Move to the top of the hash bucket */
231 if(el->hash_prev) {
232 int bucket = el->key_hash % h->numbuckets;
233
234 /* Remove from the current location */
235 if((el->hash_prev->hash_next = el->hash_next))
236 el->hash_next->hash_prev = el->hash_prev;
237
238 /* Move to the top of the hash bucket */
239 if((el->hash_next = h->buckets[bucket]))
240 el->hash_next->hash_prev = el;
241 h->buckets[bucket] = el;
242 el->hash_prev = NULL;
243 }
244
245 /* Move to the top of LRU list */
246 if(h->lru_limit && el->lru_prev) {
247
248 /* Remove from current location */
249 if((el->lru_prev->lru_next = el->lru_next))
250 el->lru_next->lru_prev = el->lru_prev;
251 else
252 h->lru_tail = el->lru_prev;
253
254 /* Append to the head */
255 el->lru_prev = NULL;
256 h->lru_head->lru_prev = el;
257 el->lru_next = h->lru_head;
258 h->lru_head = el;
259 }
260}
261
262static int
263_expand_hash(genhash_t *h) {
264 int newbuckets_count;
265 genhash_el **newbuckets;
266
267 /*
268 * Compute a new number of buckets value.
269 */
270 if(h->numbuckets) {
271 newbuckets_count = h->numbuckets << 2;
272 /* Too big hash table */
273 if(newbuckets_count > maximum_hash_buckets_number) {
274 if(h->numbuckets < maximum_hash_buckets_number) {
275 newbuckets_count = maximum_hash_buckets_number;
276 } else {
277 /* No need to set errno here. */
278 return -1;
279 }
280 }
281 } else {
282 /* 8 buckets -> 32 bytes of memory */
283 newbuckets_count = IH_VALUES << 1;
284 if(newbuckets_count > maximum_hash_buckets_number) {
285 if(maximum_hash_buckets_number) {
286 newbuckets_count = maximum_hash_buckets_number;
287 } else {
288 /* Allowed to store only IH_VALUES elements */
289 errno = EPERM;
290 return -1;
291 }
292 }
293 }
294
295 /*
296 * Allocate a new storage for buckets.
297 */
298 newbuckets = malloc(newbuckets_count * sizeof(*newbuckets));
299 if(newbuckets) {
300 memset(newbuckets, 0, newbuckets_count * sizeof(*newbuckets));
301 } else {
302 return -1;
303 }
304
305 if(h->numbuckets) {
306 genhash_el *el;
307 int bucket;
308
309 /*
310 * Rehash elements from old h->buckets to newbuckets.
311 * No need to touch LRU pointers and other stuff - it is okay.
312 */
313 for(el = h->lru_tail; el; el = el->lru_prev) {
314 bucket = el->key_hash % newbuckets_count;
315 el->hash_prev = NULL;
316 if((el->hash_next = newbuckets[bucket]))
317 el->hash_next->hash_prev = el;
318 newbuckets[bucket] = el;
319 }
320
321 free(h->buckets);
322 h->buckets = newbuckets;
323 h->numbuckets = newbuckets_count;
324 } else {
325 /*
326 * Moving from inline tiny storage into buckets.
327 */
328 genhash_el *els[IH_VALUES] = { NULL };
329 struct _internal_tiny_s tiny_substruct;
330 int i;
331 int saved_numelements;
332 int saved_lru_limit;
333 genhash_iter_t *iter;
334
335 /* Pre-allocate hash elements (for "undo") */
336 for(i = 0; i < h->numelements; i++) {
337 els[i] = (genhash_el *)malloc(sizeof(genhash_el));
338 if(els[i] == NULL) {
339 for(i = 0; i < h->numelements; i++)
340 if(els[i])
341 free(els[i]);
342 free(newbuckets);
343 return -1;
344 }
345 }
346
347 /* Save part of the union */
348 tiny_substruct = h->un._TINY;
349 /* Re-initialize this part in NORMAL model */
350 memset(&h->un._NORMAL, 0, sizeof(h->un._NORMAL));
351
352 /* There was no allocated buckets, when in tiny hash mode. */
353 h->buckets = newbuckets;
354 h->numbuckets = newbuckets_count;
355
356 saved_numelements = h->numelements;
357 saved_lru_limit = h->lru_limit;
358 h->numelements = 0;
359 h->lru_limit = 0; /* Disable LRU expiration for a while */
360
361 for(i = saved_numelements - 1; i >= 0; --i) {
362 /*
363 * genhash_normal_add won't fail, if we supply
364 * an already allocated genhash_el *.
365 */
366 (void)_genhash_normal_add(h, els[i],
367 tiny_substruct.keys[i],
368 tiny_substruct.values[i]);
369 }
370
371 /* Now, scan through iterators and convert them TINY->NORMAL */
372 for(iter = h->iters; iter; iter = iter->iter_next) {
373 assert(iter->hash_ptr == h);
374 if(iter->un.item_number < 0
375 || iter->un.item_number >= saved_numelements) {
376 iter->un.location = 0;
377 } else {
378 iter->un.location = els[iter->un.item_number];
379 }
380 }
381
382 h->lru_limit = saved_lru_limit;
383 }
384
385 return 0;
386}
387
388/*
389 * Won't return with error if el is provided.
390 */
391static int
392_genhash_normal_add(genhash_t *h, genhash_el *el, void *key, void *value) {
393 genhash_el **bucket;
394
395 if(el == NULL) {
396 el = malloc(sizeof (*el));
397 if(el == NULL) {
398 /* Errno will be set by malloc() */
399 return -1;
400 }
401 }
402
403 /* Maintain maximum number of entries */
404 if(h->lru_limit) {
405 while(h->numelements >= h->lru_limit)
406 _remove_normal_hash_el(h, h->lru_tail);
407 }
408
409 memset(el, 0, sizeof(genhash_el));
410
411 /* Compute the index of the collision list */
412 el->key_hash = h->keyhashf(key);
413 bucket = &h->buckets[el->key_hash % h->numbuckets];
414
415 el->key = key;
416 el->value = value;
417
418 /*
419 * Add to the collision list
420 */
421 el->hash_prev = NULL;
422 if((el->hash_next = *bucket))
423 (*bucket)->hash_prev = el;
424 *bucket = el;
425
426 /*
427 * Add to the LRU list.
428 */
429 if(h->lru_head) {
430 el->lru_next = h->lru_head;
431 el->lru_next->lru_prev = el;
432 h->lru_head = el;
433 } else {
434 h->lru_head = el;
435 h->lru_tail = el;
436 }
437
438 h->numelements++;
439
440 return 0;
441}
442
443
444int
445genhash_add(genhash_t *h, void *key, void *value) {
446
447 if(key == NULL) {
448 errno = EINVAL;
449 return -1;
450 }
451
452 if(h->numbuckets == 0) {
453
454 /* We have a tiny internally-held set of elements */
455 if(h->numelements < IH_VALUES) {
456 h->tiny_keys[h->numelements] = key;
457 h->tiny_values[h->numelements] = value;
458 h->numelements++;
459 return 0;
460 }
461
462 if(_expand_hash(h) == -1)
463 return -1;
464
465 } else {
466
467 if((h->numelements / h->numbuckets) > 2)
468 (void)_expand_hash(h);
469 }
470
471 return _genhash_normal_add(h, NULL, key, value);
472}
473
474int
475genhash_addunique(genhash_t *h, void *key, void *value) {
476 if(genhash_get(h, key)) {
477 errno = EEXIST;
478 return -1;
479 }
480 return genhash_add(h, key, value);
481}
482
483void *
484genhash_get(genhash_t *h, const void *key) {
485
486 if(h->numbuckets) {
487
488 genhash_el *walk;
489 int bucket = h->keyhashf(key) % h->numbuckets;
490
491 for(walk = h->buckets[bucket];
492 walk; walk = walk->hash_next) {
493
494 if (h->keycmpf(walk->key, key) == 0) {
495 _genhash_normal_el_move2top(h, walk);
496 return walk->value;
497 }
498 }
499
500 } else {
501 /* TINY mode */
502 int i;
503
504 assert(h->numelements <= IH_VALUES);
505 for(i = 0; i < h->numelements; i++) {
506 if(h->keycmpf(h->tiny_keys[i], key) == 0)
507 /* Don't reorder in TINY mode */
508 return h->tiny_values[i];
509 }
510
511 }
512
513 errno = ESRCH;
514 return NULL;
515}
516
517int
518genhash_del(genhash_t *h, void *key) {
519
520 if(h->numbuckets) {
521 /* NORMAL mode */
522 genhash_el *walk;
523 int bucket;
524
525 if(h->numelements == 0) {
526 errno = ESRCH;
527 return -1; /* not found */
528 }
529
530 bucket = h->keyhashf(key) % h->numbuckets;
531
532 for(walk = h->buckets[bucket]; walk; walk = walk->hash_next)
533 if(h->keycmpf(walk->key, key) == 0)
534 break;
535
536 if(walk) {
537 _remove_normal_hash_el(h, walk);
538 return 0;
539 }
540 } else {
541 /* TINY mode */
542 int i;
543
544 /* Look for matching key */
545 for(i = 0; i < h->numelements; i++)
546 if(h->keycmpf(h->tiny_keys[i], key) == 0)
547 break;
548
549 if(i < h->numelements) {
550 /* Remember values */
551 void *kd_arg = h->tiny_keys[i];
552 void *vd_arg = h->tiny_values[i];
553
554 h->numelements--;
555
556 if(h->iters) {
557 /* If iterators are involved, we have to
558 * shift elements to maintain iteration order
559 * and avoid duplications */
560 genhash_iter_t *iter;
561 memmove(&h->tiny_keys[i],
562 &h->tiny_keys[i+1],
563 (h->numelements - i)
564 * sizeof(h->tiny_keys[0]));
565 memmove(&h->tiny_values[i],
566 &h->tiny_values[i+1],
567 (h->numelements - i)
568 * sizeof(h->tiny_values[0]));
569 /* Shift the iterator's indexes */
570 for(iter = h->iters; iter;
571 iter = iter->iter_next) {
572 int in = iter->un.item_number;
573 if(iter->order_lru_first) {
574 if(in > i)
575 iter->un.item_number--;
576 } else {
577 if(in >= i)
578 iter->un.item_number--;
579 }
580 }
581 } else {
582 /* Substitute it with the last one */
583 /* No harm if overwriting itself */
584 h->tiny_keys[i] = h->tiny_keys[h->numelements];
585 h->tiny_values[i] = h->tiny_values[h->numelements];
586 }
587 h->tiny_keys[h->numelements] = 0;
588 h->tiny_values[h->numelements] = 0;
589 /* Delete for real */
590 if(h->keydestroyf) h->keydestroyf(kd_arg);
591 if(h->valuedestroyf) h->valuedestroyf(vd_arg);
592 return 0;
593 }
594 }
595
596 errno = ESRCH;
597 return -1;
598}
599
600/*
601 * Initialize a hash iterator.
602 */
603int
604genhash_iter_init(genhash_iter_t *iter, genhash_t *h, int reverse_order) {
605
606 iter->hash_ptr = h;
607 iter->iter_prev = 0; /* Add itself to the iterators list */
608 iter->iter_next = h->iters;
609 h->iters = iter;
610 iter->order_lru_first = reverse_order;
611
612 if(h->numbuckets) {
613 /* NORMAL mode */
614 if(reverse_order) {
615 /* Least recent first order */
616 iter->un.location = h->lru_tail;
617 } else {
618 /* Most recent first order */
619 iter->un.location = h->lru_head;
620 }
621 } else {
622 /* TINY mode */
623 if(reverse_order) {
624 iter->un.item_number = 0;
625 } else {
626 iter->un.item_number = h->numelements - 1;
627 }
628 }
629
630 return h->numelements;
631}
632
633int
634genhash_iter(genhash_iter_t *iter, void *key_p, void *val_p) {
635 void **key = key_p;
636 void **val = val_p;
637 genhash_t *h = iter->hash_ptr;
638
639 if(h->numbuckets) {
640 /* NORMAL mode */
641 genhash_el *cur_el = iter->un.location;
642 if(!cur_el)
643 /* Already finished */
644 return 0;
645
646 if(key) *key = cur_el->key;
647 if(val) *val = cur_el->value;
648
649 /* Move pointer to the next hash element */
650 iter->un.location = iter->order_lru_first
651 ? cur_el->lru_prev : cur_el->lru_next;
652 } else {
653 /* TINY mode */
654 if(iter->un.item_number < 0
655 || iter->un.item_number >= h->numelements
656 || h->tiny_keys[iter->un.item_number] == 0)
657 return 0;
658
659 if(key) *key = h->tiny_keys[iter->un.item_number];
660 if(val) *val = h->tiny_values[iter->un.item_number];
661
662 /* Advance to the next element */
663 if(iter->order_lru_first)
664 iter->un.item_number++;
665 else
666 iter->un.item_number--;
667 }
668
669
670 return 1;
671}
672
673void
674genhash_iter_done(genhash_iter_t *iter) {
675 assert(iter->hash_ptr->iters);
676 /* Remove itself from the iterators list */
677 if(iter->iter_next)
678 iter->iter_next->iter_prev = iter->iter_prev;
679 if(iter->iter_prev)
680 iter->iter_prev->iter_next = iter->iter_next;
681 else
682 iter->hash_ptr->iters = iter->iter_next; /* Shift the head */
683 iter->hash_ptr = (void *)0xdeadbeef;
684}
685
686int
687genhash_set_lru_limit(genhash_t *h, int value) {
688 if(h) {
689 int prev_limit = h->lru_limit;
690 if(value >= 0)
691 h->lru_limit = value;
692 return prev_limit;
693 } else {
694 errno = EINVAL;
695 return -1;
696 }
697}
698
699int
700genhash_set_buckets_limit(int value) {
701 int prev_limit = maximum_hash_buckets_number;
702 if(value > 0) {
703 maximum_hash_buckets_number = value;
704 }
705 return prev_limit;
706}
707
708void
709genhash_destroy(genhash_t *h) {
710 if(h) {
711 assert(h->iters == 0); /* All iterators MUST be _done(). */
712 genhash_empty(h, 1, 1);
713 free(h);
714 }
715}
716
717void
718genhash_empty(genhash_t *h, int freekeys, int freevalues) {
719 genhash_iter_t *iter;
720
721 if(h == NULL) return;
722
723 /*
724 * Don't free what could not be freed.
725 */
726 if(h->keydestroyf == NULL) freekeys = 0;
727 if(h->valuedestroyf == NULL) freevalues = 0;
728
729 if(h->numbuckets == 0) {
730 while(h->numelements > 0) {
731 int n = --h->numelements;
732 void *kd_arg = h->tiny_keys[n];
733 void *vd_arg = h->tiny_values[n];
734
735 if (freekeys) h->keydestroyf(kd_arg);
736 if (freevalues) h->valuedestroyf(vd_arg);
737 }
738 } else {
739 genhash_el *el, *el_next;
740
741 for(el = h->lru_head; el; el = el_next) {
742 void *kd_arg = el->key;
743 void *vd_arg = el->value;
744 el_next = el->lru_next;
745 free(el);
746
747 h->numelements --;
748
749 if (freekeys) h->keydestroyf(kd_arg);
750 if (freevalues) h->valuedestroyf(vd_arg);
751 }
752 free(h->buckets);
753 h->numbuckets = 0; /* Move back to TINY model */
754 }
755 memset(&h->un, 0, sizeof(h->un));
756
757 /* Invalidate iterators in TINY model */
758 for(iter = h->iters; iter; iter = iter->iter_next) {
759 assert(iter->hash_ptr == h);
760 iter->un.item_number = -1;
761 }
762
763 assert(h->numelements == 0);
764}
765
766
767/*----- Simple hash and compare functions for common data types ------*/
768
769unsigned int
770hashf_int (const void *key) {
Lev Walkin08661a62017-11-06 02:08:47 -0800771 return (*(const ptrdiff_t *)key ^ (*(const ptrdiff_t *)key >> 16));
Lev Walkinadfcde22017-11-05 22:45:54 -0800772}
773
774int
775cmpf_int (const void *key1, const void *key2) {
776 return (*(const int *)key1 != *(const int *)key2);
777}
778
779unsigned int
780hashf_void (const void *key) {
Lev Walkin08661a62017-11-06 02:08:47 -0800781 return ((ptrdiff_t)key ^ ((ptrdiff_t)key >> 16));
Lev Walkinadfcde22017-11-05 22:45:54 -0800782}
783
784int
785cmpf_void (const void *key1, const void *key2) {
786 return (key1 != key2);
787}
788
789
790/*
791 * Phong's linear congruential hash
792 */
793#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
794
795unsigned int
796hashf_string(const void *keyarg) {
797 register const unsigned char *key;
798 register unsigned int h;
799 register unsigned char c;
800
801 key = keyarg;
802 for (h = 0; (c = *key++);)
803 dcharhash(h, c);
804
805 return (h);
806}
807
808int
809cmpf_string(const void *key1, const void *key2) {
810 return strcmp((const char *)key1, (const char *)key2);
811}
812