/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 | } |