forked from robertostling/eflomal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
natmap.c
415 lines (384 loc) · 12.2 KB
/
natmap.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/* natmap.c: efficient maps between native data types.
*
* There are two main implementations under the hood: sorted lookup table for
* small maps (small enough that linear rather than binary search is used),
* and hash tables for larger maps.
*
* Configuration is done by (re)defining the macros below and including this
* file. See test.c or efmugal.c for examples of how to do this in practice.
*
* MAKE_NAME(NAME) macro to create local identifiers
* MAX_FIXED maximum numbers of elements when using
* fixed-size (non-hash) table, should be
* 2^n-1 for some integer n
* INDEX_TYPE type of table indexes (e.g. size_t)
* KEY_TYPE type of keys (e.g. uint32_t)
* VALUE_TYPE type of values
* EMPTY_KEY value of key for empty slots
* INDEX_TYPE HASH_KEY(KEY_TYPE) function to hash KEY_TYPE into INDEX_TYPE
*/
#include <stdlib.h>
#include <string.h>
#if (MAX_FIXED&(MAX_FIXED+1))
#error "MAX_FIXED must be one less than a power of 2"
#endif
#define STRUCT_NAME MAKE_NAME()
#define FUN_CREATE MAKE_NAME(_create)
#define FUN_RESET MAKE_NAME(_reset)
#define FUN_CLEAR MAKE_NAME(_clear)
#define FUN_INSERT MAKE_NAME(_insert)
#define FUN_DELETE MAKE_NAME(_delete)
#define FUN_GET MAKE_NAME(_get)
#define FUN_GET_PTR MAKE_NAME(_get_ptr)
#define FUN_ADD MAKE_NAME(_add)
#define FUN_ITEMS MAKE_NAME(_items)
#define FUN_LOOKUP_FIXED MAKE_NAME(_lookup_fixed)
#define FUN_INSERT_FIXED MAKE_NAME(_insert_fixed)
#define FUN_DELETE_FIXED MAKE_NAME(_delete_fixed)
#define FUN_LOOKUP_DYNAMIC MAKE_NAME(_lookup_dynamic)
#define FUN_INSERT_DYNAMIC MAKE_NAME(_insert_dynamic)
#define FUN_DELETE_DYNAMIC MAKE_NAME(_delete_dynamic)
#define FUN_RESIZE_DYNAMIC MAKE_NAME(_resize_dynamic)
#define FUN_MAKE_DYNAMIC MAKE_NAME(_make_dynamic)
#define FUN_IS_DYNAMIC MAKE_NAME(_is_dynamic)
// Minimun size of hash table if MAX_FIXED is 0
//
// Timings
// 4 : 22.97
// 8 : 22.72
// 16 : 22.60
#define MIN_DYNAMIC 4
// This saves some memory, but the time difference is negligible.
#define MERGE_MALLOC
struct STRUCT_NAME {
INDEX_TYPE n_items;
#if MAX_FIXED != 0
unsigned int dynamic : 1;
#endif
union {
struct {
KEY_TYPE keys[MAX_FIXED];
VALUE_TYPE values[MAX_FIXED];
} fixed;
struct {
INDEX_TYPE size;
KEY_TYPE *keys;
VALUE_TYPE *values;
} dynamic;
} structure;
};
static int FUN_RESIZE_DYNAMIC(struct STRUCT_NAME *m, INDEX_TYPE new_size);
// TODO: use one malloc() call instead of two when allocating dynamic
// keys/values arrays.
// TODO: add FUN_GET_HASH() which also takes a precomputed key hash
static inline int FUN_IS_DYNAMIC(const struct STRUCT_NAME *m) {
#if MAX_FIXED == 0
return 1;
#else
return m->dynamic;
#endif
}
// Free any buffers and put the map into the same state as after FUN_CREATE()
static void FUN_CLEAR(struct STRUCT_NAME *m) {
if (FUN_IS_DYNAMIC(m)) {
free(m->structure.dynamic.keys);
#ifndef MERGE_MALLOC
free(m->structure.dynamic.values);
#endif
}
m->n_items = 0;
#if MAX_FIXED != 0
m->dynamic = 0;
#endif
}
static int FUN_LOOKUP_FIXED(
const struct STRUCT_NAME *m, KEY_TYPE key, INDEX_TYPE *index)
{
for (INDEX_TYPE i=0; i<m->n_items; i++) {
const KEY_TYPE k = m->structure.fixed.keys[i];
if (k >= key) {
*index = i;
return k == key;
}
}
*index = m->n_items;
return 0;
}
static void FUN_ITEMS(
struct STRUCT_NAME *m, KEY_TYPE *key_buf, VALUE_TYPE *value_buf)
{
if (FUN_IS_DYNAMIC(m)) {
const KEY_TYPE *keys = m->structure.dynamic.keys;
const VALUE_TYPE *values = m->structure.dynamic.values;
size_t n = 0;
for (size_t i=0; i<m->structure.dynamic.size; i++) {
if (keys[i] != EMPTY_KEY) {
key_buf[n] = keys[i];
value_buf[n] = values[i];
n++;
}
}
assert(n == m->n_items);
} else {
memcpy(key_buf, m->structure.fixed.keys,
m->n_items*sizeof(KEY_TYPE));
memcpy(value_buf, m->structure.fixed.values,
m->n_items*sizeof(VALUE_TYPE));
}
}
static void FUN_INSERT_FIXED(
struct STRUCT_NAME *m, INDEX_TYPE index,
KEY_TYPE key, VALUE_TYPE value)
{
KEY_TYPE *keys = m->structure.fixed.keys;
KEY_TYPE *values = m->structure.fixed.values;
if (index < m->n_items && keys[index] == key) {
values[index] = value;
return;
}
for (INDEX_TYPE i=index; i<m->n_items; i++) {
const KEY_TYPE next_key = keys[i];
const VALUE_TYPE next_value = values[i];
keys[i] = key;
values[i] = value;
key = next_key;
value = next_value;
}
keys[m->n_items] = key;
values[m->n_items] = value;
m->n_items++;
}
static void FUN_DELETE_FIXED(struct STRUCT_NAME *m, INDEX_TYPE index) {
KEY_TYPE *keys = m->structure.fixed.keys;
KEY_TYPE *values = m->structure.fixed.values;
for (INDEX_TYPE i=index; i<m->n_items-1; i++) {
keys[i] = keys[i+1];
values[i] = values[i+1];
}
m->n_items--;
}
static int FUN_LOOKUP_DYNAMIC(
const struct STRUCT_NAME *m, KEY_TYPE key, INDEX_TYPE *index,
INDEX_TYPE key_hash)
{
const INDEX_TYPE mask = m->structure.dynamic.size - 1;
INDEX_TYPE i = key_hash & mask;
const KEY_TYPE *keys = m->structure.dynamic.keys;
while(1) {
if (keys[i] == key) {
*index = i;
return 1;
} else if (keys[i] == EMPTY_KEY) {
*index = i;
return 0;
}
i = (i+1) & mask;
}
}
static int FUN_INSERT_DYNAMIC(
struct STRUCT_NAME *m, KEY_TYPE key, VALUE_TYPE value,
INDEX_TYPE key_hash)
{
if (m->n_items*2 > m->structure.dynamic.size)
FUN_RESIZE_DYNAMIC(m, m->structure.dynamic.size*2);
INDEX_TYPE index;
KEY_TYPE *keys = m->structure.dynamic.keys;
KEY_TYPE *values = m->structure.dynamic.values;
if (FUN_LOOKUP_DYNAMIC(m, key, &index, key_hash)) {
values[index] = value;
return 1;
}
values[index] = value;
keys[index] = key;
m->n_items++;
return 0;
}
static void FUN_DELETE_DYNAMIC(struct STRUCT_NAME *m, INDEX_TYPE index) {
m->n_items--;
INDEX_TYPE i,j,k;
const INDEX_TYPE mask = m->structure.dynamic.size - 1;
KEY_TYPE *keys = m->structure.dynamic.keys;
KEY_TYPE *values = m->structure.dynamic.values;
i = j = index;
while(1) {
keys[i] = EMPTY_KEY;
while(1) {
j = (j+1) & mask;
if (keys[j] == EMPTY_KEY) return;
k = HASH_KEY(keys[j]) & mask;
if(!((i<=j)? ((i<k) && (k<=j)): ((i<k) || (k<=j)))) break;
}
keys[i] = keys[j];
values[i] = values[j];
i = j;
}
}
static int FUN_RESIZE_DYNAMIC(struct STRUCT_NAME *m, INDEX_TYPE new_size) {
KEY_TYPE *old_keys = m->structure.dynamic.keys;
VALUE_TYPE *old_values = m->structure.dynamic.values;
const INDEX_TYPE old_size = m->structure.dynamic.size;
#ifdef MERGE_MALLOC
if ((m->structure.dynamic.keys =
malloc(new_size*(sizeof(KEY_TYPE) + sizeof(VALUE_TYPE))))
== NULL)
{
perror("FUN_RESIZE_DYNAMIC(): unable to allocate arrays");
exit(EXIT_FAILURE);
}
m->structure.dynamic.values = (VALUE_TYPE*)(
m->structure.dynamic.keys + new_size);
#else
if ((m->structure.dynamic.keys = malloc(new_size*sizeof(KEY_TYPE)))
== NULL) {
perror("FUN_RESIZE_DYNAMIC(): unable to allocate keys array");
exit(EXIT_FAILURE);
}
if ((m->structure.dynamic.values = malloc(new_size*sizeof(VALUE_TYPE)))
== NULL) {
perror("FUN_RESIZE_DYNAMIC(): unable to allocate values array");
exit(EXIT_FAILURE);
}
#endif
m->n_items = 0;
m->structure.dynamic.size = new_size;
for (INDEX_TYPE i=0; i<new_size; i++)
m->structure.dynamic.keys[i] = EMPTY_KEY;
for (INDEX_TYPE i=0; i<old_size; i++) {
if (old_keys[i] != EMPTY_KEY) {
FUN_INSERT_DYNAMIC(m, old_keys[i], old_values[i],
HASH_KEY(old_keys[i]));
}
}
free(old_keys);
#ifndef MERGE_MALLOC
free(old_values);
#endif
return 0;
}
static int FUN_MAKE_DYNAMIC(struct STRUCT_NAME *m, INDEX_TYPE new_size) {
#if MAX_FIXED != 0
KEY_TYPE old_keys[MAX_FIXED];
VALUE_TYPE old_values[MAX_FIXED];
memcpy(old_keys, m->structure.fixed.keys, m->n_items*sizeof(KEY_TYPE));
memcpy(old_values, m->structure.fixed.values,
m->n_items*sizeof(VALUE_TYPE));
INDEX_TYPE old_n_items = m->n_items;
#endif
#ifdef MERGE_MALLOC
if ((m->structure.dynamic.keys =
malloc(new_size*(sizeof(KEY_TYPE) + sizeof(VALUE_TYPE))))
== NULL)
{
perror("FUN_MAKE_DYNAMIC(): unable to allocate arrays");
exit(EXIT_FAILURE);
}
m->structure.dynamic.values = (VALUE_TYPE*)(
m->structure.dynamic.keys + new_size);
#else
if ((m->structure.dynamic.keys = malloc(new_size*sizeof(KEY_TYPE)))
== NULL) {
perror("FUN_MAKE_DYNAMIC(): unable to allocate keys array");
exit(EXIT_FAILURE);
}
if ((m->structure.dynamic.values = malloc(new_size*sizeof(VALUE_TYPE)))
== NULL) {
perror("FUN_MAKE_DYNAMIC(): unable to allocate values array");
exit(EXIT_FAILURE);
}
#endif
#if MAX_FIXED != 0
m->dynamic = 1;
#endif
m->n_items = 0;
m->structure.dynamic.size = new_size;
for (INDEX_TYPE i=0; i<new_size; i++)
m->structure.dynamic.keys[i] = EMPTY_KEY;
#if MAX_FIXED != 0
for (INDEX_TYPE i=0; i<old_n_items; i++) {
FUN_INSERT_DYNAMIC(m, old_keys[i], old_values[i],
HASH_KEY(old_keys[i]));
}
#endif
return 0;
}
static int FUN_INSERT(struct STRUCT_NAME *m, KEY_TYPE key, VALUE_TYPE value) {
#if MAX_FIXED != 0
if (FUN_IS_DYNAMIC(m) == 0 && m->n_items == MAX_FIXED)
FUN_MAKE_DYNAMIC(m, (MAX_FIXED+1)*4);
#endif
if (FUN_IS_DYNAMIC(m)) {
return FUN_INSERT_DYNAMIC(m, key, value, HASH_KEY(key));
} else {
INDEX_TYPE index;
const int r = FUN_LOOKUP_FIXED(m, key, &index);
FUN_INSERT_FIXED(m, index, key, value);
return r;
}
}
static int FUN_DELETE(struct STRUCT_NAME *m, KEY_TYPE key) {
if (FUN_IS_DYNAMIC(m)) {
INDEX_TYPE index;
if (FUN_LOOKUP_DYNAMIC(m, key, &index, HASH_KEY(key))) {
FUN_DELETE_DYNAMIC(m, index);
return 1;
}
} else {
INDEX_TYPE index;
if (FUN_LOOKUP_FIXED(m, key, &index)) {
FUN_DELETE_FIXED(m, index);
return 1;
}
}
return 0;
}
static VALUE_TYPE *FUN_GET_PTR(struct STRUCT_NAME *m, KEY_TYPE key) {
if (FUN_IS_DYNAMIC(m)) {
INDEX_TYPE index;
if (FUN_LOOKUP_DYNAMIC(m, key, &index, HASH_KEY(key)))
return m->structure.dynamic.values + index;
} else {
INDEX_TYPE index;
if (FUN_LOOKUP_FIXED(m, key, &index)) {
return m->structure.fixed.values + index;
}
}
return NULL;
}
static int FUN_GET(struct STRUCT_NAME *m, KEY_TYPE key, VALUE_TYPE *value) {
VALUE_TYPE *ptr = FUN_GET_PTR(m, key);
if (ptr == NULL) return 0;
*value = *ptr;
return 1;
}
static VALUE_TYPE FUN_ADD(
struct STRUCT_NAME *m, KEY_TYPE key, VALUE_TYPE value)
{
VALUE_TYPE *ptr = FUN_GET_PTR(m, key);
if (ptr == NULL) {
FUN_INSERT(m, key, value);
return value;
} else {
(*ptr) += value;
return *ptr;
}
}
static void FUN_CREATE(struct STRUCT_NAME *m) {
m->n_items = (INDEX_TYPE)0;
#if MAX_FIXED == 0
FUN_MAKE_DYNAMIC(m, MIN_DYNAMIC);
#else
m->dynamic = 0;
for (INDEX_TYPE i=0; i<MAX_FIXED; i++)
m->structure.fixed.keys[i] = EMPTY_KEY;
#endif
}
static void FUN_RESET(struct STRUCT_NAME *m) {
if (FUN_IS_DYNAMIC(m)) {
FUN_CLEAR(m);
#if MAX_FIXED == 0
FUN_CREATE(m);
#endif
} else {
m->n_items = 0;
}
}