1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 /**
27 * Unit tests for hash files
28 */
29
30 #include <ktst/unit_test.hpp>
31
32 #include <arch-impl.h>
33 #include <atomic.h>
34 #include <cstdlib>
35 #include <cstring>
36 #include <kfs/defs.h>
37 #include <kfs/directory.h>
38 #include <kfs/file.h>
39 #include <klib/data-buffer.h>
40 #include <klib/hashfile.h>
41 #include <klib/hashtable.h>
42 #include <klib/log.h>
43 #include <klib/misc.h> /* is_user_admin() */
44 #include <klib/num-gen.h>
45 #include <klib/printf.h>
46 #include <klib/sort.h>
47 #include <klib/text.h>
48 #include <klib/time.h>
49 #include <klib/vector.h>
50 #include <kproc/lock.h>
51 #include <kproc/thread.h>
52 #include <stdexcept>
53 #include <stdint.h>
54 #include <stdio.h>
55 #include <string>
56 #include <sys/time.h>
57 #include <unistd.h>
58 #include <unordered_map>
59 #include <unordered_set>
60 #include <utility>
61 #include <vector>
62
63 using namespace std;
64
65 #define RANDS_SIZE 2000000
66 static uint64_t RANDS[RANDS_SIZE];
67
68 static volatile bool KEEPRUNNING = true;
69 static atomic64_t FINDCOUNT;
70 static KDirectory* DIR = NULL;
71 static KFile* BACKING = NULL;
72 static KHashFile* HMAP = NULL;
73
74 /* Very fast RNG */
fastrng(uint64_t * state0)75 static __inline uint64_t fastrng(uint64_t* state0)
76 {
77 *state0 += 3800322248010097311ULL;
78 const uint64_t s0 = *state0;
79 const uint64_t rot = s0 >> 60;
80 *state0 *= 12898287266413564321ULL;
81 uint64_t xs = uint64_ror(s0, 18) + s0;
82 xs = uint64_ror(xs, rot);
83 return xs;
84 }
85
86 //#define BENCHMARK
87
88 #ifdef BENCHMARK
89 static KLock* KLOCK = NULL;
90 static KHashTable* HTABLE = NULL;
91 // Number of microseconds since last called
stopwatch(void)92 static unsigned long stopwatch(void)
93 {
94 static unsigned long start = 0;
95 struct timeval tv_cur;
96
97 gettimeofday(&tv_cur, NULL);
98 unsigned long finish = tv_cur.tv_sec * 1000000 + tv_cur.tv_usec;
99 unsigned long elapsed = finish - start;
100 start = finish;
101 return elapsed;
102 }
103 #endif // BENCHMARK
104
105 TEST_SUITE(KHashFileTestSuite);
106
TEST_CASE(Klib_KHashFileSet)107 TEST_CASE(Klib_KHashFileSet)
108 {
109 rc_t rc;
110 const char* str1 = "Tu estas probando este hoy, no manana";
111 const char* str2 = "Tu estas probando este hoy, no mananX";
112 size_t size = strlen(str1);
113
114 KHashFile* hset = NULL;
115 rc = KHashFileMake(NULL, NULL);
116 REQUIRE_RC_FAIL(rc);
117 REQUIRE_EQ((void*)hset, (void*)NULL);
118
119 rc = KHashFileMake(&hset, NULL);
120 REQUIRE_RC(rc);
121 REQUIRE_NE((void*)hset, (void*)NULL);
122
123 rc = KHashFileReserve(hset, 100);
124 REQUIRE_RC(rc);
125 size_t sz = KHashFileCount(hset);
126 REQUIRE_EQ(sz, (size_t)0);
127
128 uint64_t hash = KHash(str1, size);
129 rc = KHashFileAdd(hset, str1, strlen(str1), hash, NULL, 0);
130 REQUIRE_RC(rc);
131
132 sz = KHashFileCount(hset);
133 REQUIRE_EQ(sz, (size_t)1);
134
135 rc = KHashFileAdd(hset, str1, strlen(str1), hash, NULL, 0);
136 REQUIRE_RC(rc);
137
138 sz = KHashFileCount(hset);
139 REQUIRE_EQ(sz, (size_t)1);
140
141 bool found;
142 found = KHashFileFind(hset, str1, strlen(str1), hash, NULL, NULL);
143 REQUIRE_EQ(found, true);
144
145 found = KHashFileFind(hset, str2, strlen(str2), hash, NULL, NULL);
146 REQUIRE_EQ(found, false);
147
148 KHashFileDispose(hset);
149 }
150
TEST_CASE(Klib_hashfileMap)151 TEST_CASE(Klib_hashfileMap)
152 {
153 rc_t rc;
154 const char* str1 = "Tu estas probando este hoy, no manana";
155 const char* str2 = "Tu estas probando este hoy, no mananX";
156
157 KHashFile* hmap;
158 rc = KHashFileMake(&hmap, BACKING);
159 REQUIRE_RC(rc);
160
161 size_t sz = KHashFileCount(hmap);
162 REQUIRE_EQ(sz, (size_t)0);
163
164 uint64_t hash = 1;
165 uint64_t val1 = 123;
166 rc = KHashFileAdd(hmap, str1, strlen(str1), hash, &val1, sizeof(val1));
167 REQUIRE_RC(rc);
168
169 sz = KHashFileCount(hmap);
170 REQUIRE_EQ(sz, (size_t)1);
171
172 rc = KHashFileAdd(hmap, str1, strlen(str1), hash, &val1, sizeof(val1));
173 REQUIRE_RC(rc);
174
175 sz = KHashFileCount(hmap);
176 REQUIRE_EQ(sz, (size_t)1);
177
178 bool found;
179 uint64_t val;
180 uint64_t len = 0;
181 found = KHashFileFind(hmap, str1, strlen(str1), hash, NULL, 0);
182 REQUIRE_EQ(found, true);
183 found = KHashFileFind(hmap, str1, strlen(str1), hash, &val, &len);
184 REQUIRE_EQ(found, true);
185 REQUIRE_EQ(len, sizeof(val));
186 len = 0;
187 REQUIRE_EQ(val, (uint64_t)123);
188
189 uint64_t val2 = 124;
190 rc = KHashFileAdd(hmap, str1, strlen(str1), hash, &val2, sizeof(val2));
191 REQUIRE_RC(rc);
192
193 sz = KHashFileCount(hmap);
194 REQUIRE_EQ(sz, (size_t)1);
195
196 found = KHashFileFind(hmap, str1, strlen(str1), hash, &val, &len);
197 REQUIRE_EQ(found, true);
198 REQUIRE_EQ(len, sizeof(val));
199 len = 0;
200 REQUIRE_EQ(val, (uint64_t)124);
201
202 found = KHashFileFind(hmap, str2, strlen(str2), hash, NULL, 0);
203 REQUIRE_EQ(found, false);
204
205 uint64_t val3 = 125;
206 rc = KHashFileAdd(hmap, str2, strlen(str2), hash, &val3, sizeof(val3));
207 REQUIRE_RC(rc);
208
209 found = KHashFileFind(hmap, str2, strlen(str2), hash, &val, &len);
210 REQUIRE_EQ(found, true);
211 REQUIRE_EQ(len, sizeof(val));
212 len = 0;
213 REQUIRE_EQ(val, (uint64_t)125);
214
215 sz = KHashFileCount(hmap);
216 REQUIRE_EQ(sz, (size_t)2);
217
218 KHashFileDispose(hmap);
219 }
220
TEST_CASE(Klib_hashfiledups)221 TEST_CASE(Klib_hashfiledups)
222 {
223 rc_t rc;
224
225 KHashFile* hmap;
226 rc = KHashFileMake(&hmap, BACKING);
227 REQUIRE_RC(rc);
228
229 uint64_t key1 = random();
230 uint64_t hash1 = KHash((const char*)&key1, sizeof(uint64_t));
231 uint64_t key2 = random();
232 uint64_t hash2 = hash1;
233 for (size_t i = 0; i != 100000; ++i) {
234 rc = KHashFileAdd(hmap, (const char*)&key1, 8, hash1,
235 (const char*)&key1, 8);
236 REQUIRE_RC(rc);
237 rc = KHashFileAdd(hmap, (const char*)&key2, 8, hash2,
238 (const char*)&key1, 8);
239 REQUIRE_RC(rc);
240 }
241
242 size_t sz = KHashFileCount(hmap);
243 REQUIRE_EQ(sz, (size_t)2);
244
245 KHashFileDispose(hmap);
246 }
247
TEST_CASE(Klib_hashfileMapDeletes)248 TEST_CASE(Klib_hashfileMapDeletes)
249 {
250 rc_t rc;
251
252 uint64_t state = random();
253
254 KHashFile* hmap;
255 rc = KHashFileMake(&hmap, BACKING);
256 REQUIRE_RC(rc);
257
258 size_t sz = KHashFileCount(hmap);
259 REQUIRE_EQ(sz, (size_t)0);
260
261 std::vector<std::string> strs, vals;
262 const size_t loops = 10000;
263 for (size_t i = 0; i != loops; ++i) {
264 strs.push_back(string(1 + (fastrng(&state) % 500),
265 char(32 + fastrng(&state) % 90)));
266 vals.push_back(string(1 + (fastrng(&state) % 500),
267 char(32 + fastrng(&state) % 90)));
268 }
269
270 std::unordered_map<std::string, std::string> map;
271 for (size_t i = 0; i != strs.size(); ++i) {
272 const auto key = strs[i];
273 const auto val = vals[i];
274 auto pair = std::make_pair(key, val);
275 map.erase(key);
276 map.insert(pair);
277
278 uint64_t hash = KHash(key.data(), key.size());
279 KHashFileDelete(hmap, key.data(), key.size(), hash);
280 rc = KHashFileAdd(hmap, key.data(), key.size(), hash, val.data(),
281 val.size());
282 REQUIRE_RC(rc);
283
284 if (fastrng(&state) % 2) {
285 map.erase(key);
286 bool found = KHashFileDelete(hmap, key.data(), key.size(), hash);
287 REQUIRE_EQ(found, true);
288 }
289
290 sz = KHashFileCount(hmap);
291 REQUIRE_EQ(sz, (size_t)map.size());
292 }
293
294 for (auto it : map) {
295 const auto key = it.first;
296 const auto value = it.second;
297
298 uint64_t hash = KHash(key.data(), key.size());
299 char val[500];
300 size_t len = 0;
301 bool found
302 = KHashFileFind(hmap, key.data(), key.size(), hash, &val, &len);
303 REQUIRE_EQ(found, true);
304 REQUIRE_EQ(value.size(), len);
305 REQUIRE_EQ((int)0, memcmp(value.data(), val, len));
306 }
307
308 KHashFileDispose(hmap);
309 }
310
inserter(const KThread * thread,void * data)311 static rc_t inserter(const KThread* thread, void* data)
312 {
313 rc_t rc;
314 uint64_t state = random();
315 while (KEEPRUNNING) {
316 size_t idx = fastrng(&state) % RANDS_SIZE;
317 uint64_t key = RANDS[idx];
318 uint64_t val = key + 1;
319 uint64_t hash = KHash((const char*)&key, 8);
320 rc = KHashFileAdd(HMAP, &key, sizeof(key), hash, &val, sizeof(val));
321 if (rc != 0) {
322 fprintf(stderr, "Add failed\n");
323 // REQUIRE_RC(rc);
324 }
325 if (val != key + 1) {
326 fprintf(stderr, "val touched\n");
327 // REQUIRE_EQ((uint64_t)val,(uint64_t)key+1);
328 }
329 }
330 return 0;
331 }
332
deleter(const KThread * thread,void * data)333 static rc_t deleter(const KThread* thread, void* data)
334 {
335 uint64_t state = random();
336 while (KEEPRUNNING) {
337 size_t idx = fastrng(&state) % RANDS_SIZE;
338 uint64_t key = RANDS[idx];
339 uint64_t hash = KHash((const char*)&key, 8);
340 KHashFileDelete(HMAP, &key, sizeof(key), hash);
341 }
342
343 return 0;
344 }
345
finder(const KThread * thread,void * data)346 static rc_t finder(const KThread* thread, void* data)
347 {
348 uint64_t state = random();
349 while (KEEPRUNNING) {
350 size_t idx = fastrng(&state) % RANDS_SIZE;
351 uint64_t key = RANDS[idx];
352 uint64_t val = 0;
353 size_t val_size = 9;
354 uint64_t hash = KHash((const char*)&key, 8);
355 atomic64_inc(&FINDCOUNT);
356 bool found
357 = KHashFileFind(HMAP, &key, sizeof(key), hash, &val, &val_size);
358 if (found) {
359 if (val_size != sizeof(val)) {
360 fprintf(stderr, "bad find size\n");
361 }
362 if (val != key + 1) {
363 fprintf(stderr, "bad find\n");
364 }
365 val_size = 9;
366 } else {
367 if (val != 0) {
368 fprintf(stderr, "bad not found\n");
369 }
370 }
371 }
372
373 return 0;
374 }
375
notfinder(const KThread * thread,void * data)376 static rc_t notfinder(const KThread* thread, void* data)
377 {
378 uint64_t state = random();
379 while (KEEPRUNNING) {
380 uint64_t key = fastrng(&state);
381 uint64_t val = 9;
382 size_t val_size = 9;
383 uint64_t hash = KHash((const char*)&key, 8);
384 bool found
385 = KHashFileFind(HMAP, &key, sizeof(key), hash, &val, &val_size);
386 if (found) {
387 fprintf(stderr, "false found\n");
388 } else {
389 if (val != 9 || val_size != 9) {
390 fprintf(stderr, "bad not not found\n");
391 }
392 }
393 }
394
395 return 0;
396 }
397
398 #ifdef BENCHMARK
bench_inserter(const KThread * thread,void * data)399 static rc_t bench_inserter(const KThread* thread, void* data)
400 {
401 uint64_t state = random();
402 rc_t rc;
403 while (KEEPRUNNING) {
404 uint64_t rnd = fastrng(&state);
405 uint64_t key = rnd;
406 uint64_t val = key + 1;
407 uint64_t hash = KHash((const char*)&key, 8);
408 rc = KHashFileAdd(HMAP, &key, sizeof(key), hash, &val, sizeof(val));
409 if (rc != 0) {
410 fprintf(stderr, "Add failed\n");
411 // REQUIRE_RC(rc);
412 }
413 }
414
415 return 0;
416 }
417
TEST_CASE(Klib_Bench_hashfilethreads)418 TEST_CASE(Klib_Bench_hashfilethreads)
419 {
420 KEEPRUNNING = true;
421 KHashFileMake(&HMAP, BACKING);
422
423 const size_t NUM_THREADS = 100;
424 for (long i = 0; i != NUM_THREADS; ++i) {
425 KThread* thrd;
426 KThreadMake(&thrd, bench_inserter, (void*)i);
427 }
428
429 size_t oldcnt = KHashFileCount(HMAP);
430 stopwatch();
431 fprintf(stderr, "10B\n");
432 while (KHashFileCount(HMAP) < 10 * 1000 * 1000 * 1000ULL) {
433 size_t cnt = KHashFileCount(HMAP);
434 unsigned long us = stopwatch();
435 double ips = (double)(cnt - oldcnt) / us;
436 oldcnt = cnt;
437 fprintf(stderr,
438 " %lu threads required %lu ms to insert %lu (%.1f "
439 "Minserts/sec)\n",
440 NUM_THREADS, us / 1000, cnt, ips);
441 KSleepMs(1000);
442 }
443 fprintf(stderr, "Done\n");
444 KEEPRUNNING = false;
445 KSleepMs(1000);
446 KHashFileDispose(HMAP);
447 }
448
TEST_CASE(Klib_Bench_hashfiletune)449 TEST_CASE(Klib_Bench_hashfiletune)
450 {
451 rc_t rc;
452 uint8_t numts[] = {1, 5, 10, 20, 50, 100};
453 for (size_t nt = 0; nt != 6; ++nt) {
454 const size_t NUM_THREADS = numts[nt];
455 time_t starttime = time(NULL);
456
457 KEEPRUNNING = true;
458 rc = KHashFileMake(&HMAP, BACKING);
459 REQUIRE_RC(rc);
460 KHashFileReserve(HMAP, 100000000);
461 REQUIRE_RC(rc);
462
463 for (size_t i = 0; i != NUM_THREADS; ++i) {
464 KThread* thrd;
465 KThreadMake(&thrd, bench_inserter, (void*)i);
466 }
467
468 size_t oldcnt = KHashFileCount(HMAP);
469 stopwatch();
470 while (KHashFileCount(HMAP) < 100000000) {
471 size_t cnt = KHashFileCount(HMAP);
472 unsigned long us = stopwatch();
473 double ips = (double)(cnt - oldcnt) / us;
474 oldcnt = cnt;
475 fprintf(stderr,
476 "%lu threads required %lu ms to insert %lu (%.1f "
477 "Minserts/sec)\n",
478 NUM_THREADS, us / 1000, cnt, ips);
479 KSleepMs(1000);
480 }
481
482 atomic64_set(&FINDCOUNT, 0);
483 KEEPRUNNING = false;
484 KSleepMs(1000);
485
486 KEEPRUNNING = true;
487 for (size_t i = 0; i != NUM_THREADS; ++i) {
488 KThread* thrd;
489 KThreadMake(&thrd, finder, (void*)i);
490 }
491
492 for (size_t loops = 0; loops != 10; ++loops) {
493 KSleepMs(1000);
494 fprintf(stderr, "FINDCOUNT is %lu\n", atomic64_read(&FINDCOUNT));
495 }
496 KEEPRUNNING = false;
497 KSleepMs(1000);
498
499 KHashFileDispose(HMAP);
500 time_t endtime = time(NULL);
501 fprintf(stderr, "time required %lu\n", endtime - starttime);
502 }
503 }
504
table_bench_inserter(const KThread * thread,void * data)505 static rc_t table_bench_inserter(const KThread* thread, void* data)
506 {
507 uint64_t state = random();
508 rc_t rc;
509 while (KEEPRUNNING) {
510 uint64_t rnd = fastrng(&state);
511 uint64_t key = rnd;
512 uint64_t val = key + 1;
513 uint64_t hash = KHash((const char*)&key, 8);
514 KLockAcquire(KLOCK);
515 rc = KHashTableAdd(HTABLE, &key, hash, &val);
516 if (rc != 0) {
517 fprintf(stderr, "Add failed\n");
518 }
519 KLockUnlock(KLOCK);
520 }
521
522 return 0;
523 }
524
table_bench_finder(const KThread * thread,void * data)525 static rc_t table_bench_finder(const KThread* thread, void* data)
526 {
527 uint64_t state = random();
528 while (KEEPRUNNING) {
529 uint64_t rnd = fastrng(&state);
530 uint64_t key = rnd;
531 uint64_t val = 0;
532 uint64_t hash = KHash((const char*)&key, 8);
533 KLockAcquire(KLOCK);
534 KHashTableFind(HTABLE, &key, hash, &val);
535 KLockUnlock(KLOCK);
536 atomic64_inc(&FINDCOUNT);
537 }
538
539 return 0;
540 }
541
TEST_CASE(Klib_Bench_hashtableetune)542 TEST_CASE(Klib_Bench_hashtableetune)
543 {
544 rc_t rc;
545 KLockMake(&KLOCK);
546 uint8_t numts[] = {1, 5, 10, 20, 50, 100};
547 for (size_t nt = 0; nt != 6; ++nt) {
548 const size_t NUM_THREADS = numts[nt];
549 time_t starttime = time(NULL);
550
551 KEEPRUNNING = true;
552 rc = KHashTableMake(&HTABLE, 8, 8, 100 * 1000 * 1000, 0, raw);
553 REQUIRE_RC(rc);
554
555 for (size_t i = 0; i != NUM_THREADS; ++i) {
556 KThread* thrd;
557 KThreadMake(&thrd, table_bench_inserter, (void*)i);
558 }
559
560 size_t oldcnt = KHashTableCount(HTABLE);
561 stopwatch();
562 while (KHashTableCount(HTABLE) < 100000000) {
563 size_t cnt = KHashTableCount(HTABLE);
564 unsigned long us = stopwatch();
565 double ips = (double)(cnt - oldcnt) / us;
566 oldcnt = cnt;
567 fprintf(stderr,
568 "table %lu threads required %lu ms to insert %lu (%.1f "
569 "Minserts/sec)\n",
570 NUM_THREADS, us / 1000, cnt, ips);
571 KSleepMs(1000);
572 }
573 KEEPRUNNING = false;
574 KSleepMs(1000);
575 time_t endtime = time(NULL);
576 fprintf(stderr, "time required %lu\n", endtime - starttime);
577
578 atomic64_set(&FINDCOUNT, 0);
579 KEEPRUNNING = true;
580 for (size_t i = 0; i != NUM_THREADS; ++i) {
581 KThread* thrd;
582 KThreadMake(&thrd, table_bench_finder, (void*)i);
583 }
584 KSleepMs(10000);
585 KEEPRUNNING = false;
586 KSleepMs(1000);
587 KHashTableDispose(HTABLE, NULL, NULL, NULL);
588 }
589 KLockRelease(KLOCK);
590 }
591 #endif // BENCHMARK
592
TEST_CASE(Klib_hashfilethreads)593 TEST_CASE(Klib_hashfilethreads)
594 {
595 atomic64_set(&FINDCOUNT, 0);
596 KEEPRUNNING = true;
597 KHashFileMake(&HMAP, BACKING);
598
599 Vector threads;
600 VectorInit(&threads, 0, 0);
601 const size_t NUM_THREADS = 40;
602 for (long i = 0; i != NUM_THREADS; ++i) {
603 KThread* thrd;
604 KThreadMake(&thrd, inserter, (void*)i);
605 VectorAppend(&threads, NULL, thrd);
606 KThreadMake(&thrd, deleter, (void*)i);
607 VectorAppend(&threads, NULL, thrd);
608 KThreadMake(&thrd, finder, (void*)i);
609 VectorAppend(&threads, NULL, thrd);
610 KThreadMake(&thrd, notfinder, (void*)i);
611 VectorAppend(&threads, NULL, thrd);
612 }
613
614 for (size_t loops = 0; loops != 10; ++loops) {
615 fprintf(stderr, "Count is %lu\n", KHashFileCount(HMAP));
616 KSleepMs(1000);
617 }
618 KEEPRUNNING = false;
619 KSleepMs(1000);
620
621 for (long i = 0; i != VectorLength(&threads); ++i) {
622 KThread* thrd = (KThread*)VectorGet(&threads, i);
623 KThreadRelease(thrd);
624 }
625 VectorWhack(&threads, NULL, NULL);
626 KHashFileDispose(HMAP);
627 }
628
TEST_CASE(Klib_HashFileIterator)629 TEST_CASE(Klib_HashFileIterator)
630 {
631 const int loops = 10000;
632 rc_t rc;
633
634 KHashFile* hmap;
635 rc = KHashFileMake(&hmap, NULL);
636 REQUIRE_RC(rc);
637 uint32_t key;
638 uint32_t value;
639
640 std::unordered_map<uint32_t, uint32_t> map;
641 for (int iter = 0; iter != 2; ++iter) {
642 for (int i = 0; i != loops; ++i) {
643 key = random() % loops;
644 value = key + 1;
645
646 auto pair = std::make_pair(key, value);
647 map.erase(key);
648 map.insert(pair);
649
650 uint64_t hash = KHash((char*)&key, 4);
651 KHashFileDelete(hmap, (void*)&key, 4, hash);
652 rc = KHashFileAdd(hmap, (void*)&key, 4, hash, (void*)&value, 4);
653
654 size_t mapcount = map.size();
655 size_t hmapcount = KHashFileCount(hmap);
656 REQUIRE_EQ(mapcount, hmapcount);
657 }
658
659 for (int i = 0; i != loops; ++i) {
660 key = random() % loops;
661
662 map.erase(key);
663
664 uint64_t hash = KHash((char*)&key, 4);
665 KHashFileDelete(hmap, (void*)&key, 4, hash);
666 bool found
667 = KHashFileFind(hmap, (void*)&key, 4, hash, NULL, NULL);
668 REQUIRE_EQ(found, false);
669
670 size_t mapcount = map.size();
671 size_t hmapcount = KHashFileCount(hmap);
672 REQUIRE_EQ(mapcount, hmapcount);
673 }
674
675 for (int i = 0; i != loops; ++i) {
676 key = random() % loops;
677 value = (uint32_t)random();
678
679 auto pair = std::make_pair(key, value);
680 map.erase(key);
681 map.insert(pair);
682
683 uint64_t hash = KHash((char*)&key, 4);
684 KHashFileDelete(hmap, (void*)&key, 4, hash);
685 rc = KHashFileAdd(hmap, (void*)&key, 4, hash, (void*)&value, 4);
686
687 size_t mapcount = map.size();
688 size_t hmapcount = KHashFileCount(hmap);
689 REQUIRE_EQ(mapcount, hmapcount);
690 }
691
692 size_t mapcount = map.size();
693 size_t hmapcount = KHashFileCount(hmap);
694 REQUIRE_EQ(mapcount, hmapcount);
695
696 size_t founds = 0;
697 key = loops + 1;
698 size_t key_size = 0;
699 size_t value_size = 0;
700 KHashFileIteratorMake(hmap);
701 while (KHashFileIteratorNext(hmap, &key, &key_size, &value,
702 &value_size)) {
703 auto mapfound = map.find(key);
704 if (mapfound == map.end()) {
705 fprintf(stderr, "no key=%d\n", key);
706 REQUIRE_EQ(key_size, (size_t)0);
707 REQUIRE_EQ(value_size, (size_t)0);
708 REQUIRE_EQ(true, false);
709 } else {
710 REQUIRE_EQ(key_size, (size_t)4);
711 REQUIRE_EQ(value_size, (size_t)4);
712 uint32_t mvalue = mapfound->second;
713 REQUIRE_EQ(value, mvalue);
714 ++founds;
715 }
716 key_size = 0;
717 value_size = 0;
718 }
719 mapcount = map.size();
720 hmapcount = KHashFileCount(hmap);
721 REQUIRE_EQ(founds, hmapcount);
722
723 KHashFileIteratorMake(hmap);
724 while (KHashFileIteratorNext(hmap, &key, NULL, NULL, NULL)) {
725 map.erase(key);
726 uint64_t hash = KHash((char*)&key, 4);
727 KHashFileDelete(hmap, (void*)&key, 4, hash);
728 }
729 mapcount = map.size();
730 hmapcount = KHashFileCount(hmap);
731 REQUIRE_EQ(mapcount, hmapcount);
732 REQUIRE_EQ(mapcount, (size_t)0);
733 }
734 KHashFileDispose(hmap);
735 }
736
737 extern "C" {
738
739 #include <kapp/args.h>
740 #include <kfg/config.h>
741
KAppVersion(void)742 ver_t CC KAppVersion(void) { return 0x1000000; }
UsageSummary(const char * progname)743 rc_t CC UsageSummary(const char* progname) { return 0; }
744
Usage(const Args * args)745 rc_t CC Usage(const Args* args) { return 0; }
746
747 const char UsageDefaultName[] = "test-hashfile";
748
KMain(int argc,char * argv[])749 rc_t CC KMain(int argc, char* argv[])
750 {
751 rc_t rc;
752 srandom(time(NULL));
753 uint64_t state = random();
754
755 for (size_t i = 0; i != RANDS_SIZE; ++i) {
756 RANDS[i] = fastrng(&state);
757 }
758
759 rc = KDirectoryNativeDir(&DIR);
760 if (rc) return rc;
761
762 const char* fname = tmpnam(NULL);
763 rc = KDirectoryCreateFile(DIR, &BACKING, true, 0600, kcmInit, fname);
764 if (rc) return rc;
765
766 KConfigDisableUserSettings();
767
768 rc = KHashFileTestSuite(argc, argv);
769 if (rc) return rc;
770
771 rc = KDirectoryRemove(DIR, true, "%s", fname);
772 if (rc) return rc;
773
774 rc = KFileRelease(BACKING);
775 if (rc) return rc;
776 rc = KDirectoryRelease(DIR);
777 if (rc) return rc;
778
779 return 0;
780 }
781 }
782