Last active
August 29, 2015 14:27
-
-
Save gyakoo/a87c8ade3e0ca7306744 to your computer and use it in GitHub Desktop.
Small C dictionary I created for a home project. Not totally perfect but gives me more performance than std::map<> in my scenario
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////////////////////////////////////// | |
// DICTIONARY : DATA | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
typedef unsigned long (*flt_dict_hash)(const unsigned char*, int); | |
typedef int (*flt_dict_keycomp)(const char*, const char*, int); | |
typedef void (*flt_dict_visitor)(char* key, void* value); | |
typedef struct flt_dict_node | |
{ | |
char* key; | |
unsigned long keyhash; | |
void* value; | |
struct flt_dict_node* next; | |
}flt_dict_node; | |
typedef struct flt_dict | |
{ | |
struct flt_dict_node** hasht; | |
struct flt_critsec* cs; | |
flt_dict_hash hashf; | |
flt_dict_keycomp keycomp; | |
fltatom32 ref; | |
int capacity; // no of entries in hash table | |
int count; // no of actual elements | |
}flt_dict; | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
// DICTIONARY : FUNCTIONS | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
// djb2 | |
unsigned long flt_dict_hash_djb2(const unsigned char* str, int unused) | |
{ | |
unused; | |
unsigned long hash = 5381; | |
int c; | |
while (c = *str++) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ | |
return hash; | |
} | |
unsigned long flt_dict_hash_face_djb2(const unsigned char* data, int size) | |
{ | |
unsigned long hash = 5381; | |
int c,i; | |
for (i=0;i<size;++i) | |
{ | |
c=*data++; | |
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ | |
} | |
return hash; | |
} | |
int flt_dict_keycomp_string(const char* a, const char* b, int extra) | |
{ | |
return strcmp(a,b); | |
} | |
int flt_dict_keycomp_face(const char* a, const char* b, int size) | |
{ | |
return memcmp(a,b,size); | |
} | |
// create_cs==1 to create a critical section object | |
void flt_dict_create(int capacity, int create_cs, flt_dict** dict, flt_dict_hash hashf, flt_dict_keycomp keycomp) | |
{ | |
flt_dict* d = (flt_dict*)flt_malloc(sizeof(flt_dict)); | |
d->cs = create_cs ? flt_critsec_create() : FLT_NULL; | |
d->hasht = (flt_dict_node**)flt_calloc(capacity,sizeof(flt_dict_node*)); | |
d->capacity = capacity; | |
d->count = 0; | |
d->hashf = hashf; | |
d->keycomp = keycomp; | |
d->ref = 1; | |
*dict = d; | |
} | |
void flt_dict_visit(flt_dict*dict, flt_dict_visitor visitor) | |
{ | |
int i; | |
flt_dict_node *n; | |
if ( dict->cs ) flt_critsec_enter(dict->cs); | |
i=dict->capacity; | |
while(i--) | |
{ | |
n=dict->hasht[i]; | |
while (n) | |
{ | |
visitor(n->key, n->value); | |
n=n->next; | |
} | |
} | |
if ( dict->cs ) flt_critsec_leave(dict->cs); | |
} | |
void flt_dict_destroy(flt_dict** dict, int free_elms, flt_dict_visitor destructor) | |
{ | |
int i; | |
flt_dict_node *n, *nn; | |
if ( (*dict)->cs ) flt_critsec_enter((*dict)->cs); | |
// entries and their lists | |
for (i=0;i<(*dict)->capacity;++i) | |
{ | |
n=(*dict)->hasht[i]; | |
while (n) | |
{ | |
nn=n->next; | |
if ( destructor ) | |
destructor(n->key, n->value); | |
if ( free_elms ) | |
flt_safefree(n->value); | |
flt_free(n->key); | |
flt_free(n); | |
n=nn; | |
} | |
} | |
flt_safefree((*dict)->hasht); | |
if ( (*dict)->cs ) | |
{ | |
flt_critsec_leave((*dict)->cs); | |
flt_critsec_destroy((*dict)->cs); | |
} | |
flt_safefree(*dict); | |
} | |
void* flt_dict_get(flt_dict* dict, const char* key, int size, flt_optional unsigned long hash) | |
{ | |
flt_dict_node *n; | |
const unsigned long hashk = !hash ? dict->hashf((const unsigned char*)key,size) : hash; | |
const int entry = hashk % dict->capacity; | |
if ( dict->cs ) flt_critsec_enter(dict->cs); | |
n = dict->hasht[entry]; | |
while(n) | |
{ | |
if ( n->keyhash == hashk && dict->keycomp(n->key,key,size)==0 ){ break; } | |
n=n->next; | |
} | |
if ( dict->cs ) flt_critsec_leave(dict->cs); | |
return n ? n->value : FLT_NULL; | |
} | |
flt_dict_node* flt_dict_create_node(const char* key, unsigned long keyhash, void* value, int size) | |
{ | |
flt_dict_node *n = (flt_dict_node*)flt_malloc(sizeof(flt_dict_node)); | |
if ( size ) // we got size, just alloc+memcpy the key | |
{ | |
n->key = (char*)flt_malloc(size); | |
memcpy(n->key,key,size); | |
} | |
else | |
{ | |
n->key = flt_strdup(key); // 0 size is for strings | |
} | |
n->keyhash = keyhash; | |
n->value = value; | |
n->next = FLT_NULL; | |
return n; | |
} | |
int flt_dict_insert(flt_dict* dict, const char* key, void* value, int size, flt_optional unsigned long hash) | |
{ | |
flt_dict_node *n, *ln; | |
unsigned long hashk; | |
int entry; | |
if ( !value || !dict ) | |
return FLT_FALSE; // must insert non-null value. | |
hashk = !hash ? dict->hashf((const unsigned char*)key, size) : hash; | |
if ( dict->cs ) flt_critsec_enter(dict->cs); // acquire | |
entry = hashk % dict->capacity; | |
n=dict->hasht[entry]; | |
++dict->count; | |
if ( !n ) | |
{ | |
dict->hasht[entry]=flt_dict_create_node(key,hashk,value,size); // not found in entry, add it | |
} | |
else | |
{ | |
ln=n; | |
// entry collision: append to the end of list: new entry, just hash collision || or already exists element | |
while (n) | |
{ | |
// same keyhash and same keyname (exists) | |
if ( n->keyhash == hashk && dict->keycomp(n->key,key,size)==0 ) { n->value = value; ln=0; --dict->count; break; } | |
ln=n; | |
n=n->next; | |
} | |
if (ln) ln->next = flt_dict_create_node(key,hashk,value,size); | |
} | |
if ( dict->cs ) flt_critsec_leave(dict->cs); | |
return FLT_TRUE; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment