Coverage Report

Created: 2025-10-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/netcdf-c/libdispatch/ncexhash.c
Line
Count
Source
1
/*
2
Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
3
See LICENSE.txt for license information.
4
*/
5
6
#include "config.h"
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <string.h>
10
#include <assert.h>
11
12
#include "netcdf.h"
13
#include "ncexhash.h"
14
#include "nccrc.h"
15
16
/* 0 => no debug */
17
#define DEBUG 0
18
#undef DEBUGTRACE
19
#undef CATCH
20
#define INLINED
21
22
#ifdef CATCH
23
/* Warning: do not evaluate x more than once */
24
#define THROW(x) throw(x)
25
static void breakpoint(void) {}
26
static int ignore[] = {NC_ENOTFOUND, 0};
27
static int throw(int x)
28
{
29
    int* p;
30
    if(x != 0) {
31
  for(p=ignore;*p;p++) {if(x == *p) break;}
32
  if(*p == 0) breakpoint();
33
    }
34
    return x;
35
}
36
#else
37
0
#define THROW(x) (x)
38
#endif
39
40
/**
41
@Internal
42
*/
43
44
#ifdef DEBUGTRACE
45
#define TRACE(x) {fprintf(stderr,">>>> %s\n",x); fflush(stderr);}
46
#else
47
#define TRACE(x)
48
#endif
49
50
/* Minimum table size is 2 */
51
0
#define MINDEPTH 1
52
53
/* Minimum leaf size is 2 entries */
54
0
#define MINLEAFLEN 2
55
56
#define MAX(a,b) ((a) > (b) ? (a) : (b))
57
58
#ifdef INLINED
59
0
#define exhashlinkleaf(map,leaf) {\
60
0
    if(leaf && map) { leaf->next = map->leaves; map->leaves = leaf; } \
61
0
}
62
63
0
#define exhashfreeleaf(map,leaf) { \
64
0
    if(leaf) {{if(leaf->entries) free(leaf->entries);} free(leaf);} \
65
0
}
66
67
#endif /*INLINED*/
68
69
static int ncexinitialized = 0;
70
71
/* Define a vector of bit masks */
72
ncexhashkey_t bitmasks[NCEXHASHKEYBITS];
73
74
/* Extract the leftmost n bits from h */
75
#if 1
76
0
#define MSB(h,d) (((h) >> (NCEXHASHKEYBITS - (d))) & bitmasks[d])
77
#else
78
static ncexhashkey_t
79
MSB(ncexhashkey_t h, int d)
80
{
81
    ncexhashkey_t bm = bitmasks[d];
82
    ncexhashkey_t hkey = h >> (NCEXHASHKEYBITS - d);
83
    hkey = hkey & bm;
84
    return hkey;
85
}
86
#endif
87
88
/* Provide a mask to get the rightmost bit
89
   of the left n bits of our hash key */
