• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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