diff --git a/upb/def.c b/upb/def.c index 363d8bb960..88237ab35c 100644 --- a/upb/def.c +++ b/upb/def.c @@ -2397,6 +2397,12 @@ static int count_bits_debug(uint64_t x) { return n; } +static int compare_int32(const void* a_ptr, const void* b_ptr) { + int32_t a = *(int32_t*)a_ptr; + int32_t b = *(int32_t*)b_ptr; + return ((a) < (b) ? -1 : ((a) == (b) ? 0 : 1)); +} + upb_MiniTable_Enum* create_enumlayout(symtab_addctx* ctx, const upb_EnumDef* e) { int n = 0; @@ -2427,6 +2433,17 @@ upb_MiniTable_Enum* create_enumlayout(symtab_addctx* ctx, UPB_ASSERT(p == values + n); } + // Enums can have duplicate values; we must sort+uniq them. + qsort(values, n, sizeof(*values), &compare_int32); + + int dst = 0; + for (int i = 0; i < n; dst++) { + int32_t val = values[i]; + while (i < n && values[i] == val) i++; // Skip duplicates. + values[dst] = val; + } + n = dst; + UPB_ASSERT(upb_inttable_count(&e->iton) == n + count_bits_debug(mask)); upb_MiniTable_Enum* layout = symtab_alloc(ctx, sizeof(*layout)); @@ -2510,7 +2527,7 @@ static void create_enumdef( if (ctx->layout) { UPB_ASSERT(ctx->enum_count < ctx->layout->enum_count); e->layout = ctx->layout->enums[ctx->enum_count++]; - UPB_ASSERT(n == + UPB_ASSERT(upb_inttable_count(&e->iton) == e->layout->value_count + count_bits_debug(e->layout->mask)); } else { e->layout = create_enumlayout(ctx, e); diff --git a/upb/util/def_to_proto_test.proto b/upb/util/def_to_proto_test.proto index c278748f88..2e4cac5f76 100644 --- a/upb/util/def_to_proto_test.proto +++ b/upb/util/def_to_proto_test.proto @@ -69,6 +69,26 @@ enum Enum { NEGATIVE_ONE = -1; } +enum EnumUpper32Value { + UPPER32_VALUE = 40; +} + +enum HasDuplicateValues { + option allow_alias = true; + A = 0; + B = 1; + C = 120; + D = 130; + + G = 120; + F = 1; + E = 0; + H = 121; + I = 121; + J = 121; + K = 121; +} + service Service { rpc Bar(Message) returns (Message); } diff --git a/upbc/protoc-gen-upb.cc b/upbc/protoc-gen-upb.cc index ff4a5028f1..da1e7f2de0 100644 --- a/upbc/protoc-gen-upb.cc +++ b/upbc/protoc-gen-upb.cc @@ -95,6 +95,17 @@ std::vector SortedEnums( return enums; } +std::vector SortedUniqueEnumNumbers( + const protobuf::EnumDescriptor* e) { + std::vector values; + for (int i = 0; i < e->value_count(); i++) { + values.push_back(e->value(i)->number()); + } + std::sort(values.begin(), values.end()); + std::unique(values.begin(), values.end()); + return values; +} + void AddMessages(const protobuf::Descriptor* message, std::vector* messages) { messages->push_back(message); @@ -1301,8 +1312,7 @@ int WriteEnums(const protobuf::FileDescriptor* file, Output& output) { for (const auto* e : this_file_enums) { uint64_t mask = 0; absl::flat_hash_set values; - for (int i = 0; i < e->value_count(); i++) { - int32_t number = e->value(i)->number(); + for (auto number : SortedUniqueEnumNumbers(e)) { if (static_cast(number) < 64) { mask |= 1 << number; } else {