90
#define MSBMASK(d) (1 << (NCEXHASHKEYBITS - d))
91
92
static int exhashlookup(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp);
93
static int exhashlocate(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp);
94
static int exhashsplit(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf* leaf);
95
static int exhashdouble(NCexhashmap* map);
96
static int exbinsearch(ncexhashkey_t hkey, NCexleaf* leaf, int* indexp);
97
static void exhashnewentry(NCexhashmap* map, NCexleaf* leaf, ncexhashkey_t hkey, int* indexp);
98
static int exhashnewleaf(NCexhashmap* map, NCexleaf** leaf);
99
static void exhashunlinkleaf(NCexhashmap* map, NCexleaf* leaf);
100
101
#ifndef INLINED
102
static void exhashlinkleaf(NCexhashmap* map, NCexleaf* leaf);
103
static void exhashfreeleaf(NCexhashmap* map, NCexleaf* leaf);
104
#endif /*INLINED*/
105
106
/**************************************************/
107
108
static void
109
ncexinit(void)
110
0
{
111
0
    int i;
112
0
    bitmasks[0] = 0;
113
0
    for(i=1;i<NCEXHASHKEYBITS;i++)
114
0
  bitmasks[i] = (1ULL << i) - 1;
115
0
    ncexinitialized = 1;
116
0
}
117
118
/**************************************************/
119
120
/** Creates a new exhash using DEPTH */
121
NCexhashmap*
122
ncexhashnew(int leaflen)
123
0
{
124
0
    NCexhashmap* map = NULL;
125
0
    NCexleaf* leaf[2] = {NULL,NULL};
126
0
    NCexleaf** topvector = NULL;
127
0
    int i;
128
0
    int gdepth;
129
130
0
    TRACE("ncexhashnew");
131
132
0
    if(!ncexinitialized) ncexinit();
133
134
0
    gdepth = MINDEPTH;
135
0
    if(leaflen < MINLEAFLEN) leaflen = MINLEAFLEN;
136
    
137
    /* Create the table */
138
0
    if((map = (NCexhashmap*)calloc(1,sizeof(NCexhashmap))) == NULL)
139
0
  goto done;
140
0
    map->leaflen = leaflen;
141
    /* Create the top level vector */
142
0
    if((topvector = calloc(1<<gdepth,sizeof(NCexleaf*))) == NULL)
143
0
  goto done;
144
0
    map->directory = topvector;
145
0
    if(exhashnewleaf(map,&leaf[0])) goto done;
146
0
    if(exhashnewleaf(map,&leaf[1])) goto done;
147
0
    exhashlinkleaf(map,leaf[0]);
148
0
    exhashlinkleaf(map,leaf[1]);
149
    /* Fill in vector */
150
0
    for(i=0;i<(1<<gdepth);i++) topvector[i] = (i & 0x1?leaf[1]:leaf[0]);
151
0
    topvector = NULL;
152
0
    leaf[0] = leaf[1] = NULL;
153
0
    map->depth = gdepth;
154
0
    assert(map->leaves != NULL);
155
0
done:
156
0
    if(leaf[0]) {exhashunlinkleaf(map,leaf[0]); exhashfreeleaf(map,leaf[0]);}
157
0
    if(leaf[1]) {exhashunlinkleaf(map,leaf[1]); exhashfreeleaf(map,leaf[1]);}
158
0
    if(topvector) free(topvector);
159
0
    return map;
160
0
}
161
162
/** Reclaims the exhash structure. */
163
void
164
ncexhashmapfree(NCexhashmap* map)
165
0
{
166
0
    NCexleaf* current = NULL;
167
0
    NCexleaf* next= NULL;
168
169
0
    if(map == NULL) return;
170
    /* Walk the leaf chain to free leaves */
171
0
    current = map->leaves; next = NULL;
172
0
    while(current) {
173
0
  next = current->next; 
174
0
  exhashfreeleaf(map,current);
175
0
  current = next; 
176
0
    }
177
0
    nullfree(map->directory);    
178
0
    free(map);
179
0
}
180
181
/** Returns the number of active elements. */
182
int
183
ncexhashcount(NCexhashmap* map)
184
0
{
185
0
    return map->nactive;
186
0
}
187
188
/* Hash key based API */
189
190
/* Lookup by Hash Key */
191
int
192
ncexhashget(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t* datap)
193
0
{
194
0
    int stat = NC_NOERR;
195
0
    NCexleaf* leaf;
196
0
    NCexentry* entry;
197
0
    int index;
198
199
    /* Do internal lookup */
200
0
    if((stat = exhashlookup(map,hkey,&leaf,&index)))
201
0
       return THROW(stat);
202
0
    entry = &leaf->entries[index];
203
0
    assert(entry->hashkey == hkey);
204
0
    if(datap) *datap = entry->data;
205
0
    return THROW(stat);
206
0
}
207
208
/* Insert by Hash Key */
209
int
210
ncexhashput(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t data)
211
0
{
212
0
    int stat = NC_NOERR;
213
0
    NCexleaf* leaf;
214
0
    NCexentry* entry;
215
0
    int index;
216
217
0
    if(map->iterator.walking) return THROW(NC_EPERM);
218
219
    /* Do internal lookup */
220
0
    if((stat = exhashlookup(map,hkey,&leaf,&index)) == NC_ENOTFOUND) {
221
        /* We have to add an entry (sigh!) so find where it goes */
222
0
        if((stat = exhashlocate(map,hkey,&leaf,&index)))
223
0
        return THROW(stat);
224
0
    }
225
0
    entry = &leaf->entries[index];
226
0
    entry->hashkey = hkey;
227
0
    assert(entry->hashkey == hkey);
228
0
    entry->data = data;
229
0
    return THROW(stat);
230
0
}
231
232
static int
233
exhashlookup(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp)
234
0
{
235
0
    int stat = NC_NOERR;
236
0
    NCexleaf* leaf;
237
0
    ncexhashkey_t offset;
238
0
    int index;
239
240
0
    TRACE("exhashlookup");
241
242
    /* Extract global depth least significant bits of hkey */
243
0
    offset = MSB(hkey,map->depth);
244
    /* Get corresponding leaf from directory */
245
0
    leaf = map->directory[offset];
246
0
    if(leafp) *leafp = leaf; /* always return this if possible */
247
    /* Binary search the leaf entries looking for hkey */
248
0
    stat = exbinsearch(hkey,leaf,&index);
249
0
    if(indexp) *indexp = index;
250
#if DEBUG >= 3
251
    fprintf(stderr,"lookup: found=%s offset=%x leaf=%d index=%d\n",
252
  (stat == NC_NOERR ? "true" : "false"),
253
  offset,leaf->uid,index);
254
#endif
255
0
    return THROW(stat);
256
0
}
257
258
static int
259
exhashlocate(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp)
260
0
{
261
0
    int stat = NC_NOERR;
262
0
    ncexhashkey_t offset;
263
0
    NCexleaf* leaf = NULL;
264
0
    int index = -1;
265
0
    int iter;
266
267
0
    TRACE("exhashlocate");
268
269
#if DEBUG >= 2
270
    fprintf(stderr,"locate.before: "); ncexhashprint(map);
271
#endif
272
273
    /* Setup */
274
0
    *leafp = NULL;
275
0
    *indexp = -1;
276
277
0
    if(map->iterator.walking) return THROW(NC_EPERM);
278
279
    /* Repeatedly split and/or double until the target leaf has room */
280
0
    for(iter=0;;iter++) {
281
        /* Extract global depth least significant bits of hkey */
282
0
        offset = MSB(hkey,map->depth);
283
        /* Get corresponding leaf from directory */
284
0
        leaf = map->directory[offset];
285
        /* Is there room in the leaf to add an entry? */
286
#if DEBUG >= 3
287
  fprintf(stderr,"locate: iter=%d offset=%x leaf=(%d)%p active=%d\n",iter,offset,leaf->uid,leaf,(int)leaf->active);
288
#else
289
0
  (void)iter;
290
0
#endif
291
0
       if(leaf->active < map->leaflen) break; /* yes, there is room */
292
       /* Not Enough room, so we need to split this leaf */
293
        /* Split this leaf and loop to verify we have room */
294
#if DEBUG >= 3
295
        fprintf(stderr,"locate.split.loop: uid=%d\n",leaf->uid);
296
#endif
297
0
        if((stat = exhashsplit(map,hkey,leaf))) return THROW(stat); /* failed */
298
0
    }
299
    /* We now now that there is room in the leaf */
300
    /* Insert into this leaf */
301
0
    exhashnewentry(map,leaf,hkey,&index);
302
#if DEBUG >= 3
303
    fprintf(stderr,"locate.inserted: index=%d\n",index);
304
#endif
305
#if DEBUG >= 1
306
    fprintf(stderr,"locate.inserted.after: "); ncexhashprint(map);
307
#endif
308
0
    *leafp = leaf;
309
0
    *indexp = index;
310
0
    return THROW(stat);
311
0
}
312
313
static int
314
exhashdouble(NCexhashmap* map)
315
0
{
316
0
    NCexleaf** olddir = NULL;
317
0
    NCexleaf** newdir = NULL;
318
0
    size_t oldcount,newcount;
319
0
    ncexhashkey_t iold, inew;
320
321
0
    TRACE("exhashdouble");
322
323
0
    if(map->iterator.walking) return THROW(NC_EPERM);
324
325
#if DEBUG >= 1
326
    fprintf(stderr,"double.before: "); ncexhashprint(map);
327
#endif
328
329
0
    olddir = map->directory;
330
    /* Attempt to double the directory count */
331
0
    oldcount = (1<<map->depth);
332
0
    newcount = 2 * oldcount;
333
0
    newdir = (NCexleaf**)malloc(newcount*sizeof(NCexleaf*));
334
0
    if(newdir == NULL) return THROW(NC_ENOMEM);
335
    /* Note that newdir == olddir is possible because realloc */
336
    /* Walk the old directory from top to bottom to double copy
337
       into newspace */
338
0
    assert(oldcount >= 1 && newcount >= 2);
339
0
    iold = oldcount;
340
0
    inew = newcount;
341
0
    do {
342
0
        iold -= 1;
343
0
        inew -= 2;
344
0
  newdir[inew] = olddir[iold];
345
0
  newdir[inew+1] = olddir[iold];
346
0
    } while(iold > 0);
347
0
    assert(iold == 0 && inew == 0);
348
    /* Fix up */
349
0
    map->directory = newdir;
350
0
    map->depth++;
351
#if DEBUG >= 1
352
    fprintf(stderr,"double.after: "); ncexhashprint(map);
353
#endif
354
0
    nullfree(olddir);
355
0
    return THROW(NC_NOERR);
356
0
}
357
358
static int
359
exhashsplit(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf* leaf)
360
0
{
361
0
    int stat = NC_NOERR;
362
0
    NCexleaf* newleaf = NULL;
363
0
    NCexleaf* leafptr = leaf;
364
0
    NCexleaf entries;
365
0
    int i, index;
366
367
0
    TRACE("exhashsplit");
368
369
0
    if(map->iterator.walking) {stat = NC_EPERM; goto done;}
370
371
#if DEBUG >= 1
372
    fprintf(stderr,"split.before: leaf=%d",leaf->uid); ncexhashprint(map);
373
#endif
374
375
    /* Save the old leaf's entries */
376
0
    entries = *leaf;
377
 
378
    /* bump leaf depth */
379
0
    leaf->depth++;
380
381
    /* May require doubling of the map directory */
382
#if DEBUG >= 3
383
  fprintf(stderr,"split: leaf.depth=%d map.depth=%d\n",leaf->depth,map->depth);
384
#endif
385
0
    if(leaf->depth > map->depth) {
386
  /* double the directory */
387
0
        if((stat = exhashdouble(map))) return THROW(stat); /* failed */
388
0
    }
389
    
390
    /* Re-build the old leaf; keep same uid */
391
0
    if((leaf->entries = (NCexentry*)calloc((size_t)map->leaflen, sizeof(NCexentry))) == NULL)
392
0
  {stat = NC_ENOMEM; goto done;}
393
0
    leaf->active = 0;
394
395
    /* Allocate and link the new leaf */
396
0
    if((stat = exhashnewleaf(map,&newleaf))) goto done;
397
0
    exhashlinkleaf(map,newleaf);
398
0
    newleaf->depth = leaf->depth;
399
#if DEBUG >= 3
400
     fprintf(stderr,"split.split: newleaf=");ncexhashprintleaf(map,newleaf);
401
#endif
402
403
    /* Now walk the directory to locate all occurrences of old
404
       leaf and replace with newleaf in those cases where the
405
       directory index % 2 == 1 
406
    */
407
0
    for(i=0;i<(1<<map->depth);i++) {
408
0
  if(map->directory[i] == leafptr) {
409
0
      if(i % 2 == 1) { /* entries should be newleaf */
410
#if DEBUG >= 3
411
    fprintf(stderr,"split.directory[%d]=%d (newleaf)\n",(int)i,newleaf->uid);
412
#endif
413
0
          map->directory[i] = newleaf;
414
0
      }
415
0
  }
416
0
    }
417
418
#if DEBUG >= 1
419
    fprintf(stderr,"split.after: leaf=%d newleaf=%d",leaf->uid,newleaf->uid); ncexhashprint(map);
420
#endif
421
422
0
    newleaf = NULL; /* no longer needed */
423
424
    /* Now re-insert the entries */
425
    /* Should not cause splits or doubles */
426
0
    for(i=0;i<entries.active;i++) {
427
0
        NCexentry* e = &entries.entries[i];
428
0
  switch (stat = exhashlookup(map,e->hashkey,&leaf,&index)) {
429
0
  case NC_NOERR: /* Already exists, so fail */
430
0
      stat = NC_EINTERNAL;
431
0
      goto done;
432
0
  default:
433
0
      stat = NC_NOERR;
434
0
      break;
435
0
  }
436
0
  assert(leaf != NULL);
437
        /* Insert in the proper leaf */
438
#if DEBUG >= 3
439
       fprintf(stderr,"split.reinsert: entry.hashkey=%x leaf.uid=%d index=%d\n",
440
    e->hashkey,leaf->uid,index);
441
#endif
442
0
  leaf->entries[index] = *e;
443
0
  leaf->active++;
444
#if DEBUG >= 1
445
       fprintf(stderr,"split.reinsert.after: "); ncexhashprint(map);
446
#endif
447
0
    }
448
449
0
done:
450
0
    if(stat) { /* unwind */
451
0
        nullfree(leaf->entries);
452
0
  *leaf = entries; 
453
0
    } else {
454
0
        nullfree(entries.entries);
455
0
    }
456
0
    if(newleaf) {
457
0
  exhashunlinkleaf(map,newleaf);
458
0
  exhashfreeleaf(map,newleaf);
459
0
    }
460
0
    return THROW(stat);
461
0
}
462
463
/**
464
 * @internal Define a binary searcher for hash keys in leaf
465
 * @param hkey for which to search
466
 * @param leaf to search
467
 * @param indexp store index of the match or if no match, the index
468
 *               where new value should be stored (0 <= *indexp <= leaf->active)
469
 * @return NC_NOERR if found
470
 * @return NC_ENOTFOUND if not found, indexp points to insertion location
471
 * @author Dennis Heimbigner
472
 */
