Skip to content

Commit

Permalink
Support C++ protobuf ctype=CORD for bytes field (generated code).
Browse files Browse the repository at this point in the history
Reflection::SetString() can take CORD as input.

PiperOrigin-RevId: 518313997
  • Loading branch information
anandolee authored and copybara-github committed Mar 21, 2023
1 parent 4b5652b commit 714f975
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 18 deletions.
11 changes: 10 additions & 1 deletion src/google/protobuf/compiler/cpp/field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,16 @@ std::unique_ptr<FieldGeneratorBase> MakeGenerator(const FieldDescriptor* field,
case FieldDescriptor::CPPTYPE_MESSAGE:
return MakeSinguarMessageGenerator(field, options, scc);
case FieldDescriptor::CPPTYPE_STRING:
return MakeSinguarStringGenerator(field, options, scc);
if (field->type() == FieldDescriptor::TYPE_BYTES &&
field->options().ctype() == FieldOptions::CORD) {
if (field->real_containing_oneof()) {
return MakeOneofCordGenerator(field, options, scc);
} else {
return MakeSingularCordGenerator(field, options, scc);
}
} else {
return MakeSinguarStringGenerator(field, options, scc);
}
case FieldDescriptor::CPPTYPE_ENUM:
return MakeSinguarEnumGenerator(field, options, scc);
default:
Expand Down
15 changes: 7 additions & 8 deletions src/google/protobuf/compiler/cpp/field_generators/cord_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,9 @@ void CordFieldGenerator::GenerateAccessorDeclarations(
"$deprecated_attr$void ${1$set_$name$$}$(const ::absl::Cord& value);\n"
"$deprecated_attr$void ${1$set_$name$$}$(::absl::string_view value);\n",
std::make_tuple(descriptor_, GeneratedCodeInfo::Annotation::SET));
format("private:\n");
format(
"private:\n"
"const ::absl::Cord& ${1$_internal_$name$$}$() const;\n"
"void ${1$_internal_set_$name$$}$(const ::absl::Cord& value);\n"
"::absl::Cord* ${1$_internal_mutable_$name$$}$();\n"
"public:\n",
Expand Down Expand Up @@ -183,13 +184,17 @@ void CordFieldGenerator::GenerateInlineAccessorDefinitions(
" $field$ = value;\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_string_piece:$full_name$)\n"
"}\n"
"inline ::absl::Cord* $classname$::_internal_mutable_$name$() {\n"
" $set_hasbit$\n"
" return &$field$;\n"
"}\n");
}

void CordFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (descriptor_->default_value_string().empty()) {
format("$field$.clear();\n");
format("$field$.Clear();\n");
} else {
format("$field$ = ::absl::string_view($default$, $default_length$);\n");
}
Expand Down Expand Up @@ -358,12 +363,6 @@ void CordOneofFieldGenerator::GenerateInlineAccessorDefinitions(
" }\n"
" }\n"
" return $field$;\n"
"}\n"
"inline ::absl::Cord* $classname$::mutable_$name$() {\n"
" ::absl::Cord* _cord = _internal_mutable_$name$();\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return _cord;\n"
"}\n");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ std::unique_ptr<FieldGeneratorBase> MakeOneofMessageGenerator(
std::unique_ptr<FieldGeneratorBase> MakeMapGenerator(
const FieldDescriptor* desc, const Options& options,
MessageSCCAnalyzer* scc);

std::unique_ptr<FieldGeneratorBase> MakeSingularCordGenerator(
const FieldDescriptor* desc, const Options& options,
MessageSCCAnalyzer* scc);

std::unique_ptr<FieldGeneratorBase> MakeOneofCordGenerator(
const FieldDescriptor* desc, const Options& options,
MessageSCCAnalyzer* scc);

} // namespace cpp
} // namespace compiler
} // namespace protobuf
Expand Down
10 changes: 5 additions & 5 deletions src/google/protobuf/compiler/cpp/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ void FileGenerator::GenerateSourceIncludes(io::Printer* p) {

if (HasCordFields(file_, options_)) {
p->Emit(R"(
#include "third_party/absl/strings/internal/string_constant.h"
#include "absl/strings/internal/string_constant.h"
)");
}

Expand Down Expand Up @@ -1322,11 +1322,11 @@ void FileGenerator::GenerateLibraryIncludes(io::Printer* p) {
if (HasStringPieceFields(file_, options_)) {
IncludeFile("third_party/protobuf/string_piece_field_support.h", p);
}
if (HasCordFields(file_, options_)) {
p->Emit(R"(
#include "third_party/absl/strings/cord.h"
}
if (HasCordFields(file_, options_)) {
p->Emit(R"(
#include "absl/strings/cord.h"
)");
}
}
if (HasMapFields(file_)) {
IncludeFileAndExport("third_party/protobuf/map.h", p);
Expand Down
7 changes: 6 additions & 1 deletion src/google/protobuf/compiler/cpp/helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1122,7 +1122,12 @@ FieldOptions::CType EffectiveStringCType(const FieldDescriptor* field,
const Options& options) {
ABSL_DCHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_STRING);
if (options.opensource_runtime) {
// Open-source protobuf release only supports STRING ctype.
// Open-source protobuf release only supports STRING ctype and CORD for
// sinuglar bytes.
if (field->type() == FieldDescriptor::TYPE_BYTES && !field->is_repeated() &&
field->options().ctype() == FieldOptions::CORD) {
return FieldOptions::CORD;
}
return FieldOptions::STRING;
} else {
// Google-internal supports all ctypes.
Expand Down
13 changes: 10 additions & 3 deletions src/google/protobuf/compiler/cpp/parse_function_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -951,10 +951,17 @@ void ParseFunctionGenerator::GenerateStrings(Formatter& format,
const FieldDescriptor* field,
bool check_utf8) {
FieldOptions::CType ctype = FieldOptions::STRING;
if (!options_.opensource_runtime) {
// Open source doesn't support other ctypes;
ctype = field->options().ctype();

if (!field->is_repeated() && field->type() == FieldDescriptor::TYPE_BYTES &&
field->options().ctype() == FieldOptions::CORD) {
ctype = FieldOptions::CORD;
} else {
if (!options_.opensource_runtime) {
// Open source doesn't support other ctypes;
ctype = field->options().ctype();
}
}

if (!field->is_repeated() && !options_.opensource_runtime &&
GetOptimizeFor(field->file(), options_) != FileOptions::LITE_RUNTIME &&
// For now only use arena string for strings with empty defaults.
Expand Down
50 changes: 50 additions & 0 deletions src/google/protobuf/generated_message_reflection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1797,6 +1797,56 @@ void Reflection::SetString(Message* message, const FieldDescriptor* field,
}
}

void Reflection::SetString(Message* message, const FieldDescriptor* field,
const absl::Cord& value) const {
USAGE_CHECK_ALL(SetString, SINGULAR, STRING);
if (field->is_extension()) {
return absl::CopyCordToString(value,
MutableExtensionSet(message)->MutableString(
field->number(), field->type(), field));
} else {
switch (field->options().ctype()) {
case FieldOptions::CORD:
if (schema_.InRealOneof(field)) {
if (!HasOneofField(*message, field)) {
ClearOneof(message, field->containing_oneof());
*MutableField<absl::Cord*>(message, field) =
Arena::Create<absl::Cord>(message->GetArenaForAllocation());
}
*(*MutableField<absl::Cord*>(message, field)) = value;
} else {
*MutableField<absl::Cord>(message, field) = value;
}
break;
default:
case FieldOptions::STRING: {
// Oneof string fields are never set as a default instance.
// We just need to pass some arbitrary default string to make it work.
// This allows us to not have the real default accessible from
// reflection.
if (schema_.InRealOneof(field) && !HasOneofField(*message, field)) {
ClearOneof(message, field->containing_oneof());
MutableField<ArenaStringPtr>(message, field)->InitDefault();
}
if (IsInlined(field)) {
auto* str = MutableField<InlinedStringField>(message, field);
const uint32_t index = schema_.InlinedStringIndex(field);
ABSL_DCHECK_GT(index, 0);
uint32_t* states =
&MutableInlinedStringDonatedArray(message)[index / 32];
uint32_t mask = ~(static_cast<uint32_t>(1) << (index % 32));
str->Set(std::string(value), message->GetArenaForAllocation(),
IsInlinedStringDonated(*message, field), states, mask,
message);
} else {
auto* str = MutableField<ArenaStringPtr>(message, field);
str->Set(std::string(value), message->GetArenaForAllocation());
}
break;
}
}
}
}

std::string Reflection::GetRepeatedString(const Message& message,
const FieldDescriptor* field,
Expand Down
2 changes: 2 additions & 0 deletions src/google/protobuf/generated_message_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
fixed_address_empty_string{}; // NOLINT


PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT const EmptyCord empty_cord_;

PROTOBUF_CONSTINIT std::atomic<bool> init_protobuf_defaults_state{false};
static bool InitProtobufDefaultsImpl() {
fixed_address_empty_string.DefaultConstruct();
Expand Down
12 changes: 12 additions & 0 deletions src/google/protobuf/generated_message_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ PROTOBUF_EXPORT inline const std::string& GetEmptyString() {
return GetEmptyStringAlreadyInited();
}

// Default empty Cord object. Don't use directly. Instead, call
// GetEmptyCordAlreadyInited() to get the reference.
union EmptyCord {
constexpr EmptyCord() : value() {}
~EmptyCord() {}
::absl::Cord value;
};
PROTOBUF_EXPORT extern const EmptyCord empty_cord_;

constexpr const ::absl::Cord& GetEmptyCordAlreadyInited() {
return empty_cord_.value;
}

// True if IsInitialized() is true for all elements of t. Type is expected
// to be a RepeatedPtrField<some message type>. It's useful to have this
Expand Down
5 changes: 5 additions & 0 deletions src/google/protobuf/message.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,11 @@ class PROTOBUF_EXPORT Reflection final {
bool value) const;
void SetString(Message* message, const FieldDescriptor* field,
std::string value) const;
// Set a string field to a Cord value. If the underlying field is
// represented using a Cord already, this involves no copies (just
// reference counting). Otherwise, a copy must be made.
void SetString(Message* message, const FieldDescriptor* field,
const absl::Cord& value) const;
void SetEnum(Message* message, const FieldDescriptor* field,
const EnumValueDescriptor* value) const;
// Set an enum field's value with an integer rather than EnumValueDescriptor.
Expand Down
8 changes: 8 additions & 0 deletions src/google/protobuf/parse_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,14 @@ inline bool VerifyUTF8(const std::string* s, const char* field_name) {
PROTOBUF_NODISCARD PROTOBUF_EXPORT const char* InlineGreedyStringParser(
std::string* s, const char* ptr, ParseContext* ctx);

PROTOBUF_NODISCARD inline const char* InlineCordParser(::absl::Cord* cord,
const char* ptr,
ParseContext* ctx) {
int size = ReadSize(&ptr);
if (!ptr) return nullptr;
return ctx->ReadCord(ptr, size, cord);
}


template <typename T>
PROTOBUF_NODISCARD const char* FieldParser(uint64_t tag, T& field_parser,
Expand Down
6 changes: 6 additions & 0 deletions src/google/protobuf/unittest.proto
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ message TestOneof {
optional int32 a = 5;
optional string b = 6;
}
bytes foo_bytes_cord = 7 [ctype=CORD];
}
}

Expand Down Expand Up @@ -1661,3 +1662,8 @@ message BadFieldNames{
message RedactedFields{
optional string optional_redacted_string = 1 [debug_redact = true];
}

message TestCord{
optional bytes optional_bytes_cord = 1 [ctype=CORD];
optional bytes optional_bytes_cord_default = 2 [ctype=CORD, default = "hello"];
}
7 changes: 7 additions & 0 deletions src/google/protobuf/wire_format.cc
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,13 @@ bool WireFormat::ParseAndMergeField(
}

case FieldDescriptor::TYPE_BYTES: {
if (field->options().ctype() == FieldOptions::CORD &&
!field->is_repeated()) {
absl::Cord value;
if (!WireFormatLite::ReadBytes(input, &value)) return false;
message_reflection->SetString(message, field, value);
break;
}
std::string value;
if (!WireFormatLite::ReadBytes(input, &value)) return false;
if (field->is_repeated()) {
Expand Down
19 changes: 19 additions & 0 deletions src/google/protobuf/wire_format_lite.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ class PROTOBUF_EXPORT WireFormatLite {
static bool ReadBytes(io::CodedInputStream* input, std::string* value);
static bool ReadBytes(io::CodedInputStream* input, std::string** p);

static inline bool ReadBytes(io::CodedInputStream* input, absl::Cord* value);
static inline bool ReadBytes(io::CodedInputStream* input, absl::Cord** p);

enum Operation {
PARSE = 0,
SERIALIZE = 1,
Expand Down Expand Up @@ -710,6 +713,7 @@ class PROTOBUF_EXPORT WireFormatLite {

static inline size_t StringSize(const std::string& value);
static inline size_t BytesSize(const std::string& value);
static inline size_t BytesSize(const absl::Cord& value);

template <typename MessageType>
static inline size_t GroupSize(const MessageType& value);
Expand Down Expand Up @@ -1257,6 +1261,17 @@ bool WireFormatLite::ReadPackedPrimitiveNoInline(io::CodedInputStream* input,
return ReadPackedPrimitive<CType, DeclaredType>(input, values);
}

inline bool WireFormatLite::ReadBytes(io::CodedInputStream* input,
absl::Cord* value) {
int length;
return input->ReadVarintSizeAsInt(&length) && input->ReadCord(value, length);
}

inline bool WireFormatLite::ReadBytes(io::CodedInputStream* input,
absl::Cord** p) {
return ReadBytes(input, *p);
}


template <typename MessageType>
inline bool WireFormatLite::ReadGroup(int field_number,
Expand Down Expand Up @@ -1788,6 +1803,10 @@ inline size_t WireFormatLite::BytesSize(const std::string& value) {
return LengthDelimitedSize(value.size());
}

inline size_t WireFormatLite::BytesSize(const absl::Cord& value) {
return LengthDelimitedSize(value.size());
}


template <typename MessageType>
inline size_t WireFormatLite::GroupSize(const MessageType& value) {
Expand Down

0 comments on commit 714f975

Please sign in to comment.