Cthulhu  0.2.10
Cthulhu compiler collection
set.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "std/set.h"
4 #include "std/str.h"
5 
6 #include "arena/arena.h"
7 #include "base/panic.h"
8 
12 typedef struct item_t
13 {
14  const void *key;
15  struct item_t *next;
16 } item_t;
17 
18 typedef struct set_t
19 {
22  STA_FIELD_RANGE(0, SIZE_MAX) size_t size;
23  STA_FIELD_SIZE(size) item_t *items;
24 } set_t;
25 
26 static item_t *item_new(const char *key, arena_t *arena)
27 {
28  item_t *item = ARENA_MALLOC(sizeof(item_t), "item", NULL, arena);
29  item->key = key;
30  item->next = NULL;
31  return item;
32 }
33 
34 static item_t *get_bucket_from_hash(set_t *set, size_t hash)
35 {
36  size_t index = hash % set->size;
37  return &set->items[index];
38 }
39 
40 static void clear_items(set_t *set)
41 {
42  for (size_t i = 0; i < set->size; i++)
43  {
44  item_t *item = &set->items[i];
45  item->key = NULL;
46  item->next = NULL;
47  }
48 }
49 
51 set_t *set_new(size_t size, hash_info_t info, arena_t *arena)
52 {
53  CTASSERT(size > 0);
54  CTASSERT(arena != NULL);
55 
56  set_t *set = ARENA_MALLOC(sizeof(set_t), "set", NULL, arena);
57  set->arena = arena;
58  set->info = info;
59  set->size = size;
60  set->items = ARENA_MALLOC(sizeof(item_t) * size, "items", set, arena);
61 
62  clear_items(set);
63 
64  return set;
65 }
66 
67 static item_t *impl_get_bucket(set_t *set, const void *key)
68 {
69  hash_info_t info = set->info;
70  CTASSERT(info.hash != NULL);
71 
72  size_t hash = info.hash(key);
73  return get_bucket_from_hash(set, hash);
74 }
75 
76 static bool impl_keys_equal(const set_t *set, const void *lhs, const void *rhs)
77 {
78  hash_info_t info = set->info;
79  CTASSERT(info.equals != NULL);
80 
81  return info.equals(lhs, rhs);
82 }
83 
85 const void *set_add(set_t *set, const void *key)
86 {
87  item_t *item = impl_get_bucket(set, key);
88 
89  while (true)
90  {
91  if (item->key == NULL)
92  {
93  item->key = key;
94  return key;
95  }
96 
97  if (impl_keys_equal(set, item->key, key))
98  {
99  return item->key;
100  }
101 
102  if (item->next != NULL)
103  {
104  item = item->next;
105  }
106  else
107  {
108  item->next = item_new(key, set->arena);
109  ARENA_REPARENT(item->next, item, set->arena);
110  return key;
111  }
112  }
113 
114  CT_NEVER("unreachable");
115 }
116 
117 STA_DECL
118 bool set_contains(const set_t *set, const void *key)
119 {
120  item_t *item = impl_get_bucket((set_t*)set, key);
121 
122  while (true)
123  {
124  if (item->key == NULL)
125  {
126  return false;
127  }
128 
129  if (impl_keys_equal(set, item->key, key))
130  {
131  return true;
132  }
133 
134  if (item->next != NULL)
135  {
136  item = item->next;
137  }
138  else
139  {
140  return false;
141  }
142  }
143 
144  CT_NEVER("unreachable");
145 }
146 
147 STA_DECL
148 void set_delete(set_t *set, const void *key)
149 {
150  item_t *item = impl_get_bucket(set, key);
151 
152  while (true)
153  {
154  if (item->key == NULL)
155  {
156  return;
157  }
158 
159  if (impl_keys_equal(set, item->key, key))
160  {
161  item->key = NULL;
162  return;
163  }
164 
165  if (item->next != NULL)
166  {
167  item = item->next;
168  }
169  else
170  {
171  return;
172  }
173  }
174 
175  CT_NEVER("unreachable");
176 }
177 
178 STA_DECL
179 bool set_empty(set_t *set)
180 {
181  set_iter_t iter = set_iter(set);
182  while (set_has_next(&iter))
183  {
184  const void *key = set_next(&iter);
185  if (key != NULL)
186  {
187  return false;
188  }
189  }
190 
191  return true;
192 }
193 
194 STA_DECL
195 void set_reset(set_t *set)
196 {
197  CTASSERT(set != NULL);
198 
199  for (size_t i = 0; i < set->size; i++)
200  {
201  item_t *item = &set->items[i];
202  item->next = NULL;
203  item->key = NULL;
204  }
205 }
206 
207 static item_t *set_next_in_chain(item_t *entry)
208 {
209  if (entry == NULL || entry->key == NULL)
210  {
211  return NULL;
212  }
213 
214  // TODO: something better than this
215 
216  while (entry->next != NULL)
217  {
218  entry = entry->next;
219 
220  if (entry->key != NULL)
221  {
222  return entry;
223  }
224  }
225 
226  return NULL;
227 }
228 
237 static item_t *set_find_next_item(set_t *set, size_t *index, item_t *previous)
238 {
239  item_t *entry = set_next_in_chain(previous);
240  if (entry != NULL)
241  {
242  return entry;
243  }
244 
245  size_t i = *index;
246 
247  while (i < set->size)
248  {
249  entry = &set->items[i++];
250  if (entry->key != NULL)
251  {
252  *index = i;
253  return entry;
254  }
255 
256  entry = set_next_in_chain(entry);
257  if (entry != NULL)
258  {
259  *index = i;
260  return entry;
261  }
262  }
263 
264  return NULL;
265 }
266 
267 STA_DECL
269 {
270  CTASSERT(set != NULL);
271 
272  size_t index = 0;
273 
274  item_t *current = set_find_next_item(set, &index, NULL);
275  item_t *next = set_find_next_item(set, &index, current);
276 
277  set_iter_t iter = {
278  .set = set,
279  .index = index,
280  .current = current,
281  .next = next,
282  };
283 
284  return iter;
285 }
286 
287 STA_DECL
288 const void *set_next(set_iter_t *iter)
289 {
290  CTASSERT(iter != NULL);
291 
292  const void *entry = iter->current->key;
293 
294  iter->current = iter->next;
295  iter->next = set_find_next_item(iter->set, &iter->index, iter->current);
296 
297  return entry;
298 }
299 
300 STA_DECL
302 {
303  CTASSERT(iter != NULL);
304 
305  return iter->current != NULL;
306 }
CT_NODISCARD size_t size
Definition: scan.h:128
#define STA_FIELD_SIZE(of)
annotate a field as being an array of of elements
#define STA_FIELD_RANGE(lo, hi)
annotate a field as being bounded by the expression of cmp and it STA_FIELD_RANGE(!...
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
STA_DECL set_t * set_new(size_t size, hash_info_t info, arena_t *arena)
create a new set
Definition: set.c:51
STA_DECL bool set_empty(set_t *set)
check if a set is empty
Definition: set.c:179
STA_DECL const void * set_add(set_t *set, const void *key)
add a key to a set
Definition: set.c:85
STA_DECL void set_reset(set_t *set)
clear all keys from a set
Definition: set.c:195
STA_DECL void set_delete(set_t *set, const void *key)
remove a key from a set
Definition: set.c:148
STA_DECL set_iter_t set_iter(set_t *set)
acquire a set iterator for a set
Definition: set.c:268
STA_DECL const void * set_next(set_iter_t *iter)
get the next item from a set iterator
Definition: set.c:288
STA_DECL bool set_contains(const set_t *set, const void *key)
check if a set contains a key
Definition: set.c:118
STA_DECL bool set_has_next(set_iter_t *iter)
check if a set iterator has more items
Definition: set.c:301
#define ARENA_REPARENT(arena, ptr, parent)
reparent a pointer in a custom allocator
Definition: arena.h:391
#define ARENA_MALLOC(size, name, parent, arena)
allocate memory from a custom allocator
Definition: arena.h:392
#define CT_NEVER(...)
assert that a code path is never reached
Definition: panic.h:136
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
an allocator object
Definition: arena.h:86
information for using a type in a hashset or hashmap
Definition: typeinfo.h:39
hash_fn_t hash
the hash function for the type
Definition: typeinfo.h:44
equals_fn_t equals
the equality function for the type
Definition: typeinfo.h:47
a node in a chain of set entries
Definition: set.c:13
const void * key
the key to this bucket
Definition: set.c:14
struct item_t * next
the next bucket in the chain
Definition: set.c:15
a set iterator handle
Definition: set.h:78
item_t * next
the next item
Definition: set.h:83
item_t * current
the current item
Definition: set.h:82
size_t index
the current bucket index
Definition: set.h:80
set_t * set
the set to iterate over
Definition: set.h:79
an unordered hash set
Definition: set.c:19
arena_t * arena
the arena this set is allocated in
Definition: set.c:20
hash_info_t info
Definition: set.c:21