Add 8-bit and 16-bit enum support to TcTable parsing and Reflection

This change introduces Protobuf C++ runtime primitives for 8-bit and 16-bit enum fields:
- Added kRep16Bits and 8-bit/16-bit enum FieldType definitions to generated_message_tctable_impl.h.
- Implemented fast-path and miniparse routines for 8-bit and 16-bit enums in generated_message_tctable_lite.cc without memory overwrites.
- Added 8-bit and 16-bit offset tagging support in generated_message_reflection.h and updated GetEnumValue/SetEnumValueInternal in generated_message_reflection.cc.
- Added comprehensive unit tests in generated_message_tctable_lite_test.cc and generated_message_reflection_unittest.cc.

PiperOrigin-RevId: 969068446
This commit is contained in:
Protobuf Team Bot 2026-08-22 11:21:29 -07:00 committed by Copybara-Service
parent a55c302e3f
commit 65b6764369
9 changed files with 749 additions and 53 deletions

View file

@ -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<uint8_t>(lhs, field),
*r->MutableRaw<uint8_t>(rhs, field));
} else if (r->schema_.IsEnum16(field)) {
std::swap(*r->MutableRaw<uint16_t>(lhs, field),
*r->MutableRaw<uint16_t>(rhs, field));
} else {
std::swap(*r->MutableRaw<int>(lhs, field),
*r->MutableRaw<int>(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<int>(message, field) =
field->default_value_enum()->number();
if (schema_.IsEnum8(field)) {
if (schema_.IsEnumSigned(field)) {
*MutableRaw<int8_t>(message, field) =
static_cast<int8_t>(field->default_value_enum()->number());
} else {
*MutableRaw<uint8_t>(message, field) =
static_cast<uint8_t>(field->default_value_enum()->number());
}
} else if (schema_.IsEnum16(field)) {
if (schema_.IsEnumSigned(field)) {
*MutableRaw<int16_t>(message, field) =
static_cast<int16_t>(field->default_value_enum()->number());
} else {
*MutableRaw<uint16_t>(message, field) =
static_cast<uint16_t>(field->default_value_enum()->number());
}
} else {
*MutableRaw<int>(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<int8_t>(message, field);
} else {
value = GetField<uint8_t>(message, field);
}
} else if (schema_.IsEnum16(field)) {
if (schema_.IsEnumSigned(field)) {
value = GetField<int16_t>(message, field);
} else {
value = GetField<uint16_t>(message, field);
}
} else {
value = GetField<int>(message, field);
}
@ -2468,6 +2509,18 @@ void Reflection::SetEnumValueInternal(Message* message,
if (field->is_extension()) {
MutableExtensionSet(message)->Set<int>(message->GetArena(), field->number(),
field->type(), value, field);
} else if (schema_.IsEnum8(field)) {
if (schema_.IsEnumSigned(field)) {
SetField<int8_t>(message, field, static_cast<int8_t>(value));
} else {
SetField<uint8_t>(message, field, static_cast<uint8_t>(value));
}
} else if (schema_.IsEnum16(field)) {
if (schema_.IsEnumSigned(field)) {
SetField<int16_t>(message, field, static_cast<int16_t>(value));
} else {
SetField<uint16_t>(message, field, static_cast<uint16_t>(value));
}
} else {
SetField<int>(message, field, value);
}
@ -3245,6 +3298,11 @@ bool Reflection::IsImplicitPresenceFieldNonEmpty(
"Code assumes uint64_t and double are the same size.");
return absl::bit_cast<uint64_t>(GetRaw<double>(message, field)) != 0;
case FieldDescriptor::CPPTYPE_ENUM:
if (schema_.IsEnum8(field)) {
return GetRaw<uint8_t>(message, field) != 0;
} else if (schema_.IsEnum16(field)) {
return GetRaw<uint16_t>(message, field) != 0;
}
return GetRaw<int>(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<int>(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) {

View file

@ -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<uint32_t>(oneof_case_offset_) +
static_cast<uint32_t>(
@ -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_;

View file

@ -22,6 +22,8 @@
#include "google/protobuf/generated_message_reflection.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
@ -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<uint32_t> offsets(desc->field_count(), 0);
std::vector<uint32_t> 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

View file

@ -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;

View file

@ -86,6 +86,11 @@ struct PROTOBUF_EXPORT TailCallTableInfo {
using StrOptions = std::variant<std::monostate, StringInlined, MicroString>;
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;

View file

@ -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<size_t, 2>,
std::uintptr_t address);
[[noreturn]] PROTOBUF_EXPORT void AlignFail(std::integral_constant<size_t, 4>,
std::uintptr_t address);
[[noreturn]] PROTOBUF_EXPORT void AlignFail(std::integral_constant<size_t, 8>,
@ -334,6 +353,7 @@ inline void AlignFail(std::integral_constant<size_t, 1>,
#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<size_t, 1>,
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 <typename TagType, uint16_t xform_val>
template <typename FieldType, typename TagType, uint16_t xform_val>
PROTOBUF_CC static inline const char* SingularEnum(PROTOBUF_TC_PARAM_DECL);
template <typename TagType, uint8_t min>
PROTOBUF_CC static inline const char* SingularEnumSmallRange(

View file

@ -64,6 +64,10 @@ using FieldEntry = TcParseTableBase::FieldEntry;
//////////////////////////////////////////////////////////////////////////////
#ifndef NDEBUG
[[noreturn]] void AlignFail(std::integral_constant<size_t, 2>,
std::uintptr_t address) {
ABSL_LOG(FATAL) << "Unaligned (2) access at " << address;
}
[[noreturn]] void AlignFail(std::integral_constant<size_t, 4>,
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<bool>(base, entry.offset) !=
RefAt<bool>(default_base, entry.offset)) {
if (RefAt<uint8_t>(base, entry.offset) !=
RefAt<uint8_t>(default_base, entry.offset)) {
return make_error_status();
}
break;
case fl::kRep16Bits:
if (RefAt<uint16_t>(base, entry.offset) !=
RefAt<uint16_t>(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<RepeatedField<uint32_t>>(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<bool, uint16_t>(PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastV16S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularVarint<uint16_t, uint8_t>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastV16S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularVarint<uint16_t, uint16_t>(
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<uint32_t, uint8_t>(
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 <typename TagType, uint16_t xform_val>
template <typename FieldType, typename TagType, uint16_t xform_val>
PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularEnum(
PROTOBUF_TC_PARAM_DECL) {
if (ABSL_PREDICT_FALSE(data.coded_tag<TagType>() != 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<int32_t>(msg, data.offset()) = tmp;
RefAt<FieldType>(msg, data.offset()) = static_cast<FieldType>(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<uint8_t, field_layout::kTvRange>(
PROTOBUF_MUSTTAIL return SingularEnum<int32_t, uint8_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastErS2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvRange>(
PROTOBUF_MUSTTAIL return SingularEnum<int32_t, uint16_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEvS1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, field_layout::kTvEnum>(
PROTOBUF_MUSTTAIL return SingularEnum<int32_t, uint8_t,
field_layout::kTvEnum>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEvS2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, field_layout::kTvEnum>(
PROTOBUF_MUSTTAIL return SingularEnum<int32_t, uint16_t,
field_layout::kTvEnum>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEr8S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, uint8_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEr8S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, uint16_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEv8S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, uint8_t,
field_layout::kTvEnum>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEv8S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint8_t, uint16_t,
field_layout::kTvEnum>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEr16S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, uint8_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEr16S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, uint16_t,
field_layout::kTvRange>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEv16S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, uint8_t,
field_layout::kTvEnum>(
PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_NOINLINE const char* TcParser::FastEv16S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularEnum<uint16_t, uint16_t,
field_layout::kTvEnum>(
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 <typename TagType, uint8_t min>
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<uint32_t>(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<uint16_t>(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<uint64_t>(base, entry.offset) = tmp;
} else if (rep == field_layout::kRep32Bits) {
RefAt<uint32_t>(base, entry.offset) = static_cast<uint32_t>(tmp);
} else if (rep == field_layout::kRep16Bits) {
RefAt<uint16_t>(base, entry.offset) = static_cast<uint16_t>(tmp);
} else {
ABSL_DCHECK_EQ(rep, static_cast<uint16_t>(field_layout::kRep8Bits));
RefAt<bool>(base, entry.offset) = static_cast<bool>(tmp);
if ((type_card & field_layout::kFmtMask) == field_layout::kFmtEnum) {
RefAt<uint8_t>(base, entry.offset) = static_cast<uint8_t>(tmp);
} else {
RefAt<bool>(base, entry.offset) = static_cast<bool>(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<is_split, bool, 0>(
PROTOBUF_TC_PARAM_PASS);
switch (xform_val >> field_layout::kTvShift) {
case 0:
PROTOBUF_MUSTTAIL return MpRepeatedVarintT<is_split, bool, 0>(
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<is_split, bool, 0>(
PROTOBUF_TC_PARAM_PASS);
switch (xform_val >> field_layout::kTvShift) {
case 0:
PROTOBUF_MUSTTAIL return MpPackedVarintT<is_split, bool, 0>(
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);

View file

@ -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<decltype(actual_field)>(i)) //
<< " hex: " << absl::StrCat(absl::Hex(actual_field));
}; break;
case 16: {
ASSERT_EQ(end_ptr - ptr, serialized.size());
auto actual_field = ReadAndReset<uint16_t>(&fake_msg[kFieldOffset]);
EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(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<uint16_t>(fl::kFcOptional) | fl::kEnum8),
"::_fl::kFcOptional | ::_fl::kEnum8");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcOptional) |
fl::kEnumRange8),
"::_fl::kFcOptional | ::_fl::kEnumRange8");
EXPECT_EQ(
TypeCardToString(static_cast<uint16_t>(fl::kFcOptional) | fl::kOpenEnum8),
"::_fl::kFcOptional | ::_fl::kOpenEnum8");
EXPECT_EQ(
TypeCardToString(static_cast<uint16_t>(fl::kFcOptional) | fl::kEnum16),
"::_fl::kFcOptional | ::_fl::kEnum16");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcOptional) |
fl::kEnumRange16),
"::_fl::kFcOptional | ::_fl::kEnumRange16");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcOptional) |
fl::kOpenEnum16),
"::_fl::kFcOptional | ::_fl::kOpenEnum16");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcRepeated) |
fl::kPackedEnum8),
"::_fl::kFcRepeated | ::_fl::kPackedEnum8");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcRepeated) |
fl::kPackedEnumRange8),
"::_fl::kFcRepeated | ::_fl::kPackedEnumRange8");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcRepeated) |
fl::kPackedOpenEnum8),
"::_fl::kFcRepeated | ::_fl::kPackedOpenEnum8");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcRepeated) |
fl::kPackedEnum16),
"::_fl::kFcRepeated | ::_fl::kPackedEnum16");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(fl::kFcRepeated) |
fl::kPackedEnumRange16),
"::_fl::kFcRepeated | ::_fl::kPackedEnumRange16");
EXPECT_EQ(TypeCardToString(static_cast<uint16_t>(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<proto2_unittest::ForeignEnum>::validation_data()},
}},
};
uint8_t serialize_buffer[64];
auto serialize_ptr = WireFormatLite::WriteUInt32ToArray(
/* field_number= */ 1, 5, serialize_buffer);
absl::string_view serialized{
reinterpret_cast<char*>(&serialize_buffer[0]),
static_cast<size_t>(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<MessageLite*>(fake_msg), ptr, &ctx,
Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0);
ASSERT_EQ(end_ptr - ptr, serialized.size());
auto actual_field = ReadAndReset<uint8_t>(&fake_msg[kFieldOffset]);
EXPECT_EQ(actual_field, 5);
auto hasbits = ReadAndReset<uint32_t>(&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<MessageLite*>(fake_msg), ptr, &ctx,
Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0);
ASSERT_EQ(end_ptr - ptr, serialized.size());
auto actual_field = ReadAndReset<uint8_t>(&fake_msg[kFieldOffset]);
EXPECT_EQ(actual_field, 5);
auto hasbits = ReadAndReset<uint32_t>(&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<MessageLite*>(fake_msg), ptr, &ctx,
Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0);
ASSERT_EQ(end_ptr - ptr, serialized.size());
auto actual_field = ReadAndReset<uint16_t>(&fake_msg[kFieldOffset]);
EXPECT_EQ(actual_field, 5);
auto hasbits = ReadAndReset<uint32_t>(&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<MessageLite*>(fake_msg), ptr, &ctx,
Xor2SerializedBytes(data, ptr), &parse_table.header, /*hasbits=*/0);
ASSERT_EQ(end_ptr - ptr, serialized.size());
auto actual_field = ReadAndReset<uint16_t>(&fake_msg[kFieldOffset]);
EXPECT_EQ(actual_field, 5);
auto hasbits = ReadAndReset<uint32_t>(&fake_msg[kHasBitsOffset]);
EXPECT_EQ(hasbits, 1 << kHasBitIndex);
}
}
} // namespace internal
} // namespace protobuf
} // namespace google

View file

@ -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<T, uint8_t> || std::is_same_v<T, int8_t> ||
std::is_same_v<T, uint16_t> || std::is_same_v<T, int16_t>)) {
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<T>()) << error();