1#include <clod/structures/table.h>
11#define LF_DENOMINATOR 100u
13#define LF(table) (((table)->elem_count + (table)->deleted_count) * LF_DENOMINATOR / (table)->table_size)
15#define LF_CAPACITY_TO_SIZE(lf, capacity) (((uint64_t)(capacity) * LF_DENOMINATOR + (lf) - 1) / (lf))
17#define CTL_HASH_BITS 7
18#define CTL_EMPTY (0b00000000)
19#define CTL_OCCUPIED(hash) (0b10000000 | (uint8_t)(hash))
20#define CTL_REMOVED (0b00000001)
21#define CTL_IS_OCCUPIED(ctl) ((0b10000000 & (ctl)) > 0)
23#define INDEX_NIL ((size_t)-1)
37 uint8_t *restrict control;
38 struct element *restrict elements;
44get_position(
const struct clod_table *t,
const void *key,
const size_t key_size) {
45 assert(t->table_size > 0);
46 auto const hash = t->opts.
hash_func((
size_t)t->control, key, key_size, t->opts.
user);
49 .ctl = CTL_OCCUPIED(hash),
50 .index = (hash >> CTL_HASH_BITS) % t->table_size
54 memcpy(&t->opts, opts,
sizeof(t->opts));
57 t->table_size = new_table_size < LF_CAPACITY_TO_SIZE(LF_MAX, t->opts.
min_capacity)
62 if (t->table_size > 0) {
63 t->elements = t->opts.
malloc_func(t->table_size * (
sizeof(t->control[0]) +
sizeof(t->elements[0])), t->opts.
user);
67 t->control = (uint8_t*)((
char*)t->elements + t->table_size *
sizeof(t->elements[0]));
68 memset(t->control, 0, t->table_size);
71 t->elements =
nullptr;
76static void destroy(
const struct clod_table *t) {
84 assert(pos.index < t->table_size);
86 size_t available = INDEX_NIL;
87 for (
size_t i = 0; i < t->table_size; i++) {
88 const size_t index = (pos.index + i) % t->table_size;
90 if (t->control[index] == CTL_EMPTY) {
91 return (
struct probe){
92 .existing = INDEX_NIL,
93 .available = available != INDEX_NIL ? available : index
98 t->control[index] == pos.ctl &&
100 t->elements[index].element,
101 t->elements[index].key_size,
107 return (
struct probe){
109 .available = INDEX_NIL
113 if (t->control[index] == CTL_REMOVED && available == INDEX_NIL) {
118 return (
struct probe){
119 .existing = INDEX_NIL,
120 .available = available
123static const void *insert(
struct clod_table *t,
const bool replace,
const void *
element,
const size_t key_size) {
124 assert(t->elem_count < t->table_size);
126 auto const pos = get_position(t,
element, key_size);
129 if (res.existing != INDEX_NIL) {
130 const void *previous = t->elements[res.existing].element;
132 t->elements[res.existing].element =
element;
133 t->elements[res.existing].key_size = key_size;
138 assert(res.available != INDEX_NIL);
140 if (t->control[res.available] == CTL_REMOVED) t->deleted_count--;
141 t->control[res.available] = pos.ctl;
142 t->elements[res.available].element =
element;
143 t->elements[res.available].key_size = key_size;
146static bool rebuild(
struct clod_table *t,
const size_t new_table_size) {
147 assert(new_table_size >= t->elem_count);
150 if (!create(&
new, &t->opts, new_table_size)) {
154 auto iter = CLOD_TABLE_ITER_INIT;
156 if (insert(&
new,
false, iter.element, iter.key_size) !=
nullptr) {
163 memcpy(t, &
new,
sizeof(
new));
166static uint64_t default_hash(
const uint64_t seed,
const void *data,
const size_t data_size,
void*) {
return clod_sip64(seed, data, data_size); }
167static int default_cmp(
const void *key1,
size_t key1_size,
const void *key2,
const size_t key2_size,
void *) {
168 if (key1_size > key2_size)
return 1;
169 if (key1_size < key2_size)
return -1;
170 return memcmp(key1, key2, key1_size);
173 uint8_t data[
sizeof(
void*)];
174 memcpy(data, &key,
sizeof(
void*));
178 if (key1 > key2)
return 1;
179 if (key1 < key2)
return -1;
182static void *default_malloc(
const size_t size,
void*) {
return malloc(size); }
183static void default_free(
void *ptr,
void*) { free(ptr); }
192 if (!t)
return nullptr;
194 if (opts) memcpy(&t->opts, opts,
sizeof(t->opts));
195 else memset(&t->opts, 0,
sizeof(t->opts));
196 apply_default_opts(&t->opts);
198 if (!create(t, &t->opts, LF_CAPACITY_TO_SIZE(LF_MAX, t->opts.
min_capacity))) {
210 return t->elem_count;
213 if (t->table_size == 0 || LF(t) >= LF_MAX) {
214 if (!rebuild(t, LF_CAPACITY_TO_SIZE(LF_MIN, t->elem_count + 1))) {
215 if (existing_out) *existing_out =
nullptr;
220 const void *existing = insert(t,
false,
element, key_size);
221 if (existing_out) *existing_out = (
void*)existing;
225 if (t->table_size == 0 || LF(t) >= LF_MAX) {
226 if (!rebuild(t, LF_CAPACITY_TO_SIZE(LF_MIN, t->elem_count + 1))) {
227 *existing_out =
nullptr;
232 const void *existing = insert(t,
true,
element, key_size);
233 *existing_out = (
void*)existing;
237 if (t->table_size == 0)
return nullptr;
238 auto const res =
probe(t, get_position(t, key, key_size), key, key_size);
239 if (res.existing != INDEX_NIL) {
240 return (
void*)t->elements[res.existing].element;
246 if (t->table_size == 0)
return nullptr;
247 auto const res =
probe(t, get_position(t, key, key_size), key, key_size);
248 if (res.existing != INDEX_NIL) {
249 t->control[res.existing] = CTL_REMOVED;
253 return (
void*)t->elements[res.existing].element;
259 while (iter->_internal < t->table_size) {
260 const size_t index = (iter->_internal + t->cursor) % t->table_size;
261 if (CTL_IS_OCCUPIED(t->control[index])) {
262 auto const res = t->elements[index];
263 iter->element = (
void*)res.element;
264 iter->key_size = res.key_size;
271 iter->element =
nullptr;
#define clod_sip64(seed, data, size)
int clod_table_cmp_ptr(const void *key1, size_t, const void *key2, size_t, void *)
size_t clod_table_len(const struct clod_table *t)
void clod_table_destroy(struct clod_table *t)
bool clod_table_iter(const struct clod_table *t, struct clod_table_iter *iter)
bool clod_table_set(struct clod_table *t, const void *element, const size_t key_size, void **existing_out)
uint64_t clod_table_hash_ptr(const uint64_t seed, const void *key, size_t, void *)
struct clod_table * clod_table_create(const struct clod_table_opts *opts)
bool clod_table_add(struct clod_table *t, const void *element, const size_t key_size, void **existing_out)
void * clod_table_get(const struct clod_table *t, const void *key, const size_t key_size)
void * clod_table_del(struct clod_table *t, const void *key, const size_t key_size)
int(* cmp_func)(const void *key1, size_t key1_size, const void *key2, size_t key2_size, void *user)
uint64_t(* hash_func)(uint64_t seed, const void *key, size_t key_size, void *user)
void *(* malloc_func)(size_t, void *user)
void(* free_func)(void *, void *user)