netcdf-c/libdispatch/ncexhash.c
Dennis Heimbigner d2316f866c Additional Fixes to NCZarr
Primary Fixes:
* Add a whole variable optimization -- used in the rare case that nc_get/put_vara covers the whole of a variable and the variable has a single chunk.
* Fix chunking error when stride causes whole chunks to be skipped.
* Fix some memory leaks
* Add test cases
* Add one performance test to nczarr_test/. This uses the timer utils from unit_test: timer_utils.[ch].
* Move ncdumpchunks utility from ncdump to nczarr_test

Misc. Other Changes:
* Make check for aws libraries conditional on --enable-nczarr-s3
* Remove all but one bm tests from nczarr_test until they are working.
* Remove another dependency on HDF5 from supposedly non-HDF5 specific code; specifically hdf5_log_hdf5.
* Make the BAIL2 macro be hdf5 specific and replace elsewhere with an HDF5 independent equivalent.
* Move hdf5cache.c to libsrc4/nc4cache.c because it is used by nczarr.
* Modify unit_tests so that some of them are run even if using Windows.
* Misc. small bug fixes and refactors and memory leaks.
* Rename some conflicting tests for cmake.
* Attempted to make nc_perf work with cmake and failed.
2020-12-16 20:48:02 -07:00

863 lines
22 KiB
C

