Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
id_table.c
Go to the documentation of this file.
1/* This file is included by symbol.c */
2
3#include "id_table.h"
4
5#ifndef ID_TABLE_DEBUG
6#define ID_TABLE_DEBUG 0
7#endif
8
9#if ID_TABLE_DEBUG == 0
10#define NDEBUG
11#endif
12#include "ruby_assert.h"
13
15
16static inline ID
17key2id(id_key_t key)
18{
19 return rb_id_serial_to_id(key);
20}
21
22static inline id_key_t
23id2key(ID id)
24{
25 return rb_id_to_serial(id);
26}
27
28/* simple open addressing with quadratic probing.
29 uses mark-bit on collisions - need extra 1 bit,
30 ID is strictly 3 bits larger than rb_id_serial_t */
31
32typedef struct rb_id_item {
34#if SIZEOF_VALUE == 8
35 int collision;
36#endif
39
41 int capa;
42 int num;
43 int used;
45};
46
47#if SIZEOF_VALUE == 8
48#define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key)
49#define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key)
50#define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].collision)
51#define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].collision = 1)
52static inline void
53ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key)
54{
55 tbl->items[i].key = key;
56}
57#else
58#define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key >> 1)
59#define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key > 1)
60#define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].key & 1)
61#define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].key |= 1)
62static inline void
63ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key)
64{
65 tbl->items[i].key = (key << 1) | ITEM_COLLIDED(tbl, i);
66}
67#endif
68
69static inline int
70round_capa(int capa)
71{
72 /* minsize is 4 */
73 capa >>= 2;
74 capa |= capa >> 1;
75 capa |= capa >> 2;
76 capa |= capa >> 4;
77 capa |= capa >> 8;
78 capa |= capa >> 16;
79 return (capa + 1) << 2;
80}
81
82static struct rb_id_table *
83rb_id_table_init(struct rb_id_table *tbl, int capa)
84{
85 MEMZERO(tbl, struct rb_id_table, 1);
86 if (capa > 0) {
87 capa = round_capa(capa);
88 tbl->capa = (int)capa;
89 tbl->items = ZALLOC_N(item_t, capa);
90 }
91 return tbl;
92}
93
94struct rb_id_table *
96{
97 struct rb_id_table *tbl = ALLOC(struct rb_id_table);
98 return rb_id_table_init(tbl, (int)capa);
99}
100
101void
103{
104 xfree(tbl->items);
105 xfree(tbl);
106}
107
108void
110{
111 tbl->num = 0;
112 tbl->used = 0;
113 MEMZERO(tbl->items, item_t, tbl->capa);
114}
115
116size_t
118{
119 return (size_t)tbl->num;
120}
121
122size_t
124{
125 return sizeof(item_t) * tbl->capa + sizeof(struct rb_id_table);
126}
127
128static int
129hash_table_index(struct rb_id_table* tbl, id_key_t key)
130{
131 if (tbl->capa > 0) {
132 int mask = tbl->capa - 1;
133 int ix = key & mask;
134 int d = 1;
135 while (key != ITEM_GET_KEY(tbl, ix)) {
136 if (!ITEM_COLLIDED(tbl, ix))
137 return -1;
138 ix = (ix + d) & mask;
139 d++;
140 }
141 return ix;
142 }
143 return -1;
144}
145
146static void
147hash_table_raw_insert(struct rb_id_table *tbl, id_key_t key, VALUE val)
148{
149 int mask = tbl->capa - 1;
150 int ix = key & mask;
151 int d = 1;
152 assert(key != 0);
153 while (ITEM_KEY_ISSET(tbl, ix)) {
154 ITEM_SET_COLLIDED(tbl, ix);
155 ix = (ix + d) & mask;
156 d++;
157 }
158 tbl->num++;
159 if (!ITEM_COLLIDED(tbl, ix)) {
160 tbl->used++;
161 }
162 ITEM_SET_KEY(tbl, ix, key);
163 tbl->items[ix].val = val;
164}
165
166static int
167hash_delete_index(struct rb_id_table *tbl, int ix)
168{
169 if (ix >= 0) {
170 if (!ITEM_COLLIDED(tbl, ix)) {
171 tbl->used--;
172 }
173 tbl->num--;
174 ITEM_SET_KEY(tbl, ix, 0);
175 tbl->items[ix].val = 0;
176 return TRUE;
177 }
178 else {
179 return FALSE;
180 }
181}
182
183static void
184hash_table_extend(struct rb_id_table* tbl)
185{
186 if (tbl->used + (tbl->used >> 1) >= tbl->capa) {
187 int new_cap = round_capa(tbl->num + (tbl->num >> 1));
188 int i;
189 item_t* old;
190 struct rb_id_table tmp_tbl = {0, 0, 0};
191 if (new_cap < tbl->capa) {
192 new_cap = round_capa(tbl->used + (tbl->used >> 1));
193 }
194 tmp_tbl.capa = new_cap;
195 tmp_tbl.items = ZALLOC_N(item_t, new_cap);
196 for (i = 0; i < tbl->capa; i++) {
197 id_key_t key = ITEM_GET_KEY(tbl, i);
198 if (key != 0) {
199 hash_table_raw_insert(&tmp_tbl, key, tbl->items[i].val);
200 }
201 }
202 old = tbl->items;
203 *tbl = tmp_tbl;
204 xfree(old);
205 }
206}
207
208#if ID_TABLE_DEBUG && 0
209static void
210hash_table_show(struct rb_id_table *tbl)
211{
212 const id_key_t *keys = tbl->keys;
213 const int capa = tbl->capa;
214 int i;
215
216 fprintf(stderr, "tbl: %p (capa: %d, num: %d, used: %d)\n", tbl, tbl->capa, tbl->num, tbl->used);
217 for (i=0; i<capa; i++) {
218 if (ITEM_KEY_ISSET(tbl, i)) {
219 fprintf(stderr, " -> [%d] %s %d\n", i, rb_id2name(key2id(keys[i])), (int)keys[i]);
220 }
221 }
222}
223#endif
224
225int
226rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
227{
228 id_key_t key = id2key(id);
229 int index = hash_table_index(tbl, key);
230
231 if (index >= 0) {
232 *valp = tbl->items[index].val;
233 return TRUE;
234 }
235 else {
236 return FALSE;
237 }
238}
239
240static int
241rb_id_table_insert_key(struct rb_id_table *tbl, const id_key_t key, const VALUE val)
242{
243 const int index = hash_table_index(tbl, key);
244
245 if (index >= 0) {
246 tbl->items[index].val = val;
247 }
248 else {
249 hash_table_extend(tbl);
250 hash_table_raw_insert(tbl, key, val);
251 }
252 return TRUE;
253}
254
255int
257{
258 return rb_id_table_insert_key(tbl, id2key(id), val);
259}
260
261int
263{
264 const id_key_t key = id2key(id);
265 int index = hash_table_index(tbl, key);
266 return hash_delete_index(tbl, index);
267}
268
269void
271{
272 int i, capa = tbl->capa;
273
274 for (i=0; i<capa; i++) {
275 if (ITEM_KEY_ISSET(tbl, i)) {
276 const id_key_t key = ITEM_GET_KEY(tbl, i);
277 enum rb_id_table_iterator_result ret = (*func)(Qundef, tbl->items[i].val, data);
278 assert(key != 0);
279
280 if (ret == ID_TABLE_REPLACE) {
281 VALUE val = tbl->items[i].val;
282 ret = (*replace)(NULL, &val, data, TRUE);
283 tbl->items[i].val = val;
284 }
285 else if (ret == ID_TABLE_STOP)
286 return;
287 }
288 }
289}
290
291void
293{
294 int i, capa = tbl->capa;
295
296 for (i=0; i<capa; i++) {
297 if (ITEM_KEY_ISSET(tbl, i)) {
298 const id_key_t key = ITEM_GET_KEY(tbl, i);
299 enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data);
300 assert(key != 0);
301
302 if (ret == ID_TABLE_DELETE)
303 hash_delete_index(tbl, i);
304 else if (ret == ID_TABLE_STOP)
305 return;
306 }
307 }
308}
309
310void
312{
313 int i, capa = tbl->capa;
314
315 for (i=0; i<capa; i++) {
316 if (ITEM_KEY_ISSET(tbl, i)) {
317 enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data);
318
319 if (ret == ID_TABLE_DELETE)
320 hash_delete_index(tbl, i);
321 else if (ret == ID_TABLE_STOP)
322 return;
323 }
324 }
325}
enum @73::@75::@76 mask
#define ITEM_SET_COLLIDED(tbl, i)
Definition: id_table.c:61
void rb_id_table_clear(struct rb_id_table *tbl)
Definition: id_table.c:109
int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val)
Definition: id_table.c:256
void rb_id_table_foreach_with_replace(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, rb_id_table_update_callback_func_t *replace, void *data)
Definition: id_table.c:270
rb_id_serial_t id_key_t
Definition: id_table.c:14
size_t rb_id_table_memsize(const struct rb_id_table *tbl)
Definition: id_table.c:123
#define ITEM_KEY_ISSET(tbl, i)
Definition: id_table.c:59
int rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
Definition: id_table.c:226
void rb_id_table_free(struct rb_id_table *tbl)
Definition: id_table.c:102
void rb_id_table_foreach_values(struct rb_id_table *tbl, rb_id_table_foreach_values_func_t *func, void *data)
Definition: id_table.c:311
size_t rb_id_table_size(const struct rb_id_table *tbl)
Definition: id_table.c:117
#define ITEM_GET_KEY(tbl, i)
Definition: id_table.c:58
struct rb_id_item item_t
struct rb_id_table * rb_id_table_create(size_t capa)
Definition: id_table.c:95
int rb_id_table_delete(struct rb_id_table *tbl, ID id)
Definition: id_table.c:262
#define ITEM_COLLIDED(tbl, i)
Definition: id_table.c:60
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
Definition: id_table.c:292
enum rb_id_table_iterator_result rb_id_table_update_callback_func_t(ID *id, VALUE *val, void *data, int existing)
enum rb_id_table_iterator_result rb_id_table_foreach_func_t(ID id, VALUE val, void *data)
enum rb_id_table_iterator_result rb_id_table_foreach_values_func_t(VALUE val, void *data)
rb_id_table_iterator_result
Definition: id_table.h:8
@ ID_TABLE_STOP
Definition: id_table.h:10
@ ID_TABLE_DELETE
Definition: id_table.h:11
@ ID_TABLE_REPLACE
Definition: id_table.h:12
#define NULL
#define ZALLOC_N(type, n)
#define xfree
#define Qundef
const char * rb_id2name(ID)
Definition: symbol.c:801
int fprintf(FILE *__restrict__, const char *__restrict__,...) __attribute__((__format__(__printf__
#define MEMZERO(p, type, n)
#define stderr
const rb_iseq_t const char const VALUE keys
uint32_t i
VALUE ID VALUE old
#define TRUE
#define FALSE
#define ALLOC(type)
__inline__ int
#define assert
unsigned long ID
unsigned long VALUE
Definition: ruby.h:102
id_key_t key
Definition: id_table.c:33
VALUE val
Definition: id_table.c:37
int capa
Definition: id_table.c:41
item_t * items
Definition: id_table.c:44
int used
Definition: id_table.c:43
uint32_t rb_id_serial_t
Definition: symbol.h:55