#include "ucx/map.h"
#include <stdlib.h>
#include <string.h>
UcxMap *ucx_map_new(
size_t size) {
return ucx_map_new_a(
NULL, size);
}
UcxMap *ucx_map_new_a(UcxAllocator *allocator,
size_t size) {
if(size ==
0) {
size =
16;
}
if(!allocator) {
allocator = ucx_default_allocator();
}
UcxMap *map = (UcxMap*)almalloc(allocator,
sizeof(UcxMap));
if (!map) {
return NULL;
}
map->allocator = allocator;
map->map = (UcxMapElement**)alcalloc(
allocator, size,
sizeof(UcxMapElement*));
if(map->map ==
NULL) {
alfree(allocator, map);
return NULL;
}
map->size = size;
map->count =
0;
return map;
}
static void ucx_map_free_elmlist_contents(UcxMap *map) {
for (
size_t n =
0 ; n < map->size ; n++) {
UcxMapElement *elem = map->map[n];
if (elem !=
NULL) {
do {
UcxMapElement *next = elem->next;
alfree(map->allocator, elem->key.data);
alfree(map->allocator, elem);
elem = next;
}
while (elem !=
NULL);
}
}
}
void ucx_map_free(UcxMap *map) {
ucx_map_free_elmlist_contents(map);
alfree(map->allocator, map->map);
alfree(map->allocator, map);
}
void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
UcxMapIterator iter = ucx_map_iterator(map);
void *val;
UCX_MAP_FOREACH(key, val, iter) {
if (destr) {
destr(val);
}
else {
alfree(map->allocator, val);
}
}
}
void ucx_map_clear(UcxMap *map) {
if (map->count ==
0) {
return;
}
ucx_map_free_elmlist_contents(map);
memset(map->map,
0, map->size*
sizeof(UcxMapElement*));
map->count =
0;
}
int ucx_map_copy(UcxMap
const *from, UcxMap *to, copy_func fnc,
void *data) {
UcxMapIterator i = ucx_map_iterator(from);
void *value;
UCX_MAP_FOREACH(key, value, i) {
if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) {
return 1;
}
}
return 0;
}
UcxMap *ucx_map_clone(UcxMap
const *map, copy_func fnc,
void *data) {
return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data);
}
UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
UcxMap
const *map, copy_func fnc,
void *data) {
size_t bs = (map->count *
5) >>
1;
UcxMap *newmap = ucx_map_new_a(allocator, bs > map->size ? bs : map->size);
if (!newmap) {
return NULL;
}
ucx_map_copy(map, newmap, fnc, data);
return newmap;
}
int ucx_map_rehash(UcxMap *map) {
size_t load = (map->size *
3) >>
2;
if (map->count > load) {
UcxMap oldmap;
oldmap.map = map->map;
oldmap.size = map->size;
oldmap.count = map->count;
oldmap.allocator = map->allocator;
map->size = (map->count *
5) >>
1;
map->map = (UcxMapElement**)alcalloc(
map->allocator, map->size,
sizeof(UcxMapElement*));
if (!map->map) {
*map = oldmap;
return 1;
}
map->count =
0;
ucx_map_copy(&oldmap, map,
NULL,
NULL);
ucx_map_free_elmlist_contents(&oldmap);
alfree(map->allocator, oldmap.map);
}
return 0;
}
int ucx_map_put(UcxMap *map, UcxKey key,
void *data) {
UcxAllocator *allocator = map->allocator;
if (key.hash ==
0) {
key.hash = ucx_hash((
const char*)key.data, key.len);
}
struct UcxMapKey mapkey;
mapkey.hash = key.hash;
size_t slot = mapkey.hash%map->size;
UcxMapElement *elm = map->map[slot];
UcxMapElement *prev =
NULL;
while (elm && elm->key.hash < mapkey.hash) {
prev = elm;
elm = elm->next;
}
if (!elm || elm->key.hash != mapkey.hash) {
UcxMapElement *e = (UcxMapElement*)almalloc(
allocator,
sizeof(UcxMapElement));
if (!e) {
return -
1;
}
e->key.data =
NULL;
if (prev) {
prev->next = e;
}
else {
map->map[slot] = e;
}
e->next = elm;
elm = e;
}
if (!elm->key.data) {
void *kd = almalloc(allocator, key.len);
if (!kd) {
return -
1;
}
memcpy(kd, key.data, key.len);
mapkey.data = kd;
mapkey.len = key.len;
elm->key = mapkey;
map->count++;
}
elm->data = data;
return 0;
}
static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key,
int remove) {
if(key.hash ==
0) {
key.hash = ucx_hash((
const char*)key.data, key.len);
}
size_t slot = key.hash%map->size;
UcxMapElement *elm = map->map[slot];
UcxMapElement *pelm =
NULL;
while (elm && elm->key.hash <= key.hash) {
if(elm->key.hash == key.hash) {
int n = (key.len > elm->key.len) ? elm->key.len : key.len;
if (memcmp(elm->key.data, key.data, n) ==
0) {
void *data = elm->data;
if (remove) {
if (pelm) {
pelm->next = elm->next;
}
else {
map->map[slot] = elm->next;
}
alfree(map->allocator, elm->key.data);
alfree(map->allocator, elm);
map->count--;
}
return data;
}
}
pelm = elm;
elm = pelm->next;
}
return NULL;
}
void *ucx_map_get(UcxMap
const *map, UcxKey key) {
return ucx_map_get_and_remove((UcxMap *)map, key,
0);
}
void *ucx_map_remove(UcxMap *map, UcxKey key) {
return ucx_map_get_and_remove(map, key,
1);
}
UcxKey ucx_key(
const void *data,
size_t len) {
UcxKey key;
key.data = data;
key.len = len;
key.hash = ucx_hash((
const char*)data, len);
return key;
}
int ucx_hash(
const char *data,
size_t len) {
int m = 0x5bd1e995;
int r =
24;
int h =
25 ^ len;
int i =
0;
while (len >=
4) {
int k = data[i +
0] & 0xFF;
k |= (data[i +
1] & 0xFF) <<
8;
k |= (data[i +
2] & 0xFF) <<
16;
k |= (data[i +
3] & 0xFF) <<
24;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
i +=
4;
len -=
4;
}
switch (len) {
case 3: h ^= (data[i +
2] & 0xFF) <<
16;
case 2: h ^= (data[i +
1] & 0xFF) <<
8;
case 1: h ^= (data[i +
0] & 0xFF); h *= m;
}
h ^= h >>
13;
h *= m;
h ^= h >>
15;
return h;
}
UcxMapIterator ucx_map_iterator(UcxMap
const *map) {
UcxMapIterator i;
i.map = map;
i.cur =
NULL;
i.index =
0;
return i;
}
int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key,
void **elm) {
UcxMapElement *e = i->cur;
if (e) {
e = e->next;
}
else {
e = i->map->map[
0];
}
while (i->index < i->map->size) {
if (e) {
if (e->data) {
i->cur = e;
*elm = e->data;
key->data = e->key.data;
key->hash = e->key.hash;
key->len = e->key.len;
return 1;
}
e = e->next;
}
else {
i->index++;
if (i->index < i->map->size) {
e = i->map->map[i->index];
}
}
}
return 0;
}
UcxMap* ucx_map_union(
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
return ucx_map_union_a(ucx_default_allocator(),
first, second, cpfnc, cpdata);
}
UcxMap* ucx_map_union_a(UcxAllocator *allocator,
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata);
ucx_map_copy(second, result, cpfnc, cpdata);
return result;
}
UcxMap* ucx_map_intersection(
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
return ucx_map_intersection_a(ucx_default_allocator(),
first, second, cpfnc, cpdata);
}
UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ?
first->size : second->size);
UcxMapIterator iter = ucx_map_iterator(first);
void* value;
UCX_MAP_FOREACH(key, value, iter) {
if (ucx_map_get(second, key)) {
ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
}
}
return result;
}
UcxMap* ucx_map_difference(
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
return ucx_map_difference_a(ucx_default_allocator(),
first, second, cpfnc, cpdata);
}
UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
const UcxMap *first,
const UcxMap *second,
copy_func cpfnc,
void* cpdata) {
UcxMap *result = ucx_map_new_a(allocator, first->size - second->count);
UcxMapIterator iter = ucx_map_iterator(first);
void* value;
UCX_MAP_FOREACH(key, value, iter) {
if (!ucx_map_get(second, key)) {
ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
}
}
ucx_map_rehash(result);
return result;
}