/*
Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata
See LICENSE.txt for license information.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "netcdf.h"
#include "ncexhash.h"
#include "nccrc.h"
/* 0 => no debug */
#define DEBUG 0
#undef DEBUGTRACE
#undef CATCH
#define INLINED
#ifdef CATCH
#define THROW(x) throw(x)
static void breakpoint(void) {}
static int ignore[] = {NC_ENOTFOUND, 0};
static int throw(int x)
{
int* p;
if(x != 0) {
for(p=ignore;*p;p++) {if(x == *p) break;}
if(*p == 0) breakpoint();
}
return x;
}
#else
#define THROW(x) (x)
#endif
/**
@Internal
*/
#ifdef DEBUGTRACE
#define TRACE(x) {fprintf(stderr,">>>> %s\n",x); fflush(stderr);}
#else
#define TRACE(x)
#endif
/* Minimum table size is 2 */
#define MINDEPTH 1
/* Minimum leaf size is 2 entries */
#define MINLEAFLEN 2
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#ifdef INLINED
#define exhashlinkleaf(map,leaf) {\
if(leaf && map) { leaf->next = map->leaves; map->leaves = leaf; } \
}
#define exhashfreeleaf(map,leaf) { \
if(leaf) {{if(leaf->entries) free(leaf->entries);} free(leaf);} \
}
#endif /*INLINED*/
static int ncexinitialized = 0;
/* Define a vector of bit masks */
ncexhashkey_t bitmasks[NCEXHASHKEYBITS];
/* Extract the leftmost n bits from h */
#if 1
#define MSB(h,d) (((h) >> (NCEXHASHKEYBITS - (d))) & bitmasks[d])
#else
static ncexhashkey_t
MSB(ncexhashkey_t h, int d)
{
ncexhashkey_t bm = bitmasks[d];
ncexhashkey_t hkey = h >> (NCEXHASHKEYBITS - d);
hkey = hkey & bm;
return hkey;
}
#endif
/* Provide a mask to get the rightmost bit
of the left n bits of our hash key */
#define MSBMASK(d) (1 << (NCEXHASHKEYBITS - d))
static int exhashlookup(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp);
static int exhashlocate(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp);
static int exhashsplit(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf* leaf);
static int exhashdouble(NCexhashmap* map);
static int exbinsearch(ncexhashkey_t hkey, NCexleaf* leaf, int* indexp);
static void exhashnewentry(NCexhashmap* map, NCexleaf* leaf, ncexhashkey_t hkey, int* indexp);
static int exhashnewleaf(NCexhashmap* map, NCexleaf** leaf);
static void exhashunlinkleaf(NCexhashmap* map, NCexleaf* leaf);
#ifndef INLINED
static void exhashlinkleaf(NCexhashmap* map, NCexleaf* leaf);
static void exhashfreeleaf(NCexhashmap* map, NCexleaf* leaf);
#endif /*INLINED*/
/**************************************************/
static void
ncexinit(void)
{
int i;
bitmasks[0] = 0;
for(i=1;i<NCEXHASHKEYBITS;i++)
bitmasks[i] = (1 << i) - 1;
ncexinitialized = 1;
}
/**************************************************/
/** Creates a new exhash using DEPTH */
NCexhashmap*
ncexhashnew(int leaflen)
{
NCexhashmap* map = NULL;
NCexleaf* leaf[2] = {NULL,NULL};
NCexleaf** topvector = NULL;
int i;
int gdepth;
TRACE("ncexhashnew");
if(!ncexinitialized) ncexinit();
gdepth = MINDEPTH;
if(leaflen < MINLEAFLEN) leaflen = MINLEAFLEN;
/* Create the table */
if((map = (NCexhashmap*)calloc(1,sizeof(NCexhashmap))) == NULL)
goto done;
map->leaflen = leaflen;
/* Create the top level vector */
if((topvector = calloc(1<<gdepth,sizeof(NCexleaf*))) == NULL)
goto done;
map->directory = topvector;
if(exhashnewleaf(map,&leaf[0])) goto done;
if(exhashnewleaf(map,&leaf[1])) goto done;
exhashlinkleaf(map,leaf[0]);
exhashlinkleaf(map,leaf[1]);
/* Fill in vector */
for(i=0;i<(1<<gdepth);i++) topvector[i] = (i & 0x1?leaf[1]:leaf[0]);
topvector = NULL;
leaf[0] = leaf[1] = NULL;
map->depth = gdepth;
assert(map->leaves != NULL);
done:
if(leaf[0]) {exhashunlinkleaf(map,leaf[0]); exhashfreeleaf(map,leaf[0]);}
if(leaf[1]) {exhashunlinkleaf(map,leaf[1]); exhashfreeleaf(map,leaf[1]);}
if(topvector) free(topvector);
return map;
}
/** Reclaims the exhash structure. */
void
ncexhashmapfree(NCexhashmap* map)
{
NCexleaf* current = NULL;
NCexleaf* next= NULL;
if(map == NULL) return;
/* Walk the leaf chain to free leaves */
current = map->leaves; next = NULL;
while(current) {
next = current->next;
exhashfreeleaf(map,current);
current = next;
}
nullfree(map->directory);
free(map);
}
/** Returns the number of active elements. */
int
ncexhashcount(NCexhashmap* map)
{
return map->nactive;
}
/* Hash key based API */
/* Lookup by Hash Key */
int
ncexhashget(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t* datap)
{
int stat = NC_NOERR;
NCexleaf* leaf;
NCexentry* entry;
int index;
/* Do internal lookup */
if((stat = exhashlookup(map,hkey,&leaf,&index)))
return THROW(stat);
entry = &leaf->entries[index];
assert(entry->hashkey == hkey);
if(datap) *datap = entry->data;
return THROW(stat);
}
/* Insert by Hash Key */
int
ncexhashput(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t data)
{
int stat = NC_NOERR;
NCexleaf* leaf;
NCexentry* entry;
int index;
if(map->iterator.walking) return THROW(NC_EPERM);
/* Do internal lookup */
if((stat = exhashlookup(map,hkey,&leaf,&index)) == NC_ENOTFOUND) {
/* We have to add an entry (sigh!) so find where it goes */
if((stat = exhashlocate(map,hkey,&leaf,&index)))
return THROW(stat);
}
entry = &leaf->entries[index];
entry->hashkey = hkey;
assert(entry->hashkey == hkey);
entry->data = data;
return THROW(stat);
}
static int
exhashlookup(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp)
{
int stat = NC_NOERR;
NCexleaf* leaf;
ncexhashkey_t offset;
int index;
TRACE("exhashlookup");
/* Extract global depth least significant bits of hkey */
offset = MSB(hkey,map->depth);
/* Get corresponding leaf from directory */
leaf = map->directory[offset];
if(leafp) *leafp = leaf; /* always return this if possible */
/* Binary search the leaf entries looking for hkey */
stat = exbinsearch(hkey,leaf,&index);
if(indexp) *indexp = index;
#if DEBUG >= 3
fprintf(stderr,"lookup: found=%s offset=%x leaf=%d index=%d\n",
(stat == NC_NOERR ? "true" : "false"),
offset,leaf->uid,index);
#endif
return THROW(stat);
}
static int
exhashlocate(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf** leafp, int* indexp)
{
int stat = NC_NOERR;
ncexhashkey_t offset;
NCexleaf* leaf = NULL;
int index = -1;
int iter;
TRACE("exhashlocate");
#if DEBUG >= 2
fprintf(stderr,"locate.before: "); ncexhashprint(map);
#endif
/* Setup */
*leafp = NULL;
*indexp = -1;
if(map->iterator.walking) return THROW(NC_EPERM);
/* Repeatedly split and/or double until the target leaf has room */
for(iter=0;;iter++) {
/* Extract global depth least significant bits of hkey */
offset = MSB(hkey,map->depth);
/* Get corresponding leaf from directory */
leaf = map->directory[offset];
/* Is there room in the leaf to add an entry? */
#if DEBUG >= 3
fprintf(stderr,"locate: iter=%d offset=%x leaf=(%d)%p active=%d\n",iter,offset,leaf->uid,leaf,(int)leaf->active);
#endif
if(leaf->active < map->leaflen) break; /* yes, there is room */
/* Not Enough room, so we need to split this leaf */
/* Split this leaf and loop to verify we have room */
#if DEBUG >= 3
fprintf(stderr,"locate.split.loop: uid=%d\n",leaf->uid);
#endif
if((stat = exhashsplit(map,hkey,leaf))) return THROW(stat); /* failed */
}
/* We now now that there is room in the leaf */
/* Insert into this leaf */
exhashnewentry(map,leaf,hkey,&index);
#if DEBUG >= 3
fprintf(stderr,"locate.inserted: index=%d\n",index);
#endif
#if DEBUG >= 1
fprintf(stderr,"locate.inserted.after: "); ncexhashprint(map);
#endif
*leafp = leaf;
*indexp = index;
return THROW(stat);
}
static int
exhashdouble(NCexhashmap* map)
{
NCexleaf** olddir = NULL;
NCexleaf** newdir = NULL;
size_t oldcount,newcount;
ncexhashkey_t iold, inew;
TRACE("exhashdouble");
if(map->iterator.walking) return THROW(NC_EPERM);
#if DEBUG >= 1
fprintf(stderr,"double.before: "); ncexhashprint(map);
#endif
olddir = map->directory;
/* Attempt to double the directory count */
oldcount = (1<<map->depth);
newcount = 2 * oldcount;
newdir = (NCexleaf**)malloc(newcount*sizeof(NCexleaf*));
if(newdir == NULL) return THROW(NC_ENOMEM);
/* Note that newdir == olddir is possible because realloc */
/* Walk the old directory from top to bottom to double copy
into newspace */
assert(oldcount >= 1 && newcount >= 2);
iold = oldcount;
inew = newcount;
do {
iold -= 1;
inew -= 2;
newdir[inew] = olddir[iold];
newdir[inew+1] = olddir[iold];
} while(iold > 0);
assert(iold == 0 && inew == 0);
/* Fix up */
map->directory = newdir;
map->depth++;
#if DEBUG >= 1
fprintf(stderr,"double.after: "); ncexhashprint(map);
#endif
nullfree(olddir);
return THROW(NC_NOERR);
}
static int
exhashsplit(NCexhashmap* map, ncexhashkey_t hkey, NCexleaf* leaf)
{
int stat = NC_NOERR;
NCexleaf* newleaf = NULL;
NCexleaf* leafptr = leaf;
NCexleaf entries;
int i, index;
TRACE("exhashsplit");
if(map->iterator.walking) {stat = NC_EPERM; goto done;}
#if DEBUG >= 1
fprintf(stderr,"split.before: leaf=%d",leaf->uid); ncexhashprint(map);
#endif
/* Save the old leaf's entries */
entries = *leaf;
/* bump leaf depth */
leaf->depth++;
/* May require doubling of the map directory */
#if DEBUG >= 3
fprintf(stderr,"split: leaf.depth=%d map.depth=%d\n",leaf->depth,map->depth);
#endif
if(leaf->depth > map->depth) {
/* double the directory */
if((stat = exhashdouble(map))) return THROW(stat); /* failed */
}
/* Re-build the old leaf; keep same uid */
if((leaf->entries = (NCexentry*)calloc(map->leaflen,sizeof(NCexentry))) == NULL)
{stat = NC_ENOMEM; goto done;}
leaf->active = 0;
/* Allocate and link the new leaf */
if((stat = exhashnewleaf(map,&newleaf))) goto done;
exhashlinkleaf(map,newleaf);
newleaf->depth = leaf->depth;
#if DEBUG >= 3
fprintf(stderr,"split.split: newleaf=");ncexhashprintleaf(map,newleaf);
#endif
/* Now walk the directory to locate all occurrences of old
leaf and replace with newleaf in those cases where the
directory index % 2 == 1
*/
for(i=0;i<(1<<map->depth);i++) {
if(map->directory[i] == leafptr) {
if(i % 2 == 1) { /* entries should be newleaf */
#if DEBUG >= 3
fprintf(stderr,"split.directory[%d]=%d (newleaf)\n",(int)i,newleaf->uid);
#endif
map->directory[i] = newleaf;
}
}
}
#if DEBUG >= 1
fprintf(stderr,"split.after: leaf=%d newleaf=%d",leaf->uid,newleaf->uid); ncexhashprint(map);
#endif
newleaf = NULL; /* no longer needed */
/* Now re-insert the entries */
/* Should not cause splits or doubles */
for(i=0;i<entries.active;i++) {
NCexentry* e = &entries.entries[i];
switch (stat = exhashlookup(map,e->hashkey,&leaf,&index)) {
case NC_NOERR: /* Already exists, so fail */
stat = NC_EINTERNAL;
goto done;
default:
stat = NC_NOERR;
break;
}
assert(leaf != NULL);
/* Insert in the proper leaf */
#if DEBUG >= 3
fprintf(stderr,"split.reinsert: entry.hashkey=%x leaf.uid=%d index=%d\n",
e->hashkey,leaf->uid,index);
#endif
leaf->entries[index] = *e;
leaf->active++;
#if DEBUG >= 1
fprintf(stderr,"split.reinsert.after: "); ncexhashprint(map);
#endif
}
done:
if(stat) { /* unwind */
nullfree(leaf->entries);
*leaf = entries;
} else {
nullfree(entries.entries);
}
if(newleaf) {
exhashunlinkleaf(map,newleaf);
exhashfreeleaf(map,newleaf);
}
return THROW(stat);
}
/**
* @internal Define a binary searcher for hash keys in leaf
* @param hkey for which to search
* @param leaf to search
* @param indexp store index of the match or if no match, the index
* where new value should be stored (0 <= *indexp <= leaf->active)
* @return NC_NOERR if found
* @return NC_ENOTFOUND if not found, indexp points to insertion location
* @author Dennis Heimbigner
*/
static int
exbinsearch(ncexhashkey_t hkey, NCexleaf* leaf, int* indexp)
{
int stat = NC_NOERR;
int n = leaf->active;
int L = 0;
int R = (n - 1);
if(n == 0) {
if(indexp) *indexp = 0; /* Insert at 0 */
return THROW(NC_ENOTFOUND);
}
while(L != R) {
int m = (L + R);
if((m & 0x1)) /* ceiling */
m = (m / 2) + 1;
else
m = (m / 2);
if(leaf->entries[m].hashkey > hkey)
R = m - 1;
else
L = m;
}
/* Return match index or insertion index */
if(leaf->entries[L].hashkey == hkey) {
/* L = L; */
/* stat = NC_NOERR; */
} else if(leaf->entries[L].hashkey > hkey) {
/* L = L; */
stat = NC_ENOTFOUND;
} else {/* (leaf->entries[m].hashkey < hkey) */
L = L+1;
stat = NC_ENOTFOUND;
}
if(indexp) *indexp = L;
return THROW(stat);
}
/* Create new entry at position index */
static void
exhashnewentry(NCexhashmap* map, NCexleaf* leaf, ncexhashkey_t hkey, int* indexp)
{
int stat;
int index;
/* Figure out where the key should be inserted (*indexp + 1)*/
stat = exbinsearch(hkey,leaf,indexp);
assert(stat != NC_NOERR); /* already present */
index = *indexp;
assert(index >= 0 && index <= leaf->active);
assert(index == leaf->active || leaf->entries[index].hashkey > hkey);
if(leaf->active > 0) {
int dst = leaf->active;
int src = leaf->active - 1;
for(;src >= index;src--,dst--)
leaf->entries[dst] = leaf->entries[src];
}
#if 0
leaf->entries[index].hashkey = hkey;
#else
leaf->entries[index].hashkey = (ncexhashkey_t)0xffffffffffffffff;
#endif
leaf->entries[index].data = 0;
leaf->active++;
map->nactive++;
}
#ifndef INLINED
static void
exhashlinkleaf(NCexhashmap* map, NCexleaf* leaf)
{
if(leaf && map) {
assert(!map->iterator.walking);
leaf->next = map->leaves;
map->leaves = leaf;
assert(leaf->next == NULL || leaf->next != leaf);
}
}
static void
exhashfreeleaf(NCexhashmap* map, NCexleaf* leaf)
{
assert(!map->iterator.walking);
if(leaf) {
nullfree(leaf->entries);
nullfree(leaf);
}
}
#endif /*INLINED*/
static void
exhashunlinkleaf(NCexhashmap* map, NCexleaf* leaf)
{
if(leaf && map && map->leaves) {
assert(!map->iterator.walking);
if(map->leaves == leaf) {/* special case */
map->leaves = leaf->next;
} else {
NCexleaf* cur;
for(cur = map->leaves;cur != NULL;cur=cur->next) {
if(cur->next == leaf) {
cur->next = leaf->next;
break;
}
}
}
}
}
static int
exhashnewleaf(NCexhashmap* map, NCexleaf** leafp)
{
int stat = NC_NOERR;
NCexleaf* leaf = NULL;
assert(!map->iterator.walking);
if(leafp) {
if((leaf = calloc(1,sizeof(NCexleaf))) == NULL)
goto done;
assert(map->leaflen > 0);
if((leaf->entries = calloc(map->leaflen,sizeof(NCexentry))) == NULL)
goto done;
leaf->uid = map->uid++;
*leafp = leaf; leaf = NULL;
}
done:
if(leaf) nullfree(leaf->entries);
nullfree(leaf);
return stat;
}
/**
* Remove by Hash Key
* @param map
* @param hkey
* @param datap Store the data of the removed hkey
* @return NC_NOERR if found
* @return NC_ENOTFOUND if not found
* @return NC_EINVAL for other errors
* @author Dennis Heimbigner
*/
int
ncexhashremove(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t* datap)
{
int stat = NC_NOERR;
NCexleaf* leaf;
int src,dst;
if(map->iterator.walking) return THROW(NC_EPERM);
if((stat = exhashlookup(map,hkey,&leaf,&dst)))
return THROW(stat);
if(datap) *datap = leaf->entries[dst].data;
/* Compress out the index'th entry */
for(src=dst+1;src<leaf->active;src++,dst++)
leaf->entries[dst] = leaf->entries[src];
leaf->active--;
map->nactive--;
return THROW(stat);
}
/**
* Change data associated with key; do not insert if not found
* @param map
* @param hkey
* @param newdata new data
* @param olddatap Store previous value
* @return NC_NOERR if found
* @return NC_ENOTFOUND if not found
* @return NC_EINVAL for other errors
* @author Dennis Heimbigner
*/
int
ncexhashsetdata(NCexhashmap* map, ncexhashkey_t hkey, uintptr_t newdata, uintptr_t* olddatap)
{
int stat = NC_NOERR;
NCexleaf* leaf = NULL;
NCexentry* e = NULL;
int index;
if(map->iterator.walking) return THROW(NC_EPERM);
if((stat = exhashlookup(map,hkey,&leaf,&index)))
return THROW(stat);
e = &leaf->entries[index];
if(olddatap) *olddatap = e->data;
e->data = newdata;
return THROW(stat);
}
/**
* Inquire map-related values
* @param map
* @param leaflenp Store map->leaflen
* @param depthp Store map->depth
* @param nactivep Store map->nactive
* @param uidp Store map->uid
* @param walkingp Store map->iterator.walking
*
* @return NC_NOERR if found
* @return NC_EINVAL for other errors
* @author Dennis Heimbigner
*/
int
ncexhashinqmap(NCexhashmap* map, int* leaflenp, int* depthp, int* nactivep, int* uidp, int* walkingp)
{
if(map == NULL) return NC_EINVAL;
if(leaflenp) *leaflenp = map->leaflen;
if(depthp) *depthp = map->depth;
if(nactivep) *nactivep = map->nactive;
if(uidp) *uidp = map->uid;
if(walkingp) *walkingp = map->iterator.walking;
return NC_NOERR;
}
/* Return the hash key for specified key; takes key+size*/
ncexhashkey_t
ncexhashkey(const unsigned char* key, size_t size)
{
return NC_crc64(0,(unsigned char*)key,(unsigned int)size);
}
/* Walk the entries in some order */
/*
@return NC_NOERR if keyp and datap are valid
@return NC_ERANGE if iteration is finished
@return NC_EINVAL for all other errors
*/
int
ncexhashiterate(NCexhashmap* map, ncexhashkey_t* keyp, uintptr_t* datap)
{
int stat = NC_NOERR;
if(!map->iterator.walking) {
map->iterator.leaf = map->leaves;
map->iterator.index = 0;
map->iterator.walking = 1;
}
for(;;) {
if(map->iterator.leaf == NULL)
{stat = NC_ERANGE; break;}
if(map->iterator.index >= map->iterator.leaf->active) {
map->iterator.leaf = map->iterator.leaf->next;
map->iterator.index = 0;
} else {
assert(map->iterator.leaf != NULL && map->iterator.index < map->iterator.leaf->active);
/* Return data from this entry */
if(keyp) *keyp = map->iterator.leaf->entries[map->iterator.index].hashkey;
if(datap) *datap = map->iterator.leaf->entries[map->iterator.index].data;
map->iterator.index++;
break;
}
}
if(stat != NC_NOERR) { /* stop */
map->iterator.walking = 0;
map->iterator.leaf = NULL;
map->iterator.index = 0;
}
return THROW(stat);
}
/**************************************************/
/* Debug support */
void
ncexhashprint(NCexhashmap* hm)
{
int dirindex,index;
if(hm == NULL) {fprintf(stderr,"NULL"); fflush(stderr); return;}
fprintf(stderr,"{depth=%u leaflen=%u",hm->depth,hm->leaflen);
if(hm->iterator.walking) {
fprintf(stderr," iterator=(leaf=%p index=%u)",
hm->iterator.leaf,hm->iterator.index);
}
fprintf(stderr,"\n");
for(dirindex=0;dirindex<(1<<hm->depth);dirindex++) {
NCexleaf* leaf = hm->directory[dirindex];
fprintf(stderr,"\tdirectory[%03d|%sb]=(%04x)[(%u)^%d|%d|",
dirindex,ncexbinstr(dirindex,hm->depth),
leaf->active,
(unsigned)(0xffff & (uintptr_t)leaf),
leaf->uid, leaf->depth);
for(index=0;index<leaf->active;index++) {
ncexhashkey_t hkey, bits;
const char* s;
hkey = leaf->entries[index].hashkey;
/* Reduce to the leaf->hash MSB */
bits = MSB(hkey,hm->depth);
s = ncexbinstr(bits,hm->depth);
fprintf(stderr,"%s(%s/",(index==0?":":" "),s);
bits = MSB(hkey,leaf->depth);
s = ncexbinstr(bits,leaf->depth);
fprintf(stderr,"%s|0x%llx,%lu)",
s,
(unsigned long long)hkey,
(uintptr_t)leaf->entries[index].data);
}
fprintf(stderr,"]\n");
}
fprintf(stderr,"}\n");
fflush(stderr);
}
void
ncexhashprintdir(NCexhashmap* map, NCexleaf** dir)
{
int dirindex;
for(dirindex=0;dirindex<(1<<map->depth);dirindex++) {
NCexleaf* leaf = dir[dirindex];
fprintf(stderr,"\tdirectory[%03d|%sb]=%d/%p\n",
dirindex,ncexbinstr(dirindex,map->depth),leaf->uid,leaf);
}
fflush(stderr);
}
void
ncexhashprintleaf(NCexhashmap* map, NCexleaf* leaf)
{
int index;
fprintf(stderr,"(%04x)[(%u)^%d|%d|",
(unsigned)(0xffff & (uintptr_t)leaf),
leaf->uid, leaf->depth,leaf->active);
for(index=0;index<leaf->active;index++) {
ncexhashkey_t hkey, bits;
const char* s;
hkey = leaf->entries[index].hashkey;
/* Reduce to the leaf->hash MSB */
bits = MSB(hkey,map->depth);
s = ncexbinstr(bits,map->depth);
fprintf(stderr,"%s(%s/",(index==0?":":" "),s);
bits = MSB(hkey,leaf->depth);
s = ncexbinstr(bits,leaf->depth);
fprintf(stderr,"%s|0x%llx,%lu)",
s, (unsigned long long)hkey, (uintptr_t)leaf->entries[index].data);
}
fprintf(stderr,"]\n");
}
void
ncexhashprintentry(NCexhashmap* map, NCexentry* entry)
{
fprintf(stderr,"{0x%llx,%lu)",(unsigned long long)entry->hashkey,(uintptr_t)entry->data);
}
char*
ncexbinstr(ncexhashkey_t hkey, int depth)
{
int i;
static char bits[NCEXHASHKEYBITS+1];
memset(bits,'0',NCEXHASHKEYBITS+1);
bits[NCEXHASHKEYBITS] = '\0';
for(i=0;i<depth;i++)
bits[(depth-1)-i] = ((hkey >> i) & 0x1) == 0 ? '0' : '1';
bits[depth] = '\0';
return bits;
}
void
ncexhashprintstats(NCexhashmap* map)
{
int nactive, nleaves;
NCexleaf* leaf = NULL;
double leafavg = 0.0;
double leafload = 0.0;
unsigned long long dirsize, leafsize, total;
nactive = 0;
nleaves = 0;
for(leaf=map->leaves;leaf;leaf=leaf->next) {
nleaves++;
nactive += leaf->active;
}
leafavg = ((double)nactive)/((double)nleaves);
leafload = leafavg / ((double)map->leaflen);
if(nactive != map->nactive) {
fprintf(stderr,"nactive mismatch: map->active=%d actual=%d\n",map->nactive,nactive);
}
fprintf(stderr,"|directory|=%llu nleaves=%d nactive=%d",
(unsigned long long)(1<<(map->depth)),nleaves,nactive);
fprintf(stderr," |leaf|=%d nactive/nleaves=%g", map->leaflen, leafavg);
fprintf(stderr," load=%g",leafload);
fprintf(stderr,"]\n");
dirsize = (1<<(map->depth)*((unsigned long long)sizeof(void*)));
leafsize = (nleaves)*((unsigned long long)sizeof(NCexleaf));
total = dirsize + leafsize;
fprintf(stderr,"\tsizeof(directory)=%llu sizeof(leaves)=%lld total=%lld\n",
dirsize,leafsize,total);
}