473
static int
474
exbinsearch(ncexhashkey_t hkey, NCexleaf* leaf, int* indexp)
475
0
{
476
0
    int stat = NC_NOERR;
477
0
    int n = leaf->active;
478
0
    int L = 0;
479
0
    int R = (n - 1);
480
481
0
    if(n == 0) {
482
0
  if(indexp) *indexp = 0; /* Insert at 0 */
483
0
        return THROW(NC_ENOTFOUND);
484
0
    }
485
0
    while(L != R) {
486
0
  int m = (L + R);
487
0
  if((m & 0x1)) /* ceiling */
488
0
      m = (m / 2) + 1;
489
0
  else
490
0
      m = (m / 2);
491
0
  if(leaf->entries[m].hashkey > hkey)
492
0
            R = m - 1;
493
0
        else
494
0
            L = m;
495
0
    }
496
    /* Return match index or insertion index */
497
0
    if(leaf->entries[L].hashkey == hkey) {
498
  /* L = L; */
499
  /* stat = NC_NOERR; */
500
0
    } else if(leaf->entries[L].hashkey > hkey) {
501
        /* L = L; */
502
0
  stat = NC_ENOTFOUND;
503
0
    } else {/* (leaf->entries[m].hashkey < hkey) */
504
0
  L = L+1;
505
0
  stat = NC_ENOTFOUND;
506
0
    }
507
0
    if(indexp) *indexp = L;
508
0
    return THROW(stat);
509
0
}
510
511
/* Create new entry at position index */
512
static void
513
exhashnewentry(NCexhashmap* map, NCexleaf* leaf, ncexhashkey_t hkey, int* indexp)
514
0
{
515
0
    int stat;
516
0
    int index;
517
518
    /* Figure out where the key should be inserted (*indexp + 1)*/
519
0
    stat = exbinsearch(hkey,leaf,indexp);
520
0
    assert(stat != NC_NOERR); /* already present */
521
0
    index = *indexp;
522
0
    assert(index >= 0 && index <= leaf->active);
523
0
    assert(index == leaf->active || leaf->entries[index].hashkey > hkey);
524
0
    if(leaf->active > 0) {
525
0
  int dst = leaf->active;
526
0
        int src = leaf->active - 1;
527
0
        for(;src >= index;src--,dst--)
528
0
            leaf->entries[dst] = leaf->entries[src];
529
0
    }
530
#if 0
531
    leaf->entries[index].hashkey = hkey;
532
#else
533
0
    leaf->entries[index].hashkey = (ncexhashkey_t)0xffffffffffffffff;
534
0
#endif
535
0
    leaf->entries[index].data = 0;
536
0
    leaf->active++;
537
0
    map->nactive++;
538
0
}
539
540
#ifndef INLINED
541
static void
542
exhashlinkleaf(NCexhashmap* map, NCexleaf* leaf)
543
{
544
    if(leaf && map) {
545
        assert(!map->iterator.walking);
546
  leaf->next = map->leaves;
547
  map->leaves = leaf;
548
        assert(leaf->next == NULL || leaf->next != leaf);
549
    }
550
}
551
552
static void
553
exhashfreeleaf(NCexhashmap* map, NCexleaf* leaf)
554
{
555
    assert(!map->iterator.walking);
556
    if(leaf) {
557
  nullfree(leaf->entries);
558
  nullfree(leaf);
559
    }
560
}
561
562
#endif /*INLINED*/
563
564
static void
565
exhashunlinkleaf(NCexhashmap* map, NCexleaf* leaf)
566
0
{
567
0
    if(leaf && map && map->leaves) {
568
0
        assert(!map->iterator.walking);
569
0
  if(map->leaves == leaf) {/* special case */
570
0
      map->leaves = leaf->next;
571
0
  } else {
572
0
      NCexleaf* cur;
573
0
      for(cur = map->leaves;cur != NULL;cur=cur->next) {
574
0
          if(cur->next == leaf) {
575
0
        cur->next = leaf->next;
576
0
        break;      
577
0
    }
578
0
      }
579
0
  }
580
0
    }
581
0
}
582
583
static int
584
exhashnewleaf(NCexhashmap* map, NCexleaf** leafp)
585
0
{
586
0
    int stat = NC_NOERR;
587
0
    NCexleaf* leaf = NULL;
588
0
    assert(!map->iterator.walking);
589
0
    if(leafp) {
590
0
        if((leaf = calloc(1,sizeof(NCexleaf))) == NULL)
591
0
      goto done;
592
0
  assert(map->leaflen > 0);
593
0
        if((leaf->entries = calloc((size_t)map->leaflen, sizeof(NCexentry))) == NULL)
594
0
      goto done; 
595
0
        leaf->uid = map->uid++;
596
0
  *leafp = leaf; leaf = NULL;
597
0
    }
598
0
done:
599
0
    if(leaf) nullfree(leaf->entries);
600
0
    nullfree(leaf);
601
0
    return stat;
602
0
}
603
604
/**
605
 * Remove by Hash Key
606
 * @param map
607
 * @param hkey
608
 * @param datap Store the data of the removed hkey
609
 * @return NC_NOERR if found
610
 * @return NC_ENOTFOUND if not found
611
 * @return NC_EINVAL for other errors
612
 * @author Dennis Heimbigner
613
 */
