Coverage Report

Created: 2023-05-28 06:42

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