#include "cx/kv_list.h"
#include "cx/hash_map.h"
#include "cx/linked_list.h"
#include <string.h>
#include <assert.h>
typedef struct cx_kv_list_s cx_kv_list;
struct cx_kv_list_map_s {
struct cx_hash_map_s map_base;
cx_kv_list *list;
};
struct cx_kv_list_s {
struct cx_linked_list_s list;
struct cx_kv_list_map_s *map;
const cx_list_class *list_methods;
const cx_map_class *map_methods;
cx_destructor_func list_destr;
cx_destructor_func2 list_destr2;
void *list_destr_data;
cx_destructor_func map_destr;
cx_destructor_func2 map_destr2;
void *map_destr_data;
};
static void cx_kv_list_destructor_wrapper(
void *list_ptr,
void *elem) {
const cx_kv_list *kv_list = list_ptr;
if (kv_list->list_destr) {
kv_list->list_destr(elem);
}
if (kv_list->list_destr2) {
kv_list->list_destr2(kv_list->list_destr_data, elem);
}
if (kv_list->map_destr) {
kv_list->map_destr(elem);
}
if (kv_list->map_destr2) {
kv_list->map_destr2(kv_list->map_destr_data, elem);
}
}
static void cx_kv_list_update_destructors(cx_kv_list *list) {
if (list->list.base.collection.simple_destructor !=
NULL) {
list->list_destr = list->list.base.collection.simple_destructor;
list->list.base.collection.simple_destructor =
NULL;
}
if (list->list.base.collection.advanced_destructor != cx_kv_list_destructor_wrapper) {
list->list_destr2 = list->list.base.collection.advanced_destructor;
list->list_destr_data = list->list.base.collection.destructor_data;
list->list.base.collection.advanced_destructor = cx_kv_list_destructor_wrapper;
list->list.base.collection.destructor_data = list;
}
if (list->map->map_base.base.collection.simple_destructor !=
NULL) {
list->map_destr = list->map->map_base.base.collection.simple_destructor;
list->map->map_base.base.collection.simple_destructor =
NULL;
}
if (list->map->map_base.base.collection.advanced_destructor !=
NULL) {
list->map_destr2 = list->map->map_base.base.collection.advanced_destructor;
list->map_destr_data = list->map->map_base.base.collection.destructor_data;
list->map->map_base.base.collection.advanced_destructor =
NULL;
list->map->map_base.base.collection.destructor_data =
NULL;
}
}
static CxHashKey *cx_kv_list_loc_key(cx_kv_list *list,
void *node_data) {
return (CxHashKey*)((
char*)node_data + list->list.base.collection.elem_size);
}
static void cx_kvl_deallocate(
struct cx_list_s *list) {
cx_kv_list *kv_list = (cx_kv_list*)list;
cx_kv_list_update_destructors(kv_list);
kv_list->map_methods->deallocate(&kv_list->map->map_base.base);
kv_list->list_methods->deallocate(list);
}
static void *cx_kvl_insert_element(
struct cx_list_s *list,
size_t index,
const void *data
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
return kv_list->list_methods->insert_element(list, index, data);
}
static size_t cx_kvl_insert_array(
struct cx_list_s *list,
size_t index,
const void *data,
size_t n
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
return kv_list->list_methods->insert_array(list, index, data, n);
}
static size_t cx_kvl_insert_sorted(
struct cx_list_s *list,
const void *sorted_data,
size_t n
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
return kv_list->list_methods->insert_sorted(list, sorted_data, n);
}
static size_t cx_kvl_insert_unique(
struct cx_list_s *list,
const void *sorted_data,
size_t n
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
return kv_list->list_methods->insert_unique(list, sorted_data, n);
}
static int cx_kvl_insert_iter(
struct cx_iterator_s *iter,
const void *elem,
int prepend
) {
cx_kv_list *kv_list = iter->src_handle;
return kv_list->list_methods->insert_iter(iter, elem, prepend);
}
static size_t cx_kvl_remove(
struct cx_list_s *list,
size_t index,
size_t num,
void *targetbuf
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
cx_kv_list_update_destructors(kv_list);
CxIterator iter = kv_list->list_methods->iterator(list, index, false);
for (
size_t i =
0; i < num && cxIteratorValid(iter); i++) {
void *node_data = cxIteratorCurrent(iter);
CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
if (key->hash !=
0) {
kv_list->map_methods->remove(&kv_list->map->map_base.base, *key,
NULL);
}
cxIteratorNext(iter);
}
return kv_list->list_methods->remove(list, index, num, targetbuf);
}
static void cx_kvl_clear(
struct cx_list_s *list) {
cx_kv_list *kv_list = (cx_kv_list*)list;
cx_kv_list_update_destructors(kv_list);
kv_list->list_methods->clear(list);
kv_list->map_methods->clear(&kv_list->map->map_base.base);
}
static int cx_kvl_swap(
struct cx_list_s *list,
size_t i,
size_t j
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
return kv_list->list_methods->swap(list, i, j);
}
static void *cx_kvl_at(
const struct cx_list_s *list,
size_t index
) {
const cx_kv_list *kv_list = (
const cx_kv_list*)list;
return kv_list->list_methods->at(list, index);
}
static size_t cx_kvl_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
cx_kv_list *kv_list = (cx_kv_list*)list;
if (list->collection.size ==
0)
return 0;
size_t index;
cx_linked_list *ll = &kv_list->list;
char *node = cx_linked_list_find(
ll->begin,
ll->loc_next, ll->loc_data,
list->collection.cmpfunc, elem,
&index
);
if (node ==
NULL) {
return list->collection.size;
}
if (remove) {
cx_kv_list_update_destructors(kv_list);
cx_invoke_advanced_destructor(list, node + ll->loc_data);
cx_linked_list_remove(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, node);
CxHashKey *key = cx_kv_list_loc_key(kv_list, node + ll->loc_data);
if (key->hash !=
0) {
kv_list->map_methods->remove(&kv_list->map->map_base.base, *key,
NULL);
}
list->collection.size--;
cxFree(list->collection.allocator, node);
}
return index;
}
static void cx_kvl_sort(
struct cx_list_s *list) {
cx_kv_list *kv_list = (cx_kv_list*)list;
kv_list->list_methods->sort(list);
}
static void cx_kvl_reverse(
struct cx_list_s *list) {
cx_kv_list *kv_list = (cx_kv_list*)list;
kv_list->list_methods->reverse(list);
}
static void cx_kvl_list_iter_next(
void *it) {
struct cx_iterator_s *iter = it;
if (iter->base.remove) {
cx_kv_list *kv_list = iter->src_handle;
cx_kv_list_update_destructors(kv_list);
char *node = iter->elem_handle;
CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data);
if (key->hash !=
0) {
kv_list->map_methods->remove(&kv_list->map->map_base.base, *key,
NULL);
}
}
iter->base.next_impl(it);
}
static struct cx_iterator_s cx_kvl_iterator(
const struct cx_list_s *list,
size_t index,
bool backward
) {
const cx_kv_list *kv_list = (
const cx_kv_list*)list;
struct cx_iterator_s iter = kv_list->list_methods->iterator(list, index, backward);
iter.base.next_impl = iter.base.next;
iter.base.next = cx_kvl_list_iter_next;
return iter;
}
static void cx_kvl_map_deallocate(
struct cx_map_s *map) {
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
kv_list->map_methods->deallocate(map);
kv_list->list_methods->deallocate(&kv_list->list.base);
}
static void cx_kvl_map_clear(
struct cx_map_s *map) {
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
cx_kv_list_update_destructors(kv_list);
kv_list->list_methods->clear(&kv_list->list.base);
kv_list->map_methods->clear(map);
}
static void *cx_kvl_map_put(CxMap *map, CxHashKey key,
void *value) {
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
if (key.hash ==
0) {
cx_hash_murmur(&key);
}
void **map_data = kv_list->map_methods->put(map, key,
NULL);
if (map_data ==
NULL)
return NULL;
kv_list->list.base.collection.sorted = false;
void *node_data = kv_list->list_methods->insert_element(
&kv_list->list.base, kv_list->list.base.collection.size,
kv_list->list.base.collection.store_pointer ? &value : value);
if (node_data ==
NULL) {
kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
return NULL;
}
*map_data = node_data;
CxHashKey *key_ptr = cx_kv_list_loc_key(kv_list, node_data);
*key_ptr = key;
return node_data;
}
void *cx_kvl_map_get(
const CxMap *map, CxHashKey key) {
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
void *node_data = kv_list->map_methods->get(map, key);
if (node_data ==
NULL)
return NULL;
return kv_list->list.base.collection.store_pointer ? *(
void**)node_data : node_data;
}
int cx_kvl_map_remove(CxMap *map, CxHashKey key,
void *targetbuf) {
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
void *node_data;
if (kv_list->map_methods->remove(map, key, &node_data)) {
return 1;
}
if (targetbuf ==
NULL) {
cx_kv_list_update_destructors(kv_list);
cx_invoke_advanced_destructor(&kv_list->list.base, node_data);
}
else {
memcpy(targetbuf, node_data, kv_list->list.base.collection.elem_size);
}
void *node_ptr = (
char*)node_data - kv_list->list.loc_data;
cx_linked_list_remove(
&kv_list->list.begin,
&kv_list->list.end,
kv_list->list.loc_prev,
kv_list->list.loc_next,
node_ptr
);
kv_list->list.base.collection.size--;
cxFree(kv_list->list.base.collection.allocator, node_ptr);
return 0;
}
static void *cx_kvl_iter_current_entry(
const void *it) {
const CxMapIterator *iter = it;
return (
void*)&iter->entry;
}
static void *cx_kvl_iter_current_key(
const void *it) {
const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
return (
void*)entry->key;
}
static void *cx_kvl_iter_current_value(
const void *it) {
const CxMapEntry *entry = cx_kvl_iter_current_entry(it);
return entry->value;
}
static void cx_kvl_iter_next(
void *it) {
CxMapIterator *iter = it;
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)iter->map)->list;
CxHashKey *key =
NULL;
char *next = iter->elem;
while (true) {
next = *(
char**)(next + kv_list->list.loc_next);
if (next ==
NULL)
break;
key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
if (key->hash !=
0)
break;
}
if (iter->base.remove) {
iter->base.remove = false;
cx_kv_list_update_destructors(kv_list);
char *elem = iter->elem;
char *elem_data = elem + kv_list->list.loc_data;
CxHashKey *elem_key = cx_kv_list_loc_key(kv_list, elem_data);
kv_list->map_methods->remove(&kv_list->map->map_base.base, *elem_key,
NULL);
cx_invoke_advanced_destructor(&kv_list->list.base, elem_data);
cx_linked_list_remove(
&kv_list->list.begin,
&kv_list->list.end,
kv_list->list.loc_prev,
kv_list->list.loc_next,
elem
);
cxFree(kv_list->list.base.collection.allocator, elem);
kv_list->list.base.collection.size--;
iter->index--;
iter->elem_count--;
}
if (next ==
NULL) {
iter->index = kv_list->list.base.collection.size;
iter->elem =
NULL;
iter->entry = (CxMapEntry){
NULL,
NULL};
return;
}
iter->index++;
iter->elem = next;
iter->entry.key = key;
if (kv_list->list.base.collection.store_pointer) {
iter->entry.value = *(
void**)(next + kv_list->list.loc_data);
}
else {
iter->entry.value = (
void*)(next + kv_list->list.loc_data);
}
}
static bool cx_kvl_iter_valid(
const void *it) {
const CxMapIterator *iter = it;
return iter->elem !=
NULL;
}
CxMapIterator cx_kvl_map_iterator(
const CxMap *map,
enum cx_map_iterator_type type) {
CxMapIterator iter = {
0};
iter.type = type;
iter.map = (CxMap*)map;
iter.elem_count = map->collection.size;
switch (type) {
case CX_MAP_ITERATOR_PAIRS:
iter.elem_size =
sizeof(CxMapEntry);
iter.base.current = cx_kvl_iter_current_entry;
break;
case CX_MAP_ITERATOR_KEYS:
iter.elem_size =
sizeof(CxHashKey);
iter.base.current = cx_kvl_iter_current_key;
break;
case CX_MAP_ITERATOR_VALUES:
iter.elem_size = map->collection.elem_size;
iter.base.current = cx_kvl_iter_current_value;
break;
default:
assert(false);
}
iter.base.allow_remove = true;
iter.base.next = cx_kvl_iter_next;
iter.base.valid = cx_kvl_iter_valid;
cx_kv_list *kv_list = ((
struct cx_kv_list_map_s*)map)->list;
CxHashKey *key =
NULL;
char *next = kv_list->list.begin;
while (next !=
NULL) {
key = cx_kv_list_loc_key(kv_list, next + kv_list->list.loc_data);
if (key->hash !=
0)
break;
next = *(
char**)(next + kv_list->list.loc_next);
}
if (next ==
NULL) {
iter.elem =
NULL;
iter.entry = (CxMapEntry){
NULL,
NULL};
}
else {
iter.elem = next;
iter.entry.key = key;
if (kv_list->list.base.collection.store_pointer) {
iter.entry.value = *(
void**)(next + kv_list->list.loc_data);
}
else {
iter.entry.value = (
void*)(next + kv_list->list.loc_data);
}
}
return iter;
}
static cx_list_class cx_kv_list_class = {
cx_kvl_deallocate,
cx_kvl_insert_element,
cx_kvl_insert_array,
cx_kvl_insert_sorted,
cx_kvl_insert_unique,
cx_kvl_insert_iter,
cx_kvl_remove,
cx_kvl_clear,
cx_kvl_swap,
cx_kvl_at,
cx_kvl_find_remove,
cx_kvl_sort,
NULL,
cx_kvl_reverse,
cx_kvl_iterator,
};
static cx_map_class cx_kv_map_class = {
cx_kvl_map_deallocate,
cx_kvl_map_clear,
cx_kvl_map_put,
cx_kvl_map_get,
cx_kvl_map_remove,
cx_kvl_map_iterator,
};
CxList *cxKvListCreate(
const CxAllocator *allocator,
cx_compare_func comparator,
size_t elem_size
) {
if (allocator ==
NULL) {
allocator = cxDefaultAllocator;
}
CxList *list = cxLinkedListCreate(allocator, comparator, elem_size);
if (list ==
NULL)
return NULL;
cx_linked_list *ll = (cx_linked_list*)list;
ll->extra_data_len =
sizeof(CxHashKey);
CxMap *map = cxHashMapCreate(allocator,
CX_STORE_POINTERS,
0);
if (map ==
NULL) {
cxListFree(list);
return NULL;
}
cx_kv_list_class.compare = list->cl->compare;
struct cx_kv_list_map_s *kv_map = cxRealloc(allocator, map,
sizeof(
struct cx_kv_list_map_s));
cx_kv_list *kv_list = cxRealloc(allocator, list,
sizeof(
struct cx_kv_list_s));
if (kv_map !=
NULL && kv_list !=
NULL) {
map = (CxMap*) kv_map;
list = (CxList*) kv_list;
}
else {
cxListFree(list);
cxMapFree(map);
return NULL;
}
memset((
char*)kv_list + offsetof(cx_kv_list, list_destr),
0,
sizeof(
void*)*
6);
kv_list->map = kv_map;
kv_map->list = kv_list;
kv_list->map_methods = map->cl;
map->cl = &cx_kv_map_class;
if (list->climpl ==
NULL) {
kv_list->list_methods = list->cl;
list->cl = &cx_kv_list_class;
}
else {
kv_list->list_methods = list->climpl;
list->climpl = &cx_kv_list_class;
}
return list;
}
CxMap *cxKvListCreateAsMap(
const CxAllocator *allocator,
cx_compare_func comparator,
size_t elem_size
) {
CxList *list = cxKvListCreate(allocator, comparator, elem_size);
return list ==
NULL ?
NULL : cxKvListAsMap(list);
}
CxList *cxKvListAsList(CxMap *map) {
return &((
struct cx_kv_list_map_s*)map)->list->list.base;
}
CxMap *cxKvListAsMap(CxList *list) {
return &((cx_kv_list*)list)->map->map_base.base;
}
int cx_kv_list_set_key(CxList *list,
size_t index, CxHashKey key) {
cx_kv_list *kv_list = (cx_kv_list*)list;
void *node_data = kv_list->list_methods->at(list, index);
if (node_data ==
NULL) {
return 1;
}
if (key.hash ==
0) {
cx_hash_murmur(&key);
}
void *existing = kv_list->map_methods->get(&kv_list->map->map_base.base, key);
if (existing == node_data) {
return 0;
}
if (existing !=
NULL) {
return 1;
}
if (
NULL == kv_list->map_methods->put(&kv_list->map->map_base.base, key, node_data)) {
return 1;
}
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
*loc_key = key;
return 0;
}
int cxKvListRemoveKey(CxList *list,
size_t index) {
cx_kv_list *kv_list = (cx_kv_list*)list;
void *node_data = kv_list->list_methods->at(list, index);
if (node_data ==
NULL) {
return 1;
}
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
if (loc_key->hash ==
0) {
return 0;
}
kv_list->map_methods->remove(&kv_list->map->map_base.base, *loc_key,
NULL);
memset(loc_key,
0,
sizeof(CxHashKey));
return 0;
}
const CxHashKey *cxKvListGetKey(CxList *list,
size_t index) {
cx_kv_list *kv_list = (cx_kv_list*)list;
void *node_data = kv_list->list_methods->at(list, index);
if (node_data ==
NULL) {
return NULL;
}
CxHashKey *key = cx_kv_list_loc_key(kv_list, node_data);
if (key->hash ==
0) {
return NULL;
}
return key;
}
int cx_kv_list_insert(CxList *list,
size_t index, CxHashKey key,
void *value) {
list->collection.sorted = false;
cx_kv_list *kv_list = (cx_kv_list*)list;
void **map_data = kv_list->map_methods->put(&kv_list->map->map_base.base, key,
NULL);
if (map_data ==
NULL)
return 1;
void *node_data = kv_list->list_methods->insert_element(&kv_list->list.base, index,
kv_list->list.base.collection.store_pointer ? &value : value);
if (node_data ==
NULL) {
kv_list->map_methods->remove(&kv_list->map->map_base.base, key, &map_data);
return 1;
}
*map_data = node_data;
CxHashKey *loc_key = cx_kv_list_loc_key(kv_list, node_data);
*loc_key = key;
return 0;
}