libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
table.c
1#include <clod/structures/table.h>
2#include <clod/hash.h>
3#include <assert.h>
4#include <stddef.h>
5#include <stdint.h>
6#include <stdlib.h>
7#include <string.h>
8
9#define LF_MAX 85u
10#define LF_MIN 50u
11#define LF_DENOMINATOR 100u
12
13#define LF(table) (((table)->elem_count + (table)->deleted_count) * LF_DENOMINATOR / (table)->table_size)
14// the size of the table needed to store capacity elements at the given load factor
15#define LF_CAPACITY_TO_SIZE(lf, capacity) (((uint64_t)(capacity) * LF_DENOMINATOR + (lf) - 1) / (lf))
16
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)
22
23#define INDEX_NIL ((size_t)-1)
24
25struct element {
26 const void *element;
27 size_t key_size;
28};
29struct clod_table {
30 struct clod_table_opts opts;
31
32 size_t elem_count;
33 size_t deleted_count;
34 size_t table_size;
35 size_t cursor;
36
37 uint8_t *restrict control;
38 struct element *restrict elements;
39};
40static struct table_position {
41 size_t index;
42 uint8_t ctl;
43}
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);
47
48 return (struct table_position){
49 .ctl = CTL_OCCUPIED(hash),
50 .index = (hash >> CTL_HASH_BITS) % t->table_size
51 };
52}
53static bool create(struct clod_table *t, const struct clod_table_opts *opts, const size_t new_table_size) {
54 memcpy(&t->opts, opts, sizeof(t->opts));
55 t->elem_count = 0;
56 t->deleted_count = 0;
57 t->table_size = new_table_size < LF_CAPACITY_TO_SIZE(LF_MAX, t->opts.min_capacity)
58 ? LF_CAPACITY_TO_SIZE(LF_MAX, t->opts.min_capacity)
59 : new_table_size;
60 t->cursor = 0;
61
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);
64 if (!t->elements) {
65 return false;
66 }
67 t->control = (uint8_t*)((char*)t->elements + t->table_size * sizeof(t->elements[0]));
68 memset(t->control, 0, t->table_size);
69 } else {
70 t->control = nullptr;
71 t->elements = nullptr;
72 }
73
74 return true;
75}
76static void destroy(const struct clod_table *t) {
77 t->opts.free_func(t->elements, t->opts.user);
78}
79static struct probe {
80 size_t existing;
81 size_t available;
82}
83probe(const struct clod_table *t, const struct table_position pos, const void *key, const size_t key_size) {
84 assert(pos.index < t->table_size);
85
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;
89
90 if (t->control[index] == CTL_EMPTY) {
91 return (struct probe){
92 .existing = INDEX_NIL,
93 .available = available != INDEX_NIL ? available : index
94 };
95 }
96
97 if (
98 t->control[index] == pos.ctl &&
99 t->opts.cmp_func(
100 t->elements[index].element,
101 t->elements[index].key_size,
102 key,
103 key_size,
104 t->opts.user
105 ) == 0
106 ) {
107 return (struct probe){
108 .existing = index,
109 .available = INDEX_NIL
110 };
111 }
112
113 if (t->control[index] == CTL_REMOVED && available == INDEX_NIL) {
114 available = index;
115 }
116 }
117
118 return (struct probe){
119 .existing = INDEX_NIL,
120 .available = available
121 };
122}
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);
125
126 auto const pos = get_position(t, element, key_size);
127 auto const res = probe(t, pos, element, key_size);
128
129 if (res.existing != INDEX_NIL) {
130 const void *previous = t->elements[res.existing].element;
131 if (replace) {
132 t->elements[res.existing].element = element;
133 t->elements[res.existing].key_size = key_size;
134 }
135 return previous;
136 }
137
138 assert(res.available != INDEX_NIL);
139 t->elem_count++;
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;
144 return nullptr;
145}
146static bool rebuild(struct clod_table *t, const size_t new_table_size) {
147 assert(new_table_size >= t->elem_count);
148
149 struct clod_table new;
150 if (!create(&new, &t->opts, new_table_size)) {
151 return false;
152 }
153
154 auto iter = CLOD_TABLE_ITER_INIT;
155 while (clod_table_iter(t, &iter)) {
156 if (insert(&new, false, iter.element, iter.key_size) != nullptr) {
157 destroy(&new);
158 return false;
159 }
160 }
161
162 destroy(t);
163 memcpy(t, &new, sizeof(new));
164 return true;
165}
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);
171}
172uint64_t clod_table_hash_ptr(const uint64_t seed, const void *key, size_t, void *) {
173 uint8_t data[sizeof(void*)];
174 memcpy(data, &key, sizeof(void*));
175 return clod_sip64(seed, data, sizeof(void*));
176}
177int clod_table_cmp_ptr(const void *key1, size_t, const void *key2, size_t, void *) {
178 if (key1 > key2) return 1;
179 if (key1 < key2) return -1;
180 return 0;
181}
182static void *default_malloc(const size_t size, void*) { return malloc(size); }
183static void default_free(void *ptr, void*) { free(ptr); }
184static void apply_default_opts(struct clod_table_opts *opts) {
185 if (!opts->hash_func) opts->hash_func = default_hash;
186 if (!opts->cmp_func) opts->cmp_func = default_cmp;
187 if (!opts->malloc_func) opts->malloc_func = default_malloc;
188 if (!opts->free_func) opts->free_func = default_free;
189}
190struct clod_table *clod_table_create(const struct clod_table_opts *opts) {
191 struct clod_table *t = opts && opts->malloc_func ? opts->malloc_func(sizeof(*t), opts->user) : malloc(sizeof(*t));
192 if (!t) return nullptr;
193
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);
197
198 if (!create(t, &t->opts, LF_CAPACITY_TO_SIZE(LF_MAX, t->opts.min_capacity))) {
199 t->opts.free_func(t, t->opts.user);
200 return nullptr;
201 }
202
203 return t;
204}
206 destroy(t);
207 t->opts.free_func(t, t->opts.user);
208}
209size_t clod_table_len(const struct clod_table *t) {
210 return t->elem_count;
211}
212bool clod_table_add(struct clod_table *t, const void *element, const size_t key_size, void **existing_out) {
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;
216 return false;
217 }
218 }
219
220 const void *existing = insert(t, false, element, key_size);
221 if (existing_out) *existing_out = (void*)existing;
222 return !existing;
223}
224bool clod_table_set(struct clod_table *t, const void *element, const size_t key_size, void **existing_out) {
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;
228 return false;
229 }
230 }
231
232 const void *existing = insert(t, true, element, key_size);
233 *existing_out = (void*)existing;
234 return true;
235}
236void *clod_table_get(const struct clod_table *t, const void *key, const size_t key_size) {
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;
241 }
242
243 return nullptr;
244}
245void *clod_table_del(struct clod_table *t, const void *key, const size_t key_size) {
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;
250 t->elem_count--;
251 t->deleted_count++;
252 t->cursor++;
253 return (void*)t->elements[res.existing].element;
254 }
255
256 return nullptr;
257}
258bool clod_table_iter(const struct clod_table *t, struct clod_table_iter *iter) {
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;
265 iter->_internal++;
266 return true;
267 }
268 iter->_internal++;
269 }
270 iter->_internal = 0;
271 iter->element = nullptr;
272 iter->key_size = 0;
273 return false;
274}
#define clod_sip64(seed, data, size)
Definition hash.h:126
int clod_table_cmp_ptr(const void *key1, size_t, const void *key2, size_t, void *)
Definition table.c:177
size_t clod_table_len(const struct clod_table *t)
Definition table.c:209
void clod_table_destroy(struct clod_table *t)
Definition table.c:205
bool clod_table_iter(const struct clod_table *t, struct clod_table_iter *iter)
Definition table.c:258
bool clod_table_set(struct clod_table *t, const void *element, const size_t key_size, void **existing_out)
Definition table.c:224
uint64_t clod_table_hash_ptr(const uint64_t seed, const void *key, size_t, void *)
Definition table.c:172
struct clod_table * clod_table_create(const struct clod_table_opts *opts)
Definition table.c:190
bool clod_table_add(struct clod_table *t, const void *element, const size_t key_size, void **existing_out)
Definition table.c:212
void * clod_table_get(const struct clod_table *t, const void *key, const size_t key_size)
Definition table.c:236
void * clod_table_del(struct clod_table *t, const void *key, const size_t key_size)
Definition table.c:245
Sized string helpers.
void * user
Definition table.h:52
size_t min_capacity
Definition table.h:37
int(* cmp_func)(const void *key1, size_t key1_size, const void *key2, size_t key2_size, void *user)
Definition table.h:46
uint64_t(* hash_func)(uint64_t seed, const void *key, size_t key_size, void *user)
Definition table.h:41
void *(* malloc_func)(size_t, void *user)
Definition table.h:48
void(* free_func)(void *, void *user)
Definition table.h:50
Definition table.c:79