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