diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index 83a026b6fc..ddd325b525 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -993,8 +993,19 @@ void SwapFieldHelper::SwapNonMessageNonStringField( SWAP_VALUES(FLOAT, float); SWAP_VALUES(DOUBLE, double); SWAP_VALUES(BOOL, bool); - SWAP_VALUES(ENUM, int); #undef SWAP_VALUES + case FieldDescriptor::CPPTYPE_ENUM: + if (r->schema_.IsEnum8(field)) { + std::swap(*r->MutableRaw(lhs, field), + *r->MutableRaw(rhs, field)); + } else if (r->schema_.IsEnum16(field)) { + std::swap(*r->MutableRaw(lhs, field), + *r->MutableRaw(rhs, field)); + } else { + std::swap(*r->MutableRaw(lhs, field), + *r->MutableRaw(rhs, field)); + } + break; default: ABSL_LOG(FATAL) << "Unimplemented type: " << field->cpp_type(); } @@ -1617,8 +1628,26 @@ void Reflection::ClearField(Message* message, #undef CLEAR_TYPE case FieldDescriptor::CPPTYPE_ENUM: - *MutableRaw(message, field) = - field->default_value_enum()->number(); + if (schema_.IsEnum8(field)) { + if (schema_.IsEnumSigned(field)) { + *MutableRaw(message, field) = + static_cast(field->default_value_enum()->number()); + } else { + *MutableRaw(message, field) = + static_cast(field->default_value_enum()->number()); + } + } else if (schema_.IsEnum16(field)) { + if (schema_.IsEnumSigned(field)) { + *MutableRaw(message, field) = + static_cast(field->default_value_enum()->number()); + } else { + *MutableRaw(message, field) = + static_cast(field->default_value_enum()->number()); + } + } else { + *MutableRaw(message, field) = + field->default_value_enum()->number(); + } break; case FieldDescriptor::CPPTYPE_STRING: { @@ -2433,6 +2462,18 @@ int Reflection::GetEnumValue(const Message& message, field->number(), field->default_value_enum()->number()); } else if (schema_.InRealOneof(field) && !HasOneofField(message, field)) { value = field->default_value_enum()->number(); + } else if (schema_.IsEnum8(field)) { + if (schema_.IsEnumSigned(field)) { + value = GetField(message, field); + } else { + value = GetField(message, field); + } + } else if (schema_.IsEnum16(field)) { + if (schema_.IsEnumSigned(field)) { + value = GetField(message, field); + } else { + value = GetField(message, field); + } } else { value = GetField(message, field); } @@ -2468,6 +2509,18 @@ void Reflection::SetEnumValueInternal(Message* message, if (field->is_extension()) { MutableExtensionSet(message)->Set(message->GetArena(), field->number(), field->type(), value, field); + } else if (schema_.IsEnum8(field)) { + if (schema_.IsEnumSigned(field)) { + SetField(message, field, static_cast(value)); + } else { + SetField(message, field, static_cast(value)); + } + } else if (schema_.IsEnum16(field)) { + if (schema_.IsEnumSigned(field)) { + SetField(message, field, static_cast(value)); + } else { + SetField(message, field, static_cast(value)); + } } else { SetField(message, field, value); } @@ -3245,6 +3298,11 @@ bool Reflection::IsImplicitPresenceFieldNonEmpty( "Code assumes uint64_t and double are the same size."); return absl::bit_cast(GetRaw(message, field)) != 0; case FieldDescriptor::CPPTYPE_ENUM: + if (schema_.IsEnum8(field)) { + return GetRaw(message, field) != 0; + } else if (schema_.IsEnum16(field)) { + return GetRaw(message, field) != 0; + } return GetRaw(message, field) != 0; case FieldDescriptor::CPPTYPE_STRING: switch (field->cpp_string_type()) { @@ -3795,6 +3853,13 @@ const internal::TcParseTableBase* Reflection::CreateTcParseTable() const { } return std::monostate{}; }; + const auto enum_rep = [&]() -> FieldOptions::EnumRep { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + if (schema_.IsEnum8(field)) return FieldOptions::kEnum8; + if (schema_.IsEnum16(field)) return FieldOptions::kEnum16; + } + return FieldOptions::kEnum32; + }; fields.push_back({ field, // static_cast(schema_.HasBitIndex(field)), @@ -3807,6 +3872,7 @@ const internal::TcParseTableBase* Reflection::CreateTcParseTable() const { /* use_direct_tcparser_table */ false, schema_.IsSplit(field), str_options(), + enum_rep(), }); } std::sort(fields.begin(), fields.end(), [](const auto& a, const auto& b) { diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 3cf3458863..ff4ffb172a 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -65,10 +65,14 @@ inline constexpr uint32_t kSplitFieldOffsetTag = 0x80000000u; inline constexpr uint32_t kLazyOffsetTag = 0x40000000u; inline constexpr uint32_t kInlinedOffsetTag = 0x40000000u; inline constexpr uint32_t kMicroStringOffsetTag = 0x20000000u; +inline constexpr uint32_t kEnum8OffsetTag = 0x20000000u; +inline constexpr uint32_t kEnum16OffsetTag = 0x40000000u; +inline constexpr uint32_t kEnumSignedOffsetTag = 0x10000000u; -inline constexpr uint32_t kAllOffsetTags = kSplitFieldOffsetTag | - kLazyOffsetTag | kInlinedOffsetTag | - kMicroStringOffsetTag; +inline constexpr uint32_t kAllOffsetTags = + kSplitFieldOffsetTag | kLazyOffsetTag | kInlinedOffsetTag | + kMicroStringOffsetTag | kEnum8OffsetTag | kEnum16OffsetTag | + kEnumSignedOffsetTag; // Structs that the code generator emits directly to describe a message. // These should never used directly except to build a ReflectionSchema @@ -158,6 +162,18 @@ class ReflectionSchema { return IsMicroString(offsets_[field->index()], field->type()); } + bool IsEnum8(const FieldDescriptor* field) const { + return IsEnum8(offsets_[field->index()], field->type()); + } + + bool IsEnum16(const FieldDescriptor* field) const { + return IsEnum16(offsets_[field->index()], field->type()); + } + + bool IsEnumSigned(const FieldDescriptor* field) const { + return IsEnumSigned(offsets_[field->index()], field->type()); + } + uint32_t GetOneofCaseOffset(const OneofDescriptor* oneof_descriptor) const { return static_cast(oneof_case_offset_) + static_cast( @@ -263,6 +279,19 @@ class ReflectionSchema { return (v & kMicroStringOffsetTag) != 0u; } + static bool IsEnum8(uint32_t v, FieldDescriptor::Type type) { + return type == FieldDescriptor::TYPE_ENUM && (v & kEnum8OffsetTag) != 0u; + } + + static bool IsEnum16(uint32_t v, FieldDescriptor::Type type) { + return type == FieldDescriptor::TYPE_ENUM && (v & kEnum16OffsetTag) != 0u; + } + + static bool IsEnumSigned(uint32_t v, FieldDescriptor::Type type) { + return type == FieldDescriptor::TYPE_ENUM && + (v & kEnumSignedOffsetTag) != 0u; + } + const Message* default_instance_; const uint32_t* offsets_; const uint32_t* has_bit_indices_; diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc index e76f9274ba..2fbf3a0f80 100644 --- a/src/google/protobuf/generated_message_reflection_unittest.cc +++ b/src/google/protobuf/generated_message_reflection_unittest.cc @@ -22,6 +22,8 @@ #include "google/protobuf/generated_message_reflection.h" #include +#include +#include #include #include #include @@ -2043,6 +2045,69 @@ TEST(CppNamespaceOption, NewNamespaceSymbolSameProtoName) { "cpp.file.options.test"); } +TEST(GeneratedMessageReflection, Enum8And16BitFields) { + const Descriptor* desc = unittest::TestAllTypes::descriptor(); + const FieldDescriptor* field = desc->FindFieldByName("optional_nested_enum"); + ASSERT_NE(field, nullptr); + + std::vector offsets(desc->field_count(), 0); + std::vector has_bits(desc->field_count(), 0); + + // Test 8-bit unsigned enum + { + offsets[field->index()] = 100 | internal::kEnum8OffsetTag; + internal::ReflectionSchema schema( + &unittest::TestAllTypes::default_instance(), offsets.data(), + has_bits.data(), /*has_bits_offset=*/-1, /*extensions_offset=*/-1, + /*oneof_case_offset=*/-1, sizeof(unittest::TestAllTypes), + /*split_offset=*/-1, /*sizeof_split=*/-1); + EXPECT_TRUE(schema.IsEnum8(field)); + EXPECT_FALSE(schema.IsEnum16(field)); + EXPECT_FALSE(schema.IsEnumSigned(field)); + } + + // Test 8-bit signed enum + { + offsets[field->index()] = + 100 | internal::kEnum8OffsetTag | internal::kEnumSignedOffsetTag; + internal::ReflectionSchema schema( + &unittest::TestAllTypes::default_instance(), offsets.data(), + has_bits.data(), /*has_bits_offset=*/-1, /*extensions_offset=*/-1, + /*oneof_case_offset=*/-1, sizeof(unittest::TestAllTypes), + /*split_offset=*/-1, /*sizeof_split=*/-1); + EXPECT_TRUE(schema.IsEnum8(field)); + EXPECT_FALSE(schema.IsEnum16(field)); + EXPECT_TRUE(schema.IsEnumSigned(field)); + } + + // Test 16-bit unsigned enum + { + offsets[field->index()] = 100 | internal::kEnum16OffsetTag; + internal::ReflectionSchema schema( + &unittest::TestAllTypes::default_instance(), offsets.data(), + has_bits.data(), /*has_bits_offset=*/-1, /*extensions_offset=*/-1, + /*oneof_case_offset=*/-1, sizeof(unittest::TestAllTypes), + /*split_offset=*/-1, /*sizeof_split=*/-1); + EXPECT_FALSE(schema.IsEnum8(field)); + EXPECT_TRUE(schema.IsEnum16(field)); + EXPECT_FALSE(schema.IsEnumSigned(field)); + } + + // Test 16-bit signed enum + { + offsets[field->index()] = + 100 | internal::kEnum16OffsetTag | internal::kEnumSignedOffsetTag; + internal::ReflectionSchema schema( + &unittest::TestAllTypes::default_instance(), offsets.data(), + has_bits.data(), /*has_bits_offset=*/-1, /*extensions_offset=*/-1, + /*oneof_case_offset=*/-1, sizeof(unittest::TestAllTypes), + /*split_offset=*/-1, /*sizeof_split=*/-1); + EXPECT_FALSE(schema.IsEnum8(field)); + EXPECT_TRUE(schema.IsEnum16(field)); + EXPECT_TRUE(schema.IsEnumSigned(field)); + } +} + } // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/generated_message_tctable_gen.cc b/src/google/protobuf/generated_message_tctable_gen.cc index f8d82ac9db..9bdda8e4b0 100644 --- a/src/google/protobuf/generated_message_tctable_gen.cc +++ b/src/google/protobuf/generated_message_tctable_gen.cc @@ -190,22 +190,46 @@ TailCallTableInfo::FastFieldInfo::Field MakeFastFieldEntry( picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastF64); break; case FieldDescriptor::TYPE_ENUM: - if (TreatEnumAsInt(field)) { - picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV32); + if (options.is_enum_8()) { + if (TreatEnumAsInt(field)) { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV8); + } else { + int32_t first, last; + if (GetEnumValidationRange(field->enum_type(), first, last)) { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr8); + } else { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEv8); + } + } + } else if (options.is_enum_16()) { + if (TreatEnumAsInt(field)) { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV16); + } else { + int32_t first, last; + if (GetEnumValidationRange(field->enum_type(), first, last)) { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr16); + } else { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEv16); + } + } } else { - switch (GetEnumRangeInfo(field, info.aux_idx)) { - case EnumRangeInfo::kNone: - picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEv); - break; - case EnumRangeInfo::kContiguous: - picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr); - break; - case EnumRangeInfo::kContiguous0: - picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr0); - break; - case EnumRangeInfo::kContiguous1: - picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr1); - break; + if (TreatEnumAsInt(field)) { + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastV32); + } else { + switch (GetEnumRangeInfo(field, info.aux_idx)) { + case EnumRangeInfo::kNone: + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEv); + break; + case EnumRangeInfo::kContiguous: + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr); + break; + case EnumRangeInfo::kContiguous0: + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr0); + break; + case EnumRangeInfo::kContiguous1: + picked = PROTOBUF_PICK_PACKABLE_FUNCTION(kFastEr1); + break; + } } } break; @@ -255,6 +279,10 @@ bool IsFieldEligibleForFastParsing( return false; } + if (field->is_repeated() && (options.is_enum_8() || options.is_enum_16())) { + return false; + } + if (HasLazyRep(field, options) && !message_options.uses_codegen) { // Can't use TDP on lazy fields if we can't do codegen. return false; @@ -548,24 +576,62 @@ uint16_t MakeTypeCardForField(const FieldDescriptor* field, bool has_hasbit, : fl::kBool; break; case FieldDescriptor::TYPE_ENUM: - if (TreatEnumAsInt(field)) { - // No validation is required. - type_card |= field->is_repeated() && field->is_packed() - ? fl::kPackedOpenEnum - : fl::kOpenEnum; - } else { - int32_t first; - int32_t last; - if (GetEnumValidationRange(field->enum_type(), first, last)) { - // Validation is done by range check (start/length in FieldAux). + if (options.is_enum_8()) { + if (TreatEnumAsInt(field)) { type_card |= field->is_repeated() && field->is_packed() - ? fl::kPackedEnumRange - : fl::kEnumRange; + ? fl::kPackedOpenEnum8 + : fl::kOpenEnum8; } else { - // Validation uses the generated _IsValid function. + int32_t first; + int32_t last; + if (GetEnumValidationRange(field->enum_type(), first, last)) { + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnumRange8 + : fl::kEnumRange8; + } else { + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnum8 + : fl::kEnum8; + } + } + } else if (options.is_enum_16()) { + if (TreatEnumAsInt(field)) { type_card |= field->is_repeated() && field->is_packed() - ? fl::kPackedEnum - : fl::kEnum; + ? fl::kPackedOpenEnum16 + : fl::kOpenEnum16; + } else { + int32_t first; + int32_t last; + if (GetEnumValidationRange(field->enum_type(), first, last)) { + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnumRange16 + : fl::kEnumRange16; + } else { + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnum16 + : fl::kEnum16; + } + } + } else { + if (TreatEnumAsInt(field)) { + // No validation is required. + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedOpenEnum + : fl::kOpenEnum; + } else { + int32_t first; + int32_t last; + if (GetEnumValidationRange(field->enum_type(), first, last)) { + // Validation is done by range check (start/length in FieldAux). + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnumRange + : fl::kEnumRange; + } else { + // Validation uses the generated _IsValid function. + type_card |= field->is_repeated() && field->is_packed() + ? fl::kPackedEnum + : fl::kEnum; + } } } break; diff --git a/src/google/protobuf/generated_message_tctable_gen.h b/src/google/protobuf/generated_message_tctable_gen.h index fd1e0e6f5f..e7e55bd2b2 100644 --- a/src/google/protobuf/generated_message_tctable_gen.h +++ b/src/google/protobuf/generated_message_tctable_gen.h @@ -86,6 +86,11 @@ struct PROTOBUF_EXPORT TailCallTableInfo { using StrOptions = std::variant; StrOptions str_options; + + enum EnumRep { kEnum32 = 0, kEnum8, kEnum16 }; + EnumRep enum_rep = kEnum32; + bool is_enum_8() const { return enum_rep == kEnum8; } + bool is_enum_16() const { return enum_rep == kEnum16; } }; struct FieldEntryInfo; diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h index 34a2a826aa..ea9a4cc50a 100644 --- a/src/google/protobuf/generated_message_tctable_impl.h +++ b/src/google/protobuf/generated_message_tctable_impl.h @@ -127,6 +127,7 @@ enum FieldRep : uint16_t { // Numeric types (used for optional and repeated fields): kRep8Bits = 0, + kRep16Bits = 1 << kRepShift, kRep32Bits = 2 << kRepShift, kRep64Bits = 3 << kRepShift, // String types: @@ -201,6 +202,14 @@ enum FieldType : uint16_t { // Numeric types: kBool = 0 | kFkVarint | kRep8Bits, + kEnum8 = 0 | kFkVarint | kRep8Bits | kFmtEnum | kTvEnum, + kEnumRange8 = 0 | kFkVarint | kRep8Bits | kFmtEnum | kTvRange, + kOpenEnum8 = 0 | kFkVarint | kRep8Bits | kFmtEnum, + + kEnum16 = 0 | kFkVarint | kRep16Bits | kFmtEnum | kTvEnum, + kEnumRange16 = 0 | kFkVarint | kRep16Bits | kFmtEnum | kTvRange, + kOpenEnum16 = 0 | kFkVarint | kRep16Bits | kFmtEnum, + kFixed32 = 0 | kFkFixed | kRep32Bits | kFmtUnsigned, kUInt32 = 0 | kFkVarint | kRep32Bits | kFmtUnsigned, kSFixed32 = 0 | kFkFixed | kRep32Bits | kFmtSigned, @@ -220,6 +229,14 @@ enum FieldType : uint16_t { kPackedBool = 0 | kFkPackedVarint | kRep8Bits, + kPackedEnum8 = 0 | kFkPackedVarint | kRep8Bits | kFmtEnum | kTvEnum, + kPackedEnumRange8 = 0 | kFkPackedVarint | kRep8Bits | kFmtEnum | kTvRange, + kPackedOpenEnum8 = 0 | kFkPackedVarint | kRep8Bits | kFmtEnum, + + kPackedEnum16 = 0 | kFkPackedVarint | kRep16Bits | kFmtEnum | kTvEnum, + kPackedEnumRange16 = 0 | kFkPackedVarint | kRep16Bits | kFmtEnum | kTvRange, + kPackedOpenEnum16 = 0 | kFkPackedVarint | kRep16Bits | kFmtEnum, + kPackedFixed32 = 0 | kFkPackedFixed | kRep32Bits | kFmtUnsigned, kPackedUInt32 = 0 | kFkPackedVarint | kRep32Bits | kFmtUnsigned, kPackedSFixed32 = 0 | kFkPackedFixed | kRep32Bits | kFmtSigned, @@ -251,6 +268,8 @@ enum FieldType : uint16_t { } // namespace field_layout #ifndef NDEBUG +[[noreturn]] PROTOBUF_EXPORT void AlignFail(std::integral_constant, + std::uintptr_t address); [[noreturn]] PROTOBUF_EXPORT void AlignFail(std::integral_constant, std::uintptr_t address); [[noreturn]] PROTOBUF_EXPORT void AlignFail(std::integral_constant, @@ -334,6 +353,7 @@ inline void AlignFail(std::integral_constant, #define PROTOBUF_TC_PARSE_FUNCTION_LIST \ /* These functions have the Fast entry ABI */ \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV8) \ + PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV16) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV32) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastV64) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastZ32) \ @@ -344,6 +364,10 @@ inline void AlignFail(std::integral_constant, PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr0) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr1) \ + PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEv8) \ + PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr8) \ + PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEv16) \ + PROTOBUF_TC_PARSE_FUNCTION_LIST_PACKED(FastEr16) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastB) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_REPEATED(FastU) \ PROTOBUF_TC_PARSE_FUNCTION_LIST_SINGLE(FastBi) \ @@ -481,6 +505,18 @@ class PROTOBUF_EXPORT TcParser final { PROTOBUF_TC_PARAM_DECL); PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV8P2( PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16S1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16S2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16R1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16R2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16P1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV16P2( + PROTOBUF_TC_PARAM_DECL); PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV32S1( PROTOBUF_TC_PARAM_DECL); PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastV32S2( @@ -537,14 +573,17 @@ class PROTOBUF_EXPORT TcParser final { if (sizeof(FieldType) == 1) { return &FastV8S1; } + if (sizeof(FieldType) == 2) { + return &FastV16S1; + } if (sizeof(FieldType) == 4) { return &FastV32S1; } if (sizeof(FieldType) == 8) { return &FastV64S1; } - static_assert(sizeof(FieldType) == 1 || sizeof(FieldType) == 4 || - sizeof(FieldType) == 8, + static_assert(sizeof(FieldType) == 1 || sizeof(FieldType) == 2 || + sizeof(FieldType) == 4 || sizeof(FieldType) == 8, ""); ABSL_LOG(FATAL) << "This should be unreachable"; } @@ -604,6 +643,56 @@ class PROTOBUF_EXPORT TcParser final { PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr1P2( PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8S1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8S2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8R1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8R2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8P1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr8P2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8S1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8S2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8R1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8R2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8P1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv8P2( + PROTOBUF_TC_PARAM_DECL); + + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16S1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16S2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16R1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16R2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16P1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEr16P2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16S1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16S2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16R1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16R2( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16P1( + PROTOBUF_TC_PARAM_DECL); + PROTOBUF_NOINLINE PROTOBUF_CC static const char* FastEv16P2( + PROTOBUF_TC_PARAM_DECL); + // Functions referenced by generated fast tables (string types): // B: bytes U: UTF-8 string // (empty): ArenaStringPtr i: InlinedString c: Cord m: MicroString @@ -972,7 +1061,7 @@ class PROTOBUF_EXPORT TcParser final { PROTOBUF_TC_PARAM_DECL); // Implementations for fast enum field parsing functions: - template + template PROTOBUF_CC static inline const char* SingularEnum(PROTOBUF_TC_PARAM_DECL); template PROTOBUF_CC static inline const char* SingularEnumSmallRange( diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc index c4c9023e91..2ca5d586f0 100644 --- a/src/google/protobuf/generated_message_tctable_lite.cc +++ b/src/google/protobuf/generated_message_tctable_lite.cc @@ -64,6 +64,10 @@ using FieldEntry = TcParseTableBase::FieldEntry; ////////////////////////////////////////////////////////////////////////////// #ifndef NDEBUG +[[noreturn]] void AlignFail(std::integral_constant, + std::uintptr_t address) { + ABSL_LOG(FATAL) << "Unaligned (2) access at " << address; +} [[noreturn]] void AlignFail(std::integral_constant, std::uintptr_t address) { ABSL_LOG(FATAL) << "Unaligned (4) access at " << address; @@ -140,8 +144,14 @@ absl::Status TcParser::VerifyHasBitConsistency(const MessageLite* msg, if (has_bit) break; switch (entry.type_card & fl::kRepMask) { case fl::kRep8Bits: - if (RefAt(base, entry.offset) != - RefAt(default_base, entry.offset)) { + if (RefAt(base, entry.offset) != + RefAt(default_base, entry.offset)) { + return make_error_status(); + } + break; + case fl::kRep16Bits: + if (RefAt(base, entry.offset) != + RefAt(default_base, entry.offset)) { return make_error_status(); } break; @@ -240,6 +250,7 @@ bool TcParser::RepeatedFieldIsEmptySlow(const MessageLite* msg, msg, is_split); return repeated_field.empty(); } + case fl::kRep16Bits: case fl::kRep32Bits: { const auto& repeated_field = GetRepeatedFieldAt>(base, entry.offset, @@ -1243,6 +1254,26 @@ PROTOBUF_NOINLINE const char* TcParser::FastV8P1(PROTOBUF_TC_PARAM_DECL) { PROTOBUF_NOINLINE const char* TcParser::FastV8P2(PROTOBUF_TC_PARAM_DECL) { PROTOBUF_MUSTTAIL return PackedVarint(PROTOBUF_TC_PARAM_PASS); } +PROTOBUF_NOINLINE const char* TcParser::FastV16S1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularVarint( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastV16S2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularVarint( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastV16R1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastV32R1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastV16R2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastV32R2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastV16P1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastV32P1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastV16P2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastV32P2(PROTOBUF_TC_PARAM_PASS); +} PROTOBUF_NOINLINE const char* TcParser::FastV32P1(PROTOBUF_TC_PARAM_DECL) { PROTOBUF_MUSTTAIL return PackedVarint( PROTOBUF_TC_PARAM_PASS); @@ -1313,7 +1344,7 @@ PROTOBUF_NOINLINE const char* TcParser::MpUnknownEnumFallback( PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS); } -template +template PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum( PROTOBUF_TC_PARAM_DECL) { if (ABSL_PREDICT_FALSE(data.coded_tag() != 0)) { @@ -1334,24 +1365,70 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum( PROTOBUF_MUSTTAIL return FastUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS); } SetCachedHasBit(hasbits, data.hasbit_idx()); - RefAt(msg, data.offset()) = tmp; + RefAt(msg, data.offset()) = static_cast(tmp); PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS); } PROTOBUF_NOINLINE const char* TcParser::FastErS1(PROTOBUF_TC_PARAM_DECL) { - PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_MUSTTAIL return SingularEnum( PROTOBUF_TC_PARAM_PASS); } PROTOBUF_NOINLINE const char* TcParser::FastErS2(PROTOBUF_TC_PARAM_DECL) { - PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_MUSTTAIL return SingularEnum( PROTOBUF_TC_PARAM_PASS); } PROTOBUF_NOINLINE const char* TcParser::FastEvS1(PROTOBUF_TC_PARAM_DECL) { - PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_MUSTTAIL return SingularEnum( PROTOBUF_TC_PARAM_PASS); } PROTOBUF_NOINLINE const char* TcParser::FastEvS2(PROTOBUF_TC_PARAM_DECL) { - PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} + +PROTOBUF_NOINLINE const char* TcParser::FastEr8S1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr8S2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8S1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8S2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} + +PROTOBUF_NOINLINE const char* TcParser::FastEr16S1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr16S2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16S1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( + PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16S2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return SingularEnum( PROTOBUF_TC_PARAM_PASS); } @@ -1505,6 +1582,56 @@ PROTOBUF_NOINLINE const char* TcParser::FastEvP2(PROTOBUF_TC_PARAM_DECL) { PROTOBUF_TC_PARAM_PASS); } +PROTOBUF_NOINLINE const char* TcParser::FastEr8R1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErR1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr8R2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErR2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8R1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvR1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8R2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvR2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr8P1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErP1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr8P2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErP2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8P1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvP1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv8P2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvP2(PROTOBUF_TC_PARAM_PASS); +} + +PROTOBUF_NOINLINE const char* TcParser::FastEr16R1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErR1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr16R2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErR2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16R1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvR1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16R2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvR2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr16P1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErP1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEr16P2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastErP2(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16P1(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvP1(PROTOBUF_TC_PARAM_PASS); +} +PROTOBUF_NOINLINE const char* TcParser::FastEv16P2(PROTOBUF_TC_PARAM_DECL) { + PROTOBUF_MUSTTAIL return FastEvP2(PROTOBUF_TC_PARAM_PASS); +} + template PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnumSmallRange( PROTOBUF_TC_PARAM_DECL) { @@ -2263,6 +2390,21 @@ PROTOBUF_NOINLINE const char* TcParser::MpVarint(PROTOBUF_TC_PARAM_DECL) { } else if (is_zigzag) { tmp = WireFormatLite::ZigZagDecode32(static_cast(tmp)); } + } else if (rep == field_layout::kRep16Bits) { + if (is_validated_enum) { + if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) { + ptr = ptr2; + PROTOBUF_MUSTTAIL return MpUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS); + } + } + } else { + ABSL_DCHECK_EQ(rep, static_cast(field_layout::kRep8Bits)); + if (is_validated_enum) { + if (!EnumIsValidAux(tmp, xform_val, *table->field_aux(&entry))) { + ptr = ptr2; + PROTOBUF_MUSTTAIL return MpUnknownEnumFallback(PROTOBUF_TC_PARAM_PASS); + } + } } // Mark the field as present: @@ -2279,9 +2421,15 @@ PROTOBUF_NOINLINE const char* TcParser::MpVarint(PROTOBUF_TC_PARAM_DECL) { RefAt(base, entry.offset) = tmp; } else if (rep == field_layout::kRep32Bits) { RefAt(base, entry.offset) = static_cast(tmp); + } else if (rep == field_layout::kRep16Bits) { + RefAt(base, entry.offset) = static_cast(tmp); } else { ABSL_DCHECK_EQ(rep, static_cast(field_layout::kRep8Bits)); - RefAt(base, entry.offset) = static_cast(tmp); + if ((type_card & field_layout::kFmtMask) == field_layout::kFmtEnum) { + RefAt(base, entry.offset) = static_cast(tmp); + } else { + RefAt(base, entry.offset) = static_cast(tmp); + } } PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_NO_DATA_PASS); @@ -2370,6 +2518,7 @@ PROTOBUF_NOINLINE const char* TcParser::MpRepeatedVarint( is_split, uint64_t, (is_split ? 0 : field_layout::kTvZigZag)>( PROTOBUF_TC_PARAM_PASS); } + case field_layout::kRep16Bits >> field_layout::kRepShift: case field_layout::kRep32Bits >> field_layout::kRepShift: switch (xform_val >> field_layout::kTvShift) { case 0: @@ -2391,8 +2540,21 @@ PROTOBUF_NOINLINE const char* TcParser::MpRepeatedVarint( Unreachable(); } case field_layout::kRep8Bits >> field_layout::kRepShift: - PROTOBUF_MUSTTAIL return MpRepeatedVarintT( - PROTOBUF_TC_PARAM_PASS); + switch (xform_val >> field_layout::kTvShift) { + case 0: + PROTOBUF_MUSTTAIL return MpRepeatedVarintT( + PROTOBUF_TC_PARAM_PASS); + case field_layout::kTvEnum >> field_layout::kTvShift: + PROTOBUF_MUSTTAIL return MpRepeatedVarintT< + is_split, uint32_t, (is_split ? 0 : field_layout::kTvEnum)>( + PROTOBUF_TC_PARAM_PASS); + case field_layout::kTvRange >> field_layout::kTvShift: + PROTOBUF_MUSTTAIL return MpRepeatedVarintT< + is_split, uint32_t, (is_split ? 0 : field_layout::kTvRange)>( + PROTOBUF_TC_PARAM_PASS); + default: + Unreachable(); + } default: Unreachable(); @@ -2470,6 +2632,7 @@ PROTOBUF_NOINLINE const char* TcParser::MpPackedVarint(PROTOBUF_TC_PARAM_DECL) { is_split, uint64_t, (is_split ? 0 : field_layout::kTvZigZag)>( PROTOBUF_TC_PARAM_PASS); } + case field_layout::kRep16Bits >> field_layout::kRepShift: case field_layout::kRep32Bits >> field_layout::kRepShift: switch (xform_val >> field_layout::kTvShift) { case 0: @@ -2491,8 +2654,21 @@ PROTOBUF_NOINLINE const char* TcParser::MpPackedVarint(PROTOBUF_TC_PARAM_DECL) { Unreachable(); } case field_layout::kRep8Bits >> field_layout::kRepShift: - PROTOBUF_MUSTTAIL return MpPackedVarintT( - PROTOBUF_TC_PARAM_PASS); + switch (xform_val >> field_layout::kTvShift) { + case 0: + PROTOBUF_MUSTTAIL return MpPackedVarintT( + PROTOBUF_TC_PARAM_PASS); + case field_layout::kTvEnum >> field_layout::kTvShift: + PROTOBUF_MUSTTAIL return MpPackedVarintT< + is_split, uint32_t, (is_split ? 0 : field_layout::kTvEnum)>( + PROTOBUF_TC_PARAM_PASS); + case field_layout::kTvRange >> field_layout::kTvShift: + PROTOBUF_MUSTTAIL return MpPackedVarintT< + is_split, uint32_t, (is_split ? 0 : field_layout::kTvRange)>( + PROTOBUF_TC_PARAM_PASS); + default: + Unreachable(); + } default: Unreachable(); @@ -3209,6 +3385,12 @@ std::string TypeCardToString(uint16_t type_card) { case fl::kFkPackedFixed: { switch (type_card & ~fl::kFcMask & ~fl::kSplitMask) { PROTOBUF_INTERNAL_TYPE_CARD_CASE(Bool); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(Enum8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(EnumRange8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(OpenEnum8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(Enum16); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(EnumRange16); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(OpenEnum16); PROTOBUF_INTERNAL_TYPE_CARD_CASE(Fixed32); PROTOBUF_INTERNAL_TYPE_CARD_CASE(UInt32); PROTOBUF_INTERNAL_TYPE_CARD_CASE(SFixed32); @@ -3225,6 +3407,12 @@ std::string TypeCardToString(uint16_t type_card) { PROTOBUF_INTERNAL_TYPE_CARD_CASE(SInt64); PROTOBUF_INTERNAL_TYPE_CARD_CASE(Double); PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedBool); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnum8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnumRange8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedOpenEnum8); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnum16); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedEnumRange16); + PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedOpenEnum16); PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedFixed32); PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedUInt32); PROTOBUF_INTERNAL_TYPE_CARD_CASE(PackedSFixed32); diff --git a/src/google/protobuf/generated_message_tctable_lite_test.cc b/src/google/protobuf/generated_message_tctable_lite_test.cc index ad4afdcb25..8b3c37acc4 100644 --- a/src/google/protobuf/generated_message_tctable_lite_test.cc +++ b/src/google/protobuf/generated_message_tctable_lite_test.cc @@ -134,7 +134,7 @@ TEST(FastVarints, NameHere) { }; uint8_t serialize_buffer[64]; - for (int size : {8, 32, 64}) { + for (int size : {8, 16, 32, 64}) { SCOPED_TRACE(size); auto next_i = [](uint64_t i) { // if i + 1 is a power of two, return that. @@ -201,6 +201,9 @@ TEST(FastVarints, NameHere) { case 8: fn = &TcParser::FastV8S1; break; + case 16: + fn = &TcParser::FastV16S1; + break; case 32: fn = &TcParser::FastV32S1; break; @@ -239,6 +242,13 @@ TEST(FastVarints, NameHere) { EXPECT_EQ(actual_field, static_cast(i)) // << " hex: " << absl::StrCat(absl::Hex(actual_field)); }; break; + case 16: { + ASSERT_EQ(end_ptr - ptr, serialized.size()); + + auto actual_field = ReadAndReset(&fake_msg[kFieldOffset]); + EXPECT_EQ(actual_field, static_cast(i)) // + << " hex: " << absl::StrCat(absl::Hex(actual_field)); + }; break; case 32: { ASSERT_TRUE(end_ptr); ASSERT_EQ(end_ptr - ptr, serialized.size()); @@ -1071,6 +1081,179 @@ TEST(TcParserTest, OobGenReproduction) { (void)msg->ParseFromString(payload); } +TEST(TcParserTest, Enum8And16TypeCardToString) { + namespace fl = internal::field_layout; + EXPECT_EQ( + TypeCardToString(static_cast(fl::kFcOptional) | fl::kEnum8), + "::_fl::kFcOptional | ::_fl::kEnum8"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcOptional) | + fl::kEnumRange8), + "::_fl::kFcOptional | ::_fl::kEnumRange8"); + EXPECT_EQ( + TypeCardToString(static_cast(fl::kFcOptional) | fl::kOpenEnum8), + "::_fl::kFcOptional | ::_fl::kOpenEnum8"); + EXPECT_EQ( + TypeCardToString(static_cast(fl::kFcOptional) | fl::kEnum16), + "::_fl::kFcOptional | ::_fl::kEnum16"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcOptional) | + fl::kEnumRange16), + "::_fl::kFcOptional | ::_fl::kEnumRange16"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcOptional) | + fl::kOpenEnum16), + "::_fl::kFcOptional | ::_fl::kOpenEnum16"); + + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedEnum8), + "::_fl::kFcRepeated | ::_fl::kPackedEnum8"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedEnumRange8), + "::_fl::kFcRepeated | ::_fl::kPackedEnumRange8"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedOpenEnum8), + "::_fl::kFcRepeated | ::_fl::kPackedOpenEnum8"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedEnum16), + "::_fl::kFcRepeated | ::_fl::kPackedEnum16"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedEnumRange16), + "::_fl::kFcRepeated | ::_fl::kPackedEnumRange16"); + EXPECT_EQ(TypeCardToString(static_cast(fl::kFcRepeated) | + fl::kPackedOpenEnum16), + "::_fl::kFcRepeated | ::_fl::kPackedOpenEnum16"); +} + +TEST(TcParserTest, FastEnum8And16Parsing) { + constexpr uint8_t kHasBitsOffset = 4; + constexpr uint8_t kHasBitIndex = 0; + constexpr uint8_t kFieldOffset = 24; + + const ClassData class_data(nullptr, nullptr, MessageCreator(), nullptr, + nullptr, nullptr, nullptr, + /*cached_size_offset=*/16, "type_name"); + + alignas(16) char fake_msg[64]; + memset(fake_msg, kDND, sizeof(fake_msg)); + memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); + + TcParseTable<1, 1, 2, 0, 2> parse_table = { + // header: + { + kHasBitsOffset, + 0, + 1, + 0, + offsetof(decltype(parse_table), field_lookup_table), + 0xFFFFFFFF - 1, + offsetof(decltype(parse_table), field_entries), + 1, + 2, + offsetof(decltype(parse_table), aux_entries), + &class_data, + nullptr, + &FastParserGaveUp, + }, + // Fast entries: + {{ + {}, + }}, + // Field Lookup Table: + {{65535, 65535}}, + // Field Entries: + {{ + {kFieldOffset, kHasBitsOffset + 0, 0, field_layout::kEnum8}, + }}, + // Aux Entries: + {{ + {0, 10}, // range 0..10 + {FieldAuxEnumData{}, + EnumTraits::validation_data()}, + }}, + }; + + uint8_t serialize_buffer[64]; + auto serialize_ptr = WireFormatLite::WriteUInt32ToArray( + /* field_number= */ 1, 5, serialize_buffer); + absl::string_view serialized{ + reinterpret_cast(&serialize_buffer[0]), + static_cast(serialize_ptr - serialize_buffer)}; + + // Test FastEr8S1 (range validation for 8-bit enum) + { + memset(fake_msg, kDND, sizeof(fake_msg)); + memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); + const char* ptr = nullptr; + ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(), + /* aliasing= */ false, &ptr, serialized); + TcFieldData data(/*coded_tag=*/8, kHasBitIndex, /*aux_idx=*/0, + kFieldOffset); + const char* end_ptr = TcParser::FastEr8S1( + reinterpret_cast(fake_msg), ptr, &ctx, + Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0); + ASSERT_EQ(end_ptr - ptr, serialized.size()); + auto actual_field = ReadAndReset(&fake_msg[kFieldOffset]); + EXPECT_EQ(actual_field, 5); + auto hasbits = ReadAndReset(&fake_msg[kHasBitsOffset]); + EXPECT_EQ(hasbits, 1 << kHasBitIndex); + } + + // Test FastEv8S1 (function validation for 8-bit enum) + { + memset(fake_msg, kDND, sizeof(fake_msg)); + memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); + const char* ptr = nullptr; + ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(), + /* aliasing= */ false, &ptr, serialized); + TcFieldData data(/*coded_tag=*/8, kHasBitIndex, /*aux_idx=*/1, + kFieldOffset); + const char* end_ptr = TcParser::FastEv8S1( + reinterpret_cast(fake_msg), ptr, &ctx, + Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0); + ASSERT_EQ(end_ptr - ptr, serialized.size()); + auto actual_field = ReadAndReset(&fake_msg[kFieldOffset]); + EXPECT_EQ(actual_field, 5); + auto hasbits = ReadAndReset(&fake_msg[kHasBitsOffset]); + EXPECT_EQ(hasbits, 1 << kHasBitIndex); + } + + // Test FastEr16S1 (range validation for 16-bit enum) + { + memset(fake_msg, kDND, sizeof(fake_msg)); + memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); + const char* ptr = nullptr; + ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(), + /* aliasing= */ false, &ptr, serialized); + TcFieldData data(/*coded_tag=*/8, kHasBitIndex, /*aux_idx=*/0, + kFieldOffset); + const char* end_ptr = TcParser::FastEr16S1( + reinterpret_cast(fake_msg), ptr, &ctx, + Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0); + ASSERT_EQ(end_ptr - ptr, serialized.size()); + auto actual_field = ReadAndReset(&fake_msg[kFieldOffset]); + EXPECT_EQ(actual_field, 5); + auto hasbits = ReadAndReset(&fake_msg[kHasBitsOffset]); + EXPECT_EQ(hasbits, 1 << kHasBitIndex); + } + + // Test FastEv16S1 (function validation for 16-bit enum) + { + memset(fake_msg, kDND, sizeof(fake_msg)); + memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); + const char* ptr = nullptr; + ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(), + /* aliasing= */ false, &ptr, serialized); + TcFieldData data(/*coded_tag=*/8, kHasBitIndex, /*aux_idx=*/1, + kFieldOffset); + const char* end_ptr = TcParser::FastEv16S1( + reinterpret_cast(fake_msg), ptr, &ctx, + Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0); + ASSERT_EQ(end_ptr - ptr, serialized.size()); + auto actual_field = ReadAndReset(&fake_msg[kFieldOffset]); + EXPECT_EQ(actual_field, 5); + auto hasbits = ReadAndReset(&fake_msg[kHasBitsOffset]); + EXPECT_EQ(hasbits, 1 << kHasBitIndex); + } +} + } // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index 63b8396369..4de91e9bef 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -1901,6 +1901,11 @@ void Reflection::VerifyFieldType(const FieldDescriptor* field) const { << error(); } else { auto cpp_type = field->cpp_type(); + if (cpp_type == field->CPPTYPE_ENUM && + (std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v)) { + return; + } // Collapse ENUM to INT32 because they are the same through reflection. if (cpp_type == field->CPPTYPE_ENUM) cpp_type = field->CPPTYPE_INT32; ABSL_DCHECK_EQ(+cpp_type, +internal::GetCppType()) << error();