1 /*
2 * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 #include "flang/ADT/hash.h"
19 #include "flang/Error/pgerror.h"
20 #include <stdlib.h>
21 #include <string.h>
22
23 #if UNIT_TESTING
24 #include <stdarg.h>
25 #include <stddef.h>
26 #include <setjmp.h>
27 #include "cmockery.h"
28 #endif /* UNIT_TESTING */
29
30 /** \file
31 * \brief Open addressing, quadratically probed hash tables.
32 *
33 * All keys are kept in a single array with 2^n entries. A key is first looked
34 * up at index hash(k) mod 2^k, then following a quadratic sequence of offsets.
35 * The quadratic probing has decent locality of reference for the first few
36 * probes.
37 *
38 * The search terminates when the key is found or a NULL is encountered. A
39 * special ERASED marker is used to avoid severing probe sequences when erasing
40 * keys.
41 */
42
43 #define ERASED (hash_key_t) ~0UL
44 #define ISKEY(k) ((k) != NULL && (k) != ERASED)
45
46 struct hashset_ {
47 hash_functions_t func;
48 hash_key_t *table;
49
50 /** Table size minus 1 is a bit mask. */
51 unsigned mask;
52
53 /** Number of ISKEY() entries in table. */
54 unsigned entries;
55
56 /** Number of ERASED entries in table. */
57 unsigned erased;
58 };
59
60 /** \brief A hashmap adds a table of data pointers after the hashset table. */
61 struct hashmap_ {
62 struct hashset_ set;
63 };
64
65 #define DTABLE(h) (((h)->table + 1) + (h)->mask)
66
67 /*
68 Quadrating probing sequence.
69
70 Given an initial hash value h, we probe at table indices
71
72 h, h+1, h+1+2, h+1+2+3, ... ( mod 2^n ), or
73
74 h+(i+1)i/2 ( mod 2^n ), for i = 0 .. 2^n.
75
76 This sequence visits the entire table without duplicates. Suppose two
77 indexes in the sequence are the same entry:
78
79 (i+1)i/2 = (j+1)j/2 ( mod 2^n )
80 ==> i^2 + i = j^2 + j ( mod 2^(n+1) )
81 ==> (i+j)(i-j) + i-j = 0 ( mod 2^(n+1) )
82 ==> (i+j+1)(i-j) = 0 ( mod 2^(n+1) )
83
84 Look at the difference of the factors on the left-hand side:
85
86 (i+j+1) - (i-j) = 2j + 1
87
88 This is an odd number, so one of the factors must be odd and thus coprime
89 with 2^(n+1). Therefore, 2^(n+1) must divide the other factor. The range of
90 the factors can be determined from the ranges of i and j:
91
92 1 <= i+j+1 <= 2^(n+1)-1, and
93 -(2^n-1) <= i-j <= 2^n-1.
94
95 The only way 2^(n+1) can divide one of the two factors is if i = j.
96
97 q.e.d.
98 */
99
100 /** \brief Search for key, return index that terminated the search. */
101 static unsigned
search(hashset_t h,hash_key_t key)102 search(hashset_t h, hash_key_t key)
103 {
104 unsigned p, s = 1;
105 hash_equality_t eq = h->func.equals;
106
107 assert(ISKEY(key), "Invalid key for hash", HKEY2INT(key), ERR_Fatal);
108
109 p = h->func.hash(key) & h->mask;
110 if (eq) {
111 /* General case where we have to call eq() to determine if keys are
112 * equivalent. */
113 while (h->table[p]) {
114 if (h->table[p] != ERASED && eq(key, h->table[p]))
115 break;
116 p = (p + s++) & h->mask;
117 }
118 } else {
119 /* Optimized case when eq is NULL and we simply test for pointer
120 * equality. */
121 while (h->table[p]) {
122 if (key == h->table[p])
123 break;
124 p = (p + s++) & h->mask;
125 }
126 }
127 return p;
128 }
129
130 /** \brief Find insertion point for key. */
131 static unsigned
insertion_point(hashset_t h,hash_key_t key)132 insertion_point(hashset_t h, hash_key_t key)
133 {
134 unsigned p, s = 1;
135
136 assert(ISKEY(key), "Invalid key for hash", HKEY2INT(key), ERR_Fatal);
137
138 p = h->func.hash(key) & h->mask;
139 while (ISKEY(h->table[p])) {
140 p = (p + s++) & h->mask;
141 }
142 return p;
143 }
144
145 /*
146 The number of NULL entries is kept above 1/8th of the whole table to avoid
147 too long probe sequences. The table is rehashed before the NULL entries drop
148 below that.
149
150 The ideal table size is the smallest power of two that keeps the load factor
151 below 2/3.
152 */
153
154 #define NEED_REHASH(h) ((h)->entries + (h)->erased >= (h)->mask - (h)->mask / 8)
155 #define MINSIZE 16
156
157 static unsigned
mask_for_entries(unsigned n)158 mask_for_entries(unsigned n)
159 {
160 unsigned m = (n + n / 2) | (MINSIZE - 1);
161
162 /* Arithmetic overflow happens after we have billions of entries. */
163 assert(m > n, "Hash table full", n, ERR_Fatal);
164
165 /* Round up to the next power of two minus one. */
166 m |= m >> 1;
167 m |= m >> 2;
168 m |= m >> 4;
169 m |= m >> 8;
170 m |= m >> 16;
171
172 return m;
173 }
174
175 /* Compute h->mask from h->entries and allocate table and dtable. */
176 static void
alloc_tables(hashset_t h,int with_dtable)177 alloc_tables(hashset_t h, int with_dtable)
178 {
179 h->mask = mask_for_entries(h->entries);
180
181 /* Allocate table + dtable in one calloc(). */
182 if (with_dtable)
183 h->table = (const void**) calloc(
184 1 + (size_t)h->mask, sizeof(hash_key_t) + sizeof(hash_data_t));
185 else
186 h->table = (const void**) calloc(
187 1 + (size_t)h->mask, sizeof(hash_key_t));
188 }
189
190 /* Resize and rehash table to get rid of ERASED entries.
191
192 If not NULL, dtable points to a table of data entries that should be
193 rearranged the same way as h->table.
194 */
195 static void
rehash(hashset_t h,int with_dtable)196 rehash(hashset_t h, int with_dtable)
197 {
198 hash_key_t *old_table = h->table;
199 hash_data_t *old_dtable = DTABLE(h);
200 size_t n, old_size = 1 + (size_t)h->mask;
201 hash_data_t *new_dtable = NULL;
202
203 alloc_tables(h, with_dtable);
204 new_dtable = DTABLE(h);
205
206 /* There will be no ERASED markers after the rehash. */
207 h->erased = 0;
208
209 for (n = 0; n < old_size; n++) {
210 hash_key_t key = old_table[n];
211 if (ISKEY(key)) {
212 unsigned i = insertion_point(h, key);
213 h->table[i] = key;
214 if (with_dtable)
215 new_dtable[i] = old_dtable[n];
216 }
217 }
218
219 free(old_table);
220 }
221
222 hashset_t
hashset_alloc(hash_functions_t f)223 hashset_alloc(hash_functions_t f)
224 {
225 hashset_t h = (hashset_t) calloc(1, sizeof(struct hashset_));
226 h->func = f;
227 alloc_tables(h, 0);
228 return h;
229 }
230
231 hashmap_t
hashmap_alloc(hash_functions_t f)232 hashmap_alloc(hash_functions_t f)
233 {
234 hashmap_t h = (hashmap_t) calloc(1, sizeof(struct hashmap_));
235 h->set.func = f;
236 alloc_tables(&h->set, 1);
237 return h;
238 }
239
240 void
hashset_free(hashset_t h)241 hashset_free(hashset_t h)
242 {
243 free(h->table);
244 memset(h, 0, sizeof(*h));
245 free(h);
246 }
247
248 void
hashmap_free(hashmap_t h)249 hashmap_free(hashmap_t h)
250 {
251 free(h->set.table);
252 memset(h, 0, sizeof(*h));
253 free(h);
254 }
255
256 void
hashset_clear(hashset_t h)257 hashset_clear(hashset_t h)
258 {
259 free(h->table);
260 h->entries = h->erased = 0;
261 alloc_tables(h, 0);
262 }
263
264 void
hashmap_clear(hashmap_t h)265 hashmap_clear(hashmap_t h)
266 {
267 free(h->set.table);
268 h->set.entries = h->set.erased = 0;
269 alloc_tables(&h->set, 1);
270 }
271
272 unsigned
hashset_size(hashset_t h)273 hashset_size(hashset_t h)
274 {
275 return h->entries;
276 }
277
278 unsigned
hashmap_size(hashmap_t h)279 hashmap_size(hashmap_t h)
280 {
281 return h->set.entries;
282 }
283
284 hash_key_t
hashset_lookup(hashset_t h,hash_key_t key)285 hashset_lookup(hashset_t h, hash_key_t key)
286 {
287 return h->table[search(h, key)];
288 }
289
290 hash_key_t
hashmap_lookup(hashmap_t h,hash_key_t key,hash_data_t * data)291 hashmap_lookup(hashmap_t h, hash_key_t key, hash_data_t *data)
292 {
293 unsigned i = search(&h->set, key);
294 hash_key_t k = h->set.table[i];
295 if (data && k)
296 *data = DTABLE(&h->set)[i];
297 return k;
298 }
299
300 void
hashset_insert(hashset_t h,hash_key_t key)301 hashset_insert(hashset_t h, hash_key_t key)
302 {
303 unsigned i;
304
305 #if DEBUG
306 assert(hashset_lookup(h, key) == NULL, "Duplicate hash key", 0, ERR_Fatal);
307 #endif
308
309 if (NEED_REHASH(h))
310 rehash(h, 0);
311
312 i = insertion_point(h, key);
313 if (h->table[i] == ERASED)
314 h->erased--;
315 h->table[i] = key;
316 h->entries++;
317 }
318
319 void
hashmap_insert(hashmap_t h,hash_key_t key,hash_data_t data)320 hashmap_insert(hashmap_t h, hash_key_t key, hash_data_t data)
321 {
322 unsigned i;
323
324 #if DEBUG
325 assert(hashmap_lookup(h, key, NULL) == NULL, "Duplicate hash key", 0, ERR_Fatal);
326 #endif
327
328 if (NEED_REHASH(&h->set))
329 rehash(&h->set, 1);
330
331 i = insertion_point(&h->set, key);
332 if (h->set.table[i] == ERASED)
333 h->set.erased--;
334 h->set.table[i] = key;
335 DTABLE(&h->set)[i] = data;
336 h->set.entries++;
337 }
338
339 hash_key_t
hashset_replace(hashset_t h,hash_key_t key)340 hashset_replace(hashset_t h, hash_key_t key)
341 {
342 unsigned i = search(h, key);
343 hash_key_t old = h->table[i];
344
345 if (ISKEY(old)) {
346 h->table[i] = key;
347 return old;
348 }
349
350 hashset_insert(h, key);
351 return NULL;
352 }
353
354 hash_key_t
hashmap_replace(hashmap_t h,hash_key_t key,hash_data_t * data)355 hashmap_replace(hashmap_t h, hash_key_t key, hash_data_t *data)
356 {
357 unsigned i = search(&h->set, key);
358 hash_key_t old = h->set.table[i];
359
360 if (ISKEY(old)) {
361 hash_data_t old_data = DTABLE(&h->set)[i];
362 h->set.table[i] = key;
363 DTABLE(&h->set)[i] = *data;
364 *data = old_data;
365 return old;
366 }
367
368 hashmap_insert(h, key, *data);
369 return NULL;
370 }
371
372 /* We never rehash while erasing elements. The rehash() at insertion can also
373 * shrink the table. */
374 hash_key_t
hashset_erase(hashset_t h,hash_key_t key)375 hashset_erase(hashset_t h, hash_key_t key)
376 {
377 unsigned i = search(h, key);
378 hash_key_t old = h->table[i];
379
380 if (!old)
381 return NULL;
382
383 h->table[i] = ERASED;
384 h->erased++;
385 h->entries--;
386 return old;
387 }
388
389 hash_key_t
hashmap_erase(hashmap_t h,hash_key_t key,hash_data_t * data)390 hashmap_erase(hashmap_t h, hash_key_t key, hash_data_t *data)
391 {
392 unsigned i = search(&h->set, key);
393 hash_key_t old = h->set.table[i];
394
395 if (!old)
396 return NULL;
397
398 h->set.table[i] = ERASED;
399 h->set.erased++;
400 h->set.entries--;
401 if (data)
402 *data = DTABLE(&h->set)[i];
403 return old;
404 }
405
406 void
hashset_iterate(hashset_t h,void (* f)(hash_key_t,void * context),void * context)407 hashset_iterate(hashset_t h, void (*f)(hash_key_t, void *context),
408 void *context)
409 {
410 size_t n, size = 1 + (size_t)h->mask;
411
412 for (n = 0; n < size; n++) {
413 hash_key_t key = h->table[n];
414 if (ISKEY(key))
415 f(key, context);
416 }
417 }
418
419 void
hashmap_iterate(hashmap_t h,void (* f)(hash_key_t,hash_data_t,void * context),void * context)420 hashmap_iterate(hashmap_t h, void (*f)(hash_key_t, hash_data_t, void *context),
421 void *context)
422 {
423 size_t n, size = 1 + (size_t)h->set.mask;
424 hash_data_t *dtable = DTABLE(&h->set);
425
426 for (n = 0; n < size; n++) {
427 hash_key_t key = h->set.table[n];
428 if (ISKEY(key))
429 f(key, dtable[n], context);
430 }
431 }
432
433 /* String hashing */
434 static hash_value_t
string_hash(hash_key_t key)435 string_hash(hash_key_t key)
436 {
437 const unsigned char *p = (const unsigned char *)key;
438 hash_accu_t hacc = HASH_ACCU_INIT;
439 for (; *p; p++)
440 HASH_ACCU_ADD(hacc, *p);
441 HASH_ACCU_FINISH(hacc);
442 return HASH_ACCU_VALUE(hacc);
443 }
444
445 static int
string_equals(hash_key_t a,hash_key_t b)446 string_equals(hash_key_t a, hash_key_t b)
447 {
448 return strcmp((const char *)a, (const char *)b) == 0;
449 }
450
451 const hash_functions_t hash_functions_strings = {string_hash, string_equals};
452
453 /* Direct hashing. */
454 static hash_value_t
direct_hash(hash_key_t key)455 direct_hash(hash_key_t key)
456 {
457 unsigned long k = (unsigned long)key;
458 hash_accu_t hacc = HASH_ACCU_INIT;
459 HASH_ACCU_ADD(hacc, k);
460 /* On an LP64 system, this ignores the high 8 bits of k. That's OK if k
461 * represents a pointer, since current 64-bit systems don't use the high
462 * bits of addresses. A shift by 32 would be undefined when long is a
463 * 32-bit type. */
464 HASH_ACCU_ADD(hacc, k >> 24);
465 HASH_ACCU_FINISH(hacc);
466 return HASH_ACCU_VALUE(hacc);
467 }
468
469 const hash_functions_t hash_functions_direct = {direct_hash, NULL};
470
471 /* Everything below is only for testing the implementation. */
472 #if UNIT_TESTING
473
474 #include <stdio.h>
475
476 static void
table_size(void ** state)477 table_size(void **state)
478 {
479 assert_int_equal(mask_for_entries(0), 15);
480 assert_int_equal(mask_for_entries(1), 15);
481 assert_int_equal(mask_for_entries(10), 15);
482 assert_int_equal(mask_for_entries(11), 31);
483 assert_int_equal(mask_for_entries(20), 31);
484 assert_int_equal(mask_for_entries(21), 31);
485 assert_int_equal(mask_for_entries(22), 63);
486 assert_int_equal(mask_for_entries(0x80000000), 0xffffffff);
487 assert_int_equal(mask_for_entries(0xa0000000), 0xffffffff);
488 /* 0xb0000000 will overflow and assert, but expect_assert_failure()
489 * segfaults... */
490 }
491
492 static void
hash_funcs(void ** state)493 hash_funcs(void **state)
494 {
495 assert_int_equal(string_hash(""), 0);
496 assert_int_equal(string_hash("a"), 0xca2e9442);
497 assert_int_equal(string_hash("A"), 0x820103f0);
498 assert_int_equal(string_hash("foo"), 0x238678dd);
499
500 assert_int_equal(direct_hash((hash_key_t)0), 0);
501 assert_int_equal(direct_hash((hash_key_t)1), 0x20e9c0b3);
502 assert_int_equal(direct_hash((hash_key_t)2), 0x41d38166);
503 }
504
505 static void
basic_direct(void ** state)506 basic_direct(void **state)
507 {
508 unsigned long i;
509 hashset_t h = hashset_alloc(hash_functions_direct);
510 assert_true(h != NULL);
511 assert_int_equal(hashset_size(h), 0);
512 assert_int_equal(h->mask, MINSIZE - 1);
513
514 assert_int_equal(hashset_lookup(h, (hash_key_t)1), 0);
515 assert_int_equal(hashset_lookup(h, (hash_key_t)2), 0);
516
517 hashset_insert(h, (hash_key_t)1);
518 assert_int_equal(hashset_lookup(h, (hash_key_t)1), 1);
519 assert_int_equal(hashset_lookup(h, (hash_key_t)2), 0);
520
521 assert_int_equal(hashset_replace(h, (hash_key_t)1), 1);
522 assert_int_equal(hashset_replace(h, (hash_key_t)2), 0);
523
524 for (i = 3; i <= 14; i++)
525 hashset_insert(h, (hash_key_t)i);
526
527 /* Table has 16 entries, 2 are NULL for exactly 7/8 load factor. */
528 assert_int_equal(hashset_size(h), 14);
529 assert_int_equal(h->mask, MINSIZE - 1);
530
531 /* Trigger a rehash when we add the 15th element. */
532 hashset_insert(h, (hash_key_t)15);
533 assert_int_equal(hashset_size(h), 15);
534 assert_int_equal(h->mask, 2 * MINSIZE - 1);
535
536 /* Remove odd entries. */
537 for (i = 1; i <= 15; i += 2)
538 assert_int_equal(hashset_erase(h, (hash_key_t)i), i);
539 assert_int_equal(hashset_size(h), 7);
540 assert_int_equal(h->mask, 2 * MINSIZE - 1);
541 assert_int_equal(h->erased, 8);
542
543 /* Remove everything. */
544 for (i = 1; i <= 15; i++)
545 assert_int_equal(hashset_erase(h, (hash_key_t)i), i % 2 ? 0 : i);
546
547 /* Set is empty, but hasn't rehashed yet. */
548 assert_int_equal(hashset_size(h), 0);
549 assert_int_equal(h->mask, 2 * MINSIZE - 1);
550 assert_int_equal(h->erased, 15);
551
552 for (i = 1; i <= 20; i++)
553 assert_int_equal(hashset_erase(h, (hash_key_t)i), 0);
554 assert_int_equal(hashset_size(h), 0);
555 assert_int_equal(h->mask, 2 * MINSIZE - 1);
556
557 /* Insert triggers a table shrink. Eventually. The exact timing depends on
558 * the hash function behavior. */
559 for (i = 1; i <= 10; i++)
560 hashset_insert(h, (hash_key_t)(100 + i));
561 assert_int_equal(h->mask, 2 * MINSIZE - 1);
562 for (i = 1; i <= 10; i++)
563 hashset_erase(h, (hash_key_t)(100 + i));
564 assert_int_equal(h->mask, 2 * MINSIZE - 1);
565 for (i = 1; i <= 10; i++)
566 hashset_insert(h, (hash_key_t)(200 + i));
567 assert_int_equal(h->mask, 2 * MINSIZE - 1);
568 for (i = 1; i <= 10; i++)
569 hashset_erase(h, (hash_key_t)(200 + i));
570 assert_int_equal(h->mask, 2 * MINSIZE - 1);
571 for (i = 1; i <= 10; i++)
572 hashset_insert(h, (hash_key_t)(300 + i));
573 assert_int_equal(h->mask, 2 * MINSIZE - 1);
574 for (i = 1; i <= 10; i++)
575 hashset_erase(h, (hash_key_t)(300 + i));
576 assert_int_equal(h->mask, 2 * MINSIZE - 1);
577 for (i = 1; i <= 10; i++)
578 hashset_insert(h, (hash_key_t)(400 + i));
579 assert_int_equal(h->erased, 0);
580 assert_int_equal(h->mask, MINSIZE - 1);
581
582 hashset_free(h);
583 }
584
585 static void
free_iterator(hash_key_t key,void * context)586 free_iterator(hash_key_t key, void *context)
587 {
588 ++*(unsigned *)context;
589 free((void *)key);
590 }
591
592 static void
basic_string(void ** state)593 basic_string(void **state)
594 {
595 unsigned i;
596 hashset_t h = hashset_alloc(hash_functions_strings);
597 const char *strs[] = {"foo", "bar", "baz", "quux"};
598 char buf[20];
599
600 for (i = 0; i != 4; i++)
601 hashset_insert(h, (hash_key_t)strs[i]);
602
603 strcpy(buf, "foo");
604 assert_int_equal(hashset_lookup(h, buf), strs[0]);
605 strcpy(buf, "fooo");
606 assert_int_not_equal(hashset_lookup(h, buf), strs[0]);
607
608 for (i = 0; i < 1000; i++) {
609 char *x = malloc(10);
610 sprintf(x, "x%d", i);
611 hashset_insert(h, x);
612 }
613
614 for (i = 0; i != 4; i++)
615 hashset_erase(h, (hash_key_t)strs[i]);
616
617 i = 0;
618 assert_int_equal(hashset_size(h), 1000);
619 hashset_iterate(h, free_iterator, &i);
620 assert_int_equal(i, 1000);
621
622 hashset_free(h);
623 }
624
625 static hash_value_t
worst_hash_ever(hash_key_t key)626 worst_hash_ever(hash_key_t key)
627 {
628 return 42;
629 }
630
631 static const hash_functions_t hash_functions_worst = {worst_hash_ever, NULL};
632
633 static void
worst_case(void ** state)634 worst_case(void **state)
635 {
636 unsigned long i;
637 hashset_t h = hashset_alloc(hash_functions_worst);
638
639 for (i = 1; i <= 14; i++)
640 hashset_insert(h, (hash_key_t)i);
641
642 /* Table has 16 entries, 2 are NULL for exactly 7/8 load factor. */
643 assert_int_equal(hashset_size(h), 14);
644 assert_int_equal(h->mask, MINSIZE - 1);
645
646 /* The bad hash function stresses the probing sequence to find the NULLS.
647 * The insert and the lookup can both loop infinitely if their probing
648 * sequence doesn't completely cover the table. */
649 for (i = 15; i <= 1000; i++)
650 hashset_insert(h, (hash_key_t)i);
651
652 for (i = 1; i <= 2000; i += 100)
653 assert_int_equal(hashset_lookup(h, (hash_key_t)i), i <= 1000 ? i : 0);
654
655 hashset_free(h);
656 }
657
658 static void
basic_map(void ** state)659 basic_map(void **state)
660 {
661 hashmap_t h = hashmap_alloc(hash_functions_direct);
662 hash_data_t datum;
663 const char *s1 = "foo", *s2 = "bar";
664 unsigned long i;
665
666 assert_int_equal(hashmap_size(h), 0);
667
668 /* Lookup nonexistent data with NULL data pointer. */
669 assert_int_equal(hashmap_lookup(h, (hash_key_t)1, NULL), 0);
670
671 /* Lookup nonexistent data with non-NULL data pointer. */
672 datum = &h;
673 assert_int_equal(hashmap_lookup(h, (hash_key_t)1, &datum), 0);
674 assert_int_equal(datum, &h);
675
676 hashmap_insert(h, (hash_key_t)1, (hash_data_t)s1);
677 hashmap_insert(h, (hash_key_t)2, (hash_data_t)s2);
678
679 /* We support lookup with NULL data pointer. */
680 assert_int_equal(hashmap_lookup(h, (hash_key_t)1, NULL), 1);
681
682 datum = (hash_data_t)s2;
683 assert_int_equal(hashmap_lookup(h, (hash_key_t)1, &datum), 1);
684 assert_int_equal(datum, s1);
685
686 /* Replace where no previous key existed. */
687 datum = (hash_data_t)s2;
688 assert_int_equal(hashmap_replace(h, (hash_key_t)3, &datum), 0);
689 assert_int_equal(datum, s2);
690
691 /* Replace previous key. Should return previous data. */
692 datum = (hash_data_t)s1;
693 assert_int_equal(hashmap_replace(h, (hash_key_t)3, &datum), 3);
694 assert_int_equal(datum, s2);
695
696 /* Force rehash. Verify that data entries are copied correctly. */
697 for (i = 1; i < 100; i++) {
698 datum = (hash_data_t)(i % 7);
699 hashmap_replace(h, (hash_key_t)i, &datum);
700 }
701 for (i = 1; i < 100; i++) {
702 assert_int_equal(hashmap_lookup(h, (hash_key_t)i, &datum), i);
703 assert_int_equal(datum, i % 7);
704 }
705
706 hashmap_free(h);
707 }
708
709 int
main()710 main()
711 {
712 const UnitTest tests[] = {
713 unit_test(table_size), unit_test(hash_funcs), unit_test(basic_direct),
714 unit_test(basic_string), unit_test(worst_case), unit_test(basic_map),
715 };
716 return run_tests(tests);
717 }
718
719 #endif /* UNIT_TESTING */
720