Last active
August 12, 2017 18:38
-
-
Save sug0/bd7dfa0bbdcb7f8772839c2431d3b1c3 to your computer and use it in GitHub Desktop.
Fixed size hash table implementation, based on https://www.youtube.com/watch?v=qTZJLJ3Gm6Q
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
#include "hash.h" | |
struct _lst { | |
void *e; | |
uint64_t hash; | |
struct _lst *next; | |
}; | |
/* | |
* Adapted sdbm hashing function | |
* */ | |
static uint64_t hash(void *data, size_t size) | |
{ | |
size_t sz = size; | |
uint64_t hash = 0; | |
uint8_t *ptr = (uint8_t *)data; | |
while (sz-- > 0) { | |
hash = *ptr + (hash << 6) + (hash << 16) - hash; | |
ptr++; | |
} | |
return hash; | |
} | |
static inline struct _lst *new_lst(void) | |
{ | |
return (struct _lst *)calloc(sizeof(struct _lst), 1); | |
} | |
static int add(struct _lst *lst, void *e, uint64_t keyhash) | |
{ | |
if (!lst || !e) | |
return -1; | |
struct _lst *ptr = lst, *new, *last; | |
while (ptr) { | |
if (!ptr->next) | |
last = ptr; | |
if (ptr->hash == keyhash) { | |
ptr->e = e; | |
goto done; | |
} | |
ptr = ptr->next; | |
} | |
new = new_lst(); | |
if (!new) | |
return -2; | |
new->e = e; | |
new->hash = keyhash; | |
last->next = new; | |
done: | |
return 0; | |
} | |
static void *get(struct _lst *lst, uint64_t hash) | |
{ | |
struct _lst *ptr = lst; | |
while (ptr) { | |
if (ptr->hash == hash) | |
return ptr->e; | |
else | |
ptr = ptr->next; | |
} | |
return NULL; | |
} | |
static void free_lst(struct _lst *lst) | |
{ | |
struct _lst *ptr = lst, *next; | |
while (ptr) { | |
next = ptr->next; | |
free(ptr); | |
ptr = next; | |
} | |
} | |
int init_dict(dict_t *d, size_t size) | |
{ | |
if (!d) | |
return -1; | |
struct _lst **es = calloc(sizeof(struct _lst), size); | |
if (!es) | |
return -2; | |
d->es = es; | |
d->size = size; | |
return 0; | |
} | |
int insert_dict(dict_t *d, size_t keysize, void *k, void *v) | |
{ | |
if (!d || !k || !v) | |
return -1; | |
uint64_t keyhash = hash(k, keysize); | |
uint64_t index = keyhash % d->size; | |
if (!d->es[index]) { | |
struct _lst *new = new_lst(); | |
if (!new) | |
return -2; | |
new->e = v; | |
new->hash = keyhash; | |
d->es[index] = new; | |
} else { | |
int rt = add(d->es[index], v, keyhash); | |
if (rt < 0) | |
return rt; | |
} | |
return 0; | |
} | |
void *at(dict_t *d, size_t keysize, void *k) | |
{ | |
if (!d || !k) | |
return NULL; | |
uint64_t keyhash = hash(k, keysize); | |
uint64_t index = keyhash % d->size; | |
return get(d->es[index], keyhash); | |
} | |
void destroy_dict(dict_t *d) | |
{ | |
if (!d) | |
return; | |
for (size_t i = 0; i < d->size; i++) | |
free_lst(d->es[i]); | |
free(d->es); | |
} |
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
#ifndef _HASHTAB_H_ | |
#define _HASHTAB_H_ | |
/* | |
* Fixed size hash table implementation, | |
* with collision handled by a single linked list; | |
* based on https://www.youtube.com/watch?v=qTZJLJ3Gm6Q | |
* */ | |
#include <stdlib.h> | |
#include <stdint.h> | |
struct _lst; | |
typedef struct hashtab dict_t; | |
struct hashtab { | |
struct _lst **es; | |
size_t size; | |
}; | |
extern int init_dict(dict_t *d, size_t size); | |
extern int insert_dict(dict_t *d, size_t keysize, void *k, void *v); | |
extern void *at(dict_t *d, size_t keysize, void *k); | |
extern void destroy_dict(dict_t *d); | |
#endif |
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
#include <stdio.h> | |
#include <string.h> | |
#include "hash.h" | |
int main(void) | |
{ | |
dict_t d; | |
char *key1 = "wew", *key2 = "lad"; | |
size_t len1 = strlen(key1) + 1, len2 = strlen(key2) + 1; | |
init_dict(&d, 10); | |
insert_dict(&d, len1, key1, (void *)123); | |
insert_dict(&d, len2, key2, (void *)321); | |
printf("%d, %d\n", | |
at(&d, len1, key1), | |
at(&d, len2, key2)); | |
destroy_dict(&d); | |
return 0; | |
} |
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
#include <stdio.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include "hash.h" | |
#define dict_entry(D, K, V) insert_dict(D, strlen(K) + 1, K, V) | |
#define entry(D, K) at(D, strlen(K) + 1, K) | |
#define call(D, T, K, ...) ((T)entry(D, K))(__VA_ARGS__) | |
static inline void hey(void) | |
{ | |
puts("sup"); | |
} | |
static inline void dude(char *a, char *b) | |
{ | |
puts(a); | |
puts(b); | |
} | |
int main(void) | |
{ | |
dict_t d; | |
init_dict(&d, 0x10); | |
dict_entry(&d, "upper", isupper); | |
dict_entry(&d, "lower", islower); | |
dict_entry(&d, "alpha", isalpha); | |
dict_entry(&d, "alnum", isalnum); | |
dict_entry(&d, "hey", hey); | |
dict_entry(&d, "dude", dude); | |
if (call(&d, int (*)(int), "upper", 'A')) | |
puts("ye"); | |
call(&d, void (*)(void), "hey"); | |
call(&d, void (*)(char *, char *), "dude", "wat up", "nigga"); | |
destroy_dict(&d); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment