Skip to content

Commit

Permalink
Prepare UnknownFieldSet for replace std::string with `absl::string_…
Browse files Browse the repository at this point in the history
…view`:

 - Add overloads that take `absl::Cord` and `std::string&&` as inputs, putting the burden in the implementation instead of users.
 - Add overload that returns `absl::Span<char>` for callers that need higher performance requirements where we can avoid copies altogether.
 - Hide the APIs that return `std::string*` when the breaking change is enabled (via `-D PROTOBUF_TEMPORARY_ENABLE_STRING_VIEW_RETURN_TYPE`).

PiperOrigin-RevId: 655600399
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Jul 24, 2024
1 parent 165d2c7 commit 75b66c2
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/google/protobuf/arena_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ TEST(ArenaTest, UnknownFields) {
arena_message_3->mutable_unknown_fields()->AddVarint(1000, 42);
arena_message_3->mutable_unknown_fields()->AddFixed32(1001, 42);
arena_message_3->mutable_unknown_fields()->AddFixed64(1002, 42);
arena_message_3->mutable_unknown_fields()->AddLengthDelimited(1003);
arena_message_3->mutable_unknown_fields()->AddLengthDelimited(1003, "");
arena_message_3->mutable_unknown_fields()->DeleteSubrange(0, 2);
arena_message_3->mutable_unknown_fields()->DeleteByNumber(1002);
arena_message_3->mutable_unknown_fields()->DeleteByNumber(1003);
Expand Down
7 changes: 4 additions & 3 deletions src/google/protobuf/descriptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8921,11 +8921,12 @@ bool DescriptorBuilder::OptionInterpreter::InterpretSingleOption(
new UnknownFieldSet());
switch ((*iter)->type()) {
case FieldDescriptor::TYPE_MESSAGE: {
std::string* outstr =
parent_unknown_fields->AddLengthDelimited((*iter)->number());
ABSL_CHECK(unknown_fields->SerializeToString(outstr))
std::string outstr;
ABSL_CHECK(unknown_fields->SerializeToString(&outstr))
<< "Unexpected failure while serializing option submessage "
<< debug_msg_name << "\".";
parent_unknown_fields->AddLengthDelimited((*iter)->number(),
std::move(outstr));
break;
}

Expand Down
18 changes: 18 additions & 0 deletions src/google/protobuf/parse_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/message_lite.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/wire_format_lite.h"
Expand Down Expand Up @@ -265,6 +266,23 @@ const char* EpsCopyInputStream::ReadCordFallback(const char* ptr, int size,
return ptr;
}

const char* EpsCopyInputStream::ReadCharsFallback(const char* ptr,
absl::Span<char> out) {
char* out_ptr = out.data();
ptr = AppendSize(ptr, out.size(), [&](const char* p, int s) {
memcpy(out_ptr, p, s);
out_ptr += s;
});

// If we had an error, set the leftover memory to make sure we don't leak
// uninit data in the object.
if (ABSL_PREDICT_FALSE(ptr == nullptr)) {
memset(out_ptr, 0xCD, out.data() + out.size() - out_ptr);
}

return ptr;
}


const char* EpsCopyInputStream::InitFrom(io::ZeroCopyInputStream* zcis) {
zcis_ = zcis;
Expand Down
10 changes: 10 additions & 0 deletions src/google/protobuf/parse_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
return ReadCordFallback(ptr, size, cord);
}

PROTOBUF_NODISCARD const char* ReadChars(const char* ptr,
absl::Span<char> out) {
if (out.size() <= static_cast<size_t>(buffer_end_ + kSlopBytes - ptr)) {
memcpy(out.data(), ptr, out.size());
return ptr + out.size();
}
return ReadCharsFallback(ptr, out);
}


template <typename Tag, typename T>
PROTOBUF_NODISCARD const char* ReadRepeatedFixed(const char* ptr,
Expand Down Expand Up @@ -369,6 +378,7 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
const char* AppendStringFallback(const char* ptr, int size, std::string* str);
const char* ReadStringFallback(const char* ptr, int size, std::string* str);
const char* ReadCordFallback(const char* ptr, int size, absl::Cord* cord);
const char* ReadCharsFallback(const char* ptr, absl::Span<char> out);
static bool ParseEndsInSlopRegion(const char* begin, int overrun, int depth);
bool StreamNext(const void** data) {
bool res = zcis_->Next(data, &size_);
Expand Down
40 changes: 38 additions & 2 deletions src/google/protobuf/unknown_field_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@

#include "google/protobuf/unknown_field_set.h"

#include <cstring>
#include <string>
#include <utility>

#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/extension_set.h"
#include "google/protobuf/generated_message_tctable_impl.h"
#include "google/protobuf/io/coded_stream.h"
Expand Down Expand Up @@ -117,13 +122,44 @@ void UnknownFieldSet::AddFixed64(int number, uint64_t value) {
field.data_.fixed64_ = value;
}

void UnknownFieldSet::AddLengthDelimited(int number, const absl::Cord& value) {
auto out = AddLengthDelimitedUninitialized(number, value.size());
for (absl::string_view part : value.Chunks()) {
memcpy(out.data(), part.data(), part.size());
out.remove_prefix(part.size());
}
}

absl::Span<char> UnknownFieldSet::AddLengthDelimitedUninitialized(int number,
size_t size) {
auto& field = *fields_.Add();
field.number_ = number;
field.SetType(UnknownField::TYPE_LENGTH_DELIMITED);
std::string* str = field.data_.string_value =
Arena::Create<std::string>(arena());
absl::strings_internal::STLStringResizeUninitialized(str, size);
return absl::Span<char>(*str);
}

template <int&...>
void UnknownFieldSet::AddLengthDelimited(int number, std::string&& value) {
auto& field = *fields_.Add();
field.number_ = number;
field.SetType(UnknownField::TYPE_LENGTH_DELIMITED);
field.data_.string_value =
Arena::Create<std::string>(arena(), std::move(value));
}
template void UnknownFieldSet::AddLengthDelimited(int, std::string&&);

#if !defined(PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE)
std::string* UnknownFieldSet::AddLengthDelimited(int number) {
auto& field = *fields_.Add();
field.number_ = number;
field.SetType(UnknownField::TYPE_LENGTH_DELIMITED);
field.data_.string_value = Arena::Create<std::string>(arena());
return field.data_.string_value;
}
#endif // PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE

UnknownFieldSet* UnknownFieldSet::AddGroup(int number) {
auto& field = *fields_.Add();
Expand Down Expand Up @@ -284,10 +320,10 @@ class UnknownFieldParserHelper {
}
const char* ParseLengthDelimited(uint32_t num, const char* ptr,
ParseContext* ctx) {
std::string* s = unknown_->AddLengthDelimited(num);
int size = ReadSize(&ptr);
GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
return ctx->ReadString(ptr, size, s);
return ctx->ReadChars(ptr,
unknown_->AddLengthDelimitedUninitialized(num, size));
}
const char* ParseGroup(uint32_t num, const char* ptr, ParseContext* ctx) {
return ctx->ParseGroupInlined(ptr, num * 8 + 3, [&](const char* ptr) {
Expand Down
48 changes: 46 additions & 2 deletions src/google/protobuf/unknown_field_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/arena.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
Expand All @@ -47,6 +48,8 @@ class InternalMetadata; // metadata_lite.h
class WireFormat; // wire_format.h
class MessageSetFieldSkipperUsingCord;
// extension_set_heavy.cc
class UnknownFieldParserHelper;
struct UnknownFieldSetTestPeer;

#if defined(PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE)
using UFSStringView = absl::string_view;
Expand Down Expand Up @@ -87,7 +90,13 @@ class PROTOBUF_EXPORT UnknownField {
inline void set_fixed32(uint32_t value);
inline void set_fixed64(uint64_t value);
inline void set_length_delimited(absl::string_view value);
// template to avoid ambiguous overload resolution.
template <int&...>
inline void set_length_delimited(std::string&& value);
inline void set_length_delimited(const absl::Cord& value);
#if !defined(PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE)
inline std::string* mutable_length_delimited();
#endif // PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE
inline UnknownFieldSet* mutable_group();

inline size_t GetLengthDelimitedSize() const;
Expand Down Expand Up @@ -191,7 +200,14 @@ class PROTOBUF_EXPORT UnknownFieldSet {
void AddFixed32(int number, uint32_t value);
void AddFixed64(int number, uint64_t value);
void AddLengthDelimited(int number, absl::string_view value);
// template to avoid ambiguous overload resolution.
template <int&...>
void AddLengthDelimited(int number, std::string&& value);
void AddLengthDelimited(int number, const absl::Cord& value);

#if !defined(PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE)
std::string* AddLengthDelimited(int number);
#endif // PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE
UnknownFieldSet* AddGroup(int number);

// Adds an unknown field from another set.
Expand Down Expand Up @@ -233,6 +249,10 @@ class PROTOBUF_EXPORT UnknownFieldSet {
: UnknownFieldSet(arena) {}

private:
friend internal::WireFormat;
friend internal::UnknownFieldParserHelper;
friend internal::UnknownFieldSetTestPeer;

using InternalArenaConstructable_ = void;
using DestructorSkippable_ = void;

Expand All @@ -241,6 +261,14 @@ class PROTOBUF_EXPORT UnknownFieldSet {

Arena* arena() { return fields_.GetArena(); }

// Returns a buffer of `size` chars for the user to fill in.
// The buffer is potentially uninitialized memory. Failing to write to it
// might lead to undefined behavior when reading it later.
// Prefer the overloads above when possible. Calling this API without
// validating the `size` parameter can lead to unintentional memory usage and
// potential OOM.
absl::Span<char> AddLengthDelimitedUninitialized(int number, size_t size);

void ClearFallback();
void SwapSlow(UnknownFieldSet* other);

Expand Down Expand Up @@ -275,7 +303,7 @@ inline void WriteVarint(uint32_t num, uint64_t val, UnknownFieldSet* unknown) {
}
inline void WriteLengthDelimited(uint32_t num, absl::string_view val,
UnknownFieldSet* unknown) {
unknown->AddLengthDelimited(num)->assign(val.data(), val.size());
unknown->AddLengthDelimited(num, val);
}

PROTOBUF_EXPORT
Expand Down Expand Up @@ -331,7 +359,10 @@ inline UnknownField* UnknownFieldSet::mutable_field(int index) {

inline void UnknownFieldSet::AddLengthDelimited(int number,
const absl::string_view value) {
AddLengthDelimited(number)->assign(value.data(), value.size());
auto field = AddLengthDelimitedUninitialized(number, value.size());
if (!value.empty()) {
memcpy(field.data(), value.data(), value.size());
}
}

inline int UnknownField::number() const { return static_cast<int>(number_); }
Expand Down Expand Up @@ -376,10 +407,21 @@ inline void UnknownField::set_length_delimited(const absl::string_view value) {
assert(type() == TYPE_LENGTH_DELIMITED);
data_.string_value->assign(value.data(), value.size());
}
template <int&...>
inline void UnknownField::set_length_delimited(std::string&& value) {
assert(type() == TYPE_LENGTH_DELIMITED);
*data_.string_value = std::move(value);
}
inline void UnknownField::set_length_delimited(const absl::Cord& value) {
assert(type() == TYPE_LENGTH_DELIMITED);
absl::CopyCordToString(value, data_.string_value);
}
#if !defined(PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE)
inline std::string* UnknownField::mutable_length_delimited() {
assert(type() == TYPE_LENGTH_DELIMITED);
return data_.string_value;
}
#endif // PROTOBUF_FUTURE_STRING_VIEW_RETURN_TYPE
inline UnknownFieldSet* UnknownField::mutable_group() {
assert(type() == TYPE_GROUP);
return data_.group_;
Expand All @@ -397,6 +439,8 @@ inline size_t UnknownField::GetLengthDelimitedSize() const {

inline void UnknownField::SetType(Type type) { type_ = type; }

extern template void UnknownFieldSet::AddLengthDelimited(int, std::string&&);

namespace internal {

// Add specialization of InternalMetadata::Container to provide arena support.
Expand Down
Loading

0 comments on commit 75b66c2

Please sign in to comment.