8a5f06f |
Jamozed |
2022-02-09 20:04:50 |
0
|
// util/map.c, version 0.1.2 |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
1
|
// Map utility source file from libutil |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
2
|
// Copyright (C) 2021, Jakob Wakeling |
7f427d9 |
Jamozed |
2022-03-06 12:55:13 |
3
|
// MIT Licence |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
4
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
5
|
/* |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
6
|
This file uses the currently non-standard 'typeof' operator. Its use is |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
7
|
considered acceptable because it is supported by both GCC and Clang, and |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
8
|
POSIX extensions are used here anyway. Additionally, it is expected that the |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
9
|
'typeof' operator will become standard in C23. |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
10
|
*/ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
11
|
|
6217745 |
Jamozed |
2021-12-08 20:00:24 |
12
|
#include "alloc.h" |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
13
|
#include "map.h" |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
14
|
#include "util.h" |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
15
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
16
|
#include <stddef.h> |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
17
|
#include <stdio.h> |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
18
|
#include <stdlib.h> |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
19
|
#include <string.h> |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
20
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
21
|
#define LOAD_FACTOR 0.90 |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
22
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
23
|
UINT map_initial_capacity = 64; |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
24
|
|
6217745 |
Jamozed |
2021-12-08 20:00:24 |
25
|
static void map_resize(map *m); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
26
|
static u64 hash(const char *s, UINT l); |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
27
|
|
6217745 |
Jamozed |
2021-12-08 20:00:24 |
28
|
/* Initialise a map. */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
29
|
map map_init(void (*free)(void *)) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
30
|
return (map){ NULL, 0, 0, free }; |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
31
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
32
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
33
|
/* Uninitialise a map. */ |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
34
|
void map_free(map *m) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
35
|
for (UINT i = 0; i < m->ac; i += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
36
|
if (m->a[i].h == 0) { continue; } free(m->a[i].k); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
37
|
if (m->free != NULL) { m->free(m->a[i].v); } |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
38
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
39
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
40
|
free(m->a); *m = (map){ NULL, 0, 0, NULL }; |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
41
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
42
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
43
|
#define SWAP(a, b) { register typeof (a) t = a; a = b; b = t; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
44
|
#define DIB(i) ((i + m->ac - (m->a[i].h % m->ac)) % m->ac) |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
45
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
46
|
/* Insert a key-value pair into a map. The key is duplicated. */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
47
|
void map_insert(map *m, char *k, void *v) { |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
48
|
if (m->ac == 0 || m->al >= ((f64)m->ac * LOAD_FACTOR)) { map_resize(m); } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
49
|
UINT h = hash(k, strlen(k)), i = h % m->ac; k = strdup(k); |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
50
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
51
|
for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
52
|
if (m->a[i].h == 0) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
53
|
/* If an empty bucket is found, insert here */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
54
|
m->a[i] = (typeof (*m->a)){ h, k, v }; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
55
|
m->al += 1; return; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
56
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
57
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
58
|
/* Calculate tsid, the DIB of the item at the current index */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
59
|
UINT tsid = (i + m->ac - (m->a[i].h % m->ac)) % m->ac; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
60
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
61
|
if (dist > tsid) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
62
|
SWAP(m->a[i].h, h); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
63
|
SWAP(m->a[i].k, k); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
64
|
SWAP(m->a[i].v, v); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
65
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
66
|
dist = tsid; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
67
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
68
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
69
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
70
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
71
|
/* Lookup the value associated with a key from a map. */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
72
|
void *map_lookup(map *m, char *k) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
73
|
UINT h = hash(k, strlen(k)), i = h % m->ac; |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
74
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
75
|
for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
76
|
if (m->a[i].h == 0) { return NULL; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
77
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
78
|
if (dist > DIB(i)) { return NULL; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
79
|
if ((m->a[i].h == h) && (strcmp(m->a[i].k, k) == 0)) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
80
|
return m->a[i].v; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
81
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
82
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
83
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
84
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
85
|
/* Remove a key-value pair from a map. */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
86
|
void *map_remove(map *m, char *k) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
87
|
UINT h = hash(k, strlen(k)), i = h % m->ac; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
88
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
89
|
for (UINT dist = 0;; i = (i + 1) % m->ac, dist += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
90
|
if (m->a[i].h == 0) { return NULL; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
91
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
92
|
if (dist > DIB(i)) { return NULL; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
93
|
if ((m->a[i].h == h) && (strcmp(m->a[i].k, k) == 0)) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
94
|
/* If the element to be removed is found, then deallocate it */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
95
|
if (m->free != NULL) { m->free(m->a[i].v); } free(m->a[i].k); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
96
|
m->a[i] = (typeof (*m->a)){ 0, NULL, NULL }; m->al -= 1; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
97
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
98
|
/* */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
99
|
for (UINT j = (i + 1) % m->ac;; i = j, j = (j + 1) % m->ac) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
100
|
if (m->a[j].h == 0 || DIB(j) == 0) { break; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
101
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
102
|
SWAP(m->a[i].h, m->a[j].h); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
103
|
SWAP(m->a[i].k, m->a[j].k); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
104
|
SWAP(m->a[i].v, m->a[j].v); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
105
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
106
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
107
|
/* |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
108
|
TODO I am unsure if I want to have this procedure return the |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
109
|
removed value or simply an acknowledgement of its removal |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
110
|
*/ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
111
|
return (void *)true; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
112
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
113
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
114
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
115
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
116
|
/* Print a basic representation of a map to stdout. */ |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
117
|
void map_print(map *m) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
118
|
for (UINT i = 0; i < m->ac; i += 1) if (m->a[i].h != 0) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
119
|
printf("%s -> %s\n", m->a[i].k, (char *)m->a[i].v); |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
120
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
121
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
122
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
123
|
/* Print a debug representation of a map to stdout. */ |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
124
|
void map_debug(map *m) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
125
|
for (UINT i = 0; i < m->ac; i += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
126
|
if (m->a[i].h == 0) { printf("[%zu] %lu\n", i, m->a[i].h); } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
127
|
else printf( |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
128
|
"[%zu] %lu, %s -> %s, DIB: %zu\n", |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
129
|
i, m->a[i].h, m->a[i].k, (char *)m->a[i].v, DIB(i) |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
130
|
); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
131
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
132
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
133
|
|
6217745 |
Jamozed |
2021-12-08 20:00:24 |
134
|
/* Double the number of buckets in a map. */ |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
135
|
static void map_resize(map *m) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
136
|
/* If the map is empty, simply allocate it without rehashing */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
137
|
if (m->ac == 0) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
138
|
m->ac = map_initial_capacity; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
139
|
m->a = xcalloc(m->ac, sizeof (*m->a)); return; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
140
|
} |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
141
|
|
6217745 |
Jamozed |
2021-12-08 20:00:24 |
142
|
/* Otherwise rehash every element into a new resized map */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
143
|
map old = *m; m->ac *= 2; m->a = xcalloc(m->ac, sizeof (*m->a)); m->al = 0; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
144
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
145
|
for (UINT i = 0; i < old.ac; i += 1) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
146
|
if (old.a[i].h == 0) { continue; } |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
147
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
148
|
map_insert(m, old.a[i].k, old.a[i].v); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
149
|
free(old.a[i].k); |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
150
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
151
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
152
|
free(old.a); |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
153
|
} |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
154
|
|
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
155
|
/* Compute the hash of some data. Will not return 0. */ |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
156
|
static u64 hash(const char *dat, UINT len) { |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
157
|
register u64 fnv = 0xCBF29CE484222325; |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
158
|
for (; len; len -= 1, dat += 1) { fnv ^= *dat; fnv *= 0x00000100000001B3; } |
7af2131 |
Jamozed |
2022-01-06 00:39:34 |
159
|
fnv |= fnv == 0; return fnv; |
6217745 |
Jamozed |
2021-12-08 20:00:24 |
160
|
} |
|
|
|
161
|
|