|
/* |
|
** LICENSE: BSD |
|
** Author: CandyMi[https://github.com/candymi] |
|
*/ |
|
|
|
#include "map.h" |
|
|
|
#ifndef and |
|
#define and && |
|
#endif |
|
|
|
#ifndef or |
|
#define or || |
|
#endif |
|
|
|
#define map_ivalue (0x811c9dc5) |
|
|
|
#define map_eq(p, hash, key, kl) ((p)->hash == (hash) and !memcmp((p)->key, (key), (kl))) |
|
|
|
#define map_need_resize(len, cap, factor) ((cap) >= ((uint32_t)((len) * ((factor) * 0.01)))) |
|
|
|
typedef struct slot { |
|
uint64_t hash; |
|
char* key; |
|
void* data; |
|
struct slot* next; |
|
} Slot; |
|
|
|
struct map { |
|
uint32_t len; |
|
uint32_t cap; |
|
uint32_t factor; |
|
struct slot** list; |
|
}; |
|
|
|
static inline void map_hash(const char* text, uint32_t tsize, uint64_t *hash) { |
|
while (tsize--) |
|
*hash ^= ((*hash << 5) + (*hash >> 2) + (text[tsize])); |
|
} |
|
|
|
static inline void map_rehash(Slot** nlist, uint32_t nlen, Slot** olist, uint32_t olen) { |
|
Slot* tmp = NULL; uint32_t idx, i; |
|
for (i = 0; i < olen; i++) |
|
{ |
|
Slot* slot = olist[i]; |
|
while (slot) |
|
{ |
|
tmp = slot->next; |
|
idx = (slot->hash) % nlen; |
|
Slot* nslot = nlist[idx]; |
|
if (!nslot) |
|
{ |
|
nlist[idx] = slot; |
|
slot->next = NULL; |
|
} |
|
else |
|
{ |
|
slot->next = nslot; |
|
nlist[idx] = slot; |
|
} |
|
slot = tmp; |
|
} |
|
} |
|
} |
|
|
|
static inline void map_resize(Map* m) { |
|
uint32_t len = m->len; |
|
if (!len) |
|
len = DEFAULT_MAP_LEN; |
|
else |
|
len <<= DEFAULT_MAP_OFFSET; |
|
|
|
Slot** list = map_xmalloc(sizeof(Slot *) * len); |
|
/* resize failed must safety and harmless. */ |
|
if (!list) |
|
return; |
|
memset(list, 0x0, len); |
|
|
|
/* maybe need rehash. */ |
|
if (m->list) { |
|
map_rehash(list, len, m->list, m->len); |
|
map_xfree(m->list); |
|
} |
|
m->list = list; |
|
m->len = len; |
|
} |
|
|
|
Map* map_new() { |
|
Map* m = map_xmalloc(sizeof(Map)); |
|
if (!m) |
|
return NULL; |
|
m->list = NULL; |
|
m->len = m->cap = 0; |
|
m->factor = DEFAULT_MAP_FACTOR; |
|
return m; |
|
} |
|
|
|
Slot* slot_new(uint64_t hash, const char* key, uint32_t ksize, void* data) { |
|
Slot* o = map_xmalloc(sizeof(Slot)); |
|
o->key = map_xmalloc(ksize + 1); memcpy(o->key, key, ksize); o->key[ksize] = '\x00'; |
|
o->next = NULL; o->data = data; o->hash = hash; |
|
return o; |
|
} |
|
|
|
void map_free(Map* m) { |
|
if (m->list) { |
|
uint32_t i; Slot* tmp = NULL; |
|
for (i = 0; i < m->len; i++) |
|
{ |
|
Slot* slot = m->list[i]; |
|
while (slot) |
|
{ |
|
tmp = slot->next; |
|
map_xfree(slot->key); |
|
map_xfree(slot); |
|
slot = tmp; |
|
} |
|
m->list[i] = NULL; |
|
} |
|
map_xfree(m->list); // map_xfree |
|
} |
|
map_xfree(m); // map_xfree |
|
} |
|
|
|
void* map_get(Map* m, const char *key) { |
|
if (!m or !key or m->cap == 0) |
|
return NULL; |
|
|
|
/* Calculate hash value */ |
|
uint32_t ksize = (uint32_t)strlen(key); |
|
uint64_t hash = map_ivalue; |
|
map_hash(key, ksize, &hash); |
|
|
|
Slot* slot = m->list[(hash) % m->len]; |
|
if (!slot) |
|
return NULL; |
|
|
|
while (slot) |
|
{ |
|
if (map_eq(slot, hash, key, ksize)) |
|
return slot->data; |
|
slot = slot->next; |
|
} |
|
return NULL; |
|
} |
|
|
|
int map_set(Map* m, const char *key, void *value) { |
|
if (!m or !key) |
|
return -1; |
|
|
|
/* Calculate hash value */ |
|
uint32_t ksize = (uint32_t)strlen(key); |
|
uint64_t hash = map_ivalue; |
|
map_hash(key, ksize, &hash); |
|
|
|
/* init map list */ |
|
if (!m->list) |
|
map_resize(m); |
|
|
|
uint32_t idx = (hash) % m->len; |
|
Slot* slot = m->list[idx]; |
|
|
|
/* Remove ? */ |
|
if (!value) { /* Return 0 if not found. */ |
|
if (!slot) |
|
return 0; |
|
Slot* prev = NULL; |
|
while (slot) |
|
{ |
|
if (map_eq(slot, hash, key, ksize)) |
|
{ |
|
if (!prev) |
|
m->list[idx] = slot->next; |
|
else |
|
prev->next = slot->next; |
|
map_xfree(slot->key); map_xfree(slot); m->cap--; |
|
return 1; |
|
} |
|
prev = slot; slot = slot->next; |
|
} |
|
return 0; |
|
} |
|
|
|
/* Checking resize and rehash. */ |
|
if (map_need_resize(m->len, m->cap, m->factor)) |
|
map_resize(m); |
|
|
|
/* Insert */ |
|
Slot* o = slot_new(hash, key, ksize, value); |
|
o->next = slot; |
|
m->list[idx] = o; |
|
|
|
/* Store capacity */ |
|
m->cap++; |
|
return 1; |
|
} |