1 /*
2 ** upb_table Implementation
3 **
4 ** Implementation is heavily inspired by Lua's ltable.c.
5 */
6
7 #include <string.h>
8
9 #include "third_party/wyhash/wyhash.h"
10 #include "upb/table.int.h"
11
12 /* Must be last. */
13 #include "upb/port_def.inc"
14
15 #define UPB_MAXARRSIZE 16 /* 64k. */
16
17 /* From Chromium. */
18 #define ARRAY_SIZE(x) \
19 ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
20
21 static const double MAX_LOAD = 0.85;
22
23 /* The minimum utilization of the array part of a mixed hash/array table. This
24 * is a speed/memory-usage tradeoff (though it's not straightforward because of
25 * cache effects). The lower this is, the more memory we'll use. */
26 static const double MIN_DENSITY = 0.1;
27
is_pow2(uint64_t v)28 bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; }
29
log2ceil(uint64_t v)30 int log2ceil(uint64_t v) {
31 int ret = 0;
32 bool pow2 = is_pow2(v);
33 while (v >>= 1) ret++;
34 ret = pow2 ? ret : ret + 1; /* Ceiling. */
35 return UPB_MIN(UPB_MAXARRSIZE, ret);
36 }
37
upb_strdup(const char * s,upb_alloc * a)38 char *upb_strdup(const char *s, upb_alloc *a) {
39 return upb_strdup2(s, strlen(s), a);
40 }
41
upb_strdup2(const char * s,size_t len,upb_alloc * a)42 char *upb_strdup2(const char *s, size_t len, upb_alloc *a) {
43 size_t n;
44 char *p;
45
46 /* Prevent overflow errors. */
47 if (len == SIZE_MAX) return NULL;
48 /* Always null-terminate, even if binary data; but don't rely on the input to
49 * have a null-terminating byte since it may be a raw binary buffer. */
50 n = len + 1;
51 p = upb_malloc(a, n);
52 if (p) {
53 memcpy(p, s, len);
54 p[len] = 0;
55 }
56 return p;
57 }
58
59 /* A type to represent the lookup key of either a strtable or an inttable. */
60 typedef union {
61 uintptr_t num;
62 struct {
63 const char *str;
64 size_t len;
65 } str;
66 } lookupkey_t;
67
strkey2(const char * str,size_t len)68 static lookupkey_t strkey2(const char *str, size_t len) {
69 lookupkey_t k;
70 k.str.str = str;
71 k.str.len = len;
72 return k;
73 }
74
intkey(uintptr_t key)75 static lookupkey_t intkey(uintptr_t key) {
76 lookupkey_t k;
77 k.num = key;
78 return k;
79 }
80
81 typedef uint32_t hashfunc_t(upb_tabkey key);
82 typedef bool eqlfunc_t(upb_tabkey k1, lookupkey_t k2);
83
84 /* Base table (shared code) ***************************************************/
85
86 /* For when we need to cast away const. */
mutable_entries(upb_table * t)87 static upb_tabent *mutable_entries(upb_table *t) {
88 return (upb_tabent*)t->entries;
89 }
90
isfull(upb_table * t)91 static bool isfull(upb_table *t) {
92 return t->count == t->max_count;
93 }
94
init(upb_table * t,uint8_t size_lg2,upb_alloc * a)95 static bool init(upb_table *t, uint8_t size_lg2, upb_alloc *a) {
96 size_t bytes;
97
98 t->count = 0;
99 t->size_lg2 = size_lg2;
100 t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
101 t->max_count = upb_table_size(t) * MAX_LOAD;
102 bytes = upb_table_size(t) * sizeof(upb_tabent);
103 if (bytes > 0) {
104 t->entries = upb_malloc(a, bytes);
105 if (!t->entries) return false;
106 memset(mutable_entries(t), 0, bytes);
107 } else {
108 t->entries = NULL;
109 }
110 return true;
111 }
112
uninit(upb_table * t,upb_alloc * a)113 static void uninit(upb_table *t, upb_alloc *a) {
114 upb_free(a, mutable_entries(t));
115 }
116
emptyent(upb_table * t,upb_tabent * e)117 static upb_tabent *emptyent(upb_table *t, upb_tabent *e) {
118 upb_tabent *begin = mutable_entries(t);
119 upb_tabent *end = begin + upb_table_size(t);
120 for (e = e + 1; e < end; e++) {
121 if (upb_tabent_isempty(e)) return e;
122 }
123 for (e = begin; e < end; e++) {
124 if (upb_tabent_isempty(e)) return e;
125 }
126 UPB_ASSERT(false);
127 return NULL;
128 }
129
getentry_mutable(upb_table * t,uint32_t hash)130 static upb_tabent *getentry_mutable(upb_table *t, uint32_t hash) {
131 return (upb_tabent*)upb_getentry(t, hash);
132 }
133
findentry(const upb_table * t,lookupkey_t key,uint32_t hash,eqlfunc_t * eql)134 static const upb_tabent *findentry(const upb_table *t, lookupkey_t key,
135 uint32_t hash, eqlfunc_t *eql) {
136 const upb_tabent *e;
137
138 if (t->size_lg2 == 0) return NULL;
139 e = upb_getentry(t, hash);
140 if (upb_tabent_isempty(e)) return NULL;
141 while (1) {
142 if (eql(e->key, key)) return e;
143 if ((e = e->next) == NULL) return NULL;
144 }
145 }
146
findentry_mutable(upb_table * t,lookupkey_t key,uint32_t hash,eqlfunc_t * eql)147 static upb_tabent *findentry_mutable(upb_table *t, lookupkey_t key,
148 uint32_t hash, eqlfunc_t *eql) {
149 return (upb_tabent*)findentry(t, key, hash, eql);
150 }
151
lookup(const upb_table * t,lookupkey_t key,upb_value * v,uint32_t hash,eqlfunc_t * eql)152 static bool lookup(const upb_table *t, lookupkey_t key, upb_value *v,
153 uint32_t hash, eqlfunc_t *eql) {
154 const upb_tabent *e = findentry(t, key, hash, eql);
155 if (e) {
156 if (v) {
157 _upb_value_setval(v, e->val.val);
158 }
159 return true;
160 } else {
161 return false;
162 }
163 }
164
165 /* The given key must not already exist in the table. */
insert(upb_table * t,lookupkey_t key,upb_tabkey tabkey,upb_value val,uint32_t hash,hashfunc_t * hashfunc,eqlfunc_t * eql)166 static void insert(upb_table *t, lookupkey_t key, upb_tabkey tabkey,
167 upb_value val, uint32_t hash,
168 hashfunc_t *hashfunc, eqlfunc_t *eql) {
169 upb_tabent *mainpos_e;
170 upb_tabent *our_e;
171
172 UPB_ASSERT(findentry(t, key, hash, eql) == NULL);
173
174 t->count++;
175 mainpos_e = getentry_mutable(t, hash);
176 our_e = mainpos_e;
177
178 if (upb_tabent_isempty(mainpos_e)) {
179 /* Our main position is empty; use it. */
180 our_e->next = NULL;
181 } else {
182 /* Collision. */
183 upb_tabent *new_e = emptyent(t, mainpos_e);
184 /* Head of collider's chain. */
185 upb_tabent *chain = getentry_mutable(t, hashfunc(mainpos_e->key));
186 if (chain == mainpos_e) {
187 /* Existing ent is in its main position (it has the same hash as us, and
188 * is the head of our chain). Insert to new ent and append to this chain. */
189 new_e->next = mainpos_e->next;
190 mainpos_e->next = new_e;
191 our_e = new_e;
192 } else {
193 /* Existing ent is not in its main position (it is a node in some other
194 * chain). This implies that no existing ent in the table has our hash.
195 * Evict it (updating its chain) and use its ent for head of our chain. */
196 *new_e = *mainpos_e; /* copies next. */
197 while (chain->next != mainpos_e) {
198 chain = (upb_tabent*)chain->next;
199 UPB_ASSERT(chain);
200 }
201 chain->next = new_e;
202 our_e = mainpos_e;
203 our_e->next = NULL;
204 }
205 }
206 our_e->key = tabkey;
207 our_e->val.val = val.val;
208 UPB_ASSERT(findentry(t, key, hash, eql) == our_e);
209 }
210
rm(upb_table * t,lookupkey_t key,upb_value * val,upb_tabkey * removed,uint32_t hash,eqlfunc_t * eql)211 static bool rm(upb_table *t, lookupkey_t key, upb_value *val,
212 upb_tabkey *removed, uint32_t hash, eqlfunc_t *eql) {
213 upb_tabent *chain = getentry_mutable(t, hash);
214 if (upb_tabent_isempty(chain)) return false;
215 if (eql(chain->key, key)) {
216 /* Element to remove is at the head of its chain. */
217 t->count--;
218 if (val) _upb_value_setval(val, chain->val.val);
219 if (removed) *removed = chain->key;
220 if (chain->next) {
221 upb_tabent *move = (upb_tabent*)chain->next;
222 *chain = *move;
223 move->key = 0; /* Make the slot empty. */
224 } else {
225 chain->key = 0; /* Make the slot empty. */
226 }
227 return true;
228 } else {
229 /* Element to remove is either in a non-head position or not in the
230 * table. */
231 while (chain->next && !eql(chain->next->key, key)) {
232 chain = (upb_tabent*)chain->next;
233 }
234 if (chain->next) {
235 /* Found element to remove. */
236 upb_tabent *rm = (upb_tabent*)chain->next;
237 t->count--;
238 if (val) _upb_value_setval(val, chain->next->val.val);
239 if (removed) *removed = rm->key;
240 rm->key = 0; /* Make the slot empty. */
241 chain->next = rm->next;
242 return true;
243 } else {
244 /* Element to remove is not in the table. */
245 return false;
246 }
247 }
248 }
249
next(const upb_table * t,size_t i)250 static size_t next(const upb_table *t, size_t i) {
251 do {
252 if (++i >= upb_table_size(t))
253 return SIZE_MAX - 1; /* Distinct from -1. */
254 } while(upb_tabent_isempty(&t->entries[i]));
255
256 return i;
257 }
258
begin(const upb_table * t)259 static size_t begin(const upb_table *t) {
260 return next(t, -1);
261 }
262
263
264 /* upb_strtable ***************************************************************/
265
266 /* A simple "subclass" of upb_table that only adds a hash function for strings. */
267
strcopy(lookupkey_t k2,upb_alloc * a)268 static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
269 uint32_t len = (uint32_t) k2.str.len;
270 char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
271 if (str == NULL) return 0;
272 memcpy(str, &len, sizeof(uint32_t));
273 if (k2.str.len) memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len);
274 str[sizeof(uint32_t) + k2.str.len] = '\0';
275 return (uintptr_t)str;
276 }
277
table_hash(const char * p,size_t n)278 static uint32_t table_hash(const char *p, size_t n) {
279 return wyhash(p, n, 0, _wyp);
280 }
281
strhash(upb_tabkey key)282 static uint32_t strhash(upb_tabkey key) {
283 uint32_t len;
284 char *str = upb_tabstr(key, &len);
285 return table_hash(str, len);
286 }
287
streql(upb_tabkey k1,lookupkey_t k2)288 static bool streql(upb_tabkey k1, lookupkey_t k2) {
289 uint32_t len;
290 char *str = upb_tabstr(k1, &len);
291 return len == k2.str.len && (len == 0 || memcmp(str, k2.str.str, len) == 0);
292 }
293
upb_strtable_init2(upb_strtable * t,upb_ctype_t ctype,size_t expected_size,upb_alloc * a)294 bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype,
295 size_t expected_size, upb_alloc *a) {
296 UPB_UNUSED(ctype); /* TODO(haberman): rm */
297 // Multiply by approximate reciprocal of MAX_LOAD (0.85), with pow2 denominator.
298 size_t need_entries = (expected_size + 1) * 1204 / 1024;
299 UPB_ASSERT(need_entries >= expected_size * 0.85);
300 int size_lg2 = _upb_lg2ceil(need_entries);
301 return init(&t->t, size_lg2, a);
302 }
303
upb_strtable_clear(upb_strtable * t)304 void upb_strtable_clear(upb_strtable *t) {
305 size_t bytes = upb_table_size(&t->t) * sizeof(upb_tabent);
306 t->t.count = 0;
307 memset((char*)t->t.entries, 0, bytes);
308 }
309
upb_strtable_uninit2(upb_strtable * t,upb_alloc * a)310 void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
311 size_t i;
312 for (i = 0; i < upb_table_size(&t->t); i++)
313 upb_free(a, (void*)t->t.entries[i].key);
314 uninit(&t->t, a);
315 }
316
upb_strtable_resize(upb_strtable * t,size_t size_lg2,upb_alloc * a)317 bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) {
318 upb_strtable new_table;
319 upb_strtable_iter i;
320
321 if (!init(&new_table.t, size_lg2, a))
322 return false;
323 upb_strtable_begin(&i, t);
324 for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
325 upb_strview key = upb_strtable_iter_key(&i);
326 upb_strtable_insert3(
327 &new_table, key.data, key.size,
328 upb_strtable_iter_value(&i), a);
329 }
330 upb_strtable_uninit2(t, a);
331 *t = new_table;
332 return true;
333 }
334
upb_strtable_insert3(upb_strtable * t,const char * k,size_t len,upb_value v,upb_alloc * a)335 bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len,
336 upb_value v, upb_alloc *a) {
337 lookupkey_t key;
338 upb_tabkey tabkey;
339 uint32_t hash;
340
341 if (isfull(&t->t)) {
342 /* Need to resize. New table of double the size, add old elements to it. */
343 if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) {
344 return false;
345 }
346 }
347
348 key = strkey2(k, len);
349 tabkey = strcopy(key, a);
350 if (tabkey == 0) return false;
351
352 hash = table_hash(key.str.str, key.str.len);
353 insert(&t->t, key, tabkey, v, hash, &strhash, &streql);
354 return true;
355 }
356
upb_strtable_lookup2(const upb_strtable * t,const char * key,size_t len,upb_value * v)357 bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
358 upb_value *v) {
359 uint32_t hash = table_hash(key, len);
360 return lookup(&t->t, strkey2(key, len), v, hash, &streql);
361 }
362
upb_strtable_remove3(upb_strtable * t,const char * key,size_t len,upb_value * val,upb_alloc * alloc)363 bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
364 upb_value *val, upb_alloc *alloc) {
365 uint32_t hash = table_hash(key, len);
366 upb_tabkey tabkey;
367 if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
368 if (alloc) {
369 /* Arena-based allocs don't need to free and won't pass this. */
370 upb_free(alloc, (void*)tabkey);
371 }
372 return true;
373 } else {
374 return false;
375 }
376 }
377
378 /* Iteration */
379
upb_strtable_begin(upb_strtable_iter * i,const upb_strtable * t)380 void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t) {
381 i->t = t;
382 i->index = begin(&t->t);
383 }
384
upb_strtable_next(upb_strtable_iter * i)385 void upb_strtable_next(upb_strtable_iter *i) {
386 i->index = next(&i->t->t, i->index);
387 }
388
upb_strtable_done(const upb_strtable_iter * i)389 bool upb_strtable_done(const upb_strtable_iter *i) {
390 if (!i->t) return true;
391 return i->index >= upb_table_size(&i->t->t) ||
392 upb_tabent_isempty(str_tabent(i));
393 }
394
upb_strtable_iter_key(const upb_strtable_iter * i)395 upb_strview upb_strtable_iter_key(const upb_strtable_iter *i) {
396 upb_strview key;
397 uint32_t len;
398 UPB_ASSERT(!upb_strtable_done(i));
399 key.data = upb_tabstr(str_tabent(i)->key, &len);
400 key.size = len;
401 return key;
402 }
403
upb_strtable_iter_value(const upb_strtable_iter * i)404 upb_value upb_strtable_iter_value(const upb_strtable_iter *i) {
405 UPB_ASSERT(!upb_strtable_done(i));
406 return _upb_value_val(str_tabent(i)->val.val);
407 }
408
upb_strtable_iter_setdone(upb_strtable_iter * i)409 void upb_strtable_iter_setdone(upb_strtable_iter *i) {
410 i->t = NULL;
411 i->index = SIZE_MAX;
412 }
413
upb_strtable_iter_isequal(const upb_strtable_iter * i1,const upb_strtable_iter * i2)414 bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,
415 const upb_strtable_iter *i2) {
416 if (upb_strtable_done(i1) && upb_strtable_done(i2))
417 return true;
418 return i1->t == i2->t && i1->index == i2->index;
419 }
420
421
422 /* upb_inttable ***************************************************************/
423
424 /* For inttables we use a hybrid structure where small keys are kept in an
425 * array and large keys are put in the hash table. */
426
inthash(upb_tabkey key)427 static uint32_t inthash(upb_tabkey key) { return upb_inthash(key); }
428
inteql(upb_tabkey k1,lookupkey_t k2)429 static bool inteql(upb_tabkey k1, lookupkey_t k2) {
430 return k1 == k2.num;
431 }
432
mutable_array(upb_inttable * t)433 static upb_tabval *mutable_array(upb_inttable *t) {
434 return (upb_tabval*)t->array;
435 }
436
inttable_val(upb_inttable * t,uintptr_t key)437 static upb_tabval *inttable_val(upb_inttable *t, uintptr_t key) {
438 if (key < t->array_size) {
439 return upb_arrhas(t->array[key]) ? &(mutable_array(t)[key]) : NULL;
440 } else {
441 upb_tabent *e =
442 findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql);
443 return e ? &e->val : NULL;
444 }
445 }
446
inttable_val_const(const upb_inttable * t,uintptr_t key)447 static const upb_tabval *inttable_val_const(const upb_inttable *t,
448 uintptr_t key) {
449 return inttable_val((upb_inttable*)t, key);
450 }
451
upb_inttable_count(const upb_inttable * t)452 size_t upb_inttable_count(const upb_inttable *t) {
453 return t->t.count + t->array_count;
454 }
455
check(upb_inttable * t)456 static void check(upb_inttable *t) {
457 UPB_UNUSED(t);
458 #if defined(UPB_DEBUG_TABLE) && !defined(NDEBUG)
459 {
460 /* This check is very expensive (makes inserts/deletes O(N)). */
461 size_t count = 0;
462 upb_inttable_iter i;
463 upb_inttable_begin(&i, t);
464 for(; !upb_inttable_done(&i); upb_inttable_next(&i), count++) {
465 UPB_ASSERT(upb_inttable_lookup(t, upb_inttable_iter_key(&i), NULL));
466 }
467 UPB_ASSERT(count == upb_inttable_count(t));
468 }
469 #endif
470 }
471
upb_inttable_sizedinit(upb_inttable * t,size_t asize,int hsize_lg2,upb_alloc * a)472 bool upb_inttable_sizedinit(upb_inttable *t, size_t asize, int hsize_lg2,
473 upb_alloc *a) {
474 size_t array_bytes;
475
476 if (!init(&t->t, hsize_lg2, a)) return false;
477 /* Always make the array part at least 1 long, so that we know key 0
478 * won't be in the hash part, which simplifies things. */
479 t->array_size = UPB_MAX(1, asize);
480 t->array_count = 0;
481 array_bytes = t->array_size * sizeof(upb_value);
482 t->array = upb_malloc(a, array_bytes);
483 if (!t->array) {
484 uninit(&t->t, a);
485 return false;
486 }
487 memset(mutable_array(t), 0xff, array_bytes);
488 check(t);
489 return true;
490 }
491
upb_inttable_init2(upb_inttable * t,upb_ctype_t ctype,upb_alloc * a)492 bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
493 UPB_UNUSED(ctype); /* TODO(haberman): rm */
494 return upb_inttable_sizedinit(t, 0, 4, a);
495 }
496
upb_inttable_uninit2(upb_inttable * t,upb_alloc * a)497 void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
498 uninit(&t->t, a);
499 upb_free(a, mutable_array(t));
500 }
501
upb_inttable_insert2(upb_inttable * t,uintptr_t key,upb_value val,upb_alloc * a)502 bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
503 upb_alloc *a) {
504 upb_tabval tabval;
505 tabval.val = val.val;
506 UPB_ASSERT(upb_arrhas(tabval)); /* This will reject (uint64_t)-1. Fix this. */
507
508 if (key < t->array_size) {
509 UPB_ASSERT(!upb_arrhas(t->array[key]));
510 t->array_count++;
511 mutable_array(t)[key].val = val.val;
512 } else {
513 if (isfull(&t->t)) {
514 /* Need to resize the hash part, but we re-use the array part. */
515 size_t i;
516 upb_table new_table;
517
518 if (!init(&new_table, t->t.size_lg2 + 1, a)) {
519 return false;
520 }
521
522 for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
523 const upb_tabent *e = &t->t.entries[i];
524 uint32_t hash;
525 upb_value v;
526
527 _upb_value_setval(&v, e->val.val);
528 hash = upb_inthash(e->key);
529 insert(&new_table, intkey(e->key), e->key, v, hash, &inthash, &inteql);
530 }
531
532 UPB_ASSERT(t->t.count == new_table.count);
533
534 uninit(&t->t, a);
535 t->t = new_table;
536 }
537 insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
538 }
539 check(t);
540 return true;
541 }
542
upb_inttable_lookup(const upb_inttable * t,uintptr_t key,upb_value * v)543 bool upb_inttable_lookup(const upb_inttable *t, uintptr_t key, upb_value *v) {
544 const upb_tabval *table_v = inttable_val_const(t, key);
545 if (!table_v) return false;
546 if (v) _upb_value_setval(v, table_v->val);
547 return true;
548 }
549
upb_inttable_replace(upb_inttable * t,uintptr_t key,upb_value val)550 bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val) {
551 upb_tabval *table_v = inttable_val(t, key);
552 if (!table_v) return false;
553 table_v->val = val.val;
554 return true;
555 }
556
upb_inttable_remove(upb_inttable * t,uintptr_t key,upb_value * val)557 bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {
558 bool success;
559 if (key < t->array_size) {
560 if (upb_arrhas(t->array[key])) {
561 upb_tabval empty = UPB_TABVALUE_EMPTY_INIT;
562 t->array_count--;
563 if (val) {
564 _upb_value_setval(val, t->array[key].val);
565 }
566 mutable_array(t)[key] = empty;
567 success = true;
568 } else {
569 success = false;
570 }
571 } else {
572 success = rm(&t->t, intkey(key), val, NULL, upb_inthash(key), &inteql);
573 }
574 check(t);
575 return success;
576 }
577
upb_inttable_insertptr2(upb_inttable * t,const void * key,upb_value val,upb_alloc * a)578 bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
579 upb_alloc *a) {
580 return upb_inttable_insert2(t, (uintptr_t)key, val, a);
581 }
582
upb_inttable_lookupptr(const upb_inttable * t,const void * key,upb_value * v)583 bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
584 upb_value *v) {
585 return upb_inttable_lookup(t, (uintptr_t)key, v);
586 }
587
upb_inttable_removeptr(upb_inttable * t,const void * key,upb_value * val)588 bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) {
589 return upb_inttable_remove(t, (uintptr_t)key, val);
590 }
591
upb_inttable_compact2(upb_inttable * t,upb_alloc * a)592 void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) {
593 /* A power-of-two histogram of the table keys. */
594 size_t counts[UPB_MAXARRSIZE + 1] = {0};
595
596 /* The max key in each bucket. */
597 uintptr_t max[UPB_MAXARRSIZE + 1] = {0};
598
599 upb_inttable_iter i;
600 size_t arr_count;
601 int size_lg2;
602 upb_inttable new_t;
603
604 upb_inttable_begin(&i, t);
605 for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
606 uintptr_t key = upb_inttable_iter_key(&i);
607 int bucket = log2ceil(key);
608 max[bucket] = UPB_MAX(max[bucket], key);
609 counts[bucket]++;
610 }
611
612 /* Find the largest power of two that satisfies the MIN_DENSITY
613 * definition (while actually having some keys). */
614 arr_count = upb_inttable_count(t);
615
616 for (size_lg2 = ARRAY_SIZE(counts) - 1; size_lg2 > 0; size_lg2--) {
617 if (counts[size_lg2] == 0) {
618 /* We can halve again without losing any entries. */
619 continue;
620 } else if (arr_count >= (1 << size_lg2) * MIN_DENSITY) {
621 break;
622 }
623
624 arr_count -= counts[size_lg2];
625 }
626
627 UPB_ASSERT(arr_count <= upb_inttable_count(t));
628
629 {
630 /* Insert all elements into new, perfectly-sized table. */
631 size_t arr_size = max[size_lg2] + 1; /* +1 so arr[max] will fit. */
632 size_t hash_count = upb_inttable_count(t) - arr_count;
633 size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
634 int hashsize_lg2 = log2ceil(hash_size);
635
636 upb_inttable_sizedinit(&new_t, arr_size, hashsize_lg2, a);
637 upb_inttable_begin(&i, t);
638 for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
639 uintptr_t k = upb_inttable_iter_key(&i);
640 upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
641 }
642 UPB_ASSERT(new_t.array_size == arr_size);
643 UPB_ASSERT(new_t.t.size_lg2 == hashsize_lg2);
644 }
645 upb_inttable_uninit2(t, a);
646 *t = new_t;
647 }
648
649 /* Iteration. */
650
int_tabent(const upb_inttable_iter * i)651 static const upb_tabent *int_tabent(const upb_inttable_iter *i) {
652 UPB_ASSERT(!i->array_part);
653 return &i->t->t.entries[i->index];
654 }
655
int_arrent(const upb_inttable_iter * i)656 static upb_tabval int_arrent(const upb_inttable_iter *i) {
657 UPB_ASSERT(i->array_part);
658 return i->t->array[i->index];
659 }
660
upb_inttable_begin(upb_inttable_iter * i,const upb_inttable * t)661 void upb_inttable_begin(upb_inttable_iter *i, const upb_inttable *t) {
662 i->t = t;
663 i->index = -1;
664 i->array_part = true;
665 upb_inttable_next(i);
666 }
667
upb_inttable_next(upb_inttable_iter * iter)668 void upb_inttable_next(upb_inttable_iter *iter) {
669 const upb_inttable *t = iter->t;
670 if (iter->array_part) {
671 while (++iter->index < t->array_size) {
672 if (upb_arrhas(int_arrent(iter))) {
673 return;
674 }
675 }
676 iter->array_part = false;
677 iter->index = begin(&t->t);
678 } else {
679 iter->index = next(&t->t, iter->index);
680 }
681 }
682
upb_inttable_done(const upb_inttable_iter * i)683 bool upb_inttable_done(const upb_inttable_iter *i) {
684 if (!i->t) return true;
685 if (i->array_part) {
686 return i->index >= i->t->array_size ||
687 !upb_arrhas(int_arrent(i));
688 } else {
689 return i->index >= upb_table_size(&i->t->t) ||
690 upb_tabent_isempty(int_tabent(i));
691 }
692 }
693
upb_inttable_iter_key(const upb_inttable_iter * i)694 uintptr_t upb_inttable_iter_key(const upb_inttable_iter *i) {
695 UPB_ASSERT(!upb_inttable_done(i));
696 return i->array_part ? i->index : int_tabent(i)->key;
697 }
698
upb_inttable_iter_value(const upb_inttable_iter * i)699 upb_value upb_inttable_iter_value(const upb_inttable_iter *i) {
700 UPB_ASSERT(!upb_inttable_done(i));
701 return _upb_value_val(
702 i->array_part ? i->t->array[i->index].val : int_tabent(i)->val.val);
703 }
704
upb_inttable_iter_setdone(upb_inttable_iter * i)705 void upb_inttable_iter_setdone(upb_inttable_iter *i) {
706 i->t = NULL;
707 i->index = SIZE_MAX;
708 i->array_part = false;
709 }
710
upb_inttable_iter_isequal(const upb_inttable_iter * i1,const upb_inttable_iter * i2)711 bool upb_inttable_iter_isequal(const upb_inttable_iter *i1,
712 const upb_inttable_iter *i2) {
713 if (upb_inttable_done(i1) && upb_inttable_done(i2))
714 return true;
715 return i1->t == i2->t && i1->index == i2->index &&
716 i1->array_part == i2->array_part;
717 }
718