614
615
int
616
ncexhashremove(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t* datap)
617
0
{
618
0
     int stat = NC_NOERR;
619
0
     NCexleaf* leaf;
620
0
     int src,dst;
621
622
0
    if(map->iterator.walking) return THROW(NC_EPERM);
623
624
0
    if((stat = exhashlookup(map,hkey,&leaf,&dst)))
625
0
       return THROW(stat);
626
0
    if(datap) *datap = leaf->entries[dst].data;
627
    /* Compress out the index'th entry */
628
0
    for(src=dst+1;src<leaf->active;src++,dst++)
629
0
  leaf->entries[dst] = leaf->entries[src];
630
0
    leaf->active--;        
631
0
    map->nactive--;
632
0
    return THROW(stat);
633
0
}
634
635
/**
636
 * Change data associated with key; do not insert if not found
637
 * @param map
638
 * @param hkey
639
 * @param newdata new data
640
 * @param olddatap Store previous value
641
 * @return NC_NOERR if found
642
 * @return NC_ENOTFOUND if not found
643
 * @return NC_EINVAL for other errors
644
 * @author Dennis Heimbigner
645
 */
646
647
int
648
ncexhashsetdata(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t newdata, uintptr_t* olddatap)
649
0
{
650
0
    int stat = NC_NOERR;
651
0
    NCexleaf* leaf = NULL;
652
0
    NCexentry* e = NULL;
653
0
    int index;
654
655
0
    if(map->iterator.walking) return THROW(NC_EPERM);
656
657
0
    if((stat = exhashlookup(map,hkey,&leaf,&index)))
658
0
       return THROW(stat);
659
0
    e = &leaf->entries[index];
660
0
    if(olddatap) *olddatap = e->data;
661
0
    e->data = newdata;
662
0
    return THROW(stat);
663
0
}
664
665
/**
666
 * Inquire map-related values
667
 * @param map
668
 * @param leaflenp Store map->leaflen
669
 * @param depthp Store map->depth
670
 * @param nactivep Store map->nactive
671
 * @param uidp Store map->uid
672
 * @param walkingp Store map->iterator.walking
673
 *
674
 * @return NC_NOERR if found
675
 * @return NC_EINVAL for other errors
676
 * @author Dennis Heimbigner
677
 */
