mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
Delete array part of inttable, reducing complexity
PiperOrigin-RevId: 911624420
This commit is contained in:
parent
3b402b46ea
commit
7b650943ad
8 changed files with 125 additions and 378 deletions
|
|
@ -26,17 +26,6 @@
|
|||
// Must be last.
|
||||
#include "upb/port/def.inc"
|
||||
|
||||
#define UPB_MAXARRSIZE 16 // 2**16 = 64k.
|
||||
|
||||
// From Chromium.
|
||||
#define ARRAY_SIZE(x) \
|
||||
((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x])))))
|
||||
|
||||
/* The minimum utilization of the array part of a mixed hash/array table. This
|
||||
* is a speed/memory-usage tradeoff (though it's not straightforward because of
|
||||
* cache effects). The lower this is, the more memory we'll use. */
|
||||
static const double MIN_DENSITY = 0.1;
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#if __has_builtin(__builtin_popcount)
|
||||
#define UPB_FAST_POPCOUNT32(i) __builtin_popcount(i)
|
||||
|
|
@ -67,16 +56,6 @@ UPB_INLINE uint8_t _upb_log2_table_size(upb_table* t) {
|
|||
return _upb_popcnt32(t->mask);
|
||||
}
|
||||
|
||||
static bool is_pow2(uint64_t v) { return v == 0 || (v & (v - 1)) == 0; }
|
||||
|
||||
static int log2ceil(uint64_t v) {
|
||||
int ret = 0;
|
||||
bool pow2 = is_pow2(v);
|
||||
while (v >>= 1) ret++;
|
||||
ret = pow2 ? ret : ret + 1; // Ceiling.
|
||||
return UPB_MIN(UPB_MAXARRSIZE, ret);
|
||||
}
|
||||
|
||||
/* A type to represent the lookup key of either a strtable, inttable or
|
||||
* exttable. */
|
||||
typedef union {
|
||||
|
|
@ -751,18 +730,6 @@ size_t upb_exttable_size(const upb_exttable* t) { return t->t.count; }
|
|||
|
||||
/* upb_inttable ***************************************************************/
|
||||
|
||||
/* For inttables we use a hybrid structure where small keys are kept in an
|
||||
* array and large keys are put in the hash table. */
|
||||
|
||||
// The sentinel value used in the dense array part. Note that callers must
|
||||
// ensure that inttable is never used with a value of this sentinel type
|
||||
// (pointers and u32 values will never be; i32 needs to be handled carefully
|
||||
// to avoid sign-extending into this value).
|
||||
static const upb_value kInttableSentinel = {.val = UINT64_MAX};
|
||||
static uint32_t presence_mask_arr_size(uint32_t array_size) {
|
||||
return (array_size + 7) / 8; // sizeof(uint8_t) is always 1.
|
||||
}
|
||||
|
||||
static uint32_t inthash(upb_key key, upb_value val) {
|
||||
UPB_UNUSED(val);
|
||||
return upb_inthash(key.num);
|
||||
|
|
@ -773,35 +740,7 @@ static bool inteql(upb_key k1, upb_value v1, lookupkey_t k2) {
|
|||
return k1.num == k2.num;
|
||||
}
|
||||
|
||||
static upb_value* mutable_array(upb_inttable* t) {
|
||||
return (upb_value*)t->array;
|
||||
}
|
||||
|
||||
static const upb_value* inttable_array_get(const upb_inttable* t,
|
||||
uintptr_t key) {
|
||||
UPB_ASSERT(key < t->array_size);
|
||||
const upb_value* val = &t->array[key];
|
||||
return upb_inttable_arrhas(t, key) ? val : NULL;
|
||||
}
|
||||
|
||||
static upb_value* inttable_val(upb_inttable* t, uintptr_t key) {
|
||||
if (key < t->array_size) {
|
||||
return (upb_value*)inttable_array_get(t, key);
|
||||
} else {
|
||||
upb_tabent* e =
|
||||
findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql);
|
||||
return e ? &e->val : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const upb_value* inttable_val_const(const upb_inttable* t,
|
||||
uintptr_t key) {
|
||||
return inttable_val((upb_inttable*)t, key);
|
||||
}
|
||||
|
||||
size_t upb_inttable_count(const upb_inttable* t) {
|
||||
return t->t.count + t->array_count;
|
||||
}
|
||||
size_t upb_inttable_count(const upb_inttable* t) { return t->t.count; }
|
||||
|
||||
static void check(upb_inttable* t) {
|
||||
UPB_UNUSED(t);
|
||||
|
|
@ -821,291 +760,125 @@ static void check(upb_inttable* t) {
|
|||
#endif
|
||||
}
|
||||
|
||||
bool upb_inttable_sizedinit(upb_inttable* t, uint32_t asize, int hsize_lg2,
|
||||
upb_Arena* a) {
|
||||
static bool upb_inttable_sizedinit(upb_inttable* t, int hsize_lg2,
|
||||
upb_Arena* a) {
|
||||
if (!init(&t->t, hsize_lg2, a)) return false;
|
||||
/* Always make the array part at least 1 long, so that we know key 0
|
||||
* won't be in the hash part, which simplifies things. */
|
||||
t->array_size = UPB_MAX(1, asize);
|
||||
t->array_count = 0;
|
||||
#if UINT32_MAX >= SIZE_MAX
|
||||
if (UPB_UNLIKELY(SIZE_MAX / sizeof(upb_value) < t->array_size)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Allocate the array part and the presence mask array in one allocation.
|
||||
size_t array_bytes = t->array_size * sizeof(upb_value);
|
||||
uint32_t presence_bytes = presence_mask_arr_size(t->array_size);
|
||||
uintptr_t total_bytes = array_bytes + presence_bytes;
|
||||
if (UPB_UNLIKELY(total_bytes > SIZE_MAX)) {
|
||||
return false;
|
||||
}
|
||||
void* alloc = upb_Arena_Malloc(a, total_bytes);
|
||||
if (!alloc) {
|
||||
return false;
|
||||
}
|
||||
t->array = alloc;
|
||||
memset(mutable_array(t), 0xff, array_bytes);
|
||||
t->presence_mask = (uint8_t*)alloc + array_bytes;
|
||||
memset((uint8_t*)t->presence_mask, 0, presence_bytes);
|
||||
|
||||
check(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_init(upb_inttable* t, upb_Arena* a) {
|
||||
// The init size of the table part to match that of strtable.
|
||||
return upb_inttable_sizedinit(t, 0, 3, a);
|
||||
return upb_inttable_sizedinit(t, 3, a);
|
||||
}
|
||||
|
||||
bool upb_inttable_insert(upb_inttable* t, uintptr_t key, upb_value val,
|
||||
upb_Arena* a) {
|
||||
if (key < t->array_size) {
|
||||
UPB_ASSERT(!upb_inttable_arrhas(t, key));
|
||||
t->array_count++;
|
||||
mutable_array(t)[key] = val;
|
||||
((uint8_t*)t->presence_mask)[key / 8] |= (1 << (key % 8));
|
||||
} else {
|
||||
if (isfull(&t->t)) {
|
||||
/* Need to resize the hash part, but we re-use the array part. */
|
||||
size_t i;
|
||||
upb_table new_table;
|
||||
if (isfull(&t->t)) {
|
||||
upb_table new_table;
|
||||
|
||||
if (!init(&new_table, _upb_log2_table_size(&t->t) + 1, a)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
|
||||
const upb_tabent* e = &t->t.entries[i];
|
||||
insert(&new_table, intkey(e->key.num), e->key, e->val,
|
||||
inthash(e->key, e->val), &inthash, &inteql);
|
||||
}
|
||||
|
||||
UPB_ASSERT(t->t.count == new_table.count);
|
||||
|
||||
t->t = new_table;
|
||||
if (!init(&new_table, _upb_log2_table_size(&t->t) + 1, a)) {
|
||||
return false;
|
||||
}
|
||||
upb_key tabkey = {.num = key};
|
||||
insert(&t->t, intkey(key), tabkey, val, upb_inthash(key), &inthash,
|
||||
&inteql);
|
||||
|
||||
for (size_t i = begin(&t->t); i < upb_table_size(&t->t);
|
||||
i = next(&t->t, i)) {
|
||||
const upb_tabent* e = &t->t.entries[i];
|
||||
insert(&new_table, intkey(e->key.num), e->key, e->val,
|
||||
inthash(e->key, e->val), &inthash, &inteql);
|
||||
}
|
||||
|
||||
UPB_ASSERT(t->t.count == new_table.count);
|
||||
|
||||
t->t = new_table;
|
||||
}
|
||||
upb_key tabkey = {.num = key};
|
||||
insert(&t->t, intkey(key), tabkey, val, upb_inthash(key), &inthash, &inteql);
|
||||
check(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_lookup(const upb_inttable* t, uintptr_t key, upb_value* v) {
|
||||
const upb_value* table_v = inttable_val_const(t, key);
|
||||
if (!table_v) return false;
|
||||
if (v) *v = *table_v;
|
||||
return true;
|
||||
return lookup(&t->t, intkey(key), v, upb_inthash(key), &inteql);
|
||||
}
|
||||
|
||||
bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val) {
|
||||
upb_value* table_v = inttable_val(t, key);
|
||||
if (!table_v) return false;
|
||||
*table_v = val;
|
||||
upb_tabent* e =
|
||||
findentry_mutable(&t->t, intkey(key), upb_inthash(key), &inteql);
|
||||
if (!e) return false;
|
||||
e->val = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val) {
|
||||
bool success;
|
||||
if (key < t->array_size) {
|
||||
if (upb_inttable_arrhas(t, key)) {
|
||||
t->array_count--;
|
||||
if (val) {
|
||||
*val = t->array[key];
|
||||
}
|
||||
mutable_array(t)[key] = kInttableSentinel;
|
||||
((uint8_t*)t->presence_mask)[key / 8] &= ~(1 << (key % 8));
|
||||
success = true;
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
} else {
|
||||
success = rm(&t->t, intkey(key), val, upb_inthash(key), &inteql);
|
||||
}
|
||||
bool success = rm(&t->t, intkey(key), val, upb_inthash(key), &inteql);
|
||||
check(t);
|
||||
return success;
|
||||
}
|
||||
|
||||
bool upb_inttable_compact(upb_inttable* t, upb_Arena* a) {
|
||||
/* A power-of-two histogram of the table keys. */
|
||||
uint32_t counts[UPB_MAXARRSIZE + 1] = {0};
|
||||
|
||||
/* The max key in each bucket. */
|
||||
uintptr_t max[UPB_MAXARRSIZE + 1] = {0};
|
||||
|
||||
{
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
uintptr_t key;
|
||||
upb_value val;
|
||||
while (upb_inttable_next(t, &key, &val, &iter)) {
|
||||
int bucket = log2ceil(key);
|
||||
max[bucket] = UPB_MAX(max[bucket], key);
|
||||
counts[bucket]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the largest power of two that satisfies the MIN_DENSITY
|
||||
* definition (while actually having some keys). */
|
||||
uint32_t arr_count = upb_inttable_count(t);
|
||||
|
||||
// Scan all buckets except capped bucket
|
||||
int size_lg2 = ARRAY_SIZE(counts) - 1;
|
||||
for (; size_lg2 > 0; size_lg2--) {
|
||||
if (counts[size_lg2] == 0) {
|
||||
/* We can halve again without losing any entries. */
|
||||
continue;
|
||||
} else if (arr_count >= (1 << size_lg2) * MIN_DENSITY) {
|
||||
break;
|
||||
}
|
||||
|
||||
arr_count -= counts[size_lg2];
|
||||
}
|
||||
|
||||
UPB_ASSERT(arr_count <= upb_inttable_count(t));
|
||||
|
||||
upb_inttable new_t;
|
||||
{
|
||||
/* Insert all elements into new, perfectly-sized table. */
|
||||
uintptr_t arr_size = max[size_lg2] + 1; /* +1 so arr[max] will fit. */
|
||||
uint32_t hash_count = upb_inttable_count(t) - arr_count;
|
||||
size_t hash_size = hash_count ? _upb_entries_needed_for(hash_count) : 0;
|
||||
int hashsize_lg2 = log2ceil(hash_size);
|
||||
|
||||
if (!upb_inttable_sizedinit(&new_t, arr_size, hashsize_lg2, a)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
uintptr_t key;
|
||||
upb_value val;
|
||||
while (upb_inttable_next(t, &key, &val, &iter)) {
|
||||
upb_inttable_insert(&new_t, key, val, a);
|
||||
}
|
||||
}
|
||||
|
||||
UPB_ASSERT(new_t.array_size == arr_size);
|
||||
}
|
||||
*t = new_t;
|
||||
return true;
|
||||
}
|
||||
|
||||
void upb_inttable_clear(upb_inttable* t) {
|
||||
// Clear the array part.
|
||||
size_t array_bytes = t->array_size * sizeof(upb_value);
|
||||
t->array_count = 0;
|
||||
// Clear the array by setting all bits to 1, as UINT64_MAX is the sentinel
|
||||
// value for an empty array.
|
||||
memset(mutable_array(t), 0xff, array_bytes);
|
||||
// Clear the presence mask array.
|
||||
memset((uint8_t*)t->presence_mask, 0, presence_mask_arr_size(t->array_size));
|
||||
// Clear the table part.
|
||||
size_t bytes = upb_table_size(&t->t) * sizeof(upb_tabent);
|
||||
t->t.count = 0;
|
||||
memset((char*)t->t.entries, 0, bytes);
|
||||
}
|
||||
|
||||
// Iteration.
|
||||
|
||||
bool upb_inttable_next(const upb_inttable* t, uintptr_t* key, upb_value* val,
|
||||
intptr_t* iter) {
|
||||
intptr_t i = *iter;
|
||||
if ((size_t)(i + 1) <= t->array_size) {
|
||||
while ((size_t)++i < t->array_size) {
|
||||
const upb_value* ent = inttable_array_get(t, i);
|
||||
if (ent) {
|
||||
*key = i;
|
||||
*val = *ent;
|
||||
*iter = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
i--; // Back up to exactly one position before the start of the table.
|
||||
}
|
||||
|
||||
size_t tab_idx = next(&t->t, i - t->array_size);
|
||||
size_t tab_idx = next(&t->t, *iter);
|
||||
if (tab_idx < upb_table_size(&t->t)) {
|
||||
upb_tabent* ent = &t->t.entries[tab_idx];
|
||||
*key = ent->key.num;
|
||||
*val = ent->val;
|
||||
*iter = tab_idx + t->array_size;
|
||||
*iter = tab_idx;
|
||||
return true;
|
||||
} else {
|
||||
// We should set the iterator any way. When we are done, the iterator value
|
||||
// is invalidated. `upb_inttable_done` will check on the iterator value to
|
||||
// determine if the iteration is done.
|
||||
*iter = INTPTR_MAX - 1; // To disambiguate from UPB_INTTABLE_BEGIN, to
|
||||
// match the behavior of `upb_strtable_iter`.
|
||||
return false;
|
||||
}
|
||||
*iter = INTPTR_MAX - 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
void upb_inttable_removeiter(upb_inttable* t, intptr_t* iter) {
|
||||
intptr_t i = *iter;
|
||||
if ((size_t)i < t->array_size) {
|
||||
t->array_count--;
|
||||
mutable_array(t)[i].val = -1;
|
||||
} else {
|
||||
upb_tabent* ent = &t->t.entries[i - t->array_size];
|
||||
upb_tabent* prev = NULL;
|
||||
upb_tabent* ent = &t->t.entries[i];
|
||||
upb_tabent* prev = NULL;
|
||||
|
||||
// Linear search, not great.
|
||||
upb_tabent* end = &t->t.entries[upb_table_size(&t->t)];
|
||||
for (upb_tabent* e = t->t.entries; e != end; e++) {
|
||||
if (!upb_tabent_isempty(e) && upb_tabent_hasnext(e) &&
|
||||
upb_tabent_next(e) == ent) {
|
||||
prev = e;
|
||||
break;
|
||||
}
|
||||
// Linear search, not great.
|
||||
upb_tabent* end = &t->t.entries[upb_table_size(&t->t)];
|
||||
for (upb_tabent* e = t->t.entries; e != end; e++) {
|
||||
if (!upb_tabent_isempty(e) && upb_tabent_hasnext(e) &&
|
||||
upb_tabent_next(e) == ent) {
|
||||
prev = e;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
if (upb_tabent_hasnext(ent)) {
|
||||
upb_tabent_setnext(prev, upb_tabent_next(ent));
|
||||
} else {
|
||||
upb_tabent_clearnext(prev);
|
||||
}
|
||||
}
|
||||
|
||||
t->t.count--;
|
||||
upb_tabent_clear(ent);
|
||||
}
|
||||
|
||||
if (prev) {
|
||||
if (upb_tabent_hasnext(ent)) {
|
||||
upb_tabent_setnext(prev, upb_tabent_next(ent));
|
||||
} else {
|
||||
upb_tabent_clearnext(prev);
|
||||
}
|
||||
}
|
||||
|
||||
t->t.count--;
|
||||
upb_tabent_clear(ent);
|
||||
}
|
||||
|
||||
void upb_inttable_setentryvalue(upb_inttable* t, intptr_t iter, upb_value v) {
|
||||
if ((size_t)iter < t->array_size) {
|
||||
mutable_array(t)[iter] = v;
|
||||
} else {
|
||||
upb_tabent* ent = &t->t.entries[iter - t->array_size];
|
||||
ent->val = v;
|
||||
}
|
||||
t->t.entries[iter].val = v;
|
||||
}
|
||||
|
||||
bool upb_inttable_done(const upb_inttable* t, intptr_t iter) {
|
||||
if ((uintptr_t)iter >= t->array_size + upb_table_size(&t->t)) {
|
||||
if (iter == INTPTR_MAX - 1 || (size_t)iter >= upb_table_size(&t->t)) {
|
||||
return true;
|
||||
} else if ((size_t)iter < t->array_size) {
|
||||
return !upb_inttable_arrhas(t, iter);
|
||||
} else {
|
||||
return upb_tabent_isempty(&t->t.entries[iter - t->array_size]);
|
||||
}
|
||||
return upb_tabent_isempty(&t->t.entries[iter]);
|
||||
}
|
||||
|
||||
uintptr_t upb_inttable_iter_key(const upb_inttable* t, intptr_t iter) {
|
||||
UPB_ASSERT(!upb_inttable_done(t, iter));
|
||||
return (size_t)iter < t->array_size
|
||||
? iter
|
||||
: t->t.entries[iter - t->array_size].key.num;
|
||||
return t->t.entries[iter].key.num;
|
||||
}
|
||||
|
||||
upb_value upb_inttable_iter_value(const upb_inttable* t, intptr_t iter) {
|
||||
UPB_ASSERT(!upb_inttable_done(t, iter));
|
||||
if ((size_t)iter < t->array_size) {
|
||||
return t->array[iter];
|
||||
} else {
|
||||
return t->t.entries[iter - t->array_size].val;
|
||||
}
|
||||
return t->t.entries[iter].val;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,18 +18,7 @@
|
|||
#include "upb/port/def.inc"
|
||||
|
||||
typedef struct {
|
||||
upb_table t; // For entries that don't fit in the array part.
|
||||
// Array part of the table.
|
||||
// Pointers on this table are const so we can create static initializers for
|
||||
// tables. We cast away const sometimes, but *only* when the containing
|
||||
// upb_table is known to be non-const. This requires a bit of care, but
|
||||
// the subtlety is confined to table.c.
|
||||
const upb_value* array;
|
||||
// Track presence in the array part. Each bit at index (key % 8) at the
|
||||
// presence_mask[key/8] indicates if the element is present in the array part.
|
||||
const uint8_t* presence_mask;
|
||||
uint32_t array_size; // Array part size.
|
||||
uint32_t array_count; // Array part number of elements.
|
||||
upb_table t;
|
||||
} upb_inttable;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -45,7 +34,6 @@ size_t upb_inttable_count(const upb_inttable* t);
|
|||
|
||||
// Inserts the given key into the hashtable with the given value.
|
||||
// The key must not already exist in the hash table.
|
||||
// The value must not be UINTPTR_MAX.
|
||||
//
|
||||
// If a table resize was required but memory allocation failed, false is
|
||||
// returned and the table is unchanged.
|
||||
|
|
@ -65,12 +53,6 @@ bool upb_inttable_remove(upb_inttable* t, uintptr_t key, upb_value* val);
|
|||
// Unlike insert/remove, this does not invalidate iterators.
|
||||
bool upb_inttable_replace(upb_inttable* t, uintptr_t key, upb_value val);
|
||||
|
||||
// Optimizes the table for the current set of entries, for both memory use and
|
||||
// lookup time. Client should call this after all entries have been inserted;
|
||||
// inserting more entries is legal, but will likely require a table resize.
|
||||
// Returns false if reallocation fails.
|
||||
UPB_NODISCARD bool upb_inttable_compact(upb_inttable* t, upb_Arena* a);
|
||||
|
||||
// Clears the table.
|
||||
void upb_inttable_clear(upb_inttable* t);
|
||||
|
||||
|
|
@ -93,10 +75,6 @@ bool upb_inttable_done(const upb_inttable* t, intptr_t i);
|
|||
uintptr_t upb_inttable_iter_key(const upb_inttable* t, intptr_t iter);
|
||||
upb_value upb_inttable_iter_value(const upb_inttable* t, intptr_t iter);
|
||||
|
||||
UPB_INLINE bool upb_inttable_arrhas(const upb_inttable* t, uintptr_t key) {
|
||||
return (t->presence_mask[key / 8] & (1 << (key % 8))) != 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -171,22 +171,6 @@ TEST_P(IntTableTest, TestIntTable) {
|
|||
EXPECT_EQ(count, keys_.size());
|
||||
EXPECT_EQ(count, upb_inttable_count(&t));
|
||||
|
||||
// Compact and test correctness again.
|
||||
upb_inttable_compact(&t, arena.ptr());
|
||||
count = 0;
|
||||
for (uint32_t i = 0; i <= largest_key; i++) {
|
||||
upb_value val;
|
||||
bool ok = upb_inttable_lookup(&t, i, &val);
|
||||
if (ok) { /* Assume map implementation is correct. */
|
||||
EXPECT_EQ(val.val, i * 3);
|
||||
EXPECT_EQ(m[i], i * 3);
|
||||
EXPECT_EQ(hm[i], i * 3);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(count, keys_.size());
|
||||
EXPECT_EQ(count, upb_inttable_count(&t));
|
||||
|
||||
for (const auto& key : keys_) {
|
||||
upb_value val;
|
||||
bool ok = upb_inttable_remove(&t, key, &val);
|
||||
|
|
@ -467,14 +451,6 @@ TEST(Table, MaxValue) {
|
|||
EXPECT_TRUE(upb_inttable_lookup(&t, 2, &val));
|
||||
EXPECT_EQ(val.val, uint64_max);
|
||||
|
||||
upb_inttable_compact(&t, arena.ptr());
|
||||
EXPECT_TRUE(upb_inttable_lookup(&t, 0, &val));
|
||||
EXPECT_EQ(val.val, uint64_max);
|
||||
EXPECT_TRUE(upb_inttable_lookup(&t, 1, &val));
|
||||
EXPECT_EQ(val.val, uint64_max);
|
||||
EXPECT_TRUE(upb_inttable_lookup(&t, 2, &val));
|
||||
EXPECT_EQ(val.val, uint64_max);
|
||||
|
||||
upb_inttable_remove(&t, 0, nullptr);
|
||||
upb_inttable_remove(&t, 1, nullptr);
|
||||
upb_inttable_remove(&t, 2, nullptr);
|
||||
|
|
@ -497,12 +473,6 @@ TEST(Table, MaxValueWithLargeArray) {
|
|||
EXPECT_EQ(val.val, uint64_max);
|
||||
}
|
||||
|
||||
upb_inttable_compact(&t, arena.ptr());
|
||||
for (int i = 1; i < 121; i++) {
|
||||
EXPECT_TRUE(upb_inttable_lookup(&t, i, &val));
|
||||
EXPECT_EQ(val.val, uint64_max);
|
||||
}
|
||||
|
||||
for (int i = 1; i < 121; i++) {
|
||||
upb_inttable_remove(&t, i, nullptr);
|
||||
}
|
||||
|
|
@ -511,6 +481,73 @@ TEST(Table, MaxValueWithLargeArray) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(IntTableTest, RemoveIter) {
|
||||
upb::Arena arena;
|
||||
upb_inttable t;
|
||||
upb_inttable_init(&t, arena.ptr());
|
||||
upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr());
|
||||
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
uintptr_t key;
|
||||
upb_value val;
|
||||
|
||||
EXPECT_TRUE(upb_inttable_next(&t, &key, &val, &iter));
|
||||
bool saw_0 = false;
|
||||
bool saw_2 = false;
|
||||
bool saw_4 = false;
|
||||
do {
|
||||
switch (key) {
|
||||
case 0:
|
||||
EXPECT_EQ(upb_inttable_iter_key(&t, iter), 0);
|
||||
upb_inttable_removeiter(&t, &iter);
|
||||
EXPECT_FALSE(saw_0);
|
||||
saw_0 = true;
|
||||
break;
|
||||
case 2:
|
||||
EXPECT_EQ(upb_inttable_iter_key(&t, iter), 2);
|
||||
EXPECT_FALSE(saw_2);
|
||||
saw_2 = true;
|
||||
break;
|
||||
case 4:
|
||||
EXPECT_EQ(upb_inttable_iter_key(&t, iter), 4);
|
||||
upb_inttable_removeiter(&t, &iter);
|
||||
EXPECT_FALSE(saw_4);
|
||||
saw_4 = true;
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unexpected key: " << key;
|
||||
}
|
||||
} while (upb_inttable_next(&t, &key, &val, &iter));
|
||||
|
||||
EXPECT_TRUE(saw_0);
|
||||
EXPECT_TRUE(saw_2);
|
||||
EXPECT_TRUE(saw_4);
|
||||
EXPECT_FALSE(upb_inttable_lookup(&t, 0, &val));
|
||||
EXPECT_TRUE(upb_inttable_lookup(&t, 2, &val));
|
||||
EXPECT_FALSE(upb_inttable_lookup(&t, 4, &val));
|
||||
}
|
||||
|
||||
TEST(IntTableTest, RemoveIterAll) {
|
||||
upb::Arena arena;
|
||||
upb_inttable t;
|
||||
upb_inttable_init(&t, arena.ptr());
|
||||
upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr());
|
||||
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
uintptr_t key;
|
||||
upb_value val;
|
||||
|
||||
while (upb_inttable_next(&t, &key, &val, &iter)) {
|
||||
upb_inttable_removeiter(&t, &iter);
|
||||
}
|
||||
|
||||
EXPECT_EQ(upb_inttable_count(&t), 0);
|
||||
}
|
||||
|
||||
TEST(IntTableTest, Delete) {
|
||||
upb::Arena arena;
|
||||
upb_inttable t;
|
||||
|
|
@ -518,7 +555,6 @@ TEST(IntTableTest, Delete) {
|
|||
upb_inttable_insert(&t, 0, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 2, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_insert(&t, 4, upb_value_bool(true), arena.ptr());
|
||||
upb_inttable_compact(&t, arena.ptr());
|
||||
upb_inttable_remove(&t, 0, nullptr);
|
||||
upb_inttable_remove(&t, 2, nullptr);
|
||||
upb_inttable_remove(&t, 4, nullptr);
|
||||
|
|
|
|||
|
|
@ -124,14 +124,7 @@ static bool _upb_mapsorter_resize(_upb_mapsorter* s, _upb_sortedmap* sorted,
|
|||
|
||||
bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
|
||||
const upb_Map* map, _upb_sortedmap* sorted) {
|
||||
int map_size;
|
||||
if (map->UPB_PRIVATE(is_strtable)) {
|
||||
map_size = _upb_Map_Size(map);
|
||||
} else {
|
||||
// For inttable, only sort the table entries, since the array part is
|
||||
// already in a sorted order.
|
||||
map_size = map->t.inttable.t.count;
|
||||
}
|
||||
int map_size = _upb_Map_Size(map);
|
||||
|
||||
if (!_upb_mapsorter_resize(s, sorted, map_size)) return false;
|
||||
|
||||
|
|
@ -143,8 +136,6 @@ bool _upb_mapsorter_pushmap(_upb_mapsorter* s, upb_FieldType key_type,
|
|||
src = map->t.strtable.t.entries;
|
||||
end = src + upb_table_size(&map->t.strtable.t);
|
||||
} else {
|
||||
// For inttable, only sort the table entries, since the array part is
|
||||
// already in a sorted order.
|
||||
src = map->t.inttable.t.entries;
|
||||
end = src + upb_table_size(&map->t.inttable.t);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,9 +301,6 @@ static void create_enumdef(upb_DefBuilder* ctx, const char* prefix,
|
|||
e->res_names = _upb_EnumReservedNames_New(ctx, n_res_name, res_names);
|
||||
|
||||
e->visibility = google_protobuf_EnumDescriptorProto_visibility(enum_proto);
|
||||
|
||||
if (!upb_inttable_compact(&e->iton, ctx->arena)) _upb_DefBuilder_OomErr(ctx);
|
||||
|
||||
if (upb_EnumDef_IsClosed(e)) {
|
||||
if (ctx->layout) {
|
||||
e->layout = upb_MiniTableFile_Enum(ctx->layout, ctx->enum_count++);
|
||||
|
|
|
|||
|
|
@ -745,7 +745,6 @@ static void create_msgdef(upb_DefBuilder* ctx, const char* prefix,
|
|||
m->real_oneof_count = m->oneof_count - synthetic_count;
|
||||
|
||||
assign_msg_wellknowntype(m);
|
||||
if (!upb_inttable_compact(&m->itof, ctx->arena)) _upb_DefBuilder_OomErr(ctx);
|
||||
|
||||
const google_protobuf_EnumDescriptorProto* const* enums =
|
||||
google_protobuf_DescriptorProto_enum_type(msg_proto, &n_enum);
|
||||
|
|
|
|||
|
|
@ -147,19 +147,6 @@ static void _upb_TextEncode_Map(txtenc* e, const upb_Map* map,
|
|||
} else {
|
||||
if (upb_Map_Size(map) == 0) return;
|
||||
|
||||
if (!map->UPB_PRIVATE(is_strtable)) {
|
||||
// For inttable, first encode the array part, then sort the table entries.
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
while ((size_t)++iter < map->t.inttable.array_size) {
|
||||
upb_value value = map->t.inttable.array[iter];
|
||||
if (upb_inttable_arrhas(&map->t.inttable, iter)) {
|
||||
upb_MessageValue key, val;
|
||||
memcpy(&key, &iter, sizeof(iter));
|
||||
_upb_map_fromvalue(value, &val, map->val_size);
|
||||
_upb_TextEncode_MapEntry(e, key, val, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
const upb_MessageDef* entry = upb_FieldDef_MessageSubDef(f);
|
||||
const upb_FieldDef* key_f = upb_MessageDef_Field(entry, 0);
|
||||
_upb_sortedmap sorted;
|
||||
|
|
|
|||
|
|
@ -602,20 +602,6 @@ static char* encode_map(char* ptr, upb_encstate* e, const upb_Message* msg,
|
|||
if (!map || !upb_Map_Size(map)) return ptr;
|
||||
|
||||
if (e->options & kUpb_EncodeOption_Deterministic) {
|
||||
if (!map->UPB_PRIVATE(is_strtable)) {
|
||||
// For inttable, first encode the array part, then sort the table entries.
|
||||
intptr_t iter = UPB_INTTABLE_BEGIN;
|
||||
while ((size_t)++iter < map->t.inttable.array_size) {
|
||||
upb_value value = map->t.inttable.array[iter];
|
||||
if (upb_inttable_arrhas(&map->t.inttable, iter)) {
|
||||
upb_MapEntry ent;
|
||||
memcpy(&ent.k, &iter, sizeof(iter));
|
||||
_upb_map_fromvalue(value, &ent.v, map->val_size);
|
||||
ptr = encode_mapentry(ptr, e, upb_MiniTableField_Number(f), layout,
|
||||
&ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
_upb_sortedmap sorted;
|
||||
_upb_mapsorter_pushmap(
|
||||
&e->sorter, layout->UPB_PRIVATE(fields)[0].UPB_PRIVATE(descriptortype),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue