#include "nc3internal.h" #include #include #include #include /* this should be prime */ #define TABLE_STARTSIZE 1021 #define ACTIVE 1 #define MAX(a,b) ((a) > (b) ? (a) : (b)) extern uint32_t hash_fast(const void *key, size_t length); static int isPrime(unsigned long val) { int i; for (i = 9; i--;) { #ifdef HAVE_RANDOM unsigned long a = ((unsigned long)random() % (val-4)) + 2; #else unsigned long a = ((unsigned long)rand() % (val - 4)) + 2; #endif unsigned long p = 1; unsigned long exp = val-1; while (exp) { if (exp & 1) p = (p*a)%val; a = (a*a)%val; exp >>= 1; } if (p != 1) return 0; } return 1; } static unsigned long findPrimeGreaterThan(unsigned long val) { if (val & 1) val+=2; else val++; while (!isPrime(val)) val+=2; return val; } static void rehashDim(const NC_dimarray* ncap) { NC_hashmap* hm = ncap->hashmap; unsigned long size = hm->size; unsigned long count = hm->count; hEntry* table = hm->table; hm->size = findPrimeGreaterThan(size<<1); hm->table = (hEntry*)calloc(sizeof(hEntry), hm->size); hm->count = 0; while(size > 0) { --size; if (table[size].flags == ACTIVE) { NC_dim *elem = ncap->value[table[size].data-1]; NC_hashmapAddDim(ncap, table[size].data-1, elem->name->cp); assert(NC_hashmapGetDim(ncap, elem->name->cp) == table[size].data-1); } } free(table); assert(count == hm->count); } static void rehashVar(const NC_vararray* ncap) { NC_hashmap* hm = ncap->hashmap; unsigned long size = hm->size; unsigned long count = hm->count; hEntry* table = hm->table; hm->size = findPrimeGreaterThan(size<<1); hm->table = (hEntry*)calloc(sizeof(hEntry), (size_t)hm->size); hm->count = 0; while(size > 0) { --size; if (table[size].flags == ACTIVE) { NC_var *elem = ncap->value[table[size].data-1]; NC_hashmapAddVar(ncap, table[size].data-1, elem->name->cp); assert(NC_hashmapGetVar(ncap, elem->name->cp) == table[size].data-1); } } free(table); assert(count == hm->count); } NC_hashmap* NC_hashmapCreate(unsigned long startsize) { NC_hashmap* hm = (NC_hashmap*)malloc(sizeof(NC_hashmap)); if (!startsize) startsize = TABLE_STARTSIZE; else { startsize *= 4; startsize /= 3; startsize = findPrimeGreaterThan(startsize-2); } hm->table = (hEntry*)calloc(sizeof(hEntry), (size_t)startsize); hm->size = startsize; hm->count = 0; return hm; } void NC_hashmapAddDim(const NC_dimarray* ncap, long data, const char *name) { unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; if (hash->size*3/4 <= hash->count) { rehashDim(ncap); } do { unsigned long i; unsigned long index = key % hash->size; unsigned long step = (key % MAX(1,(hash->size-2))) + 1; for (i = 0; i < hash->size; i++) { if (hash->table[index].flags & ACTIVE) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { hash->table[index].data = data+1; return; } } else { hash->table[index].flags |= ACTIVE; hash->table[index].data = data+1; hash->table[index].key = key; ++hash->count; return; } index = (index + step) % hash->size; } /* it should not be possible that we EVER come this far, but unfortunately not every generated prime number is prime (Carmichael numbers...) */ rehashDim(ncap); } while (1); } void NC_hashmapAddVar(const NC_vararray* ncap, long data, const char *name) { unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; if (hash->size*3/4 <= hash->count) { rehashVar(ncap); } do { unsigned long i; unsigned long index = key % hash->size; unsigned long step = (key % MAX(1,(hash->size-2))) + 1; for (i = 0; i < hash->size; i++) { if (hash->table[index].flags & ACTIVE) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { hash->table[index].data = data+1; return; } } else { hash->table[index].flags |= ACTIVE; hash->table[index].data = data+1; hash->table[index].key = key; ++hash->count; return; } index = (index + step) % hash->size; } /* it should not be possible that we EVER come this far, but unfortunately not every generated prime number is prime (Carmichael numbers...) */ rehashVar(ncap); } while (1); } long NC_hashmapRemoveDim(const NC_dimarray* ncap, const char *name) { unsigned long i; unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; unsigned long index = key % hash->size; unsigned long step = (key % (hash->size-2)) + 1; for (i = 0; i < hash->size; i++) { if (hash->table[index].data > 0) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { if (hash->table[index].flags & ACTIVE) { hash->table[index].flags &= ~ACTIVE; --hash->count; return hash->table[index].data-1; } else /* in, but not active (i.e. deleted) */ return -1; } } else /* found an empty place (can't be in) */ return -1; index = (index + step) % hash->size; } /* everything searched through, but not in */ return -1; } long NC_hashmapRemoveVar(const NC_vararray* ncap, const char *name) { unsigned long i; unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; unsigned long index = key % hash->size; unsigned long step = (key % (hash->size-2)) + 1; for (i = 0; i < hash->size; i++) { if (hash->table[index].data > 0) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { if (hash->table[index].flags & ACTIVE) { hash->table[index].flags &= ~ACTIVE; --hash->count; return hash->table[index].data-1; } else /* in, but not active (i.e. deleted) */ return -1; } } else /* found an empty place (can't be in) */ return -1; index = (index + step) % hash->size; } /* everything searched through, but not in */ return -1; } long NC_hashmapGetDim(const NC_dimarray* ncap, const char *name) { NC_hashmap* hash = ncap->hashmap; if (hash->count) { unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; unsigned long i; unsigned long index = key % hash->size; unsigned long step = (key % (hash->size-2)) + 1; for (i = 0; i < hash->size; i++) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { if (entry.flags & ACTIVE) return entry.data-1; break; } else if (!(entry.flags & ACTIVE)) break; index = (index + step) % hash->size; } } return -1; } long NC_hashmapGetVar(const NC_vararray* ncap, const char *name) { NC_hashmap* hash = ncap->hashmap; if (hash->count) { unsigned long key = hash_fast(name, strlen(name)); NC_hashmap* hash = ncap->hashmap; unsigned long i; unsigned long index = key % hash->size; unsigned long step = (key % (hash->size-2)) + 1; for (i = 0; i < hash->size; i++) { hEntry entry = hash->table[index]; if (entry.key == key && strncmp(name, ncap->value[entry.data-1]->name->cp, ncap->value[entry.data-1]->name->nchars) == 0) { if (entry.flags & ACTIVE) return entry.data-1; break; } else if (!(entry.flags & ACTIVE)) break; index = (index + step) % hash->size; } } return -1; } unsigned long NC_hashmapCount(NC_hashmap* hash) { return hash->count; } void NC_hashmapDelete(NC_hashmap* hash) { if (hash) { free(hash->table); free(hash); } } void NC_hashmap_verify(NC_hashmap* hash, NC_dim** dims) { unsigned long i; if(hash->count == 0) { fprintf(stderr,"\n"); goto done; } for(i=0;isize;i++) { hEntry* e = &hash->table[i]; if(e->flags == ACTIVE) { fprintf(stderr,"[%d] key=%lu data=%ld",i,e->key,e->data-1); if(dims != NULL) { fprintf(stderr," name=%s",dims[e->data-1]->name->cp); } fprintf(stderr,"\n"); } } done: fflush(stderr); }