678
int
679
ncexhashinqmap(NCexhashmap* map, int* leaflenp, int* depthp, int* nactivep, int* uidp, int* walkingp)
680
0
{
681
0
    if(map == NULL) return NC_EINVAL;
682
0
    if(leaflenp) *leaflenp = map->leaflen;
683
0
    if(depthp) *depthp = map->depth;
684
0
    if(nactivep) *nactivep = map->nactive;
685
0
    if(uidp) *uidp = map->uid;
686
0
    if(walkingp) *walkingp = map->iterator.walking;
687
0
    return NC_NOERR;
688
0
}
689
690
/* Return the hash key for specified key; takes key+size*/
691
ncexhashkey_t
692
ncexhashkey(const unsigned char* key, size_t size)
693
0
{
694
0
    return NC_crc64(0,(unsigned char*)key,(unsigned int)size);
695
0
}
696
697
/* Walk the entries in some order */
698
/*
699
@return NC_NOERR if keyp and datap are valid
700
@return NC_ERANGE if iteration is finished
701
@return NC_EINVAL for all other errors
702
*/
703
int
704
ncexhashiterate(NCexhashmap* map, ncexhashkey_t* keyp, uintptr_t* datap)
705
0
{
706
0
    int stat = NC_NOERR;
707
708
0
    if(!map->iterator.walking) {
709
0
  map->iterator.leaf = map->leaves;
710
0
  map->iterator.index = 0;
711
0
  map->iterator.walking = 1;
712
0
    }
713
0
    for(;;) {
714
0
        if(map->iterator.leaf == NULL)
715
0
        {stat = NC_ERANGE; break;}
716
0
  if(map->iterator.index >= map->iterator.leaf->active) {
717
0
      map->iterator.leaf = map->iterator.leaf->next;
718
0
      map->iterator.index = 0;
719
0
  } else {
720
0
            assert(map->iterator.leaf != NULL && map->iterator.index < map->iterator.leaf->active);
721
      /* Return data from this entry */
722
0
      if(keyp) *keyp = map->iterator.leaf->entries[map->iterator.index].hashkey;
723
0
      if(datap) *datap = map->iterator.leaf->entries[map->iterator.index].data;
724
0
        map->iterator.index++;
725
0
      break;
726
0
  }
727
0
    }
728
0
    if(stat != NC_NOERR) { /* stop */
729
0
  map->iterator.walking = 0;
730
0
  map->iterator.leaf = NULL;
731
0
  map->iterator.index = 0;  
732
0
    }
733
0
    return THROW(stat);
734
0
}
735
/**************************************************/
736
/* Debug support */
737
738
void
739
ncexhashprint(NCexhashmap* hm)
740
0
{
741
0
    int index;
742
743
0
    if(hm == NULL) {fprintf(stderr,"NULL"); fflush(stderr); return;}
744
0
    fprintf(stderr,"{depth=%u leaflen=%u",hm->depth,hm->leaflen);
745
0
    if(hm->iterator.walking) {
746
0
        fprintf(stderr," iterator=(leaf=%p index=%u)",
747
0
    hm->iterator.leaf,hm->iterator.index);
748
0
    }
749
0
    fprintf(stderr,"\n");
750
0
    for(size_t dirindex=0;dirindex<(1<<hm->depth);dirindex++) {
751
0
  NCexleaf* leaf = hm->directory[dirindex];
752
0
  fprintf(stderr,"\tdirectory[%03zu|%sb]=(%04x)[(%u)^%d|%d|",
753
0
    dirindex,ncexbinstr(dirindex,hm->depth),
754
0
    leaf->active,
755
0
    (unsigned)(0xffff & (uintptr_t)leaf),
756
0
    leaf->uid, leaf->depth);
757
0
  for(index=0;index<leaf->active;index++) {
758
0
      ncexhashkey_t hkey, bits;
759
0
      const char* s;
760
0
      hkey = leaf->entries[index].hashkey;
761
      /* Reduce to the leaf->hash MSB */
762
0
      bits = MSB(hkey,hm->depth);
763
0
      s = ncexbinstr(bits,hm->depth);
764
0
      fprintf(stderr,"%s(%s/",(index==0?":":" "),s);
765
0
      bits = MSB(hkey,leaf->depth);
766
0
      s = ncexbinstr(bits,leaf->depth);
767
0
      fprintf(stderr,"%s|0x%llx,%llu)",
768
0
        s,
769
0
          (unsigned long long)hkey,
770
0
        (unsigned long long)leaf->entries[index].data);
771
0
  }
772
0
  fprintf(stderr,"]\n");
773
0
    }
774
0
    fprintf(stderr,"}\n");
775
0
    fflush(stderr);
776
0
}
777
778
void
779
ncexhashprintdir(NCexhashmap* map, NCexleaf** dir)
780
0
{
781
0
    for(unsigned long long dirindex=0;dirindex<(1<<map->depth);dirindex++) {
782
0
  NCexleaf* leaf = dir[dirindex];
783
0
  fprintf(stderr,"\tdirectory[%03llu|%sb]=%d/%p\n",
784
0
    dirindex,ncexbinstr(dirindex,map->depth),leaf->uid,leaf);
785
0
    }
786
0
    fflush(stderr);
787
0
}
788
789
void
790
ncexhashprintleaf(NCexhashmap* map, NCexleaf* leaf)
791
0
{
792
0
    int index;
793
0
    fprintf(stderr,"(%04x)[(%u)^%d|%d|",
794
0
  (unsigned)(0xffff & (uintptr_t)leaf),
795
0
  leaf->uid, leaf->depth,leaf->active);
796
0
    for(index=0;index<leaf->active;index++) {
797
0
  ncexhashkey_t hkey, bits;
798
0
  const char* s;
799
0
  hkey = leaf->entries[index].hashkey;
800
  /* Reduce to the leaf->hash MSB */
801
0
  bits = MSB(hkey,map->depth);
802
0
        s = ncexbinstr(bits,map->depth);
803
0
        fprintf(stderr,"%s(%s/",(index==0?":":" "),s);
804
0
  bits = MSB(hkey,leaf->depth);
805
0
  s = ncexbinstr(bits,leaf->depth);
806
0
  fprintf(stderr,"%s|0x%llx,%llu)",
807
0
        s, (unsigned long long)hkey, (unsigned long long)leaf->entries[index].data);
808
0
    }
809
0
    fprintf(stderr,"]\n");
810
0
}
811
812
void
813
ncexhashprintentry(NCexhashmap* map, NCexentry* entry)
814
0
{
815
816
0
    fprintf(stderr,"{0x%llx,%llu)",(unsigned long long)entry->hashkey,(unsigned long long)entry->data);
817
0
}
818
819
char*
820
ncexbinstr(ncexhashkey_t hkey, int depth)
821
0
{
822
0
    int i;
823
0
    static char bits[NCEXHASHKEYBITS+1];
824
0
    memset(bits,'0',NCEXHASHKEYBITS+1);
825
0
    bits[NCEXHASHKEYBITS] = '\0';
826
0
    for(i=0;i<depth;i++)
827
0
        bits[(depth-1)-i] = ((hkey >> i) & 0x1) == 0 ? '0' : '1';
828
0
    bits[depth] = '\0';
829
0
    return bits;    
830
0
}
831
832
void
833
ncexhashprintstats(NCexhashmap* map)
834
0
{
835
0
    int nactive;
836
0
    NCexleaf* leaf = NULL;
837
0
    double leafavg = 0.0;
838
0
    double leafload = 0.0;
839
0
    unsigned long long dirsize, leafsize, total;
840
    
841
0
    nactive = 0;
842
0
    unsigned long long nleaves = 0;
843
0
    for(leaf=map->leaves;leaf;leaf=leaf->next) {
844
0
        nleaves++;
845
0
  nactive += leaf->active;
846
0
    }
847
  
848
0
    leafavg = ((double)nactive)/((double)nleaves);
849
0
    leafload = leafavg / ((double)map->leaflen);
850
851
0
    if(nactive != map->nactive) {
852
0
  fprintf(stderr,"nactive mismatch: map->active=%d actual=%d\n",map->nactive,nactive);
853
0
    }
854
0
    fprintf(stderr,"|directory|=%llu nleaves=%llu nactive=%d",
855
0
  (unsigned long long)(1<<(map->depth)),nleaves,nactive);
856
0
    fprintf(stderr," |leaf|=%d nactive/nleaves=%g", map->leaflen, leafavg);
857
0
    fprintf(stderr," load=%g",leafload);
858
0
    fprintf(stderr,"]\n");
859
0
    dirsize = (1ULL<<(map->depth))*((unsigned long long)sizeof(void*));
860
0
    leafsize = (nleaves)*((unsigned long long)sizeof(NCexleaf));
861
0
    total = dirsize + leafsize;
862
    fprintf(stderr,"\tsizeof(directory)=%llu sizeof(leaves)=%lld total=%lld\n",
863
0
    dirsize,leafsize,total);
864
0
}