备份-基础ssl通信

This commit is contained in:
睿 安
2026-01-23 08:39:07 +08:00
parent ef6b8511b1
commit dbb053a691
625 changed files with 305003 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/any.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/message.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
bool AnyMetadata::PackFrom(Arena* arena, const Message& message) {
return PackFrom(arena, message, kTypeGoogleApisComPrefix);
}
bool AnyMetadata::PackFrom(Arena* arena, const Message& message,
StringPiece type_url_prefix) {
type_url_->Set(
GetTypeUrl(message.GetDescriptor()->full_name(), type_url_prefix), arena);
return message.SerializeToString(value_->Mutable(arena));
}
bool AnyMetadata::UnpackTo(Message* message) const {
if (!InternalIs(message->GetDescriptor()->full_name())) {
return false;
}
return message->ParseFromString(value_->Get());
}
bool GetAnyFieldDescriptors(const Message& message,
const FieldDescriptor** type_url_field,
const FieldDescriptor** value_field) {
const Descriptor* descriptor = message.GetDescriptor();
if (descriptor->full_name() != kAnyFullTypeName) {
return false;
}
*type_url_field = descriptor->FindFieldByNumber(1);
*value_field = descriptor->FindFieldByNumber(2);
return (*type_url_field != nullptr &&
(*type_url_field)->type() == FieldDescriptor::TYPE_STRING &&
*value_field != nullptr &&
(*value_field)->type() == FieldDescriptor::TYPE_BYTES);
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,157 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_ANY_H__
#define GOOGLE_PROTOBUF_ANY_H__
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/message_lite.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
class FieldDescriptor;
class Message;
namespace internal {
extern const char kAnyFullTypeName[]; // "google.protobuf.Any".
extern const char kTypeGoogleApisComPrefix[]; // "type.googleapis.com/".
extern const char kTypeGoogleProdComPrefix[]; // "type.googleprod.com/".
std::string GetTypeUrl(StringPiece message_name,
StringPiece type_url_prefix);
// Helper class used to implement google::protobuf::Any.
class PROTOBUF_EXPORT AnyMetadata {
typedef ArenaStringPtr UrlType;
typedef ArenaStringPtr ValueType;
public:
// AnyMetadata does not take ownership of "type_url" and "value".
constexpr AnyMetadata(UrlType* type_url, ValueType* value)
: type_url_(type_url), value_(value) {}
// Packs a message using the default type URL prefix: "type.googleapis.com".
// The resulted type URL will be "type.googleapis.com/<message_full_name>".
// Returns false if serializing the message failed.
template <typename T>
bool PackFrom(Arena* arena, const T& message) {
return InternalPackFrom(arena, message, kTypeGoogleApisComPrefix,
T::FullMessageName());
}
bool PackFrom(Arena* arena, const Message& message);
// Packs a message using the given type URL prefix. The type URL will be
// constructed by concatenating the message type's full name to the prefix
// with an optional "/" separator if the prefix doesn't already end with "/".
// For example, both PackFrom(message, "type.googleapis.com") and
// PackFrom(message, "type.googleapis.com/") yield the same result type
// URL: "type.googleapis.com/<message_full_name>".
// Returns false if serializing the message failed.
template <typename T>
bool PackFrom(Arena* arena, const T& message,
StringPiece type_url_prefix) {
return InternalPackFrom(arena, message, type_url_prefix,
T::FullMessageName());
}
bool PackFrom(Arena* arena, const Message& message,
StringPiece type_url_prefix);
// Unpacks the payload into the given message. Returns false if the message's
// type doesn't match the type specified in the type URL (i.e., the full
// name after the last "/" of the type URL doesn't match the message's actual
// full name) or parsing the payload has failed.
template <typename T>
bool UnpackTo(T* message) const {
return InternalUnpackTo(T::FullMessageName(), message);
}
bool UnpackTo(Message* message) const;
// Checks whether the type specified in the type URL matches the given type.
// A type is considered matching if its full name matches the full name after
// the last "/" in the type URL.
template <typename T>
bool Is() const {
return InternalIs(T::FullMessageName());
}
private:
bool InternalPackFrom(Arena* arena, const MessageLite& message,
StringPiece type_url_prefix,
StringPiece type_name);
bool InternalUnpackTo(StringPiece type_name,
MessageLite* message) const;
bool InternalIs(StringPiece type_name) const;
UrlType* type_url_;
ValueType* value_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AnyMetadata);
};
// Get the proto type name from Any::type_url value. For example, passing
// "type.googleapis.com/rpc.QueryOrigin" will return "rpc.QueryOrigin" in
// *full_type_name. Returns false if the type_url does not have a "/"
// in the type url separating the full type name.
//
// NOTE: this function is available publicly as a static method on the
// generated message type: google::protobuf::Any::ParseAnyTypeUrl()
bool ParseAnyTypeUrl(StringPiece type_url, std::string* full_type_name);
// Get the proto type name and prefix from Any::type_url value. For example,
// passing "type.googleapis.com/rpc.QueryOrigin" will return
// "type.googleapis.com/" in *url_prefix and "rpc.QueryOrigin" in
// *full_type_name. Returns false if the type_url does not have a "/" in the
// type url separating the full type name.
bool ParseAnyTypeUrl(StringPiece type_url, std::string* url_prefix,
std::string* full_type_name);
// See if message is of type google.protobuf.Any, if so, return the descriptors
// for "type_url" and "value" fields.
bool GetAnyFieldDescriptors(const Message& message,
const FieldDescriptor** type_url_field,
const FieldDescriptor** value_field);
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_ANY_H__

View File

@@ -0,0 +1,368 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/any.proto
#include <google/protobuf/any.pb.h>
#include <algorithm>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/reflection_ops.h>
#include <google/protobuf/wire_format.h>
// @@protoc_insertion_point(includes)
#include <google/protobuf/port_def.inc>
PROTOBUF_PRAGMA_INIT_SEG
namespace _pb = ::PROTOBUF_NAMESPACE_ID;
namespace _pbi = _pb::internal;
#if defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
#endif // __llvm__
PROTOBUF_NAMESPACE_OPEN
PROTOBUF_CONSTEXPR Any::Any(
::_pbi::ConstantInitialized): _impl_{
/*decltype(_impl_.type_url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
, /*decltype(_impl_.value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
, /*decltype(_impl_._cached_size_)*/{}
, /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}} {}
struct AnyDefaultTypeInternal {
PROTOBUF_CONSTEXPR AnyDefaultTypeInternal()
: _instance(::_pbi::ConstantInitialized{}) {}
~AnyDefaultTypeInternal() {}
union {
Any _instance;
};
};
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 AnyDefaultTypeInternal _Any_default_instance_;
PROTOBUF_NAMESPACE_CLOSE
static ::_pb::Metadata file_level_metadata_google_2fprotobuf_2fany_2eproto[1];
static constexpr ::_pb::EnumDescriptor const** file_level_enum_descriptors_google_2fprotobuf_2fany_2eproto = nullptr;
static constexpr ::_pb::ServiceDescriptor const** file_level_service_descriptors_google_2fprotobuf_2fany_2eproto = nullptr;
const uint32_t TableStruct_google_2fprotobuf_2fany_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
~0u, // no _has_bits_
PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _internal_metadata_),
~0u, // no _extensions_
~0u, // no _oneof_case_
~0u, // no _weak_field_map_
~0u, // no _inlined_string_donated_
PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _impl_.type_url_),
PROTOBUF_FIELD_OFFSET(::PROTOBUF_NAMESPACE_ID::Any, _impl_.value_),
};
static const ::_pbi::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
{ 0, -1, -1, sizeof(::PROTOBUF_NAMESPACE_ID::Any)},
};
static const ::_pb::Message* const file_default_instances[] = {
&::PROTOBUF_NAMESPACE_ID::_Any_default_instance_._instance,
};
const char descriptor_table_protodef_google_2fprotobuf_2fany_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) =
"\n\031google/protobuf/any.proto\022\017google.prot"
"obuf\"&\n\003Any\022\020\n\010type_url\030\001 \001(\t\022\r\n\005value\030\002"
" \001(\014Bv\n\023com.google.protobufB\010AnyProtoP\001Z"
",google.golang.org/protobuf/types/known/"
"anypb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownT"
"ypesb\006proto3"
;
static ::_pbi::once_flag descriptor_table_google_2fprotobuf_2fany_2eproto_once;
const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fany_2eproto = {
false, false, 212, descriptor_table_protodef_google_2fprotobuf_2fany_2eproto,
"google/protobuf/any.proto",
&descriptor_table_google_2fprotobuf_2fany_2eproto_once, nullptr, 0, 1,
schemas, file_default_instances, TableStruct_google_2fprotobuf_2fany_2eproto::offsets,
file_level_metadata_google_2fprotobuf_2fany_2eproto, file_level_enum_descriptors_google_2fprotobuf_2fany_2eproto,
file_level_service_descriptors_google_2fprotobuf_2fany_2eproto,
};
PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* descriptor_table_google_2fprotobuf_2fany_2eproto_getter() {
return &descriptor_table_google_2fprotobuf_2fany_2eproto;
}
// Force running AddDescriptors() at dynamic initialization time.
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 static ::_pbi::AddDescriptorsRunner dynamic_init_dummy_google_2fprotobuf_2fany_2eproto(&descriptor_table_google_2fprotobuf_2fany_2eproto);
PROTOBUF_NAMESPACE_OPEN
// ===================================================================
bool Any::GetAnyFieldDescriptors(
const ::PROTOBUF_NAMESPACE_ID::Message& message,
const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** type_url_field,
const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** value_field) {
return ::_pbi::GetAnyFieldDescriptors(
message, type_url_field, value_field);
}
bool Any::ParseAnyTypeUrl(
::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url,
std::string* full_type_name) {
return ::_pbi::ParseAnyTypeUrl(type_url, full_type_name);
}
class Any::_Internal {
public:
};
Any::Any(::PROTOBUF_NAMESPACE_ID::Arena* arena,
bool is_message_owned)
: ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) {
SharedCtor(arena, is_message_owned);
// @@protoc_insertion_point(arena_constructor:google.protobuf.Any)
}
Any::Any(const Any& from)
: ::PROTOBUF_NAMESPACE_ID::Message() {
Any* const _this = this; (void)_this;
new (&_impl_) Impl_{
decltype(_impl_.type_url_){}
, decltype(_impl_.value_){}
, /*decltype(_impl_._cached_size_)*/{}
, /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}};
_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
_impl_.type_url_.InitDefault();
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
_impl_.type_url_.Set("", GetArenaForAllocation());
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
if (!from._internal_type_url().empty()) {
_this->_impl_.type_url_.Set(from._internal_type_url(),
_this->GetArenaForAllocation());
}
_impl_.value_.InitDefault();
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
_impl_.value_.Set("", GetArenaForAllocation());
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
if (!from._internal_value().empty()) {
_this->_impl_.value_.Set(from._internal_value(),
_this->GetArenaForAllocation());
}
// @@protoc_insertion_point(copy_constructor:google.protobuf.Any)
}
inline void Any::SharedCtor(
::_pb::Arena* arena, bool is_message_owned) {
(void)arena;
(void)is_message_owned;
new (&_impl_) Impl_{
decltype(_impl_.type_url_){}
, decltype(_impl_.value_){}
, /*decltype(_impl_._cached_size_)*/{}
, /*decltype(_impl_._any_metadata_)*/{&_impl_.type_url_, &_impl_.value_}
};
_impl_.type_url_.InitDefault();
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
_impl_.type_url_.Set("", GetArenaForAllocation());
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
_impl_.value_.InitDefault();
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
_impl_.value_.Set("", GetArenaForAllocation());
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
}
Any::~Any() {
// @@protoc_insertion_point(destructor:google.protobuf.Any)
if (auto *arena = _internal_metadata_.DeleteReturnArena<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>()) {
(void)arena;
return;
}
SharedDtor();
}
inline void Any::SharedDtor() {
GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
_impl_.type_url_.Destroy();
_impl_.value_.Destroy();
_impl_._any_metadata_.~AnyMetadata();
}
void Any::SetCachedSize(int size) const {
_impl_._cached_size_.Set(size);
}
void Any::Clear() {
// @@protoc_insertion_point(message_clear_start:google.protobuf.Any)
uint32_t cached_has_bits = 0;
// Prevent compiler warnings about cached_has_bits being unused
(void) cached_has_bits;
_impl_.type_url_.ClearToEmpty();
_impl_.value_.ClearToEmpty();
_internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>();
}
const char* Any::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
while (!ctx->Done(&ptr)) {
uint32_t tag;
ptr = ::_pbi::ReadTag(ptr, &tag);
switch (tag >> 3) {
// string type_url = 1;
case 1:
if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
auto str = _internal_mutable_type_url();
ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
CHK_(ptr);
CHK_(::_pbi::VerifyUTF8(str, "google.protobuf.Any.type_url"));
} else
goto handle_unusual;
continue;
// bytes value = 2;
case 2:
if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
auto str = _internal_mutable_value();
ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
CHK_(ptr);
} else
goto handle_unusual;
continue;
default:
goto handle_unusual;
} // switch
handle_unusual:
if ((tag == 0) || ((tag & 7) == 4)) {
CHK_(ptr);
ctx->SetLastTag(tag);
goto message_done;
}
ptr = UnknownFieldParse(
tag,
_internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(),
ptr, ctx);
CHK_(ptr != nullptr);
} // while
message_done:
return ptr;
failure:
ptr = nullptr;
goto message_done;
#undef CHK_
}
uint8_t* Any::_InternalSerialize(
uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
// @@protoc_insertion_point(serialize_to_array_start:google.protobuf.Any)
uint32_t cached_has_bits = 0;
(void) cached_has_bits;
// string type_url = 1;
if (!this->_internal_type_url().empty()) {
::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String(
this->_internal_type_url().data(), static_cast<int>(this->_internal_type_url().length()),
::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE,
"google.protobuf.Any.type_url");
target = stream->WriteStringMaybeAliased(
1, this->_internal_type_url(), target);
}
// bytes value = 2;
if (!this->_internal_value().empty()) {
target = stream->WriteBytesMaybeAliased(
2, this->_internal_value(), target);
}
if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
target = ::_pbi::WireFormat::InternalSerializeUnknownFieldsToArray(
_internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream);
}
// @@protoc_insertion_point(serialize_to_array_end:google.protobuf.Any)
return target;
}
size_t Any::ByteSizeLong() const {
// @@protoc_insertion_point(message_byte_size_start:google.protobuf.Any)
size_t total_size = 0;
uint32_t cached_has_bits = 0;
// Prevent compiler warnings about cached_has_bits being unused
(void) cached_has_bits;
// string type_url = 1;
if (!this->_internal_type_url().empty()) {
total_size += 1 +
::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
this->_internal_type_url());
}
// bytes value = 2;
if (!this->_internal_value().empty()) {
total_size += 1 +
::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
this->_internal_value());
}
return MaybeComputeUnknownFieldsSize(total_size, &_impl_._cached_size_);
}
const ::PROTOBUF_NAMESPACE_ID::Message::ClassData Any::_class_data_ = {
::PROTOBUF_NAMESPACE_ID::Message::CopyWithSourceCheck,
Any::MergeImpl
};
const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*Any::GetClassData() const { return &_class_data_; }
void Any::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg) {
auto* const _this = static_cast<Any*>(&to_msg);
auto& from = static_cast<const Any&>(from_msg);
// @@protoc_insertion_point(class_specific_merge_from_start:google.protobuf.Any)
GOOGLE_DCHECK_NE(&from, _this);
uint32_t cached_has_bits = 0;
(void) cached_has_bits;
if (!from._internal_type_url().empty()) {
_this->_internal_set_type_url(from._internal_type_url());
}
if (!from._internal_value().empty()) {
_this->_internal_set_value(from._internal_value());
}
_this->_internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_);
}
void Any::CopyFrom(const Any& from) {
// @@protoc_insertion_point(class_specific_copy_from_start:google.protobuf.Any)
if (&from == this) return;
Clear();
MergeFrom(from);
}
bool Any::IsInitialized() const {
return true;
}
void Any::InternalSwap(Any* other) {
using std::swap;
auto* lhs_arena = GetArenaForAllocation();
auto* rhs_arena = other->GetArenaForAllocation();
_internal_metadata_.InternalSwap(&other->_internal_metadata_);
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
&_impl_.type_url_, lhs_arena,
&other->_impl_.type_url_, rhs_arena
);
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
&_impl_.value_, lhs_arena,
&other->_impl_.value_, rhs_arena
);
}
::PROTOBUF_NAMESPACE_ID::Metadata Any::GetMetadata() const {
return ::_pbi::AssignDescriptors(
&descriptor_table_google_2fprotobuf_2fany_2eproto_getter, &descriptor_table_google_2fprotobuf_2fany_2eproto_once,
file_level_metadata_google_2fprotobuf_2fany_2eproto[0]);
}
// @@protoc_insertion_point(namespace_scope)
PROTOBUF_NAMESPACE_CLOSE
PROTOBUF_NAMESPACE_OPEN
template<> PROTOBUF_NOINLINE ::PROTOBUF_NAMESPACE_ID::Any*
Arena::CreateMaybeMessage< ::PROTOBUF_NAMESPACE_ID::Any >(Arena* arena) {
return Arena::CreateMessageInternal< ::PROTOBUF_NAMESPACE_ID::Any >(arena);
}
PROTOBUF_NAMESPACE_CLOSE
// @@protoc_insertion_point(global_scope)
#if defined(__llvm__)
#pragma clang diagnostic pop
#endif // __llvm__
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,384 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/any.proto
#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto
#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto
#include <limits>
#include <string>
#include <google/protobuf/port_def.inc>
#if PROTOBUF_VERSION < 3021000
#error This file was generated by a newer version of protoc which is
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
#if 3021009 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
#endif
#include <google/protobuf/port_undef.inc>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/metadata_lite.h>
#include <google/protobuf/generated_message_reflection.h>
#include <google/protobuf/message.h>
#include <google/protobuf/repeated_field.h> // IWYU pragma: export
#include <google/protobuf/extension_set.h> // IWYU pragma: export
#include <google/protobuf/unknown_field_set.h>
// @@protoc_insertion_point(includes)
#include <google/protobuf/port_def.inc>
#define PROTOBUF_INTERNAL_EXPORT_google_2fprotobuf_2fany_2eproto PROTOBUF_EXPORT
PROTOBUF_NAMESPACE_OPEN
namespace internal {
class AnyMetadata;
} // namespace internal
PROTOBUF_NAMESPACE_CLOSE
// Internal implementation detail -- do not use these members.
struct PROTOBUF_EXPORT TableStruct_google_2fprotobuf_2fany_2eproto {
static const uint32_t offsets[];
};
PROTOBUF_EXPORT extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_google_2fprotobuf_2fany_2eproto;
PROTOBUF_NAMESPACE_OPEN
class Any;
struct AnyDefaultTypeInternal;
PROTOBUF_EXPORT extern AnyDefaultTypeInternal _Any_default_instance_;
PROTOBUF_NAMESPACE_CLOSE
PROTOBUF_NAMESPACE_OPEN
template<> PROTOBUF_EXPORT ::PROTOBUF_NAMESPACE_ID::Any* Arena::CreateMaybeMessage<::PROTOBUF_NAMESPACE_ID::Any>(Arena*);
PROTOBUF_NAMESPACE_CLOSE
PROTOBUF_NAMESPACE_OPEN
// ===================================================================
class PROTOBUF_EXPORT Any final :
public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:google.protobuf.Any) */ {
public:
inline Any() : Any(nullptr) {}
~Any() override;
explicit PROTOBUF_CONSTEXPR Any(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
Any(const Any& from);
Any(Any&& from) noexcept
: Any() {
*this = ::std::move(from);
}
inline Any& operator=(const Any& from) {
CopyFrom(from);
return *this;
}
inline Any& operator=(Any&& from) noexcept {
if (this == &from) return *this;
if (GetOwningArena() == from.GetOwningArena()
#ifdef PROTOBUF_FORCE_COPY_IN_MOVE
&& GetOwningArena() != nullptr
#endif // !PROTOBUF_FORCE_COPY_IN_MOVE
) {
InternalSwap(&from);
} else {
CopyFrom(from);
}
return *this;
}
static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() {
return GetDescriptor();
}
static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() {
return default_instance().GetMetadata().descriptor;
}
static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() {
return default_instance().GetMetadata().reflection;
}
static const Any& default_instance() {
return *internal_default_instance();
}
static inline const Any* internal_default_instance() {
return reinterpret_cast<const Any*>(
&_Any_default_instance_);
}
static constexpr int kIndexInFileMessages =
0;
// implements Any -----------------------------------------------
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message) {
GOOGLE_DCHECK_NE(&message, this);
return _impl_._any_metadata_.PackFrom(GetArena(), message);
}
bool PackFrom(const ::PROTOBUF_NAMESPACE_ID::Message& message,
::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url_prefix) {
GOOGLE_DCHECK_NE(&message, this);
return _impl_._any_metadata_.PackFrom(GetArena(), message, type_url_prefix);
}
bool UnpackTo(::PROTOBUF_NAMESPACE_ID::Message* message) const {
return _impl_._any_metadata_.UnpackTo(message);
}
static bool GetAnyFieldDescriptors(
const ::PROTOBUF_NAMESPACE_ID::Message& message,
const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** type_url_field,
const ::PROTOBUF_NAMESPACE_ID::FieldDescriptor** value_field);
template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
bool PackFrom(const T& message) {
return _impl_._any_metadata_.PackFrom<T>(GetArena(), message);
}
template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
bool PackFrom(const T& message,
::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url_prefix) {
return _impl_._any_metadata_.PackFrom<T>(GetArena(), message, type_url_prefix);}
template <typename T, class = typename std::enable_if<!std::is_convertible<T, const ::PROTOBUF_NAMESPACE_ID::Message&>::value>::type>
bool UnpackTo(T* message) const {
return _impl_._any_metadata_.UnpackTo<T>(message);
}
template<typename T> bool Is() const {
return _impl_._any_metadata_.Is<T>();
}
static bool ParseAnyTypeUrl(::PROTOBUF_NAMESPACE_ID::ConstStringParam type_url,
std::string* full_type_name);
friend void swap(Any& a, Any& b) {
a.Swap(&b);
}
inline void Swap(Any* other) {
if (other == this) return;
#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
if (GetOwningArena() != nullptr &&
GetOwningArena() == other->GetOwningArena()) {
#else // PROTOBUF_FORCE_COPY_IN_SWAP
if (GetOwningArena() == other->GetOwningArena()) {
#endif // !PROTOBUF_FORCE_COPY_IN_SWAP
InternalSwap(other);
} else {
::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
}
}
void UnsafeArenaSwap(Any* other) {
if (other == this) return;
GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
InternalSwap(other);
}
// implements Message ----------------------------------------------
Any* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
return CreateMaybeMessage<Any>(arena);
}
using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom;
void CopyFrom(const Any& from);
using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom;
void MergeFrom( const Any& from) {
Any::MergeImpl(*this, from);
}
private:
static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message& to_msg, const ::PROTOBUF_NAMESPACE_ID::Message& from_msg);
public:
PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
bool IsInitialized() const final;
size_t ByteSizeLong() const final;
const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
uint8_t* _InternalSerialize(
uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
private:
void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
void SharedDtor();
void SetCachedSize(int size) const final;
void InternalSwap(Any* other);
private:
friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
return "google.protobuf.Any";
}
protected:
explicit Any(::PROTOBUF_NAMESPACE_ID::Arena* arena,
bool is_message_owned = false);
public:
static const ClassData _class_data_;
const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final;
::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final;
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
enum : int {
kTypeUrlFieldNumber = 1,
kValueFieldNumber = 2,
};
// string type_url = 1;
void clear_type_url();
const std::string& type_url() const;
template <typename ArgT0 = const std::string&, typename... ArgT>
void set_type_url(ArgT0&& arg0, ArgT... args);
std::string* mutable_type_url();
PROTOBUF_NODISCARD std::string* release_type_url();
void set_allocated_type_url(std::string* type_url);
private:
const std::string& _internal_type_url() const;
inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_url(const std::string& value);
std::string* _internal_mutable_type_url();
public:
// bytes value = 2;
void clear_value();
const std::string& value() const;
template <typename ArgT0 = const std::string&, typename... ArgT>
void set_value(ArgT0&& arg0, ArgT... args);
std::string* mutable_value();
PROTOBUF_NODISCARD std::string* release_value();
void set_allocated_value(std::string* value);
private:
const std::string& _internal_value() const;
inline PROTOBUF_ALWAYS_INLINE void _internal_set_value(const std::string& value);
std::string* _internal_mutable_value();
public:
// @@protoc_insertion_point(class_scope:google.protobuf.Any)
private:
class _Internal;
template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
typedef void InternalArenaConstructable_;
typedef void DestructorSkippable_;
struct Impl_ {
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr type_url_;
::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_;
mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata _any_metadata_;
};
union { Impl_ _impl_; };
friend struct ::TableStruct_google_2fprotobuf_2fany_2eproto;
};
// ===================================================================
// ===================================================================
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif // __GNUC__
// Any
// string type_url = 1;
inline void Any::clear_type_url() {
_impl_.type_url_.ClearToEmpty();
}
inline const std::string& Any::type_url() const {
// @@protoc_insertion_point(field_get:google.protobuf.Any.type_url)
return _internal_type_url();
}
template <typename ArgT0, typename... ArgT>
inline PROTOBUF_ALWAYS_INLINE
void Any::set_type_url(ArgT0&& arg0, ArgT... args) {
_impl_.type_url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
// @@protoc_insertion_point(field_set:google.protobuf.Any.type_url)
}
inline std::string* Any::mutable_type_url() {
std::string* _s = _internal_mutable_type_url();
// @@protoc_insertion_point(field_mutable:google.protobuf.Any.type_url)
return _s;
}
inline const std::string& Any::_internal_type_url() const {
return _impl_.type_url_.Get();
}
inline void Any::_internal_set_type_url(const std::string& value) {
_impl_.type_url_.Set(value, GetArenaForAllocation());
}
inline std::string* Any::_internal_mutable_type_url() {
return _impl_.type_url_.Mutable(GetArenaForAllocation());
}
inline std::string* Any::release_type_url() {
// @@protoc_insertion_point(field_release:google.protobuf.Any.type_url)
return _impl_.type_url_.Release();
}
inline void Any::set_allocated_type_url(std::string* type_url) {
if (type_url != nullptr) {
} else {
}
_impl_.type_url_.SetAllocated(type_url, GetArenaForAllocation());
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
if (_impl_.type_url_.IsDefault()) {
_impl_.type_url_.Set("", GetArenaForAllocation());
}
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
// @@protoc_insertion_point(field_set_allocated:google.protobuf.Any.type_url)
}
// bytes value = 2;
inline void Any::clear_value() {
_impl_.value_.ClearToEmpty();
}
inline const std::string& Any::value() const {
// @@protoc_insertion_point(field_get:google.protobuf.Any.value)
return _internal_value();
}
template <typename ArgT0, typename... ArgT>
inline PROTOBUF_ALWAYS_INLINE
void Any::set_value(ArgT0&& arg0, ArgT... args) {
_impl_.value_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
// @@protoc_insertion_point(field_set:google.protobuf.Any.value)
}
inline std::string* Any::mutable_value() {
std::string* _s = _internal_mutable_value();
// @@protoc_insertion_point(field_mutable:google.protobuf.Any.value)
return _s;
}
inline const std::string& Any::_internal_value() const {
return _impl_.value_.Get();
}
inline void Any::_internal_set_value(const std::string& value) {
_impl_.value_.Set(value, GetArenaForAllocation());
}
inline std::string* Any::_internal_mutable_value() {
return _impl_.value_.Mutable(GetArenaForAllocation());
}
inline std::string* Any::release_value() {
// @@protoc_insertion_point(field_release:google.protobuf.Any.value)
return _impl_.value_.Release();
}
inline void Any::set_allocated_value(std::string* value) {
if (value != nullptr) {
} else {
}
_impl_.value_.SetAllocated(value, GetArenaForAllocation());
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
if (_impl_.value_.IsDefault()) {
_impl_.value_.Set("", GetArenaForAllocation());
}
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
// @@protoc_insertion_point(field_set_allocated:google.protobuf.Any.value)
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
// @@protoc_insertion_point(namespace_scope)
PROTOBUF_NAMESPACE_CLOSE
// @@protoc_insertion_point(global_scope)
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto

View File

@@ -0,0 +1,158 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option go_package = "google.golang.org/protobuf/types/known/anypb";
option java_package = "com.google.protobuf";
option java_outer_classname = "AnyProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
// `Any` contains an arbitrary serialized protocol buffer message along with a
// URL that describes the type of the serialized message.
//
// Protobuf library provides support to pack/unpack Any values in the form
// of utility functions or additional generated methods of the Any type.
//
// Example 1: Pack and unpack a message in C++.
//
// Foo foo = ...;
// Any any;
// any.PackFrom(foo);
// ...
// if (any.UnpackTo(&foo)) {
// ...
// }
//
// Example 2: Pack and unpack a message in Java.
//
// Foo foo = ...;
// Any any = Any.pack(foo);
// ...
// if (any.is(Foo.class)) {
// foo = any.unpack(Foo.class);
// }
//
// Example 3: Pack and unpack a message in Python.
//
// foo = Foo(...)
// any = Any()
// any.Pack(foo)
// ...
// if any.Is(Foo.DESCRIPTOR):
// any.Unpack(foo)
// ...
//
// Example 4: Pack and unpack a message in Go
//
// foo := &pb.Foo{...}
// any, err := anypb.New(foo)
// if err != nil {
// ...
// }
// ...
// foo := &pb.Foo{}
// if err := any.UnmarshalTo(foo); err != nil {
// ...
// }
//
// The pack methods provided by protobuf library will by default use
// 'type.googleapis.com/full.type.name' as the type URL and the unpack
// methods only use the fully qualified type name after the last '/'
// in the type URL, for example "foo.bar.com/x/y.z" will yield type
// name "y.z".
//
//
// JSON
//
// The JSON representation of an `Any` value uses the regular
// representation of the deserialized, embedded message, with an
// additional field `@type` which contains the type URL. Example:
//
// package google.profile;
// message Person {
// string first_name = 1;
// string last_name = 2;
// }
//
// {
// "@type": "type.googleapis.com/google.profile.Person",
// "firstName": <string>,
// "lastName": <string>
// }
//
// If the embedded message type is well-known and has a custom JSON
// representation, that representation will be embedded adding a field
// `value` which holds the custom JSON in addition to the `@type`
// field. Example (for message [google.protobuf.Duration][]):
//
// {
// "@type": "type.googleapis.com/google.protobuf.Duration",
// "value": "1.212s"
// }
//
message Any {
// A URL/resource name that uniquely identifies the type of the serialized
// protocol buffer message. This string must contain at least
// one "/" character. The last segment of the URL's path must represent
// the fully qualified name of the type (as in
// `path/google.protobuf.Duration`). The name should be in a canonical form
// (e.g., leading "." is not accepted).
//
// In practice, teams usually precompile into the binary all types that they
// expect it to use in the context of Any. However, for URLs which use the
// scheme `http`, `https`, or no scheme, one can optionally set up a type
// server that maps type URLs to message definitions as follows:
//
// * If no scheme is provided, `https` is assumed.
// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
// value in binary format, or produce an error.
// * Applications are allowed to cache lookup results based on the
// URL, or have them precompiled into a binary to avoid any
// lookup. Therefore, binary compatibility needs to be preserved
// on changes to types. (Use versioned type names to manage
// breaking changes.)
//
// Note: this functionality is not currently available in the official
// protobuf release, and it is not used for type URLs beginning with
// type.googleapis.com.
//
// Schemes other than `http`, `https` (or the empty scheme) might be
// used with implementation specific semantics.
//
string type_url = 1;
// Must be a valid serialized protocol buffer of the above specified type.
bytes value = 2;
}

View File

@@ -0,0 +1,96 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/any.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/generated_message_util.h>
namespace google {
namespace protobuf {
namespace internal {
std::string GetTypeUrl(StringPiece message_name,
StringPiece type_url_prefix) {
if (!type_url_prefix.empty() &&
type_url_prefix[type_url_prefix.size() - 1] == '/') {
return StrCat(type_url_prefix, message_name);
} else {
return StrCat(type_url_prefix, "/", message_name);
}
}
const char kAnyFullTypeName[] = "google.protobuf.Any";
const char kTypeGoogleApisComPrefix[] = "type.googleapis.com/";
const char kTypeGoogleProdComPrefix[] = "type.googleprod.com/";
bool AnyMetadata::InternalPackFrom(Arena* arena, const MessageLite& message,
StringPiece type_url_prefix,
StringPiece type_name) {
type_url_->Set(GetTypeUrl(type_name, type_url_prefix), arena);
return message.SerializeToString(value_->Mutable(arena));
}
bool AnyMetadata::InternalUnpackTo(StringPiece type_name,
MessageLite* message) const {
if (!InternalIs(type_name)) {
return false;
}
return message->ParseFromString(value_->Get());
}
bool AnyMetadata::InternalIs(StringPiece type_name) const {
StringPiece type_url = type_url_->Get();
return type_url.size() >= type_name.size() + 1 &&
type_url[type_url.size() - type_name.size() - 1] == '/' &&
HasSuffixString(type_url, type_name);
}
bool ParseAnyTypeUrl(StringPiece type_url, std::string* url_prefix,
std::string* full_type_name) {
size_t pos = type_url.find_last_of('/');
if (pos == std::string::npos || pos + 1 == type_url.size()) {
return false;
}
if (url_prefix) {
*url_prefix = std::string(type_url.substr(0, pos + 1));
}
*full_type_name = std::string(type_url.substr(pos + 1));
return true;
}
bool ParseAnyTypeUrl(StringPiece type_url, std::string* full_type_name) {
return ParseAnyTypeUrl(type_url, nullptr, full_type_name);
}
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,194 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/any_test.pb.h>
#include <google/protobuf/unittest.pb.h>
#include <gtest/gtest.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace {
TEST(AnyMetadataTest, ConstInit) {
PROTOBUF_CONSTINIT static internal::AnyMetadata metadata(nullptr, nullptr);
(void)metadata;
}
TEST(AnyTest, TestPackAndUnpack) {
protobuf_unittest::TestAny submessage;
submessage.set_int32_value(12345);
protobuf_unittest::TestAny message;
ASSERT_TRUE(message.mutable_any_value()->PackFrom(submessage));
std::string data = message.SerializeAsString();
ASSERT_TRUE(message.ParseFromString(data));
EXPECT_TRUE(message.has_any_value());
submessage.Clear();
ASSERT_TRUE(message.any_value().UnpackTo(&submessage));
EXPECT_EQ(12345, submessage.int32_value());
}
TEST(AnyTest, TestPackFromSerializationExceedsSizeLimit) {
protobuf_unittest::TestAny submessage;
submessage.mutable_text()->resize(INT_MAX, 'a');
protobuf_unittest::TestAny message;
EXPECT_FALSE(message.mutable_any_value()->PackFrom(submessage));
}
TEST(AnyTest, TestUnpackWithTypeMismatch) {
protobuf_unittest::TestAny payload;
payload.set_int32_value(13);
google::protobuf::Any any;
any.PackFrom(payload);
// Attempt to unpack into the wrong type.
protobuf_unittest::TestAllTypes dest;
EXPECT_FALSE(any.UnpackTo(&dest));
}
TEST(AnyTest, TestPackAndUnpackAny) {
// We can pack a Any message inside another Any message.
protobuf_unittest::TestAny submessage;
submessage.set_int32_value(12345);
google::protobuf::Any any;
any.PackFrom(submessage);
protobuf_unittest::TestAny message;
message.mutable_any_value()->PackFrom(any);
std::string data = message.SerializeAsString();
ASSERT_TRUE(message.ParseFromString(data));
EXPECT_TRUE(message.has_any_value());
any.Clear();
submessage.Clear();
ASSERT_TRUE(message.any_value().UnpackTo(&any));
ASSERT_TRUE(any.UnpackTo(&submessage));
EXPECT_EQ(12345, submessage.int32_value());
}
TEST(AnyTest, TestPackWithCustomTypeUrl) {
protobuf_unittest::TestAny submessage;
submessage.set_int32_value(12345);
google::protobuf::Any any;
// Pack with a custom type URL prefix.
any.PackFrom(submessage, "type.myservice.com");
EXPECT_EQ("type.myservice.com/protobuf_unittest.TestAny", any.type_url());
// Pack with a custom type URL prefix ending with '/'.
any.PackFrom(submessage, "type.myservice.com/");
EXPECT_EQ("type.myservice.com/protobuf_unittest.TestAny", any.type_url());
// Pack with an empty type URL prefix.
any.PackFrom(submessage, "");
EXPECT_EQ("/protobuf_unittest.TestAny", any.type_url());
// Test unpacking the type.
submessage.Clear();
EXPECT_TRUE(any.UnpackTo(&submessage));
EXPECT_EQ(12345, submessage.int32_value());
}
TEST(AnyTest, TestIs) {
protobuf_unittest::TestAny submessage;
submessage.set_int32_value(12345);
google::protobuf::Any any;
any.PackFrom(submessage);
ASSERT_TRUE(any.ParseFromString(any.SerializeAsString()));
EXPECT_TRUE(any.Is<protobuf_unittest::TestAny>());
EXPECT_FALSE(any.Is<google::protobuf::Any>());
protobuf_unittest::TestAny message;
message.mutable_any_value()->PackFrom(any);
ASSERT_TRUE(message.ParseFromString(message.SerializeAsString()));
EXPECT_FALSE(message.any_value().Is<protobuf_unittest::TestAny>());
EXPECT_TRUE(message.any_value().Is<google::protobuf::Any>());
any.set_type_url("/protobuf_unittest.TestAny");
EXPECT_TRUE(any.Is<protobuf_unittest::TestAny>());
// The type URL must contain at least one "/".
any.set_type_url("protobuf_unittest.TestAny");
EXPECT_FALSE(any.Is<protobuf_unittest::TestAny>());
// The type name after the slash must be fully qualified.
any.set_type_url("/TestAny");
EXPECT_FALSE(any.Is<protobuf_unittest::TestAny>());
}
TEST(AnyTest, MoveConstructor) {
protobuf_unittest::TestAny payload;
payload.set_int32_value(12345);
google::protobuf::Any src;
src.PackFrom(payload);
const char* type_url = src.type_url().data();
google::protobuf::Any dst(std::move(src));
EXPECT_EQ(type_url, dst.type_url().data());
payload.Clear();
ASSERT_TRUE(dst.UnpackTo(&payload));
EXPECT_EQ(12345, payload.int32_value());
}
TEST(AnyTest, MoveAssignment) {
protobuf_unittest::TestAny payload;
payload.set_int32_value(12345);
google::protobuf::Any src;
src.PackFrom(payload);
const char* type_url = src.type_url().data();
google::protobuf::Any dst;
dst = std::move(src);
EXPECT_EQ(type_url, dst.type_url().data());
payload.Clear();
ASSERT_TRUE(dst.UnpackTo(&payload));
EXPECT_EQ(12345, payload.int32_value());
}
#ifdef PROTOBUF_HAS_DEATH_TEST
#ifndef NDEBUG
TEST(AnyTest, PackSelfDeath) {
google::protobuf::Any any;
EXPECT_DEATH(any.PackFrom(any), "&message");
EXPECT_DEATH(any.PackFrom(any, ""), "&message");
}
#endif // !NDEBUG
#endif // PROTOBUF_HAS_DEATH_TEST
} // namespace
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,44 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package protobuf_unittest;
import "google/protobuf/any.proto";
option java_outer_classname = "TestAnyProto";
message TestAny {
int32 int32_value = 1;
google.protobuf.Any any_value = 2;
repeated google.protobuf.Any repeated_any_value = 3;
string text = 4;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
import "google/protobuf/source_context.proto";
import "google/protobuf/type.proto";
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option java_package = "com.google.protobuf";
option java_outer_classname = "ApiProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
option go_package = "google.golang.org/protobuf/types/known/apipb";
// Api is a light-weight descriptor for an API Interface.
//
// Interfaces are also described as "protocol buffer services" in some contexts,
// such as by the "service" keyword in a .proto file, but they are different
// from API Services, which represent a concrete implementation of an interface
// as opposed to simply a description of methods and bindings. They are also
// sometimes simply referred to as "APIs" in other contexts, such as the name of
// this message itself. See https://cloud.google.com/apis/design/glossary for
// detailed terminology.
message Api {
// The fully qualified name of this interface, including package name
// followed by the interface's simple name.
string name = 1;
// The methods of this interface, in unspecified order.
repeated Method methods = 2;
// Any metadata attached to the interface.
repeated Option options = 3;
// A version string for this interface. If specified, must have the form
// `major-version.minor-version`, as in `1.10`. If the minor version is
// omitted, it defaults to zero. If the entire version field is empty, the
// major version is derived from the package name, as outlined below. If the
// field is not empty, the version in the package name will be verified to be
// consistent with what is provided here.
//
// The versioning schema uses [semantic
// versioning](http://semver.org) where the major version number
// indicates a breaking change and the minor version an additive,
// non-breaking change. Both version numbers are signals to users
// what to expect from different versions, and should be carefully
// chosen based on the product plan.
//
// The major version is also reflected in the package name of the
// interface, which must end in `v<major-version>`, as in
// `google.feature.v1`. For major versions 0 and 1, the suffix can
// be omitted. Zero major versions must only be used for
// experimental, non-GA interfaces.
//
//
string version = 4;
// Source context for the protocol buffer service represented by this
// message.
SourceContext source_context = 5;
// Included interfaces. See [Mixin][].
repeated Mixin mixins = 6;
// The source syntax of the service.
Syntax syntax = 7;
}
// Method represents a method of an API interface.
message Method {
// The simple name of this method.
string name = 1;
// A URL of the input message type.
string request_type_url = 2;
// If true, the request is streamed.
bool request_streaming = 3;
// The URL of the output message type.
string response_type_url = 4;
// If true, the response is streamed.
bool response_streaming = 5;
// Any metadata attached to the method.
repeated Option options = 6;
// The source syntax of this method.
Syntax syntax = 7;
}
// Declares an API Interface to be included in this interface. The including
// interface must redeclare all the methods from the included interface, but
// documentation and options are inherited as follows:
//
// - If after comment and whitespace stripping, the documentation
// string of the redeclared method is empty, it will be inherited
// from the original method.
//
// - Each annotation belonging to the service config (http,
// visibility) which is not set in the redeclared method will be
// inherited.
//
// - If an http annotation is inherited, the path pattern will be
// modified as follows. Any version prefix will be replaced by the
// version of the including interface plus the [root][] path if
// specified.
//
// Example of a simple mixin:
//
// package google.acl.v1;
// service AccessControl {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v1/{resource=**}:getAcl";
// }
// }
//
// package google.storage.v2;
// service Storage {
// rpc GetAcl(GetAclRequest) returns (Acl);
//
// // Get a data record.
// rpc GetData(GetDataRequest) returns (Data) {
// option (google.api.http).get = "/v2/{resource=**}";
// }
// }
//
// Example of a mixin configuration:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
//
// The mixin construct implies that all methods in `AccessControl` are
// also declared with same name and request/response types in
// `Storage`. A documentation generator or annotation processor will
// see the effective `Storage.GetAcl` method after inheriting
// documentation and annotations as follows:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/{resource=**}:getAcl";
// }
// ...
// }
//
// Note how the version in the path pattern changed from `v1` to `v2`.
//
// If the `root` field in the mixin is specified, it should be a
// relative path under which inherited HTTP paths are placed. Example:
//
// apis:
// - name: google.storage.v2.Storage
// mixins:
// - name: google.acl.v1.AccessControl
// root: acls
//
// This implies the following inherited HTTP annotation:
//
// service Storage {
// // Get the underlying ACL object.
// rpc GetAcl(GetAclRequest) returns (Acl) {
// option (google.api.http).get = "/v2/acls/{resource=**}:getAcl";
// }
// ...
// }
message Mixin {
// The fully qualified name of the interface which is included.
string name = 1;
// If non-empty specifies a path under which inherited HTTP paths
// are rooted.
string root = 2;
}

View File

@@ -0,0 +1,537 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arena.h>
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <typeinfo>
#include <google/protobuf/arena_impl.h>
#include <google/protobuf/arenaz_sampler.h>
#include <google/protobuf/port.h>
#include <google/protobuf/stubs/mutex.h>
#ifdef ADDRESS_SANITIZER
#include <sanitizer/asan_interface.h>
#endif // ADDRESS_SANITIZER
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
static SerialArena::Memory AllocateMemory(const AllocationPolicy* policy_ptr,
size_t last_size, size_t min_bytes) {
AllocationPolicy policy; // default policy
if (policy_ptr) policy = *policy_ptr;
size_t size;
if (last_size != 0) {
// Double the current block size, up to a limit.
auto max_size = policy.max_block_size;
size = std::min(2 * last_size, max_size);
} else {
size = policy.start_block_size;
}
// Verify that min_bytes + kBlockHeaderSize won't overflow.
GOOGLE_CHECK_LE(min_bytes,
std::numeric_limits<size_t>::max() - SerialArena::kBlockHeaderSize);
size = std::max(size, SerialArena::kBlockHeaderSize + min_bytes);
void* mem;
if (policy.block_alloc == nullptr) {
mem = ::operator new(size);
} else {
mem = policy.block_alloc(size);
}
return {mem, size};
}
class GetDeallocator {
public:
GetDeallocator(const AllocationPolicy* policy, size_t* space_allocated)
: dealloc_(policy ? policy->block_dealloc : nullptr),
space_allocated_(space_allocated) {}
void operator()(SerialArena::Memory mem) const {
#ifdef ADDRESS_SANITIZER
// This memory was provided by the underlying allocator as unpoisoned,
// so return it in an unpoisoned state.
ASAN_UNPOISON_MEMORY_REGION(mem.ptr, mem.size);
#endif // ADDRESS_SANITIZER
if (dealloc_) {
dealloc_(mem.ptr, mem.size);
} else {
internal::SizedDelete(mem.ptr, mem.size);
}
*space_allocated_ += mem.size;
}
private:
void (*dealloc_)(void*, size_t);
size_t* space_allocated_;
};
SerialArena::SerialArena(Block* b, void* owner, ThreadSafeArenaStats* stats)
: space_allocated_(b->size) {
owner_ = owner;
head_ = b;
ptr_ = b->Pointer(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize);
limit_ = b->Pointer(b->size & static_cast<size_t>(-8));
arena_stats_ = stats;
}
SerialArena* SerialArena::New(Memory mem, void* owner,
ThreadSafeArenaStats* stats) {
GOOGLE_DCHECK_LE(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize, mem.size);
ThreadSafeArenaStats::RecordAllocateStats(
stats, /*requested=*/mem.size, /*allocated=*/mem.size, /*wasted=*/0);
auto b = new (mem.ptr) Block{nullptr, mem.size};
return new (b->Pointer(kBlockHeaderSize)) SerialArena(b, owner, stats);
}
template <typename Deallocator>
SerialArena::Memory SerialArena::Free(Deallocator deallocator) {
Block* b = head_;
Memory mem = {b, b->size};
while (b->next) {
b = b->next; // We must first advance before deleting this block
deallocator(mem);
mem = {b, b->size};
}
return mem;
}
PROTOBUF_NOINLINE
std::pair<void*, SerialArena::CleanupNode*>
SerialArena::AllocateAlignedWithCleanupFallback(
size_t n, const AllocationPolicy* policy) {
AllocateNewBlock(n + kCleanupSize, policy);
return AllocateFromExistingWithCleanupFallback(n);
}
PROTOBUF_NOINLINE
void* SerialArena::AllocateAlignedFallback(size_t n,
const AllocationPolicy* policy) {
AllocateNewBlock(n, policy);
return AllocateFromExisting(n);
}
void SerialArena::AllocateNewBlock(size_t n, const AllocationPolicy* policy) {
// Sync limit to block
head_->start = reinterpret_cast<CleanupNode*>(limit_);
// Record how much used in this block.
size_t used = ptr_ - head_->Pointer(kBlockHeaderSize);
size_t wasted = head_->size - used;
space_used_ += used;
// TODO(sbenza): Evaluate if pushing unused space into the cached blocks is a
// win. In preliminary testing showed increased memory savings as expected,
// but with a CPU regression. The regression might have been an artifact of
// the microbenchmark.
auto mem = AllocateMemory(policy, head_->size, n);
// We don't want to emit an expensive RMW instruction that requires
// exclusive access to a cacheline. Hence we write it in terms of a
// regular add.
auto relaxed = std::memory_order_relaxed;
space_allocated_.store(space_allocated_.load(relaxed) + mem.size, relaxed);
ThreadSafeArenaStats::RecordAllocateStats(arena_stats_, /*requested=*/n,
/*allocated=*/mem.size, wasted);
head_ = new (mem.ptr) Block{head_, mem.size};
ptr_ = head_->Pointer(kBlockHeaderSize);
limit_ = head_->Pointer(head_->size);
#ifdef ADDRESS_SANITIZER
ASAN_POISON_MEMORY_REGION(ptr_, limit_ - ptr_);
#endif // ADDRESS_SANITIZER
}
uint64_t SerialArena::SpaceUsed() const {
uint64_t space_used = ptr_ - head_->Pointer(kBlockHeaderSize);
space_used += space_used_;
// Remove the overhead of the SerialArena itself.
space_used -= ThreadSafeArena::kSerialArenaSize;
return space_used;
}
void SerialArena::CleanupList() {
Block* b = head_;
b->start = reinterpret_cast<CleanupNode*>(limit_);
do {
auto* limit = reinterpret_cast<CleanupNode*>(
b->Pointer(b->size & static_cast<size_t>(-8)));
auto it = b->start;
auto num = limit - it;
if (num > 0) {
for (; it < limit; it++) {
it->cleanup(it->elem);
}
}
b = b->next;
} while (b);
}
ThreadSafeArena::CacheAlignedLifecycleIdGenerator
ThreadSafeArena::lifecycle_id_generator_;
#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() {
static internal::ThreadLocalStorage<ThreadCache>* thread_cache_ =
new internal::ThreadLocalStorage<ThreadCache>();
return *thread_cache_->Get();
}
#elif defined(PROTOBUF_USE_DLLS)
ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() {
static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_ = {
0, static_cast<LifecycleIdAtomic>(-1), nullptr};
return thread_cache_;
}
#else
PROTOBUF_THREAD_LOCAL ThreadSafeArena::ThreadCache
ThreadSafeArena::thread_cache_ = {0, static_cast<LifecycleIdAtomic>(-1),
nullptr};
#endif
void ThreadSafeArena::InitializeFrom(void* mem, size_t size) {
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(mem) & 7, 0u);
GOOGLE_DCHECK(!AllocPolicy()); // Reset should call InitializeWithPolicy instead.
Init();
// Ignore initial block if it is too small.
if (mem != nullptr && size >= kBlockHeaderSize + kSerialArenaSize) {
alloc_policy_.set_is_user_owned_initial_block(true);
SetInitialBlock(mem, size);
}
}
void ThreadSafeArena::InitializeWithPolicy(void* mem, size_t size,
AllocationPolicy policy) {
#ifndef NDEBUG
const uint64_t old_alloc_policy = alloc_policy_.get_raw();
// If there was a policy (e.g., in Reset()), make sure flags were preserved.
#define GOOGLE_DCHECK_POLICY_FLAGS_() \
if (old_alloc_policy > 3) \
GOOGLE_CHECK_EQ(old_alloc_policy & 3, alloc_policy_.get_raw() & 3)
#else
#define GOOGLE_DCHECK_POLICY_FLAGS_()
#endif // NDEBUG
if (policy.IsDefault()) {
// Legacy code doesn't use the API above, but provides the initial block
// through ArenaOptions. I suspect most do not touch the allocation
// policy parameters.
InitializeFrom(mem, size);
GOOGLE_DCHECK_POLICY_FLAGS_();
return;
}
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(mem) & 7, 0u);
Init();
// Ignore initial block if it is too small. We include an optional
// AllocationPolicy in this check, so that this can be allocated on the
// first block.
constexpr size_t kAPSize = internal::AlignUpTo8(sizeof(AllocationPolicy));
constexpr size_t kMinimumSize = kBlockHeaderSize + kSerialArenaSize + kAPSize;
// The value for alloc_policy_ stores whether or not allocations should be
// recorded.
alloc_policy_.set_should_record_allocs(
policy.metrics_collector != nullptr &&
policy.metrics_collector->RecordAllocs());
// Make sure we have an initial block to store the AllocationPolicy.
if (mem != nullptr && size >= kMinimumSize) {
alloc_policy_.set_is_user_owned_initial_block(true);
} else {
auto tmp = AllocateMemory(&policy, 0, kMinimumSize);
mem = tmp.ptr;
size = tmp.size;
}
SetInitialBlock(mem, size);
auto sa = threads_.load(std::memory_order_relaxed);
// We ensured enough space so this cannot fail.
void* p;
if (!sa || !sa->MaybeAllocateAligned(kAPSize, &p)) {
GOOGLE_LOG(FATAL) << "MaybeAllocateAligned cannot fail here.";
return;
}
new (p) AllocationPolicy{policy};
// Low bits store flags, so they mustn't be overwritten.
GOOGLE_DCHECK_EQ(0, reinterpret_cast<uintptr_t>(p) & 3);
alloc_policy_.set_policy(reinterpret_cast<AllocationPolicy*>(p));
GOOGLE_DCHECK_POLICY_FLAGS_();
#undef GOOGLE_DCHECK_POLICY_FLAGS_
}
void ThreadSafeArena::Init() {
#ifndef NDEBUG
const bool was_message_owned = IsMessageOwned();
#endif // NDEBUG
ThreadCache& tc = thread_cache();
auto id = tc.next_lifecycle_id;
// We increment lifecycle_id's by multiples of two so we can use bit 0 as
// a tag.
constexpr uint64_t kDelta = 2;
constexpr uint64_t kInc = ThreadCache::kPerThreadIds * kDelta;
if (PROTOBUF_PREDICT_FALSE((id & (kInc - 1)) == 0)) {
constexpr auto relaxed = std::memory_order_relaxed;
// On platforms that don't support uint64_t atomics we can certainly not
// afford to increment by large intervals and expect uniqueness due to
// wrapping, hence we only add by 1.
id = lifecycle_id_generator_.id.fetch_add(1, relaxed) * kInc;
}
tc.next_lifecycle_id = id + kDelta;
// Message ownership is stored in tag_and_id_, and is set in the constructor.
// This flag bit must be preserved, even across calls to Reset().
tag_and_id_ = id | (tag_and_id_ & kMessageOwnedArena);
hint_.store(nullptr, std::memory_order_relaxed);
threads_.store(nullptr, std::memory_order_relaxed);
#ifndef NDEBUG
GOOGLE_CHECK_EQ(was_message_owned, IsMessageOwned());
#endif // NDEBUG
arena_stats_ = Sample();
}
void ThreadSafeArena::SetInitialBlock(void* mem, size_t size) {
SerialArena* serial = SerialArena::New({mem, size}, &thread_cache(),
arena_stats_.MutableStats());
serial->set_next(NULL);
threads_.store(serial, std::memory_order_relaxed);
CacheSerialArena(serial);
}
ThreadSafeArena::~ThreadSafeArena() {
// Have to do this in a first pass, because some of the destructors might
// refer to memory in other blocks.
CleanupList();
size_t space_allocated = 0;
auto mem = Free(&space_allocated);
// Policy is about to get deleted.
auto* p = alloc_policy_.get();
ArenaMetricsCollector* collector = p ? p->metrics_collector : nullptr;
if (alloc_policy_.is_user_owned_initial_block()) {
#ifdef ADDRESS_SANITIZER
// Unpoison the initial block, now that it's going back to the user.
ASAN_UNPOISON_MEMORY_REGION(mem.ptr, mem.size);
#endif // ADDRESS_SANITIZER
space_allocated += mem.size;
} else {
GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
}
if (collector) collector->OnDestroy(space_allocated);
}
SerialArena::Memory ThreadSafeArena::Free(size_t* space_allocated) {
SerialArena::Memory mem = {nullptr, 0};
auto deallocator = GetDeallocator(alloc_policy_.get(), space_allocated);
PerSerialArena([deallocator, &mem](SerialArena* a) {
if (mem.ptr) deallocator(mem);
mem = a->Free(deallocator);
});
return mem;
}
uint64_t ThreadSafeArena::Reset() {
// Have to do this in a first pass, because some of the destructors might
// refer to memory in other blocks.
CleanupList();
// Discard all blocks except the special block (if present).
size_t space_allocated = 0;
auto mem = Free(&space_allocated);
arena_stats_.RecordReset();
AllocationPolicy* policy = alloc_policy_.get();
if (policy) {
auto saved_policy = *policy;
if (alloc_policy_.is_user_owned_initial_block()) {
space_allocated += mem.size;
} else {
GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
mem.ptr = nullptr;
mem.size = 0;
}
ArenaMetricsCollector* collector = saved_policy.metrics_collector;
if (collector) collector->OnReset(space_allocated);
InitializeWithPolicy(mem.ptr, mem.size, saved_policy);
} else {
GOOGLE_DCHECK(!alloc_policy_.should_record_allocs());
// Nullptr policy
if (alloc_policy_.is_user_owned_initial_block()) {
space_allocated += mem.size;
InitializeFrom(mem.ptr, mem.size);
} else {
GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
Init();
}
}
return space_allocated;
}
std::pair<void*, SerialArena::CleanupNode*>
ThreadSafeArena::AllocateAlignedWithCleanup(size_t n,
const std::type_info* type) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
GetSerialArenaFast(&arena))) {
return arena->AllocateAlignedWithCleanup(n, alloc_policy_.get());
} else {
return AllocateAlignedWithCleanupFallback(n, type);
}
}
void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) {
SerialArena* arena;
if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) {
arena = GetSerialArenaFallback(&thread_cache());
}
arena->AddCleanup(elem, cleanup, AllocPolicy());
}
PROTOBUF_NOINLINE
void* ThreadSafeArena::AllocateAlignedFallback(size_t n,
const std::type_info* type) {
if (alloc_policy_.should_record_allocs()) {
alloc_policy_.RecordAlloc(type, n);
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
return arena->AllocateAligned(n, alloc_policy_.get());
}
}
return GetSerialArenaFallback(&thread_cache())
->AllocateAligned(n, alloc_policy_.get());
}
PROTOBUF_NOINLINE
std::pair<void*, SerialArena::CleanupNode*>
ThreadSafeArena::AllocateAlignedWithCleanupFallback(
size_t n, const std::type_info* type) {
if (alloc_policy_.should_record_allocs()) {
alloc_policy_.RecordAlloc(type, n);
SerialArena* arena;
if (GetSerialArenaFast(&arena)) {
return arena->AllocateAlignedWithCleanup(n, alloc_policy_.get());
}
}
return GetSerialArenaFallback(&thread_cache())
->AllocateAlignedWithCleanup(n, alloc_policy_.get());
}
uint64_t ThreadSafeArena::SpaceAllocated() const {
SerialArena* serial = threads_.load(std::memory_order_acquire);
uint64_t res = 0;
for (; serial; serial = serial->next()) {
res += serial->SpaceAllocated();
}
return res;
}
uint64_t ThreadSafeArena::SpaceUsed() const {
SerialArena* serial = threads_.load(std::memory_order_acquire);
uint64_t space_used = 0;
for (; serial; serial = serial->next()) {
space_used += serial->SpaceUsed();
}
return space_used - (alloc_policy_.get() ? sizeof(AllocationPolicy) : 0);
}
void ThreadSafeArena::CleanupList() {
PerSerialArena([](SerialArena* a) { a->CleanupList(); });
}
PROTOBUF_NOINLINE
SerialArena* ThreadSafeArena::GetSerialArenaFallback(void* me) {
// Look for this SerialArena in our linked list.
SerialArena* serial = threads_.load(std::memory_order_acquire);
for (; serial; serial = serial->next()) {
if (serial->owner() == me) {
break;
}
}
if (!serial) {
// This thread doesn't have any SerialArena, which also means it doesn't
// have any blocks yet. So we'll allocate its first block now.
serial = SerialArena::New(
AllocateMemory(alloc_policy_.get(), 0, kSerialArenaSize), me,
arena_stats_.MutableStats());
SerialArena* head = threads_.load(std::memory_order_relaxed);
do {
serial->set_next(head);
} while (!threads_.compare_exchange_weak(
head, serial, std::memory_order_release, std::memory_order_relaxed));
}
CacheSerialArena(serial);
return serial;
}
} // namespace internal
PROTOBUF_FUNC_ALIGN(32)
void* Arena::AllocateAlignedNoHook(size_t n) {
return impl_.AllocateAligned(n, nullptr);
}
PROTOBUF_FUNC_ALIGN(32)
void* Arena::AllocateAlignedWithHook(size_t n, const std::type_info* type) {
return impl_.AllocateAligned(n, type);
}
PROTOBUF_FUNC_ALIGN(32)
void* Arena::AllocateAlignedWithHookForArray(size_t n,
const std::type_info* type) {
return impl_.AllocateAligned<internal::AllocationClient::kArray>(n, type);
}
PROTOBUF_FUNC_ALIGN(32)
std::pair<void*, internal::SerialArena::CleanupNode*>
Arena::AllocateAlignedWithCleanup(size_t n, const std::type_info* type) {
return impl_.AllocateAlignedWithCleanup(n, type);
}
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,851 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file defines an Arena allocator for better allocation performance.
#ifndef GOOGLE_PROTOBUF_ARENA_H__
#define GOOGLE_PROTOBUF_ARENA_H__
#include <limits>
#include <type_traits>
#include <utility>
#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER) && !_HAS_EXCEPTIONS
// Work around bugs in MSVC <typeinfo> header when _HAS_EXCEPTIONS=0.
#include <exception>
#include <typeinfo>
namespace std {
using type_info = ::type_info;
}
#else
#include <typeinfo>
#endif
#include <type_traits>
#include <google/protobuf/arena_impl.h>
#include <google/protobuf/port.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
#ifdef SWIG
#error "You cannot SWIG proto headers"
#endif
namespace google {
namespace protobuf {
struct ArenaOptions; // defined below
class Arena; // defined below
class Message; // defined in message.h
class MessageLite;
template <typename Key, typename T>
class Map;
namespace arena_metrics {
void EnableArenaMetrics(ArenaOptions* options);
} // namespace arena_metrics
namespace TestUtil {
class ReflectionTester; // defined in test_util.h
} // namespace TestUtil
namespace internal {
struct ArenaTestPeer; // defined in arena_test_util.h
class InternalMetadata; // defined in metadata_lite.h
class LazyField; // defined in lazy_field.h
class EpsCopyInputStream; // defined in parse_context.h
class RepeatedPtrFieldBase; // defined in repeated_ptr_field.h
template <typename Type>
class GenericTypeHandler; // defined in repeated_field.h
inline PROTOBUF_ALWAYS_INLINE
void* AlignTo(void* ptr, size_t align) {
return reinterpret_cast<void*>(
(reinterpret_cast<uintptr_t>(ptr) + align - 1) & (~align + 1));
}
// Templated cleanup methods.
template <typename T>
void arena_destruct_object(void* object) {
reinterpret_cast<T*>(object)->~T();
}
template <bool destructor_skippable, typename T>
struct ObjectDestructor {
constexpr static void (*destructor)(void*) = &arena_destruct_object<T>;
};
template <typename T>
struct ObjectDestructor<true, T> {
constexpr static void (*destructor)(void*) = nullptr;
};
template <typename T>
void arena_delete_object(void* object) {
delete reinterpret_cast<T*>(object);
}
} // namespace internal
// ArenaOptions provides optional additional parameters to arena construction
// that control its block-allocation behavior.
struct ArenaOptions {
// This defines the size of the first block requested from the system malloc.
// Subsequent block sizes will increase in a geometric series up to a maximum.
size_t start_block_size;
// This defines the maximum block size requested from system malloc (unless an
// individual arena allocation request occurs with a size larger than this
// maximum). Requested block sizes increase up to this value, then remain
// here.
size_t max_block_size;
// An initial block of memory for the arena to use, or NULL for none. If
// provided, the block must live at least as long as the arena itself. The
// creator of the Arena retains ownership of the block after the Arena is
// destroyed.
char* initial_block;
// The size of the initial block, if provided.
size_t initial_block_size;
// A function pointer to an alloc method that returns memory blocks of size
// requested. By default, it contains a ptr to the malloc function.
//
// NOTE: block_alloc and dealloc functions are expected to behave like
// malloc and free, including Asan poisoning.
void* (*block_alloc)(size_t);
// A function pointer to a dealloc method that takes ownership of the blocks
// from the arena. By default, it contains a ptr to a wrapper function that
// calls free.
void (*block_dealloc)(void*, size_t);
ArenaOptions()
: start_block_size(internal::AllocationPolicy::kDefaultStartBlockSize),
max_block_size(internal::AllocationPolicy::kDefaultMaxBlockSize),
initial_block(NULL),
initial_block_size(0),
block_alloc(nullptr),
block_dealloc(nullptr),
make_metrics_collector(nullptr) {}
private:
// If make_metrics_collector is not nullptr, it will be called at Arena init
// time. It may return a pointer to a collector instance that will be notified
// of interesting events related to the arena.
internal::ArenaMetricsCollector* (*make_metrics_collector)();
internal::ArenaMetricsCollector* MetricsCollector() const {
return make_metrics_collector ? (*make_metrics_collector)() : nullptr;
}
internal::AllocationPolicy AllocationPolicy() const {
internal::AllocationPolicy res;
res.start_block_size = start_block_size;
res.max_block_size = max_block_size;
res.block_alloc = block_alloc;
res.block_dealloc = block_dealloc;
res.metrics_collector = MetricsCollector();
return res;
}
friend void arena_metrics::EnableArenaMetrics(ArenaOptions*);
friend class Arena;
friend class ArenaOptionsTestFriend;
};
// Support for non-RTTI environments. (The metrics hooks API uses type
// information.)
#if PROTOBUF_RTTI
#define RTTI_TYPE_ID(type) (&typeid(type))
#else
#define RTTI_TYPE_ID(type) (NULL)
#endif
// Arena allocator. Arena allocation replaces ordinary (heap-based) allocation
// with new/delete, and improves performance by aggregating allocations into
// larger blocks and freeing allocations all at once. Protocol messages are
// allocated on an arena by using Arena::CreateMessage<T>(Arena*), below, and
// are automatically freed when the arena is destroyed.
//
// This is a thread-safe implementation: multiple threads may allocate from the
// arena concurrently. Destruction is not thread-safe and the destructing
// thread must synchronize with users of the arena first.
//
// An arena provides two allocation interfaces: CreateMessage<T>, which works
// for arena-enabled proto2 message types as well as other types that satisfy
// the appropriate protocol (described below), and Create<T>, which works for
// any arbitrary type T. CreateMessage<T> is better when the type T supports it,
// because this interface (i) passes the arena pointer to the created object so
// that its sub-objects and internal allocations can use the arena too, and (ii)
// elides the object's destructor call when possible. Create<T> does not place
// any special requirements on the type T, and will invoke the object's
// destructor when the arena is destroyed.
//
// The arena message allocation protocol, required by
// CreateMessage<T>(Arena* arena, Args&&... args), is as follows:
//
// - The type T must have (at least) two constructors: a constructor callable
// with `args` (without `arena`), called when a T is allocated on the heap;
// and a constructor callable with `Arena* arena, Args&&... args`, called when
// a T is allocated on an arena. If the second constructor is called with a
// NULL arena pointer, it must be equivalent to invoking the first
// (`args`-only) constructor.
//
// - The type T must have a particular type trait: a nested type
// |InternalArenaConstructable_|. This is usually a typedef to |void|. If no
// such type trait exists, then the instantiation CreateMessage<T> will fail
// to compile.
//
// - The type T *may* have the type trait |DestructorSkippable_|. If this type
// trait is present in the type, then its destructor will not be called if and
// only if it was passed a non-NULL arena pointer. If this type trait is not
// present on the type, then its destructor is always called when the
// containing arena is destroyed.
//
// This protocol is implemented by all arena-enabled proto2 message classes as
// well as protobuf container types like RepeatedPtrField and Map. The protocol
// is internal to protobuf and is not guaranteed to be stable. Non-proto types
// should not rely on this protocol.
class PROTOBUF_EXPORT PROTOBUF_ALIGNAS(8) Arena final {
public:
// Default constructor with sensible default options, tuned for average
// use-cases.
inline Arena() : impl_() {}
// Construct an arena with default options, except for the supplied
// initial block. It is more efficient to use this constructor
// instead of passing ArenaOptions if the only configuration needed
// by the caller is supplying an initial block.
inline Arena(char* initial_block, size_t initial_block_size)
: impl_(initial_block, initial_block_size) {}
// Arena constructor taking custom options. See ArenaOptions above for
// descriptions of the options available.
explicit Arena(const ArenaOptions& options)
: impl_(options.initial_block, options.initial_block_size,
options.AllocationPolicy()) {}
// Block overhead. Use this as a guide for how much to over-allocate the
// initial block if you want an allocation of size N to fit inside it.
//
// WARNING: if you allocate multiple objects, it is difficult to guarantee
// that a series of allocations will fit in the initial block, especially if
// Arena changes its alignment guarantees in the future!
static const size_t kBlockOverhead =
internal::ThreadSafeArena::kBlockHeaderSize +
internal::ThreadSafeArena::kSerialArenaSize;
inline ~Arena() {}
// TODO(protobuf-team): Fix callers to use constructor and delete this method.
void Init(const ArenaOptions&) {}
// API to create proto2 message objects on the arena. If the arena passed in
// is NULL, then a heap allocated object is returned. Type T must be a message
// defined in a .proto file with cc_enable_arenas set to true, otherwise a
// compilation error will occur.
//
// RepeatedField and RepeatedPtrField may also be instantiated directly on an
// arena with this method.
//
// This function also accepts any type T that satisfies the arena message
// allocation protocol, documented above.
template <typename T, typename... Args>
PROTOBUF_ALWAYS_INLINE static T* CreateMessage(Arena* arena, Args&&... args) {
static_assert(
InternalHelper<T>::is_arena_constructable::value,
"CreateMessage can only construct types that are ArenaConstructable");
// We must delegate to CreateMaybeMessage() and NOT CreateMessageInternal()
// because protobuf generated classes specialize CreateMaybeMessage() and we
// need to use that specialization for code size reasons.
return Arena::CreateMaybeMessage<T>(arena, static_cast<Args&&>(args)...);
}
// API to create any objects on the arena. Note that only the object will
// be created on the arena; the underlying ptrs (in case of a proto2 message)
// will be still heap allocated. Proto messages should usually be allocated
// with CreateMessage<T>() instead.
//
// Note that even if T satisfies the arena message construction protocol
// (InternalArenaConstructable_ trait and optional DestructorSkippable_
// trait), as described above, this function does not follow the protocol;
// instead, it treats T as a black-box type, just as if it did not have these
// traits. Specifically, T's constructor arguments will always be only those
// passed to Create<T>() -- no additional arena pointer is implicitly added.
// Furthermore, the destructor will always be called at arena destruction time
// (unless the destructor is trivial). Hence, from T's point of view, it is as
// if the object were allocated on the heap (except that the underlying memory
// is obtained from the arena).
template <typename T, typename... Args>
PROTOBUF_NDEBUG_INLINE static T* Create(Arena* arena, Args&&... args) {
return CreateInternal<T>(arena, std::is_convertible<T*, MessageLite*>(),
static_cast<Args&&>(args)...);
}
// Allocates memory with the specific size and alignment.
void* AllocateAligned(size_t size, size_t align = 8) {
if (align <= 8) {
return AllocateAlignedNoHook(internal::AlignUpTo8(size));
} else {
// We are wasting space by over allocating align - 8 bytes. Compared
// to a dedicated function that takes current alignment in consideration.
// Such a scheme would only waste (align - 8)/2 bytes on average, but
// requires a dedicated function in the outline arena allocation
// functions. Possibly re-evaluate tradeoffs later.
return internal::AlignTo(AllocateAlignedNoHook(size + align - 8), align);
}
}
// Create an array of object type T on the arena *without* invoking the
// constructor of T. If `arena` is null, then the return value should be freed
// with `delete[] x;` (or `::operator delete[](x);`).
// To ensure safe uses, this function checks at compile time
// (when compiled as C++11) that T is trivially default-constructible and
// trivially destructible.
template <typename T>
PROTOBUF_NDEBUG_INLINE static T* CreateArray(Arena* arena,
size_t num_elements) {
static_assert(std::is_trivial<T>::value,
"CreateArray requires a trivially constructible type");
static_assert(std::is_trivially_destructible<T>::value,
"CreateArray requires a trivially destructible type");
GOOGLE_CHECK_LE(num_elements, std::numeric_limits<size_t>::max() / sizeof(T))
<< "Requested size is too large to fit into size_t.";
if (arena == NULL) {
return static_cast<T*>(::operator new[](num_elements * sizeof(T)));
} else {
return arena->CreateInternalRawArray<T>(num_elements);
}
}
// The following are routines are for monitoring. They will approximate the
// total sum allocated and used memory, but the exact value is an
// implementation deal. For instance allocated space depends on growth
// policies. Do not use these in unit tests.
// Returns the total space allocated by the arena, which is the sum of the
// sizes of the underlying blocks.
uint64_t SpaceAllocated() const { return impl_.SpaceAllocated(); }
// Returns the total space used by the arena. Similar to SpaceAllocated but
// does not include free space and block overhead. The total space returned
// may not include space used by other threads executing concurrently with
// the call to this method.
uint64_t SpaceUsed() const { return impl_.SpaceUsed(); }
// Frees all storage allocated by this arena after calling destructors
// registered with OwnDestructor() and freeing objects registered with Own().
// Any objects allocated on this arena are unusable after this call. It also
// returns the total space used by the arena which is the sums of the sizes
// of the allocated blocks. This method is not thread-safe.
uint64_t Reset() { return impl_.Reset(); }
// Adds |object| to a list of heap-allocated objects to be freed with |delete|
// when the arena is destroyed or reset.
template <typename T>
PROTOBUF_ALWAYS_INLINE void Own(T* object) {
OwnInternal(object, std::is_convertible<T*, MessageLite*>());
}
// Adds |object| to a list of objects whose destructors will be manually
// called when the arena is destroyed or reset. This differs from Own() in
// that it does not free the underlying memory with |delete|; hence, it is
// normally only used for objects that are placement-newed into
// arena-allocated memory.
template <typename T>
PROTOBUF_ALWAYS_INLINE void OwnDestructor(T* object) {
if (object != NULL) {
impl_.AddCleanup(object, &internal::arena_destruct_object<T>);
}
}
// Adds a custom member function on an object to the list of destructors that
// will be manually called when the arena is destroyed or reset. This differs
// from OwnDestructor() in that any member function may be specified, not only
// the class destructor.
PROTOBUF_ALWAYS_INLINE void OwnCustomDestructor(void* object,
void (*destruct)(void*)) {
impl_.AddCleanup(object, destruct);
}
// Retrieves the arena associated with |value| if |value| is an arena-capable
// message, or NULL otherwise. If possible, the call resolves at compile time.
// Note that we can often devirtualize calls to `value->GetArena()` so usually
// calling this method is unnecessary.
template <typename T>
PROTOBUF_ALWAYS_INLINE static Arena* GetArena(const T* value) {
return GetArenaInternal(value);
}
template <typename T>
class InternalHelper {
private:
// Provides access to protected GetOwningArena to generated messages.
static Arena* GetOwningArena(const T* p) { return p->GetOwningArena(); }
static void InternalSwap(T* a, T* b) { a->InternalSwap(b); }
static Arena* GetArenaForAllocationInternal(
const T* p, std::true_type /*is_derived_from<MessageLite>*/) {
return p->GetArenaForAllocation();
}
static Arena* GetArenaForAllocationInternal(
const T* p, std::false_type /*is_derived_from<MessageLite>*/) {
return GetArenaForAllocationForNonMessage(
p, typename is_arena_constructable::type());
}
static Arena* GetArenaForAllocationForNonMessage(
const T* p, std::true_type /*is_arena_constructible*/) {
return p->GetArena();
}
static Arena* GetArenaForAllocationForNonMessage(
const T* p, std::false_type /*is_arena_constructible*/) {
return GetArenaForAllocationForNonMessageNonArenaConstructible(
p, typename has_get_arena::type());
}
static Arena* GetArenaForAllocationForNonMessageNonArenaConstructible(
const T* p, std::true_type /*has_get_arena*/) {
return p->GetArena();
}
static Arena* GetArenaForAllocationForNonMessageNonArenaConstructible(
const T* /* p */, std::false_type /*has_get_arena*/) {
return nullptr;
}
template <typename U>
static char DestructorSkippable(const typename U::DestructorSkippable_*);
template <typename U>
static double DestructorSkippable(...);
typedef std::integral_constant<
bool, sizeof(DestructorSkippable<T>(static_cast<const T*>(0))) ==
sizeof(char) ||
std::is_trivially_destructible<T>::value>
is_destructor_skippable;
template <typename U>
static char ArenaConstructable(
const typename U::InternalArenaConstructable_*);
template <typename U>
static double ArenaConstructable(...);
typedef std::integral_constant<bool, sizeof(ArenaConstructable<T>(
static_cast<const T*>(0))) ==
sizeof(char)>
is_arena_constructable;
template <typename U,
typename std::enable_if<
std::is_same<Arena*, decltype(std::declval<const U>()
.GetArena())>::value,
int>::type = 0>
static char HasGetArena(decltype(&U::GetArena));
template <typename U>
static double HasGetArena(...);
typedef std::integral_constant<bool, sizeof(HasGetArena<T>(nullptr)) ==
sizeof(char)>
has_get_arena;
template <typename... Args>
static T* Construct(void* ptr, Args&&... args) {
return new (ptr) T(static_cast<Args&&>(args)...);
}
static inline PROTOBUF_ALWAYS_INLINE T* New() {
return new T(nullptr);
}
static Arena* GetArena(const T* p) { return p->GetArena(); }
friend class Arena;
friend class TestUtil::ReflectionTester;
};
// Provides access to protected GetOwningArena to generated messages. For
// internal use only.
template <typename T>
static Arena* InternalGetOwningArena(const T* p) {
return InternalHelper<T>::GetOwningArena(p);
}
// Provides access to protected GetArenaForAllocation to generated messages.
// For internal use only.
template <typename T>
static Arena* InternalGetArenaForAllocation(const T* p) {
return InternalHelper<T>::GetArenaForAllocationInternal(
p, std::is_convertible<T*, MessageLite*>());
}
// Creates message-owned arena. For internal use only.
static Arena* InternalCreateMessageOwnedArena() {
return new Arena(internal::MessageOwned{});
}
// Checks whether this arena is message-owned. For internal use only.
bool InternalIsMessageOwnedArena() { return IsMessageOwned(); }
// Helper typetraits that indicates support for arenas in a type T at compile
// time. This is public only to allow construction of higher-level templated
// utilities.
//
// is_arena_constructable<T>::value is true if the message type T has arena
// support enabled, and false otherwise.
//
// is_destructor_skippable<T>::value is true if the message type T has told
// the arena that it is safe to skip the destructor, and false otherwise.
//
// This is inside Arena because only Arena has the friend relationships
// necessary to see the underlying generated code traits.
template <typename T>
struct is_arena_constructable : InternalHelper<T>::is_arena_constructable {};
template <typename T>
struct is_destructor_skippable : InternalHelper<T>::is_destructor_skippable {
};
private:
internal::ThreadSafeArena impl_;
template <typename T>
struct has_get_arena : InternalHelper<T>::has_get_arena {};
// Constructor solely used by message-owned arena.
inline Arena(internal::MessageOwned) : impl_(internal::MessageOwned{}) {}
// Checks whether this arena is message-owned.
PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
return impl_.IsMessageOwned();
}
void ReturnArrayMemory(void* p, size_t size) {
impl_.ReturnArrayMemory(p, size);
}
template <typename T, typename... Args>
PROTOBUF_NDEBUG_INLINE static T* CreateMessageInternal(Arena* arena,
Args&&... args) {
static_assert(
InternalHelper<T>::is_arena_constructable::value,
"CreateMessage can only construct types that are ArenaConstructable");
if (arena == NULL) {
return new T(nullptr, static_cast<Args&&>(args)...);
} else {
return arena->DoCreateMessage<T>(static_cast<Args&&>(args)...);
}
}
// This specialization for no arguments is necessary, because its behavior is
// slightly different. When the arena pointer is nullptr, it calls T()
// instead of T(nullptr).
template <typename T>
PROTOBUF_NDEBUG_INLINE static T* CreateMessageInternal(Arena* arena) {
static_assert(
InternalHelper<T>::is_arena_constructable::value,
"CreateMessage can only construct types that are ArenaConstructable");
if (arena == NULL) {
// Generated arena constructor T(Arena*) is protected. Call via
// InternalHelper.
return InternalHelper<T>::New();
} else {
return arena->DoCreateMessage<T>();
}
}
// Allocate and also optionally call collector with the allocated type info
// when allocation recording is enabled.
PROTOBUF_NDEBUG_INLINE void* AllocateInternal(size_t size, size_t align,
void (*destructor)(void*),
const std::type_info* type) {
// Monitor allocation if needed.
if (destructor == nullptr) {
return AllocateAlignedWithHook(size, align, type);
} else {
if (align <= 8) {
auto res = AllocateAlignedWithCleanup(internal::AlignUpTo8(size), type);
res.second->elem = res.first;
res.second->cleanup = destructor;
return res.first;
} else {
auto res = AllocateAlignedWithCleanup(size + align - 8, type);
auto ptr = internal::AlignTo(res.first, align);
res.second->elem = ptr;
res.second->cleanup = destructor;
return ptr;
}
}
}
// CreateMessage<T> requires that T supports arenas, but this private method
// works whether or not T supports arenas. These are not exposed to user code
// as it can cause confusing API usages, and end up having double free in
// user code. These are used only internally from LazyField and Repeated
// fields, since they are designed to work in all mode combinations.
template <typename Msg, typename... Args>
PROTOBUF_ALWAYS_INLINE static Msg* DoCreateMaybeMessage(Arena* arena,
std::true_type,
Args&&... args) {
return CreateMessageInternal<Msg>(arena, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
PROTOBUF_ALWAYS_INLINE static T* DoCreateMaybeMessage(Arena* arena,
std::false_type,
Args&&... args) {
return Create<T>(arena, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
PROTOBUF_ALWAYS_INLINE static T* CreateMaybeMessage(Arena* arena,
Args&&... args) {
return DoCreateMaybeMessage<T>(arena, is_arena_constructable<T>(),
std::forward<Args>(args)...);
}
// Just allocate the required size for the given type assuming the
// type has a trivial constructor.
template <typename T>
PROTOBUF_NDEBUG_INLINE T* CreateInternalRawArray(size_t num_elements) {
GOOGLE_CHECK_LE(num_elements, std::numeric_limits<size_t>::max() / sizeof(T))
<< "Requested size is too large to fit into size_t.";
// We count on compiler to realize that if sizeof(T) is a multiple of
// 8 AlignUpTo can be elided.
const size_t n = sizeof(T) * num_elements;
return static_cast<T*>(
AllocateAlignedWithHookForArray(n, alignof(T), RTTI_TYPE_ID(T)));
}
template <typename T, typename... Args>
PROTOBUF_NDEBUG_INLINE T* DoCreateMessage(Args&&... args) {
return InternalHelper<T>::Construct(
AllocateInternal(sizeof(T), alignof(T),
internal::ObjectDestructor<
InternalHelper<T>::is_destructor_skippable::value,
T>::destructor,
RTTI_TYPE_ID(T)),
this, std::forward<Args>(args)...);
}
// CreateInArenaStorage is used to implement map field. Without it,
// Map need to call generated message's protected arena constructor,
// which needs to declare Map as friend of generated message.
template <typename T, typename... Args>
static void CreateInArenaStorage(T* ptr, Arena* arena, Args&&... args) {
CreateInArenaStorageInternal(ptr, arena,
typename is_arena_constructable<T>::type(),
std::forward<Args>(args)...);
if (arena != nullptr) {
RegisterDestructorInternal(
ptr, arena,
typename InternalHelper<T>::is_destructor_skippable::type());
}
}
template <typename T, typename... Args>
static void CreateInArenaStorageInternal(T* ptr, Arena* arena,
std::true_type, Args&&... args) {
InternalHelper<T>::Construct(ptr, arena, std::forward<Args>(args)...);
}
template <typename T, typename... Args>
static void CreateInArenaStorageInternal(T* ptr, Arena* /* arena */,
std::false_type, Args&&... args) {
new (ptr) T(std::forward<Args>(args)...);
}
template <typename T>
static void RegisterDestructorInternal(T* /* ptr */, Arena* /* arena */,
std::true_type) {}
template <typename T>
static void RegisterDestructorInternal(T* ptr, Arena* arena,
std::false_type) {
arena->OwnDestructor(ptr);
}
// These implement Create(). The second parameter has type 'true_type' if T is
// a subtype of Message and 'false_type' otherwise.
template <typename T, typename... Args>
PROTOBUF_ALWAYS_INLINE static T* CreateInternal(Arena* arena, std::true_type,
Args&&... args) {
if (arena == nullptr) {
return new T(std::forward<Args>(args)...);
} else {
auto destructor =
internal::ObjectDestructor<std::is_trivially_destructible<T>::value,
T>::destructor;
T* result =
new (arena->AllocateInternal(sizeof(T), alignof(T), destructor,
RTTI_TYPE_ID(T)))
T(std::forward<Args>(args)...);
return result;
}
}
template <typename T, typename... Args>
PROTOBUF_ALWAYS_INLINE static T* CreateInternal(Arena* arena, std::false_type,
Args&&... args) {
if (arena == nullptr) {
return new T(std::forward<Args>(args)...);
} else {
auto destructor =
internal::ObjectDestructor<std::is_trivially_destructible<T>::value,
T>::destructor;
return new (arena->AllocateInternal(sizeof(T), alignof(T), destructor,
RTTI_TYPE_ID(T)))
T(std::forward<Args>(args)...);
}
}
// These implement Own(), which registers an object for deletion (destructor
// call and operator delete()). The second parameter has type 'true_type' if T
// is a subtype of Message and 'false_type' otherwise. Collapsing
// all template instantiations to one for generic Message reduces code size,
// using the virtual destructor instead.
template <typename T>
PROTOBUF_ALWAYS_INLINE void OwnInternal(T* object, std::true_type) {
if (object != NULL) {
impl_.AddCleanup(object, &internal::arena_delete_object<MessageLite>);
}
}
template <typename T>
PROTOBUF_ALWAYS_INLINE void OwnInternal(T* object, std::false_type) {
if (object != NULL) {
impl_.AddCleanup(object, &internal::arena_delete_object<T>);
}
}
// Implementation for GetArena(). Only message objects with
// InternalArenaConstructable_ tags can be associated with an arena, and such
// objects must implement a GetArena() method.
template <typename T, typename std::enable_if<
is_arena_constructable<T>::value, int>::type = 0>
PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
return InternalHelper<T>::GetArena(value);
}
template <typename T,
typename std::enable_if<!is_arena_constructable<T>::value &&
has_get_arena<T>::value,
int>::type = 0>
PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
return value->GetArena();
}
template <typename T,
typename std::enable_if<!is_arena_constructable<T>::value &&
!has_get_arena<T>::value,
int>::type = 0>
PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
(void)value;
return nullptr;
}
template <typename T>
PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArena(const T* value) {
return GetOwningArenaInternal(
value, std::is_convertible<T*, MessageLite*>());
}
// Implementation for GetOwningArena(). All and only message objects have
// GetOwningArena() method.
template <typename T>
PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArenaInternal(
const T* value, std::true_type) {
return InternalHelper<T>::GetOwningArena(value);
}
template <typename T>
PROTOBUF_ALWAYS_INLINE static Arena* GetOwningArenaInternal(
const T* /* value */, std::false_type) {
return nullptr;
}
void* AllocateAlignedWithHookForArray(size_t n, size_t align,
const std::type_info* type) {
if (align <= 8) {
return AllocateAlignedWithHookForArray(internal::AlignUpTo8(n), type);
} else {
// We are wasting space by over allocating align - 8 bytes. Compared
// to a dedicated function that takes current alignment in consideration.
// Such a scheme would only waste (align - 8)/2 bytes on average, but
// requires a dedicated function in the outline arena allocation
// functions. Possibly re-evaluate tradeoffs later.
return internal::AlignTo(
AllocateAlignedWithHookForArray(n + align - 8, type), align);
}
}
void* AllocateAlignedWithHook(size_t n, size_t align,
const std::type_info* type) {
if (align <= 8) {
return AllocateAlignedWithHook(internal::AlignUpTo8(n), type);
} else {
// We are wasting space by over allocating align - 8 bytes. Compared
// to a dedicated function that takes current alignment in consideration.
// Such a scheme would only waste (align - 8)/2 bytes on average, but
// requires a dedicated function in the outline arena allocation
// functions. Possibly re-evaluate tradeoffs later.
return internal::AlignTo(AllocateAlignedWithHook(n + align - 8, type),
align);
}
}
void* AllocateAlignedNoHook(size_t n);
void* AllocateAlignedWithHook(size_t n, const std::type_info* type);
void* AllocateAlignedWithHookForArray(size_t n, const std::type_info* type);
std::pair<void*, internal::SerialArena::CleanupNode*>
AllocateAlignedWithCleanup(size_t n, const std::type_info* type);
template <typename Type>
friend class internal::GenericTypeHandler;
friend class internal::InternalMetadata; // For user_arena().
friend class internal::LazyField; // For CreateMaybeMessage.
friend class internal::EpsCopyInputStream; // For parser performance
friend class MessageLite;
template <typename Key, typename T>
friend class Map;
template <typename>
friend class RepeatedField; // For ReturnArrayMemory
friend class internal::RepeatedPtrFieldBase; // For ReturnArrayMemory
friend struct internal::ArenaTestPeer;
};
// Defined above for supporting environments without RTTI.
#undef RTTI_TYPE_ID
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_ARENA_H__

View File

@@ -0,0 +1,686 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file defines an Arena allocator for better allocation performance.
#ifndef GOOGLE_PROTOBUF_ARENA_IMPL_H__
#define GOOGLE_PROTOBUF_ARENA_IMPL_H__
#include <atomic>
#include <limits>
#include <typeinfo>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/port.h>
#ifdef ADDRESS_SANITIZER
#include <sanitizer/asan_interface.h>
#endif // ADDRESS_SANITIZER
#include <google/protobuf/arenaz_sampler.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
// To prevent sharing cache lines between threads
#ifdef __cpp_aligned_new
enum { kCacheAlignment = 64 };
#else
enum { kCacheAlignment = alignof(max_align_t) }; // do the best we can
#endif
inline constexpr size_t AlignUpTo8(size_t n) {
// Align n to next multiple of 8 (from Hacker's Delight, Chapter 3.)
return (n + 7) & static_cast<size_t>(-8);
}
using LifecycleIdAtomic = uint64_t;
// MetricsCollector collects stats for a particular arena.
class PROTOBUF_EXPORT ArenaMetricsCollector {
public:
ArenaMetricsCollector(bool record_allocs) : record_allocs_(record_allocs) {}
// Invoked when the arena is about to be destroyed. This method will
// typically finalize any metric collection and delete the collector.
// space_allocated is the space used by the arena.
virtual void OnDestroy(uint64_t space_allocated) = 0;
// OnReset() is called when the associated arena is reset.
// space_allocated is the space used by the arena just before the reset.
virtual void OnReset(uint64_t space_allocated) = 0;
// OnAlloc is called when an allocation happens.
// type_info is promised to be static - its lifetime extends to
// match program's lifetime (It is given by typeid operator).
// Note: typeid(void) will be passed as allocated_type every time we
// intentionally want to avoid monitoring an allocation. (i.e. internal
// allocations for managing the arena)
virtual void OnAlloc(const std::type_info* allocated_type,
uint64_t alloc_size) = 0;
// Does OnAlloc() need to be called? If false, metric collection overhead
// will be reduced since we will not do extra work per allocation.
bool RecordAllocs() { return record_allocs_; }
protected:
// This class is destructed by the call to OnDestroy().
~ArenaMetricsCollector() = default;
const bool record_allocs_;
};
struct AllocationPolicy {
static constexpr size_t kDefaultStartBlockSize = 256;
static constexpr size_t kDefaultMaxBlockSize = 8192;
size_t start_block_size = kDefaultStartBlockSize;
size_t max_block_size = kDefaultMaxBlockSize;
void* (*block_alloc)(size_t) = nullptr;
void (*block_dealloc)(void*, size_t) = nullptr;
ArenaMetricsCollector* metrics_collector = nullptr;
bool IsDefault() const {
return start_block_size == kDefaultMaxBlockSize &&
max_block_size == kDefaultMaxBlockSize && block_alloc == nullptr &&
block_dealloc == nullptr && metrics_collector == nullptr;
}
};
// Tagged pointer to an AllocationPolicy.
class TaggedAllocationPolicyPtr {
public:
constexpr TaggedAllocationPolicyPtr() : policy_(0) {}
explicit TaggedAllocationPolicyPtr(AllocationPolicy* policy)
: policy_(reinterpret_cast<uintptr_t>(policy)) {}
void set_policy(AllocationPolicy* policy) {
auto bits = policy_ & kTagsMask;
policy_ = reinterpret_cast<uintptr_t>(policy) | bits;
}
AllocationPolicy* get() {
return reinterpret_cast<AllocationPolicy*>(policy_ & kPtrMask);
}
const AllocationPolicy* get() const {
return reinterpret_cast<const AllocationPolicy*>(policy_ & kPtrMask);
}
AllocationPolicy& operator*() { return *get(); }
const AllocationPolicy& operator*() const { return *get(); }
AllocationPolicy* operator->() { return get(); }
const AllocationPolicy* operator->() const { return get(); }
bool is_user_owned_initial_block() const {
return static_cast<bool>(get_mask<kUserOwnedInitialBlock>());
}
void set_is_user_owned_initial_block(bool v) {
set_mask<kUserOwnedInitialBlock>(v);
}
bool should_record_allocs() const {
return static_cast<bool>(get_mask<kRecordAllocs>());
}
void set_should_record_allocs(bool v) { set_mask<kRecordAllocs>(v); }
uintptr_t get_raw() const { return policy_; }
inline void RecordAlloc(const std::type_info* allocated_type,
size_t n) const {
get()->metrics_collector->OnAlloc(allocated_type, n);
}
private:
enum : uintptr_t {
kUserOwnedInitialBlock = 1,
kRecordAllocs = 2,
};
static constexpr uintptr_t kTagsMask = 7;
static constexpr uintptr_t kPtrMask = ~kTagsMask;
template <uintptr_t kMask>
uintptr_t get_mask() const {
return policy_ & kMask;
}
template <uintptr_t kMask>
void set_mask(bool v) {
if (v) {
policy_ |= kMask;
} else {
policy_ &= ~kMask;
}
}
uintptr_t policy_;
};
enum class AllocationClient { kDefault, kArray };
// A simple arena allocator. Calls to allocate functions must be properly
// serialized by the caller, hence this class cannot be used as a general
// purpose allocator in a multi-threaded program. It serves as a building block
// for ThreadSafeArena, which provides a thread-safe arena allocator.
//
// This class manages
// 1) Arena bump allocation + owning memory blocks.
// 2) Maintaining a cleanup list.
// It delagetes the actual memory allocation back to ThreadSafeArena, which
// contains the information on block growth policy and backing memory allocation
// used.
class PROTOBUF_EXPORT SerialArena {
public:
struct Memory {
void* ptr;
size_t size;
};
// Node contains the ptr of the object to be cleaned up and the associated
// cleanup function ptr.
struct CleanupNode {
void* elem; // Pointer to the object to be cleaned up.
void (*cleanup)(void*); // Function pointer to the destructor or deleter.
};
void CleanupList();
uint64_t SpaceAllocated() const {
return space_allocated_.load(std::memory_order_relaxed);
}
uint64_t SpaceUsed() const;
bool HasSpace(size_t n) const {
return n <= static_cast<size_t>(limit_ - ptr_);
}
// See comments on `cached_blocks_` member for details.
PROTOBUF_ALWAYS_INLINE void* TryAllocateFromCachedBlock(size_t size) {
if (PROTOBUF_PREDICT_FALSE(size < 16)) return nullptr;
// We round up to the next larger block in case the memory doesn't match
// the pattern we are looking for.
const size_t index = Bits::Log2FloorNonZero64(size - 1) - 3;
if (index >= cached_block_length_) return nullptr;
auto& cached_head = cached_blocks_[index];
if (cached_head == nullptr) return nullptr;
void* ret = cached_head;
#ifdef ADDRESS_SANITIZER
ASAN_UNPOISON_MEMORY_REGION(ret, size);
#endif // ADDRESS_SANITIZER
cached_head = cached_head->next;
return ret;
}
// In kArray mode we look through cached blocks.
// We do not do this by default because most non-array allocations will not
// have the right size and will fail to find an appropriate cached block.
//
// TODO(sbenza): Evaluate if we should use cached blocks for message types of
// the right size. We can statically know if the allocation size can benefit
// from it.
template <AllocationClient alloc_client = AllocationClient::kDefault>
void* AllocateAligned(size_t n, const AllocationPolicy* policy) {
GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
GOOGLE_DCHECK_GE(limit_, ptr_);
if (alloc_client == AllocationClient::kArray) {
if (void* res = TryAllocateFromCachedBlock(n)) {
return res;
}
}
if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) {
return AllocateAlignedFallback(n, policy);
}
return AllocateFromExisting(n);
}
private:
void* AllocateFromExisting(size_t n) {
void* ret = ptr_;
ptr_ += n;
#ifdef ADDRESS_SANITIZER
ASAN_UNPOISON_MEMORY_REGION(ret, n);
#endif // ADDRESS_SANITIZER
return ret;
}
// See comments on `cached_blocks_` member for details.
void ReturnArrayMemory(void* p, size_t size) {
// We only need to check for 32-bit platforms.
// In 64-bit platforms the minimum allocation size from Repeated*Field will
// be 16 guaranteed.
if (sizeof(void*) < 8) {
if (PROTOBUF_PREDICT_FALSE(size < 16)) return;
} else {
GOOGLE_DCHECK(size >= 16);
}
// We round down to the next smaller block in case the memory doesn't match
// the pattern we are looking for. eg, someone might have called Reserve()
// on the repeated field.
const size_t index = Bits::Log2FloorNonZero64(size) - 4;
if (PROTOBUF_PREDICT_FALSE(index >= cached_block_length_)) {
// We can't put this object on the freelist so make this object the
// freelist. It is guaranteed it is larger than the one we have, and
// large enough to hold another allocation of `size`.
CachedBlock** new_list = static_cast<CachedBlock**>(p);
size_t new_size = size / sizeof(CachedBlock*);
std::copy(cached_blocks_, cached_blocks_ + cached_block_length_,
new_list);
std::fill(new_list + cached_block_length_, new_list + new_size, nullptr);
cached_blocks_ = new_list;
// Make the size fit in uint8_t. This is the power of two, so we don't
// need anything larger.
cached_block_length_ =
static_cast<uint8_t>(std::min(size_t{64}, new_size));
return;
}
auto& cached_head = cached_blocks_[index];
auto* new_node = static_cast<CachedBlock*>(p);
new_node->next = cached_head;
cached_head = new_node;
#ifdef ADDRESS_SANITIZER
ASAN_POISON_MEMORY_REGION(p, size);
#endif // ADDRESS_SANITIZER
}
public:
// Allocate space if the current region provides enough space.
bool MaybeAllocateAligned(size_t n, void** out) {
GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
GOOGLE_DCHECK_GE(limit_, ptr_);
if (PROTOBUF_PREDICT_FALSE(!HasSpace(n))) return false;
*out = AllocateFromExisting(n);
return true;
}
std::pair<void*, CleanupNode*> AllocateAlignedWithCleanup(
size_t n, const AllocationPolicy* policy) {
GOOGLE_DCHECK_EQ(internal::AlignUpTo8(n), n); // Must be already aligned.
if (PROTOBUF_PREDICT_FALSE(!HasSpace(n + kCleanupSize))) {
return AllocateAlignedWithCleanupFallback(n, policy);
}
return AllocateFromExistingWithCleanupFallback(n);
}
private:
std::pair<void*, CleanupNode*> AllocateFromExistingWithCleanupFallback(
size_t n) {
void* ret = ptr_;
ptr_ += n;
limit_ -= kCleanupSize;
#ifdef ADDRESS_SANITIZER
ASAN_UNPOISON_MEMORY_REGION(ret, n);
ASAN_UNPOISON_MEMORY_REGION(limit_, kCleanupSize);
#endif // ADDRESS_SANITIZER
return CreatePair(ret, reinterpret_cast<CleanupNode*>(limit_));
}
public:
void AddCleanup(void* elem, void (*cleanup)(void*),
const AllocationPolicy* policy) {
auto res = AllocateAlignedWithCleanup(0, policy);
res.second->elem = elem;
res.second->cleanup = cleanup;
}
void* owner() const { return owner_; }
SerialArena* next() const { return next_; }
void set_next(SerialArena* next) { next_ = next; }
private:
friend class ThreadSafeArena;
friend class ArenaBenchmark;
// Creates a new SerialArena inside mem using the remaining memory as for
// future allocations.
static SerialArena* New(SerialArena::Memory mem, void* owner,
ThreadSafeArenaStats* stats);
// Free SerialArena returning the memory passed in to New
template <typename Deallocator>
Memory Free(Deallocator deallocator);
// Blocks are variable length malloc-ed objects. The following structure
// describes the common header for all blocks.
struct Block {
Block(Block* next, size_t size) : next(next), size(size), start(nullptr) {}
char* Pointer(size_t n) {
GOOGLE_DCHECK(n <= size);
return reinterpret_cast<char*>(this) + n;
}
Block* const next;
const size_t size;
CleanupNode* start;
// data follows
};
void* owner_; // &ThreadCache of this thread;
Block* head_; // Head of linked list of blocks.
SerialArena* next_; // Next SerialArena in this linked list.
size_t space_used_ = 0; // Necessary for metrics.
std::atomic<size_t> space_allocated_;
// Next pointer to allocate from. Always 8-byte aligned. Points inside
// head_ (and head_->pos will always be non-canonical). We keep these
// here to reduce indirection.
char* ptr_;
// Limiting address up to which memory can be allocated from the head block.
char* limit_;
// For holding sampling information. The pointer is owned by the
// ThreadSafeArena that holds this serial arena.
ThreadSafeArenaStats* arena_stats_;
// Repeated*Field and Arena play together to reduce memory consumption by
// reusing blocks. Currently, natural growth of the repeated field types makes
// them allocate blocks of size `8 + 2^N, N>=3`.
// When the repeated field grows returns the previous block and we put it in
// this free list.
// `cached_blocks_[i]` points to the free list for blocks of size `8+2^(i+3)`.
// The array of freelists is grown when needed in `ReturnArrayMemory()`.
struct CachedBlock {
// Simple linked list.
CachedBlock* next;
};
uint8_t cached_block_length_ = 0;
CachedBlock** cached_blocks_ = nullptr;
// Constructor is private as only New() should be used.
inline SerialArena(Block* b, void* owner, ThreadSafeArenaStats* stats);
void* AllocateAlignedFallback(size_t n, const AllocationPolicy* policy);
std::pair<void*, CleanupNode*> AllocateAlignedWithCleanupFallback(
size_t n, const AllocationPolicy* policy);
void AllocateNewBlock(size_t n, const AllocationPolicy* policy);
std::pair<void*, CleanupNode*> CreatePair(void* ptr, CleanupNode* node) {
return {ptr, node};
}
public:
static constexpr size_t kBlockHeaderSize = AlignUpTo8(sizeof(Block));
static constexpr size_t kCleanupSize = AlignUpTo8(sizeof(CleanupNode));
};
// Tag type used to invoke the constructor of message-owned arena.
// Only message-owned arenas use this constructor for creation.
// Such constructors are internal implementation details of the library.
struct MessageOwned {
explicit MessageOwned() = default;
};
// This class provides the core Arena memory allocation library. Different
// implementations only need to implement the public interface below.
// Arena is not a template type as that would only be useful if all protos
// in turn would be templates, which will/cannot happen. However separating
// the memory allocation part from the cruft of the API users expect we can
// use #ifdef the select the best implementation based on hardware / OS.
class PROTOBUF_EXPORT ThreadSafeArena {
public:
ThreadSafeArena() { Init(); }
// Constructor solely used by message-owned arena.
ThreadSafeArena(internal::MessageOwned) : tag_and_id_(kMessageOwnedArena) {
Init();
}
ThreadSafeArena(char* mem, size_t size) { InitializeFrom(mem, size); }
explicit ThreadSafeArena(void* mem, size_t size,
const AllocationPolicy& policy) {
InitializeWithPolicy(mem, size, policy);
}
// Destructor deletes all owned heap allocated objects, and destructs objects
// that have non-trivial destructors, except for proto2 message objects whose
// destructors can be skipped. Also, frees all blocks except the initial block
// if it was passed in.
~ThreadSafeArena();
uint64_t Reset();
uint64_t SpaceAllocated() const;
uint64_t SpaceUsed() const;
template <AllocationClient alloc_client = AllocationClient::kDefault>
void* AllocateAligned(size_t n, const std::type_info* type) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
GetSerialArenaFast(&arena))) {
return arena->AllocateAligned<alloc_client>(n, AllocPolicy());
} else {
return AllocateAlignedFallback(n, type);
}
}
void ReturnArrayMemory(void* p, size_t size) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
arena->ReturnArrayMemory(p, size);
}
}
// This function allocates n bytes if the common happy case is true and
// returns true. Otherwise does nothing and returns false. This strange
// semantics is necessary to allow callers to program functions that only
// have fallback function calls in tail position. This substantially improves
// code for the happy path.
PROTOBUF_NDEBUG_INLINE bool MaybeAllocateAligned(size_t n, void** out) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(!alloc_policy_.should_record_allocs() &&
GetSerialArenaFromThreadCache(&arena))) {
return arena->MaybeAllocateAligned(n, out);
}
return false;
}
std::pair<void*, SerialArena::CleanupNode*> AllocateAlignedWithCleanup(
size_t n, const std::type_info* type);
// Add object pointer and cleanup function pointer to the list.
void AddCleanup(void* elem, void (*cleanup)(void*));
// Checks whether this arena is message-owned.
PROTOBUF_ALWAYS_INLINE bool IsMessageOwned() const {
return tag_and_id_ & kMessageOwnedArena;
}
private:
// Unique for each arena. Changes on Reset().
uint64_t tag_and_id_ = 0;
// The LSB of tag_and_id_ indicates if the arena is message-owned.
enum : uint64_t { kMessageOwnedArena = 1 };
TaggedAllocationPolicyPtr alloc_policy_; // Tagged pointer to AllocPolicy.
static_assert(std::is_trivially_destructible<SerialArena>{},
"SerialArena needs to be trivially destructible.");
// Pointer to a linked list of SerialArena.
std::atomic<SerialArena*> threads_;
std::atomic<SerialArena*> hint_; // Fast thread-local block access
const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
void InitializeFrom(void* mem, size_t size);
void InitializeWithPolicy(void* mem, size_t size, AllocationPolicy policy);
void* AllocateAlignedFallback(size_t n, const std::type_info* type);
std::pair<void*, SerialArena::CleanupNode*>
AllocateAlignedWithCleanupFallback(size_t n, const std::type_info* type);
void Init();
void SetInitialBlock(void* mem, size_t size);
// Delete or Destruct all objects owned by the arena.
void CleanupList();
inline uint64_t LifeCycleId() const {
return tag_and_id_ & ~kMessageOwnedArena;
}
inline void CacheSerialArena(SerialArena* serial) {
thread_cache().last_serial_arena = serial;
thread_cache().last_lifecycle_id_seen = tag_and_id_;
// TODO(haberman): evaluate whether we would gain efficiency by getting rid
// of hint_. It's the only write we do to ThreadSafeArena in the allocation
// path, which will dirty the cache line.
hint_.store(serial, std::memory_order_release);
}
PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(SerialArena** arena) {
if (GetSerialArenaFromThreadCache(arena)) return true;
// Check whether we own the last accessed SerialArena on this arena. This
// fast path optimizes the case where a single thread uses multiple arenas.
ThreadCache* tc = &thread_cache();
SerialArena* serial = hint_.load(std::memory_order_acquire);
if (PROTOBUF_PREDICT_TRUE(serial != nullptr && serial->owner() == tc)) {
*arena = serial;
return true;
}
return false;
}
PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFromThreadCache(
SerialArena** arena) {
// If this thread already owns a block in this arena then try to use that.
// This fast path optimizes the case where multiple threads allocate from
// the same arena.
ThreadCache* tc = &thread_cache();
if (PROTOBUF_PREDICT_TRUE(tc->last_lifecycle_id_seen == tag_and_id_)) {
*arena = tc->last_serial_arena;
return true;
}
return false;
}
SerialArena* GetSerialArenaFallback(void* me);
template <typename Functor>
void PerSerialArena(Functor fn) {
// By omitting an Acquire barrier we ensure that any user code that doesn't
// properly synchronize Reset() or the destructor will throw a TSAN warning.
SerialArena* serial = threads_.load(std::memory_order_relaxed);
for (; serial; serial = serial->next()) fn(serial);
}
// Releases all memory except the first block which it returns. The first
// block might be owned by the user and thus need some extra checks before
// deleting.
SerialArena::Memory Free(size_t* space_allocated);
#ifdef _MSC_VER
#pragma warning(disable : 4324)
#endif
struct alignas(kCacheAlignment) ThreadCache {
#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
// If we are using the ThreadLocalStorage class to store the ThreadCache,
// then the ThreadCache's default constructor has to be responsible for
// initializing it.
ThreadCache()
: next_lifecycle_id(0),
last_lifecycle_id_seen(-1),
last_serial_arena(nullptr) {}
#endif
// Number of per-thread lifecycle IDs to reserve. Must be power of two.
// To reduce contention on a global atomic, each thread reserves a batch of
// IDs. The following number is calculated based on a stress test with
// ~6500 threads all frequently allocating a new arena.
static constexpr size_t kPerThreadIds = 256;
// Next lifecycle ID available to this thread. We need to reserve a new
// batch, if `next_lifecycle_id & (kPerThreadIds - 1) == 0`.
uint64_t next_lifecycle_id;
// The ThreadCache is considered valid as long as this matches the
// lifecycle_id of the arena being used.
uint64_t last_lifecycle_id_seen;
SerialArena* last_serial_arena;
};
// Lifecycle_id can be highly contended variable in a situation of lots of
// arena creation. Make sure that other global variables are not sharing the
// cacheline.
#ifdef _MSC_VER
#pragma warning(disable : 4324)
#endif
struct alignas(kCacheAlignment) CacheAlignedLifecycleIdGenerator {
std::atomic<LifecycleIdAtomic> id;
};
static CacheAlignedLifecycleIdGenerator lifecycle_id_generator_;
#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
// iOS does not support __thread keyword so we use a custom thread local
// storage class we implemented.
static ThreadCache& thread_cache();
#elif defined(PROTOBUF_USE_DLLS)
// Thread local variables cannot be exposed through DLL interface but we can
// wrap them in static functions.
static ThreadCache& thread_cache();
#else
static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache_;
static ThreadCache& thread_cache() { return thread_cache_; }
#endif
ThreadSafeArenaStatsHandle arena_stats_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ThreadSafeArena);
// All protos have pointers back to the arena hence Arena must have
// pointer stability.
ThreadSafeArena(ThreadSafeArena&&) = delete;
ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
public:
// kBlockHeaderSize is sizeof(Block), aligned up to the nearest multiple of 8
// to protect the invariant that pos is always at a multiple of 8.
static constexpr size_t kBlockHeaderSize = SerialArena::kBlockHeaderSize;
static constexpr size_t kSerialArenaSize =
(sizeof(SerialArena) + 7) & static_cast<size_t>(-8);
static_assert(kBlockHeaderSize % 8 == 0,
"kBlockHeaderSize must be a multiple of 8.");
static_assert(kSerialArenaSize % 8 == 0,
"kSerialArenaSize must be a multiple of 8.");
};
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_ARENA_IMPL_H__

View File

@@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arena_test_util.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#define EXPECT_EQ GOOGLE_CHECK_EQ
namespace google {
namespace protobuf {
namespace internal {
NoHeapChecker::~NoHeapChecker() {
capture_alloc.Unhook();
EXPECT_EQ(0, capture_alloc.alloc_count());
EXPECT_EQ(0, capture_alloc.free_count());
}
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,132 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__
#define GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
namespace google {
namespace protobuf {
template <typename T, bool use_arena>
void TestParseCorruptedString(const T& message) {
int success_count = 0;
std::string s;
{
// Map order is not deterministic. To make the test deterministic we want
// to serialize the proto deterministically.
io::StringOutputStream output(&s);
io::CodedOutputStream out(&output);
out.SetSerializationDeterministic(true);
message.SerializePartialToCodedStream(&out);
}
const int kMaxIters = 900;
const int stride = s.size() <= kMaxIters ? 1 : s.size() / kMaxIters;
const int start = stride == 1 || use_arena ? 0 : (stride + 1) / 2;
for (int i = start; i < s.size(); i += stride) {
for (int c = 1 + (i % 17); c < 256; c += 2 * c + (i & 3)) {
s[i] ^= c;
Arena arena;
T* message = Arena::CreateMessage<T>(use_arena ? &arena : nullptr);
if (message->ParseFromString(s)) {
++success_count;
}
if (!use_arena) {
delete message;
}
s[i] ^= c; // Restore s to its original state.
}
}
// This next line is a low bar. But getting through the test without crashing
// due to use-after-free or other bugs is a big part of what we're checking.
GOOGLE_CHECK_GT(success_count, 0);
}
namespace internal {
struct ArenaTestPeer {
static void ReturnArrayMemory(Arena* arena, void* p, size_t size) {
arena->ReturnArrayMemory(p, size);
}
};
class NoHeapChecker {
public:
NoHeapChecker() { capture_alloc.Hook(); }
~NoHeapChecker();
private:
class NewDeleteCapture {
public:
// TODO(xiaofeng): Implement this for opensource protobuf.
void Hook() {}
void Unhook() {}
int alloc_count() { return 0; }
int free_count() { return 0; }
} capture_alloc;
};
// Owns the internal T only if it's not owned by an arena.
// T needs to be arena constructible and destructor skippable.
template <typename T>
class ArenaHolder {
public:
explicit ArenaHolder(Arena* arena)
: field_(Arena::CreateMessage<T>(arena)),
owned_by_arena_(arena != nullptr) {
GOOGLE_DCHECK(google::protobuf::Arena::is_arena_constructable<T>::value);
GOOGLE_DCHECK(google::protobuf::Arena::is_destructor_skippable<T>::value);
}
~ArenaHolder() {
if (!owned_by_arena_) {
delete field_;
}
}
T* get() { return field_; }
T* operator->() { return field_; }
T& operator*() { return *field_; }
private:
T* field_;
bool owned_by_arena_;
};
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_ARENA_TEST_UTIL_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arenastring.h>
#include <cstddef>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/stubs/mutex.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/message_lite.h>
#include <google/protobuf/parse_context.h>
#include <google/protobuf/stubs/stl_util.h>
// clang-format off
#include <google/protobuf/port_def.inc>
// clang-format on
namespace google {
namespace protobuf {
namespace internal {
namespace {
// TaggedStringPtr::Flags uses the lower 2 bits as tags.
// Enforce that allocated data aligns to at least 4 bytes, and that
// the alignment of the global const string value does as well.
// The alignment guaranteed by `new std::string` depends on both:
// - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t
// - alignof(std::string)
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
constexpr size_t kNewAlign = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40900
constexpr size_t kNewAlign = alignof(::max_align_t);
#else
constexpr size_t kNewAlign = alignof(std::max_align_t);
#endif
constexpr size_t kStringAlign = alignof(std::string);
static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
} // namespace
const std::string& LazyString::Init() const {
static WrappedMutex mu{GOOGLE_PROTOBUF_LINKER_INITIALIZED};
mu.Lock();
const std::string* res = inited_.load(std::memory_order_acquire);
if (res == nullptr) {
auto init_value = init_value_;
res = ::new (static_cast<void*>(string_buf_))
std::string(init_value.ptr, init_value.size);
inited_.store(res, std::memory_order_release);
}
mu.Unlock();
return *res;
}
namespace {
#if defined(NDEBUG) || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
class ScopedCheckPtrInvariants {
public:
explicit ScopedCheckPtrInvariants(const TaggedStringPtr*) {}
};
#endif // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
// Creates a heap allocated std::string value.
inline TaggedStringPtr CreateString(ConstStringParam value) {
TaggedStringPtr res;
res.SetAllocated(new std::string(value.data(), value.length()));
return res;
}
#if !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
// Creates an arena allocated std::string value.
TaggedStringPtr CreateArenaString(Arena& arena, ConstStringParam s) {
TaggedStringPtr res;
res.SetMutableArena(Arena::Create<std::string>(&arena, s.data(), s.length()));
return res;
}
#endif // !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
} // namespace
void ArenaStringPtr::Set(ConstStringParam value, Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (IsDefault()) {
// If we're not on an arena, skip straight to a true string to avoid
// possible copy cost later.
tagged_ptr_ = arena != nullptr ? CreateArenaString(*arena, value)
: CreateString(value);
} else {
UnsafeMutablePointer()->assign(value.data(), value.length());
}
}
void ArenaStringPtr::Set(std::string&& value, Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (IsDefault()) {
NewString(arena, std::move(value));
} else if (IsFixedSizeArena()) {
std::string* current = tagged_ptr_.Get();
auto* s = new (current) std::string(std::move(value));
arena->OwnDestructor(s);
tagged_ptr_.SetMutableArena(s);
} else /* !IsFixedSizeArena() */ {
*UnsafeMutablePointer() = std::move(value);
}
}
std::string* ArenaStringPtr::Mutable(Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (tagged_ptr_.IsMutable()) {
return tagged_ptr_.Get();
} else {
return MutableSlow(arena);
}
}
std::string* ArenaStringPtr::Mutable(const LazyString& default_value,
Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (tagged_ptr_.IsMutable()) {
return tagged_ptr_.Get();
} else {
return MutableSlow(arena, default_value);
}
}
std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (tagged_ptr_.IsMutable()) {
return tagged_ptr_.Get();
} else {
GOOGLE_DCHECK(IsDefault());
// Allocate empty. The contents are not relevant.
return NewString(arena);
}
}
template <typename... Lazy>
std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena,
const Lazy&... lazy_default) {
GOOGLE_DCHECK(IsDefault());
// For empty defaults, this ends up calling the default constructor which is
// more efficient than a copy construction from
// GetEmptyStringAlreadyInited().
return NewString(arena, lazy_default.get()...);
}
std::string* ArenaStringPtr::Release() {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (IsDefault()) return nullptr;
std::string* released = tagged_ptr_.Get();
if (tagged_ptr_.IsArena()) {
released = tagged_ptr_.IsMutable() ? new std::string(std::move(*released))
: new std::string(*released);
}
InitDefault();
return released;
}
void ArenaStringPtr::SetAllocated(std::string* value, Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
// Release what we have first.
Destroy();
if (value == nullptr) {
InitDefault();
} else {
#ifndef NDEBUG
// On debug builds, copy the string so the address differs. delete will
// fail if value was a stack-allocated temporary/etc., which would have
// failed when arena ran its cleanup list.
std::string* new_value = new std::string(std::move(*value));
delete value;
value = new_value;
#endif // !NDEBUG
InitAllocated(value, arena);
}
}
void ArenaStringPtr::Destroy() {
delete tagged_ptr_.GetIfAllocated();
}
void ArenaStringPtr::ClearToEmpty() {
ScopedCheckPtrInvariants check(&tagged_ptr_);
if (IsDefault()) {
// Already set to default -- do nothing.
} else {
// Unconditionally mask away the tag.
//
// UpdateArenaString uses assign when capacity is larger than the new
// value, which is trivially true in the donated string case.
// const_cast<std::string*>(PtrValue<std::string>())->clear();
tagged_ptr_.Get()->clear();
}
}
void ArenaStringPtr::ClearToDefault(const LazyString& default_value,
::google::protobuf::Arena* arena) {
ScopedCheckPtrInvariants check(&tagged_ptr_);
(void)arena;
if (IsDefault()) {
// Already set to default -- do nothing.
} else {
UnsafeMutablePointer()->assign(default_value.get());
}
}
const char* EpsCopyInputStream::ReadArenaString(const char* ptr,
ArenaStringPtr* s,
Arena* arena) {
ScopedCheckPtrInvariants check(&s->tagged_ptr_);
GOOGLE_DCHECK(arena != nullptr);
int size = ReadSize(&ptr);
if (!ptr) return nullptr;
auto* str = s->NewString(arena);
ptr = ReadString(ptr, size, str);
GOOGLE_PROTOBUF_PARSER_ASSERT(ptr);
return ptr;
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,480 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_ARENASTRING_H__
#define GOOGLE_PROTOBUF_ARENASTRING_H__
#include <algorithm>
#include <string>
#include <type_traits>
#include <utility>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arena.h>
#include <google/protobuf/port.h>
#include <google/protobuf/explicitly_constructed.h>
// must be last:
#include <google/protobuf/port_def.inc>
#ifdef SWIG
#error "You cannot SWIG proto headers"
#endif
namespace google {
namespace protobuf {
namespace internal {
class EpsCopyInputStream;
class SwapFieldHelper;
// Declared in message_lite.h
PROTOBUF_EXPORT extern ExplicitlyConstructedArenaString
fixed_address_empty_string;
// Lazy string instance to support string fields with non-empty default.
// These are initialized on the first call to .get().
class PROTOBUF_EXPORT LazyString {
public:
// We explicitly make LazyString an aggregate so that MSVC can do constant
// initialization on it without marking it `constexpr`.
// We do not want to use `constexpr` because it makes it harder to have extern
// storage for it and causes library bloat.
struct InitValue {
const char* ptr;
size_t size;
};
// We keep a union of the initialization value and the std::string to save on
// space. We don't need the string array after Init() is done.
union {
mutable InitValue init_value_;
alignas(std::string) mutable char string_buf_[sizeof(std::string)];
};
mutable std::atomic<const std::string*> inited_;
const std::string& get() const {
// This check generates less code than a call-once invocation.
auto* res = inited_.load(std::memory_order_acquire);
if (PROTOBUF_PREDICT_FALSE(res == nullptr)) return Init();
return *res;
}
private:
// Initialize the string in `string_buf_`, update `inited_` and return it.
// We return it here to avoid having to read it again in the inlined code.
const std::string& Init() const;
};
class TaggedStringPtr {
public:
// Bit flags qualifying string properties. We can use 2 bits as
// ptr_ is guaranteed and enforced to be aligned on 4 byte boundaries.
enum Flags {
kArenaBit = 0x1, // ptr is arena allocated
kMutableBit = 0x2, // ptr contents are fully mutable
kMask = 0x3 // Bit mask
};
// Composed logical types
enum Type {
// Default strings are immutable and never owned.
kDefault = 0,
// Allocated strings are mutable and (as the name implies) owned.
// A heap allocated string must be deleted.
kAllocated = kMutableBit,
// Mutable arena strings are strings where the string instance is owned
// by the arena, but the string contents itself are owned by the string
// instance. Mutable arena string instances need to be destroyed which is
// typically done through a cleanup action added to the arena owning it.
kMutableArena = kArenaBit | kMutableBit,
// Fixed size arena strings are strings where both the string instance and
// the string contents are fully owned by the arena. Fixed size arena
// strings are a platform and c++ library specific customization. Fixed
// size arena strings are immutable, with the exception of custom internal
// updates to the content that fit inside the existing capacity.
// Fixed size arena strings must never be deleted or destroyed.
kFixedSizeArena = kArenaBit,
};
TaggedStringPtr() = default;
explicit constexpr TaggedStringPtr(ExplicitlyConstructedArenaString* ptr)
: ptr_(ptr) {}
// Sets the value to `p`, tagging the value as being a 'default' value.
// See documentation for kDefault for more info.
inline const std::string* SetDefault(const std::string* p) {
return TagAs(kDefault, const_cast<std::string*>(p));
}
// Sets the value to `p`, tagging the value as a heap allocated value.
// Allocated strings are mutable and (as the name implies) owned.
// `p` must not be null
inline std::string* SetAllocated(std::string* p) {
return TagAs(kAllocated, p);
}
// Sets the value to `p`, tagging the value as a fixed size arena string.
// See documentation for kFixedSizeArena for more info.
// `p` must not be null
inline std::string* SetFixedSizeArena(std::string* p) {
return TagAs(kFixedSizeArena, p);
}
// Sets the value to `p`, tagging the value as a mutable arena string.
// See documentation for kMutableArena for more info.
// `p` must not be null
inline std::string* SetMutableArena(std::string* p) {
return TagAs(kMutableArena, p);
}
// Returns true if the contents of the current string are fully mutable.
inline bool IsMutable() const { return as_int() & kMutableBit; }
// Returns true if the current string is an immutable default value.
inline bool IsDefault() const { return (as_int() & kMask) == kDefault; }
// If the current string is a heap-allocated mutable value, returns a pointer
// to it. Returns nullptr otherwise.
inline std::string *GetIfAllocated() const {
auto allocated = as_int() ^ kAllocated;
if (allocated & kMask) return nullptr;
auto ptr = reinterpret_cast<std::string*>(allocated);
PROTOBUF_ASSUME(ptr != nullptr);
return ptr;
}
// Returns true if the current string is an arena allocated value.
// This means it's either a mutable or fixed size arena string.
inline bool IsArena() const { return as_int() & kArenaBit; }
// Returns true if the current string is a fixed size arena allocated value.
inline bool IsFixedSizeArena() const {
return (as_int() & kMask) == kFixedSizeArena;
}
// Returns the contained string pointer.
inline std::string* Get() const {
return reinterpret_cast<std::string*>(as_int() & ~kMask);
}
// Returns true if the contained pointer is null, indicating some error.
// The Null value is only used during parsing for temporary values.
// A persisted ArenaStringPtr value is never null.
inline bool IsNull() { return ptr_ == nullptr; }
private:
static inline void assert_aligned(const void* p) {
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(p) & kMask, 0UL);
}
inline std::string* TagAs(Type type, std::string* p) {
GOOGLE_DCHECK(p != nullptr);
assert_aligned(p);
ptr_ = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(p) | type);
return p;
}
uintptr_t as_int() const { return reinterpret_cast<uintptr_t>(ptr_); }
void* ptr_;
};
static_assert(std::is_trivial<TaggedStringPtr>::value,
"TaggedStringPtr must be trivial");
// This class encapsulates a pointer to a std::string with or without arena
// owned contents, tagged by the bottom bits of the string pointer. It is a
// high-level wrapper that almost directly corresponds to the interface required
// by string fields in generated code. It replaces the old std::string* pointer
// in such cases.
//
// The string pointer is tagged to be either a default, externally owned value,
// a mutable heap allocated value, or an arena allocated value. The object uses
// a single global instance of an empty string that is used as the initial
// default value. Fields that have empty default values directly use this global
// default. Fields that have non empty default values are supported through
// lazily initialized default values managed by the LazyString class.
//
// Generated code and reflection code both ensure that ptr_ is never null.
// Because ArenaStringPtr is used in oneof unions, its constructor is a NOP and
// the field is always manually initialized via method calls.
//
// See TaggedStringPtr for more information about the types of string values
// being held, and the mutable and ownership invariants for each type.
struct PROTOBUF_EXPORT ArenaStringPtr {
ArenaStringPtr() = default;
constexpr ArenaStringPtr(ExplicitlyConstructedArenaString* default_value,
ConstantInitialized)
: tagged_ptr_(default_value) {}
// Called from generated code / reflection runtime only. Resets value to point
// to a default string pointer, with the semantics that this ArenaStringPtr
// does not own the pointed-to memory. Disregards initial value of ptr_ (so
// this is the *ONLY* safe method to call after construction or when
// reinitializing after becoming the active field in a oneof union).
inline void InitDefault();
// Similar to `InitDefault` except that it allows the default value to be
// initialized to an externally owned string. This method is called from
// parsing code. `str` must not be null and outlive this instance.
inline void InitExternal(const std::string* str);
// Called from generated code / reflection runtime only. Resets the value of
// this instances to the heap allocated value in `str`. `str` must not be
// null. Invokes `arena->Own(str)` to transfer ownership into the arena if
// `arena` is not null, else, `str` will be owned by ArenaStringPtr. This
// function should only be used to initialize a ArenaStringPtr or on an
// instance known to not carry any heap allocated value.
inline void InitAllocated(std::string* str, Arena* arena);
void Set(ConstStringParam value, Arena* arena);
void Set(std::string&& value, Arena* arena);
void Set(const char* s, Arena* arena);
void Set(const char* s, size_t n, Arena* arena);
void SetBytes(ConstStringParam value, Arena* arena);
void SetBytes(std::string&& value, Arena* arena);
void SetBytes(const char* s, Arena* arena);
void SetBytes(const void* p, size_t n, Arena* arena);
template <typename RefWrappedType>
void Set(std::reference_wrapper<RefWrappedType> const_string_ref,
::google::protobuf::Arena* arena) {
Set(const_string_ref.get(), arena);
}
// Returns a mutable std::string reference.
// The version accepting a `LazyString` value is used in the generated code to
// initialize mutable copies for fields with a non-empty default where the
// default value is lazily initialized.
std::string* Mutable(Arena* arena);
std::string* Mutable(const LazyString& default_value, Arena* arena);
// Gets a mutable pointer with unspecified contents.
// This function is identical to Mutable(), except it is optimized for the
// case where the caller is not interested in the current contents. For
// example, if the current field is not mutable, it will re-initialize the
// value with an empty string rather than a (non-empty) default value.
// Likewise, if the current value is a fixed size arena string with contents,
// it will be initialized into an empty mutable arena string.
std::string* MutableNoCopy(Arena* arena);
// Basic accessors.
PROTOBUF_NDEBUG_INLINE const std::string& Get() const {
// Unconditionally mask away the tag.
return *tagged_ptr_.Get();
}
// Returns a pointer to the stored contents for this instance.
// This method is for internal debugging and tracking purposes only.
PROTOBUF_NDEBUG_INLINE const std::string* UnsafeGetPointer() const
PROTOBUF_RETURNS_NONNULL {
return tagged_ptr_.Get();
}
// Release returns a std::string* instance that is heap-allocated and is not
// Own()'d by any arena. If the field is not set, this returns nullptr. The
// caller retains ownership. Clears this field back to the default state.
// Used to implement release_<field>() methods on generated classes.
PROTOBUF_NODISCARD std::string* Release();
// Takes a std::string that is heap-allocated, and takes ownership. The
// std::string's destructor is registered with the arena. Used to implement
// set_allocated_<field> in generated classes.
void SetAllocated(std::string* value, Arena* arena);
// Frees storage (if not on an arena).
void Destroy();
// Clears content, but keeps allocated std::string, to avoid the overhead of
// heap operations. After this returns, the content (as seen by the user) will
// always be the empty std::string. Assumes that |default_value| is an empty
// std::string.
void ClearToEmpty();
// Clears content, assuming that the current value is not the empty
// string default.
void ClearNonDefaultToEmpty();
// Clears content, but keeps allocated std::string if arena != nullptr, to
// avoid the overhead of heap operations. After this returns, the content
// (as seen by the user) will always be equal to |default_value|.
void ClearToDefault(const LazyString& default_value, ::google::protobuf::Arena* arena);
// Swaps internal pointers. Arena-safety semantics: this is guarded by the
// logic in Swap()/UnsafeArenaSwap() at the message level, so this method is
// 'unsafe' if called directly.
inline PROTOBUF_NDEBUG_INLINE static void InternalSwap(ArenaStringPtr* rhs,
Arena* rhs_arena,
ArenaStringPtr* lhs,
Arena* lhs_arena);
// Internal setter used only at parse time to directly set a donated string
// value.
void UnsafeSetTaggedPointer(TaggedStringPtr value) { tagged_ptr_ = value; }
// Generated code only! An optimization, in certain cases the generated
// code is certain we can obtain a std::string with no default checks and
// tag tests.
std::string* UnsafeMutablePointer() PROTOBUF_RETURNS_NONNULL;
// Returns true if this instances holds an immutable default value.
inline bool IsDefault() const { return tagged_ptr_.IsDefault(); }
private:
template <typename... Args>
inline std::string* NewString(Arena* arena, Args&&... args) {
if (arena == nullptr) {
auto* s = new std::string(std::forward<Args>(args)...);
return tagged_ptr_.SetAllocated(s);
} else {
auto* s = Arena::Create<std::string>(arena, std::forward<Args>(args)...);
return tagged_ptr_.SetMutableArena(s);
}
}
TaggedStringPtr tagged_ptr_;
bool IsFixedSizeArena() const { return false; }
// Swaps tagged pointer without debug hardening. This is to allow python
// protobuf to maintain pointer stability even in DEBUG builds.
inline PROTOBUF_NDEBUG_INLINE static void UnsafeShallowSwap(
ArenaStringPtr* rhs, ArenaStringPtr* lhs) {
std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
}
friend class ::google::protobuf::internal::SwapFieldHelper;
friend class TcParser;
// Slow paths.
// MutableSlow requires that !IsString() || IsDefault
// Variadic to support 0 args for empty default and 1 arg for LazyString.
template <typename... Lazy>
std::string* MutableSlow(::google::protobuf::Arena* arena, const Lazy&... lazy_default);
friend class EpsCopyInputStream;
};
inline void ArenaStringPtr::InitDefault() {
tagged_ptr_ = TaggedStringPtr(&fixed_address_empty_string);
}
inline void ArenaStringPtr::InitExternal(const std::string* str) {
tagged_ptr_.SetDefault(str);
}
inline void ArenaStringPtr::InitAllocated(std::string* str, Arena* arena) {
if (arena != nullptr) {
tagged_ptr_.SetMutableArena(str);
arena->Own(str);
} else {
tagged_ptr_.SetAllocated(str);
}
}
inline void ArenaStringPtr::Set(const char* s, Arena* arena) {
Set(ConstStringParam{s}, arena);
}
inline void ArenaStringPtr::Set(const char* s, size_t n, Arena* arena) {
Set(ConstStringParam{s, n}, arena);
}
inline void ArenaStringPtr::SetBytes(ConstStringParam value, Arena* arena) {
Set(value, arena);
}
inline void ArenaStringPtr::SetBytes(std::string&& value, Arena* arena) {
Set(std::move(value), arena);
}
inline void ArenaStringPtr::SetBytes(const char* s, Arena* arena) {
Set(s, arena);
}
inline void ArenaStringPtr::SetBytes(const void* p, size_t n, Arena* arena) {
Set(ConstStringParam{static_cast<const char*>(p), n}, arena);
}
// Make sure rhs_arena allocated rhs, and lhs_arena allocated lhs.
inline PROTOBUF_NDEBUG_INLINE void ArenaStringPtr::InternalSwap( //
ArenaStringPtr* rhs, Arena* rhs_arena, //
ArenaStringPtr* lhs, Arena* lhs_arena) {
// Silence unused variable warnings in release buildls.
(void)rhs_arena;
(void)lhs_arena;
std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_);
#ifdef PROTOBUF_FORCE_COPY_IN_SWAP
auto force_realloc = [](ArenaStringPtr* p, Arena* arena) {
if (p->IsDefault()) return;
std::string* old_value = p->tagged_ptr_.Get();
std::string* new_value =
p->IsFixedSizeArena()
? Arena::Create<std::string>(arena, *old_value)
: Arena::Create<std::string>(arena, std::move(*old_value));
if (arena == nullptr) {
delete old_value;
p->tagged_ptr_.SetAllocated(new_value);
} else {
p->tagged_ptr_.SetMutableArena(new_value);
}
};
// Because, at this point, tagged_ptr_ has been swapped, arena should also be
// swapped.
force_realloc(lhs, rhs_arena);
force_realloc(rhs, lhs_arena);
#endif // PROTOBUF_FORCE_COPY_IN_SWAP
}
inline void ArenaStringPtr::ClearNonDefaultToEmpty() {
// Unconditionally mask away the tag.
tagged_ptr_.Get()->clear();
}
inline std::string* ArenaStringPtr::UnsafeMutablePointer() {
GOOGLE_DCHECK(tagged_ptr_.IsMutable());
GOOGLE_DCHECK(tagged_ptr_.Get() != nullptr);
return tagged_ptr_.Get();
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_ARENASTRING_H__

View File

@@ -0,0 +1,154 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arenastring.h>
#include <algorithm>
#include <cstdlib>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/generated_message_util.h>
#include <google/protobuf/message_lite.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/strutil.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
using internal::ArenaStringPtr;
const internal::LazyString nonempty_default{{{"default", 7}}, {nullptr}};
const std::string* empty_default = &internal::GetEmptyString();
class SingleArena : public testing::TestWithParam<bool> {
public:
std::unique_ptr<Arena> GetArena() {
if (this->GetParam()) return nullptr;
return std::unique_ptr<Arena>(new Arena());
}
};
INSTANTIATE_TEST_SUITE_P(ArenaString, SingleArena, testing::Bool());
TEST_P(SingleArena, GetSet) {
auto arena = GetArena();
ArenaStringPtr field;
field.InitDefault();
EXPECT_EQ("", field.Get());
field.Set("Test short", arena.get());
EXPECT_EQ("Test short", field.Get());
field.Set("Test long long long long value", arena.get());
EXPECT_EQ("Test long long long long value", field.Get());
field.Set("", arena.get());
field.Destroy();
}
TEST_P(SingleArena, MutableAccessor) {
auto arena = GetArena();
ArenaStringPtr field;
field.InitDefault();
std::string* mut = field.Mutable(arena.get());
EXPECT_EQ(mut, field.Mutable(arena.get()));
EXPECT_EQ(mut, &field.Get());
EXPECT_NE(empty_default, mut);
EXPECT_EQ("", *mut);
*mut = "Test long long long long value"; // ensure string allocates storage
EXPECT_EQ("Test long long long long value", field.Get());
field.Destroy();
}
TEST_P(SingleArena, NullDefault) {
auto arena = GetArena();
ArenaStringPtr field;
field.InitDefault();
std::string* mut = field.Mutable(nonempty_default, arena.get());
EXPECT_EQ(mut, field.Mutable(nonempty_default, arena.get()));
EXPECT_EQ(mut, &field.Get());
EXPECT_NE(nullptr, mut);
EXPECT_EQ("default", *mut);
*mut = "Test long long long long value"; // ensure string allocates storage
EXPECT_EQ("Test long long long long value", field.Get());
field.Destroy();
}
class DualArena : public testing::TestWithParam<std::tuple<bool, bool>> {
public:
std::unique_ptr<Arena> GetLhsArena() {
if (std::get<0>(this->GetParam())) return nullptr;
return std::unique_ptr<Arena>(new Arena());
}
std::unique_ptr<Arena> GetRhsArena() {
if (std::get<1>(this->GetParam())) return nullptr;
return std::unique_ptr<Arena>(new Arena());
}
};
INSTANTIATE_TEST_SUITE_P(ArenaString, DualArena,
testing::Combine(testing::Bool(), testing::Bool()));
TEST_P(DualArena, Swap) {
auto lhs_arena = GetLhsArena();
ArenaStringPtr lhs;
lhs.InitDefault();
ArenaStringPtr rhs;
rhs.InitDefault();
{
auto rhs_arena = GetRhsArena();
lhs.Set("lhs value that has some heft", lhs_arena.get());
rhs.Set("rhs value that has some heft", rhs_arena.get());
ArenaStringPtr::InternalSwap(&lhs, lhs_arena.get(), //
&rhs, rhs_arena.get());
EXPECT_EQ("rhs value that has some heft", lhs.Get());
EXPECT_EQ("lhs value that has some heft", rhs.Get());
lhs.Destroy();
}
EXPECT_EQ("lhs value that has some heft", rhs.Get());
rhs.Destroy();
}
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>

View File

@@ -0,0 +1,177 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arenaz_sampler.h>
#include <atomic>
#include <cstdint>
#include <limits>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler() {
static auto* sampler = new ThreadSafeArenazSampler();
return *sampler;
}
void UnsampleSlow(ThreadSafeArenaStats* info) {
GlobalThreadSafeArenazSampler().Unregister(info);
}
#if defined(PROTOBUF_ARENAZ_SAMPLE)
namespace {
PROTOBUF_CONSTINIT std::atomic<bool> g_arenaz_enabled{true};
PROTOBUF_CONSTINIT std::atomic<int32_t> g_arenaz_sample_parameter{1 << 10};
PROTOBUF_THREAD_LOCAL absl::profiling_internal::ExponentialBiased
g_exponential_biased_generator;
} // namespace
PROTOBUF_THREAD_LOCAL int64_t global_next_sample = 1LL << 10;
ThreadSafeArenaStats::ThreadSafeArenaStats() { PrepareForSampling(); }
ThreadSafeArenaStats::~ThreadSafeArenaStats() = default;
void ThreadSafeArenaStats::PrepareForSampling() {
num_allocations.store(0, std::memory_order_relaxed);
num_resets.store(0, std::memory_order_relaxed);
bytes_requested.store(0, std::memory_order_relaxed);
bytes_allocated.store(0, std::memory_order_relaxed);
bytes_wasted.store(0, std::memory_order_relaxed);
max_bytes_allocated.store(0, std::memory_order_relaxed);
thread_ids.store(0, std::memory_order_relaxed);
// The inliner makes hardcoded skip_count difficult (especially when combined
// with LTO). We use the ability to exclude stacks by regex when encoding
// instead.
depth = absl::GetStackTrace(stack, kMaxStackDepth, /* skip_count= */ 0);
}
void RecordResetSlow(ThreadSafeArenaStats* info) {
const size_t max_bytes =
info->max_bytes_allocated.load(std::memory_order_relaxed);
const size_t allocated_bytes =
info->bytes_allocated.load(std::memory_order_relaxed);
if (max_bytes < allocated_bytes) {
info->max_bytes_allocated.store(allocated_bytes);
}
info->bytes_requested.store(0, std::memory_order_relaxed);
info->bytes_allocated.store(0, std::memory_order_relaxed);
info->bytes_wasted.fetch_add(0, std::memory_order_relaxed);
info->num_allocations.fetch_add(0, std::memory_order_relaxed);
info->num_resets.fetch_add(1, std::memory_order_relaxed);
}
void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t requested,
size_t allocated, size_t wasted) {
info->bytes_requested.fetch_add(requested, std::memory_order_relaxed);
info->bytes_allocated.fetch_add(allocated, std::memory_order_relaxed);
info->bytes_wasted.fetch_add(wasted, std::memory_order_relaxed);
info->num_allocations.fetch_add(1, std::memory_order_relaxed);
const uint64_t tid = (1ULL << (GetCachedTID() % 63));
const uint64_t thread_ids = info->thread_ids.load(std::memory_order_relaxed);
if (!(thread_ids & tid)) {
info->thread_ids.store(thread_ids | tid, std::memory_order_relaxed);
}
}
ThreadSafeArenaStats* SampleSlow(int64_t* next_sample) {
bool first = *next_sample < 0;
*next_sample = g_exponential_biased_generator.GetStride(
g_arenaz_sample_parameter.load(std::memory_order_relaxed));
// Small values of interval are equivalent to just sampling next time.
ABSL_ASSERT(*next_sample >= 1);
// g_arenaz_enabled can be dynamically flipped, we need to set a threshold low
// enough that we will start sampling in a reasonable time, so we just use the
// default sampling rate.
if (!g_arenaz_enabled.load(std::memory_order_relaxed)) return nullptr;
// We will only be negative on our first count, so we should just retry in
// that case.
if (first) {
if (PROTOBUF_PREDICT_TRUE(--*next_sample > 0)) return nullptr;
return SampleSlow(next_sample);
}
return GlobalThreadSafeArenazSampler().Register();
}
void SetThreadSafeArenazEnabled(bool enabled) {
g_arenaz_enabled.store(enabled, std::memory_order_release);
}
void SetThreadSafeArenazSampleParameter(int32_t rate) {
if (rate > 0) {
g_arenaz_sample_parameter.store(rate, std::memory_order_release);
} else {
ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz sample rate: %lld",
static_cast<long long>(rate)); // NOLINT(runtime/int)
}
}
void SetThreadSafeArenazMaxSamples(int32_t max) {
if (max > 0) {
GlobalThreadSafeArenazSampler().SetMaxSamples(max);
} else {
ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz max samples: %lld",
static_cast<long long>(max)); // NOLINT(runtime/int)
}
}
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {
if (next_sample >= 0) {
global_next_sample = next_sample;
} else {
ABSL_RAW_LOG(ERROR, "Invalid thread safe arenaz next sample: %lld",
static_cast<long long>(next_sample)); // NOLINT(runtime/int)
}
}
#else
ThreadSafeArenaStats* SampleSlow(int64_t* next_sample) {
*next_sample = std::numeric_limits<int64_t>::max();
return nullptr;
}
void SetThreadSafeArenazEnabled(bool enabled) {}
void SetThreadSafeArenazSampleParameter(int32_t rate) {}
void SetThreadSafeArenazMaxSamples(int32_t max) {}
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,207 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
#define GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
#include <atomic>
#include <cstddef>
#include <cstdint>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
#if defined(PROTOBUF_ARENAZ_SAMPLE)
struct ThreadSafeArenaStats;
void RecordResetSlow(ThreadSafeArenaStats* info);
void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t requested,
size_t allocated, size_t wasted);
// Stores information about a sampled thread safe arena. All mutations to this
// *must* be made through `Record*` functions below. All reads from this *must*
// only occur in the callback to `ThreadSafeArenazSampler::Iterate`.
struct ThreadSafeArenaStats
: public absl::profiling_internal::Sample<ThreadSafeArenaStats> {
// Constructs the object but does not fill in any fields.
ThreadSafeArenaStats();
~ThreadSafeArenaStats();
// Puts the object into a clean state, fills in the logically `const` members,
// blocking for any readers that are currently sampling the object.
void PrepareForSampling() ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
// These fields are mutated by the various Record* APIs and need to be
// thread-safe.
std::atomic<int> num_allocations;
std::atomic<int> num_resets;
std::atomic<size_t> bytes_requested;
std::atomic<size_t> bytes_allocated;
std::atomic<size_t> bytes_wasted;
// Records the largest size an arena ever had. Maintained across resets.
std::atomic<size_t> max_bytes_allocated;
// Bit i when set to 1 indicates that a thread with tid % 63 = i accessed the
// underlying arena. The field is maintained across resets.
std::atomic<uint64_t> thread_ids;
// All of the fields below are set by `PrepareForSampling`, they must not
// be mutated in `Record*` functions. They are logically `const` in that
// sense. These are guarded by init_mu, but that is not externalized to
// clients, who can only read them during
// `ThreadSafeArenazSampler::Iterate` which will hold the lock.
static constexpr int kMaxStackDepth = 64;
int32_t depth;
void* stack[kMaxStackDepth];
static void RecordAllocateStats(ThreadSafeArenaStats* info, size_t requested,
size_t allocated, size_t wasted) {
if (PROTOBUF_PREDICT_TRUE(info == nullptr)) return;
RecordAllocateSlow(info, requested, allocated, wasted);
}
};
ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
void UnsampleSlow(ThreadSafeArenaStats* info);
class ThreadSafeArenaStatsHandle {
public:
explicit ThreadSafeArenaStatsHandle() = default;
explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats* info)
: info_(info) {}
~ThreadSafeArenaStatsHandle() {
if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
UnsampleSlow(info_);
}
ThreadSafeArenaStatsHandle(ThreadSafeArenaStatsHandle&& other) noexcept
: info_(absl::exchange(other.info_, nullptr)) {}
ThreadSafeArenaStatsHandle& operator=(
ThreadSafeArenaStatsHandle&& other) noexcept {
if (PROTOBUF_PREDICT_FALSE(info_ != nullptr)) {
UnsampleSlow(info_);
}
info_ = absl::exchange(other.info_, nullptr);
return *this;
}
void RecordReset() {
if (PROTOBUF_PREDICT_TRUE(info_ == nullptr)) return;
RecordResetSlow(info_);
}
ThreadSafeArenaStats* MutableStats() { return info_; }
friend void swap(ThreadSafeArenaStatsHandle& lhs,
ThreadSafeArenaStatsHandle& rhs) {
std::swap(lhs.info_, rhs.info_);
}
friend class ThreadSafeArenaStatsHandlePeer;
private:
ThreadSafeArenaStats* info_ = nullptr;
};
using ThreadSafeArenazSampler =
::absl::profiling_internal::SampleRecorder<ThreadSafeArenaStats>;
extern PROTOBUF_THREAD_LOCAL int64_t global_next_sample;
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline ThreadSafeArenaStatsHandle Sample() {
if (PROTOBUF_PREDICT_TRUE(--global_next_sample > 0)) {
return ThreadSafeArenaStatsHandle(nullptr);
}
return ThreadSafeArenaStatsHandle(SampleSlow(&global_next_sample));
}
#else
struct ThreadSafeArenaStats {
static void RecordAllocateStats(ThreadSafeArenaStats*, size_t /*requested*/,
size_t /*allocated*/, size_t /*wasted*/) {}
};
ThreadSafeArenaStats* SampleSlow(int64_t* next_sample);
void UnsampleSlow(ThreadSafeArenaStats* info);
class ThreadSafeArenaStatsHandle {
public:
explicit ThreadSafeArenaStatsHandle() = default;
explicit ThreadSafeArenaStatsHandle(ThreadSafeArenaStats*) {}
void RecordReset() {}
ThreadSafeArenaStats* MutableStats() { return nullptr; }
friend void swap(ThreadSafeArenaStatsHandle&, ThreadSafeArenaStatsHandle&) {}
private:
friend class ThreadSafeArenaStatsHandlePeer;
};
class ThreadSafeArenazSampler {
public:
void Unregister(ThreadSafeArenaStats*) {}
void SetMaxSamples(int32_t) {}
};
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline ThreadSafeArenaStatsHandle Sample() {
return ThreadSafeArenaStatsHandle(nullptr);
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
// Returns a global Sampler.
ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler();
// Enables or disables sampling for thread safe arenas.
void SetThreadSafeArenazEnabled(bool enabled);
// Sets the rate at which thread safe arena will be sampled.
void SetThreadSafeArenazSampleParameter(int32_t rate);
// Sets a soft max for the number of samples that will be kept.
void SetThreadSafeArenazMaxSamples(int32_t max);
// Sets the current value for when arenas should be next sampled.
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample);
} // namespace internal
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_SRC_PROTOBUF_ARENAZ_SAMPLER_H__

View File

@@ -0,0 +1,382 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/arenaz_sampler.h>
#include <memory>
#include <random>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/strutil.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace internal {
#if defined(PROTOBUF_ARENAZ_SAMPLE)
class ThreadSafeArenaStatsHandlePeer {
public:
static bool IsSampled(const ThreadSafeArenaStatsHandle& h) {
return h.info_ != nullptr;
}
static ThreadSafeArenaStats* GetInfo(ThreadSafeArenaStatsHandle* h) {
return h->info_;
}
};
std::vector<size_t> GetBytesAllocated(ThreadSafeArenazSampler* s) {
std::vector<size_t> res;
s->Iterate([&](const ThreadSafeArenaStats& info) {
res.push_back(info.bytes_allocated.load(std::memory_order_acquire));
});
return res;
}
ThreadSafeArenaStats* Register(ThreadSafeArenazSampler* s, size_t size) {
auto* info = s->Register();
assert(info != nullptr);
info->bytes_allocated.store(size);
return info;
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
namespace {
#if defined(PROTOBUF_ARENAZ_SAMPLE)
TEST(ThreadSafeArenaStatsTest, PrepareForSampling) {
ThreadSafeArenaStats info;
MutexLock l(&info.init_mu);
info.PrepareForSampling();
EXPECT_EQ(info.num_allocations.load(), 0);
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_requested.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 0);
EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0);
info.num_allocations.store(1, std::memory_order_relaxed);
info.num_resets.store(1, std::memory_order_relaxed);
info.bytes_requested.store(1, std::memory_order_relaxed);
info.bytes_allocated.store(1, std::memory_order_relaxed);
info.bytes_wasted.store(1, std::memory_order_relaxed);
info.max_bytes_allocated.store(1, std::memory_order_relaxed);
info.PrepareForSampling();
EXPECT_EQ(info.num_allocations.load(), 0);
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_requested.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 0);
EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0);
}
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlow) {
ThreadSafeArenaStats info;
MutexLock l(&info.init_mu);
info.PrepareForSampling();
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0);
EXPECT_EQ(info.num_allocations.load(), 1);
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_requested.load(), 100);
EXPECT_EQ(info.bytes_allocated.load(), 128);
EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28);
EXPECT_EQ(info.num_allocations.load(), 2);
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_requested.load(), 200);
EXPECT_EQ(info.bytes_allocated.load(), 384);
EXPECT_EQ(info.bytes_wasted.load(), 28);
EXPECT_EQ(info.max_bytes_allocated.load(), 0);
}
TEST(ThreadSafeArenaStatsTest, RecordResetSlow) {
ThreadSafeArenaStats info;
MutexLock l(&info.init_mu);
info.PrepareForSampling();
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 0);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0);
EXPECT_EQ(info.num_resets.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 128);
RecordResetSlow(&info);
EXPECT_EQ(info.num_resets.load(), 1);
EXPECT_EQ(info.bytes_allocated.load(), 0);
}
TEST(ThreadSafeArenazSamplerTest, SmallSampleParameter) {
SetThreadSafeArenazEnabled(true);
SetThreadSafeArenazSampleParameter(100);
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
ThreadSafeArenaStats* sample = SampleSlow(&next_sample);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
}
}
TEST(ThreadSafeArenazSamplerTest, LargeSampleParameter) {
SetThreadSafeArenazEnabled(true);
SetThreadSafeArenazSampleParameter(std::numeric_limits<int32_t>::max());
for (int i = 0; i < 1000; ++i) {
int64_t next_sample = 0;
ThreadSafeArenaStats* sample = SampleSlow(&next_sample);
EXPECT_GT(next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
}
}
TEST(ThreadSafeArenazSamplerTest, Sample) {
SetThreadSafeArenazEnabled(true);
SetThreadSafeArenazSampleParameter(100);
SetThreadSafeArenazGlobalNextSample(0);
int64_t num_sampled = 0;
int64_t total = 0;
double sample_rate = 0.0;
for (int i = 0; i < 1000000; ++i) {
ThreadSafeArenaStatsHandle h = Sample();
++total;
if (ThreadSafeArenaStatsHandlePeer::IsSampled(h)) {
++num_sampled;
}
sample_rate = static_cast<double>(num_sampled) / total;
if (0.005 < sample_rate && sample_rate < 0.015) break;
}
EXPECT_NEAR(sample_rate, 0.01, 0.005);
}
TEST(ThreadSafeArenazSamplerTest, Handle) {
auto& sampler = GlobalThreadSafeArenazSampler();
ThreadSafeArenaStatsHandle h(sampler.Register());
auto* info = ThreadSafeArenaStatsHandlePeer::GetInfo(&h);
info->bytes_allocated.store(0x12345678, std::memory_order_relaxed);
bool found = false;
sampler.Iterate([&](const ThreadSafeArenaStats& h) {
if (&h == info) {
EXPECT_EQ(h.bytes_allocated.load(), 0x12345678);
found = true;
}
});
EXPECT_TRUE(found);
h = ThreadSafeArenaStatsHandle();
found = false;
sampler.Iterate([&](const ThreadSafeArenaStats& h) {
if (&h == info) {
// this will only happen if some other thread has resurrected the info
// the old handle was using.
if (h.bytes_allocated.load() == 0x12345678) {
found = true;
}
}
});
EXPECT_FALSE(found);
}
TEST(ThreadSafeArenazSamplerTest, Registration) {
ThreadSafeArenazSampler sampler;
auto* info1 = Register(&sampler, 1);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1));
auto* info2 = Register(&sampler, 2);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2));
info1->bytes_allocated.store(3);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(3, 2));
sampler.Unregister(info1);
sampler.Unregister(info2);
}
TEST(ThreadSafeArenazSamplerTest, Unregistration) {
ThreadSafeArenazSampler sampler;
std::vector<ThreadSafeArenaStats*> infos;
for (size_t i = 0; i < 3; ++i) {
infos.push_back(Register(&sampler, i));
}
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 1, 2));
sampler.Unregister(infos[1]);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2));
infos.push_back(Register(&sampler, 3));
infos.push_back(Register(&sampler, 4));
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 3, 4));
sampler.Unregister(infos[3]);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 4));
sampler.Unregister(infos[0]);
sampler.Unregister(infos[2]);
sampler.Unregister(infos[4]);
EXPECT_THAT(GetBytesAllocated(&sampler), IsEmpty());
}
TEST(ThreadSafeArenazSamplerTest, MultiThreaded) {
ThreadSafeArenazSampler sampler;
absl::Notification stop;
ThreadPool pool(10);
for (int i = 0; i < 10; ++i) {
pool.Schedule([&sampler, &stop]() {
std::random_device rd;
std::mt19937 gen(rd());
std::vector<ThreadSafeArenaStats*> infoz;
while (!stop.HasBeenNotified()) {
if (infoz.empty()) {
infoz.push_back(sampler.Register());
}
switch (std::uniform_int_distribution<>(0, 1)(gen)) {
case 0: {
infoz.push_back(sampler.Register());
break;
}
case 1: {
size_t p =
std::uniform_int_distribution<>(0, infoz.size() - 1)(gen);
ThreadSafeArenaStats* info = infoz[p];
infoz[p] = infoz.back();
infoz.pop_back();
sampler.Unregister(info);
break;
}
}
}
});
}
// The threads will hammer away. Give it a little bit of time for tsan to
// spot errors.
absl::SleepFor(absl::Seconds(3));
stop.Notify();
}
TEST(ThreadSafeArenazSamplerTest, Callback) {
ThreadSafeArenazSampler sampler;
auto* info1 = Register(&sampler, 1);
auto* info2 = Register(&sampler, 2);
static const ThreadSafeArenaStats* expected;
auto callback = [](const ThreadSafeArenaStats& info) {
// We can't use `info` outside of this callback because the object will be
// disposed as soon as we return from here.
EXPECT_EQ(&info, expected);
};
// Set the callback.
EXPECT_EQ(sampler.SetDisposeCallback(callback), nullptr);
expected = info1;
sampler.Unregister(info1);
// Unset the callback.
EXPECT_EQ(callback, sampler.SetDisposeCallback(nullptr));
expected = nullptr; // no more calls.
sampler.Unregister(info2);
}
class ThreadSafeArenazSamplerTestThread : public Thread {
protected:
void Run() override {
google::protobuf::ArenaSafeUniquePtr<
protobuf_test_messages::proto2::TestAllTypesProto2>
message = google::protobuf::MakeArenaSafeUnique<
protobuf_test_messages::proto2::TestAllTypesProto2>(arena_);
GOOGLE_CHECK(message != nullptr);
// Signal that a message on the arena has been created. This should create
// a SerialArena for this thread.
if (barrier_->Block()) {
delete barrier_;
}
}
public:
ThreadSafeArenazSamplerTestThread(const thread::Options& options,
StringPiece name,
google::protobuf::Arena* arena,
absl::Barrier* barrier)
: Thread(options, name), arena_(arena), barrier_(barrier) {}
private:
google::protobuf::Arena* arena_;
absl::Barrier* barrier_;
};
TEST(ThreadSafeArenazSamplerTest, MultiThread) {
SetThreadSafeArenazEnabled(true);
// Setting 1 as the parameter value means one in every two arenas would be
// sampled, on average.
SetThreadSafeArenazSampleParameter(1);
SetThreadSafeArenazGlobalNextSample(0);
auto& sampler = GlobalThreadSafeArenazSampler();
int count = 0;
for (int i = 0; i < 10; ++i) {
const int kNumThreads = 10;
absl::Barrier* barrier = new absl::Barrier(kNumThreads + 1);
google::protobuf::Arena arena;
thread::Options options;
options.set_joinable(true);
std::vector<std::unique_ptr<ThreadSafeArenazSamplerTestThread>> threads;
for (int i = 0; i < kNumThreads; i++) {
auto t = std::make_unique<ThreadSafeArenazSamplerTestThread>(
options, StrCat("thread", i), &arena, barrier);
t->Start();
threads.push_back(std::move(t));
}
// Wait till each thread has created a message on the arena.
if (barrier->Block()) {
delete barrier;
}
sampler.Iterate([&](const ThreadSafeArenaStats& h) { ++count; });
for (int i = 0; i < kNumThreads; i++) {
threads[i]->Join();
}
}
EXPECT_GT(count, 0);
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,167 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/annotation_test_util.h>
#include <cstdint>
#include <memory>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace annotation_test_util {
namespace {
// A CodeGenerator that captures the FileDescriptor it's passed as a
// FileDescriptorProto.
class DescriptorCapturingGenerator : public CodeGenerator {
public:
// Does not own file; file must outlive the Generator.
explicit DescriptorCapturingGenerator(FileDescriptorProto* file)
: file_(file) {}
bool Generate(const FileDescriptor* file, const std::string& parameter,
GeneratorContext* context, std::string* error) const override {
file->CopyTo(file_);
return true;
}
private:
FileDescriptorProto* file_;
};
} // namespace
void AddFile(const std::string& filename, const std::string& data) {
GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/" + filename, data,
true));
}
bool RunProtoCompiler(const std::string& filename,
const std::string& plugin_specific_args,
CommandLineInterface* cli, FileDescriptorProto* file) {
cli->SetInputsAreProtoPathRelative(true);
DescriptorCapturingGenerator capturing_generator(file);
cli->RegisterGenerator("--capture_out", &capturing_generator, "");
std::string proto_path = "-I" + TestTempDir();
std::string capture_out = "--capture_out=" + TestTempDir();
const char* argv[] = {"protoc", proto_path.c_str(),
plugin_specific_args.c_str(), capture_out.c_str(),
filename.c_str()};
return cli->Run(5, argv) == 0;
}
bool DecodeMetadata(const std::string& path, GeneratedCodeInfo* info) {
std::string data;
GOOGLE_CHECK_OK(File::GetContents(path, &data, true));
io::ArrayInputStream input(data.data(), data.size());
return info->ParseFromZeroCopyStream(&input);
}
void FindAnnotationsOnPath(
const GeneratedCodeInfo& info, const std::string& source_file,
const std::vector<int>& path,
std::vector<const GeneratedCodeInfo::Annotation*>* annotations) {
for (int i = 0; i < info.annotation_size(); ++i) {
const GeneratedCodeInfo::Annotation* annotation = &info.annotation(i);
if (annotation->source_file() != source_file ||
annotation->path_size() != path.size()) {
continue;
}
int node = 0;
for (; node < path.size(); ++node) {
if (annotation->path(node) != path[node]) {
break;
}
}
if (node == path.size()) {
annotations->push_back(annotation);
}
}
}
const GeneratedCodeInfo::Annotation* FindAnnotationOnPath(
const GeneratedCodeInfo& info, const std::string& source_file,
const std::vector<int>& path) {
std::vector<const GeneratedCodeInfo::Annotation*> annotations;
FindAnnotationsOnPath(info, source_file, path, &annotations);
if (annotations.empty()) {
return nullptr;
}
return annotations[0];
}
bool AtLeastOneAnnotationMatchesSubstring(
const std::string& file_content,
const std::vector<const GeneratedCodeInfo::Annotation*>& annotations,
const std::string& expected_text) {
for (std::vector<const GeneratedCodeInfo::Annotation*>::const_iterator
i = annotations.begin(),
e = annotations.end();
i != e; ++i) {
const GeneratedCodeInfo::Annotation* annotation = *i;
uint32_t begin = annotation->begin();
uint32_t end = annotation->end();
if (end < begin || end > file_content.size()) {
return false;
}
if (file_content.substr(begin, end - begin) == expected_text) {
return true;
}
}
return false;
}
bool AnnotationMatchesSubstring(const std::string& file_content,
const GeneratedCodeInfo::Annotation* annotation,
const std::string& expected_text) {
std::vector<const GeneratedCodeInfo::Annotation*> annotations;
annotations.push_back(annotation);
return AtLeastOneAnnotationMatchesSubstring(file_content, annotations,
expected_text);
}
} // namespace annotation_test_util
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,115 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_ANNOTATION_TEST_UTIL_H__
#define GOOGLE_PROTOBUF_COMPILER_ANNOTATION_TEST_UTIL_H__
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
// Utilities that assist in writing tests for generator annotations.
// See java/internal/annotation_unittest.cc for an example.
namespace google {
namespace protobuf {
namespace compiler {
namespace annotation_test_util {
// Struct that contains the file generated from a .proto file and its
// GeneratedCodeInfo. For example, the Java generator will fill this struct
// (for some 'foo.proto') with:
// file_path = "Foo.java"
// file_content = content of Foo.java
// file_info = parsed content of Foo.java.pb.meta
struct ExpectedOutput {
std::string file_path;
std::string file_content;
GeneratedCodeInfo file_info;
explicit ExpectedOutput(const std::string& file_path)
: file_path(file_path) {}
};
// Creates a file with name `filename` and content `data` in temp test
// directory.
void AddFile(const std::string& filename, const std::string& data);
// Runs proto compiler. Captures proto file structure in FileDescriptorProto.
// Files will be generated in TestTempDir() folder. Callers of this
// function must read generated files themselves.
//
// filename: source .proto file used to generate code.
// plugin_specific_args: command line arguments specific to current generator.
// For Java, this value might be "--java_out=annotate_code:test_temp_dir"
// cli: instance of command line interface to run generator. See Java's
// annotation_unittest.cc for an example of how to initialize it.
// file: output parameter, will be set to the descriptor of the proto file
// specified in filename.
bool RunProtoCompiler(const std::string& filename,
const std::string& plugin_specific_args,
CommandLineInterface* cli, FileDescriptorProto* file);
bool DecodeMetadata(const std::string& path, GeneratedCodeInfo* info);
// Finds all of the Annotations for a given source file and path.
// See Location.path in https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto for
// explanation of what path vector is.
void FindAnnotationsOnPath(
const GeneratedCodeInfo& info, const std::string& source_file,
const std::vector<int>& path,
std::vector<const GeneratedCodeInfo::Annotation*>* annotations);
// Finds the Annotation for a given source file and path (or returns null if it
// couldn't). If there are several annotations for given path, returns the first
// one. See Location.path in
// https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/descriptor.proto for explanation of what path
// vector is.
const GeneratedCodeInfo::Annotation* FindAnnotationOnPath(
const GeneratedCodeInfo& info, const std::string& source_file,
const std::vector<int>& path);
// Returns true if at least one of the provided annotations covers a given
// substring in file_content.
bool AtLeastOneAnnotationMatchesSubstring(
const std::string& file_content,
const std::vector<const GeneratedCodeInfo::Annotation*>& annotations,
const std::string& expected_text);
// Returns true if the provided annotation covers a given substring in
// file_content.
bool AnnotationMatchesSubstring(const std::string& file_content,
const GeneratedCodeInfo::Annotation* annotation,
const std::string& expected_text);
} // namespace annotation_test_util
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_ANNOTATION_TEST_UTIL_H__

View File

@@ -0,0 +1,137 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/plugin.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
CodeGenerator::~CodeGenerator() {}
bool CodeGenerator::GenerateAll(const std::vector<const FileDescriptor*>& files,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const {
// Default implementation is just to call the per file method, and prefix any
// error string with the file to provide context.
bool succeeded = true;
for (int i = 0; i < files.size(); i++) {
const FileDescriptor* file = files[i];
succeeded = Generate(file, parameter, generator_context, error);
if (!succeeded && error && error->empty()) {
*error =
"Code generator returned false but provided no error "
"description.";
}
if (error && !error->empty()) {
*error = file->name() + ": " + *error;
break;
}
if (!succeeded) {
break;
}
}
return succeeded;
}
GeneratorContext::~GeneratorContext() {}
io::ZeroCopyOutputStream* GeneratorContext::OpenForAppend(
const std::string& filename) {
return nullptr;
}
io::ZeroCopyOutputStream* GeneratorContext::OpenForInsert(
const std::string& filename, const std::string& insertion_point) {
GOOGLE_LOG(FATAL) << "This GeneratorContext does not support insertion.";
return nullptr; // make compiler happy
}
io::ZeroCopyOutputStream* GeneratorContext::OpenForInsertWithGeneratedCodeInfo(
const std::string& filename, const std::string& insertion_point,
const google::protobuf::GeneratedCodeInfo& /*info*/) {
return OpenForInsert(filename, insertion_point);
}
void GeneratorContext::ListParsedFiles(
std::vector<const FileDescriptor*>* output) {
GOOGLE_LOG(FATAL) << "This GeneratorContext does not support ListParsedFiles";
}
void GeneratorContext::GetCompilerVersion(Version* version) const {
version->set_major(GOOGLE_PROTOBUF_VERSION / 1000000);
version->set_minor(GOOGLE_PROTOBUF_VERSION / 1000 % 1000);
version->set_patch(GOOGLE_PROTOBUF_VERSION % 1000);
version->set_suffix(GOOGLE_PROTOBUF_VERSION_SUFFIX);
}
// Parses a set of comma-delimited name/value pairs.
void ParseGeneratorParameter(
const std::string& text,
std::vector<std::pair<std::string, std::string> >* output) {
std::vector<std::string> parts = Split(text, ",", true);
for (int i = 0; i < parts.size(); i++) {
std::string::size_type equals_pos = parts[i].find_first_of('=');
std::pair<std::string, std::string> value;
if (equals_pos == std::string::npos) {
value.first = parts[i];
value.second = "";
} else {
value.first = parts[i].substr(0, equals_pos);
value.second = parts[i].substr(equals_pos + 1);
}
output->push_back(value);
}
}
// Strips ".proto" or ".protodevel" from the end of a filename.
std::string StripProto(const std::string& filename) {
if (HasSuffixString(filename, ".protodevel")) {
return StripSuffixString(filename, ".protodevel");
} else {
return StripSuffixString(filename, ".proto");
}
}
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,207 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Defines the abstract interface implemented by each of the language-specific
// code generators.
#ifndef GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/stubs/common.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace io {
class ZeroCopyOutputStream;
}
class FileDescriptor;
class GeneratedCodeInfo;
namespace compiler {
class AccessInfoMap;
class Version;
// Defined in this file.
class CodeGenerator;
class GeneratorContext;
// The abstract interface to a class which generates code implementing a
// particular proto file in a particular language. A number of these may
// be registered with CommandLineInterface to support various languages.
class PROTOC_EXPORT CodeGenerator {
public:
inline CodeGenerator() {}
virtual ~CodeGenerator();
// Generates code for the given proto file, generating one or more files in
// the given output directory.
//
// A parameter to be passed to the generator can be specified on the command
// line. This is intended to be used to pass generator specific parameters.
// It is empty if no parameter was given. ParseGeneratorParameter (below),
// can be used to accept multiple parameters within the single parameter
// command line flag.
//
// Returns true if successful. Otherwise, sets *error to a description of
// the problem (e.g. "invalid parameter") and returns false.
virtual bool Generate(const FileDescriptor* file,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const = 0;
// Generates code for all given proto files.
//
// WARNING: The canonical code generator design produces one or two output
// files per input .proto file, and we do not wish to encourage alternate
// designs.
//
// A parameter is given as passed on the command line, as in |Generate()|
// above.
//
// Returns true if successful. Otherwise, sets *error to a description of
// the problem (e.g. "invalid parameter") and returns false.
virtual bool GenerateAll(const std::vector<const FileDescriptor*>& files,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const;
// This must be kept in sync with plugin.proto. See that file for
// documentation on each value.
enum Feature {
FEATURE_PROTO3_OPTIONAL = 1,
};
// Implement this to indicate what features this code generator supports.
//
// This must be a bitwise OR of values from the Feature enum above (or zero).
virtual uint64_t GetSupportedFeatures() const { return 0; }
// This is no longer used, but this class is part of the opensource protobuf
// library, so it has to remain to keep vtables the same for the current
// version of the library. When protobufs does a api breaking change, the
// method can be removed.
virtual bool HasGenerateAll() const { return true; }
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodeGenerator);
};
// CodeGenerators generate one or more files in a given directory. This
// abstract interface represents the directory to which the CodeGenerator is
// to write and other information about the context in which the Generator
// runs.
class PROTOC_EXPORT GeneratorContext {
public:
inline GeneratorContext() {
}
virtual ~GeneratorContext();
// Opens the given file, truncating it if it exists, and returns a
// ZeroCopyOutputStream that writes to the file. The caller takes ownership
// of the returned object. This method never fails (a dummy stream will be
// returned instead).
//
// The filename given should be relative to the root of the source tree.
// E.g. the C++ generator, when generating code for "foo/bar.proto", will
// generate the files "foo/bar.pb.h" and "foo/bar.pb.cc"; note that
// "foo/" is included in these filenames. The filename is not allowed to
// contain "." or ".." components.
virtual io::ZeroCopyOutputStream* Open(const std::string& filename) = 0;
// Similar to Open() but the output will be appended to the file if exists
virtual io::ZeroCopyOutputStream* OpenForAppend(const std::string& filename);
// Creates a ZeroCopyOutputStream which will insert code into the given file
// at the given insertion point. See plugin.proto (plugin.pb.h) for more
// information on insertion points. The default implementation
// assert-fails -- it exists only for backwards-compatibility.
//
// WARNING: This feature is currently EXPERIMENTAL and is subject to change.
virtual io::ZeroCopyOutputStream* OpenForInsert(
const std::string& filename, const std::string& insertion_point);
// Similar to OpenForInsert, but if `info` is non-empty, will open (or create)
// filename.pb.meta and insert info at the appropriate place with the
// necessary shifts. The default implementation ignores `info`.
//
// WARNING: This feature will be REMOVED in the near future.
virtual io::ZeroCopyOutputStream* OpenForInsertWithGeneratedCodeInfo(
const std::string& filename, const std::string& insertion_point,
const google::protobuf::GeneratedCodeInfo& info);
// Returns a vector of FileDescriptors for all the files being compiled
// in this run. Useful for languages, such as Go, that treat files
// differently when compiled as a set rather than individually.
virtual void ListParsedFiles(std::vector<const FileDescriptor*>* output);
// Retrieves the version number of the protocol compiler associated with
// this GeneratorContext.
virtual void GetCompilerVersion(Version* version) const;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratorContext);
};
// The type GeneratorContext was once called OutputDirectory. This typedef
// provides backward compatibility.
typedef GeneratorContext OutputDirectory;
// Several code generators treat the parameter argument as holding a
// list of options separated by commas. This helper function parses
// a set of comma-delimited name/value pairs: e.g.,
// "foo=bar,baz,moo=corge"
// parses to the pairs:
// ("foo", "bar"), ("baz", ""), ("moo", "corge")
PROTOC_EXPORT void ParseGeneratorParameter(
const std::string&, std::vector<std::pair<std::string, std::string> >*);
// Strips ".proto" or ".protodevel" from the end of a filename.
PROTOC_EXPORT std::string StripProto(const std::string& filename);
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,464 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Implements the Protocol Compiler front-end such that it may be reused by
// custom compilers written to support other languages.
#ifndef GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__
#define GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__
#include <cstdint>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <google/protobuf/stubs/common.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
class Descriptor; // descriptor.h
class DescriptorDatabase; // descriptor_database.h
class DescriptorPool; // descriptor.h
class FileDescriptor; // descriptor.h
class FileDescriptorSet; // descriptor.h
class FileDescriptorProto; // descriptor.pb.h
template <typename T>
class RepeatedPtrField; // repeated_field.h
class SimpleDescriptorDatabase; // descriptor_database.h
namespace compiler {
class CodeGenerator; // code_generator.h
class GeneratorContext; // code_generator.h
class DiskSourceTree; // importer.h
// This class implements the command-line interface to the protocol compiler.
// It is designed to make it very easy to create a custom protocol compiler
// supporting the languages of your choice. For example, if you wanted to
// create a custom protocol compiler binary which includes both the regular
// C++ support plus support for your own custom output "Foo", you would
// write a class "FooGenerator" which implements the CodeGenerator interface,
// then write a main() procedure like this:
//
// int main(int argc, char* argv[]) {
// google::protobuf::compiler::CommandLineInterface cli;
//
// // Support generation of C++ source and headers.
// google::protobuf::compiler::cpp::CppGenerator cpp_generator;
// cli.RegisterGenerator("--cpp_out", &cpp_generator,
// "Generate C++ source and header.");
//
// // Support generation of Foo code.
// FooGenerator foo_generator;
// cli.RegisterGenerator("--foo_out", &foo_generator,
// "Generate Foo file.");
//
// return cli.Run(argc, argv);
// }
//
// The compiler is invoked with syntax like:
// protoc --cpp_out=outdir --foo_out=outdir --proto_path=src src/foo.proto
//
// The .proto file to compile can be specified on the command line using either
// its physical file path, or a virtual path relative to a directory specified
// in --proto_path. For example, for src/foo.proto, the following two protoc
// invocations work the same way:
// 1. protoc --proto_path=src src/foo.proto (physical file path)
// 2. protoc --proto_path=src foo.proto (virtual path relative to src)
//
// If a file path can be interpreted both as a physical file path and as a
// relative virtual path, the physical file path takes precedence.
//
// For a full description of the command-line syntax, invoke it with --help.
class PROTOC_EXPORT CommandLineInterface {
public:
static const char* const kPathSeparator;
CommandLineInterface();
~CommandLineInterface();
// Register a code generator for a language.
//
// Parameters:
// * flag_name: The command-line flag used to specify an output file of
// this type. The name must start with a '-'. If the name is longer
// than one letter, it must start with two '-'s.
// * generator: The CodeGenerator which will be called to generate files
// of this type.
// * help_text: Text describing this flag in the --help output.
//
// Some generators accept extra parameters. You can specify this parameter
// on the command-line by placing it before the output directory, separated
// by a colon:
// protoc --foo_out=enable_bar:outdir
// The text before the colon is passed to CodeGenerator::Generate() as the
// "parameter".
void RegisterGenerator(const std::string& flag_name, CodeGenerator* generator,
const std::string& help_text);
// Register a code generator for a language.
// Besides flag_name you can specify another option_flag_name that could be
// used to pass extra parameters to the registered code generator.
// Suppose you have registered a generator by calling:
// command_line_interface.RegisterGenerator("--foo_out", "--foo_opt", ...)
// Then you could invoke the compiler with a command like:
// protoc --foo_out=enable_bar:outdir --foo_opt=enable_baz
// This will pass "enable_bar,enable_baz" as the parameter to the generator.
void RegisterGenerator(const std::string& flag_name,
const std::string& option_flag_name,
CodeGenerator* generator,
const std::string& help_text);
// Enables "plugins". In this mode, if a command-line flag ends with "_out"
// but does not match any registered generator, the compiler will attempt to
// find a "plugin" to implement the generator. Plugins are just executables.
// They should live somewhere in the PATH.
//
// The compiler determines the executable name to search for by concatenating
// exe_name_prefix with the unrecognized flag name, removing "_out". So, for
// example, if exe_name_prefix is "protoc-" and you pass the flag --foo_out,
// the compiler will try to run the program "protoc-gen-foo".
//
// The plugin program should implement the following usage:
// plugin [--out=OUTDIR] [--parameter=PARAMETER] PROTO_FILES < DESCRIPTORS
// --out indicates the output directory (as passed to the --foo_out
// parameter); if omitted, the current directory should be used. --parameter
// gives the generator parameter, if any was provided (see below). The
// PROTO_FILES list the .proto files which were given on the compiler
// command-line; these are the files for which the plugin is expected to
// generate output code. Finally, DESCRIPTORS is an encoded FileDescriptorSet
// (as defined in descriptor.proto). This is piped to the plugin's stdin.
// The set will include descriptors for all the files listed in PROTO_FILES as
// well as all files that they import. The plugin MUST NOT attempt to read
// the PROTO_FILES directly -- it must use the FileDescriptorSet.
//
// The plugin should generate whatever files are necessary, as code generators
// normally do. It should write the names of all files it generates to
// stdout. The names should be relative to the output directory, NOT absolute
// names or relative to the current directory. If any errors occur, error
// messages should be written to stderr. If an error is fatal, the plugin
// should exit with a non-zero exit code.
//
// Plugins can have generator parameters similar to normal built-in
// generators. Extra generator parameters can be passed in via a matching
// "_opt" parameter. For example:
// protoc --plug_out=enable_bar:outdir --plug_opt=enable_baz
// This will pass "enable_bar,enable_baz" as the parameter to the plugin.
//
void AllowPlugins(const std::string& exe_name_prefix);
// Run the Protocol Compiler with the given command-line parameters.
// Returns the error code which should be returned by main().
//
// It may not be safe to call Run() in a multi-threaded environment because
// it calls strerror(). I'm not sure why you'd want to do this anyway.
int Run(int argc, const char* const argv[]);
// DEPRECATED. Calling this method has no effect. Protocol compiler now
// always try to find the .proto file relative to the current directory
// first and if the file is not found, it will then treat the input path
// as a virtual path.
void SetInputsAreProtoPathRelative(bool /* enable */) {}
// Provides some text which will be printed when the --version flag is
// used. The version of libprotoc will also be printed on the next line
// after this text.
void SetVersionInfo(const std::string& text) { version_info_ = text; }
private:
// -----------------------------------------------------------------
class ErrorPrinter;
class GeneratorContextImpl;
class MemoryOutputStream;
typedef std::unordered_map<std::string, std::unique_ptr<GeneratorContextImpl>>
GeneratorContextMap;
// Clear state from previous Run().
void Clear();
// Remaps the proto file so that it is relative to one of the directories
// in proto_path_. Returns false if an error occurred.
bool MakeProtoProtoPathRelative(DiskSourceTree* source_tree,
std::string* proto,
DescriptorDatabase* fallback_database);
// Remaps each file in input_files_ so that it is relative to one of the
// directories in proto_path_. Returns false if an error occurred.
bool MakeInputsBeProtoPathRelative(DiskSourceTree* source_tree,
DescriptorDatabase* fallback_database);
// Fails if these files use proto3 optional and the code generator doesn't
// support it. This is a permanent check.
bool EnforceProto3OptionalSupport(
const std::string& codegen_name, uint64_t supported_features,
const std::vector<const FileDescriptor*>& parsed_files) const;
// Return status for ParseArguments() and InterpretArgument().
enum ParseArgumentStatus {
PARSE_ARGUMENT_DONE_AND_CONTINUE,
PARSE_ARGUMENT_DONE_AND_EXIT,
PARSE_ARGUMENT_FAIL
};
// Parse all command-line arguments.
ParseArgumentStatus ParseArguments(int argc, const char* const argv[]);
// Read an argument file and append the file's content to the list of
// arguments. Return false if the file cannot be read.
bool ExpandArgumentFile(const std::string& file,
std::vector<std::string>* arguments);
// Parses a command-line argument into a name/value pair. Returns
// true if the next argument in the argv should be used as the value,
// false otherwise.
//
// Examples:
// "-Isrc/protos" ->
// name = "-I", value = "src/protos"
// "--cpp_out=src/foo.pb2.cc" ->
// name = "--cpp_out", value = "src/foo.pb2.cc"
// "foo.proto" ->
// name = "", value = "foo.proto"
bool ParseArgument(const char* arg, std::string* name, std::string* value);
// Interprets arguments parsed with ParseArgument.
ParseArgumentStatus InterpretArgument(const std::string& name,
const std::string& value);
// Print the --help text to stderr.
void PrintHelpText();
// Loads proto_path_ into the provided source_tree.
bool InitializeDiskSourceTree(DiskSourceTree* source_tree,
DescriptorDatabase* fallback_database);
// Verify that all the input files exist in the given database.
bool VerifyInputFilesInDescriptors(DescriptorDatabase* fallback_database);
// Parses input_files_ into parsed_files
bool ParseInputFiles(DescriptorPool* descriptor_pool,
DiskSourceTree* source_tree,
std::vector<const FileDescriptor*>* parsed_files);
// Generate the given output file from the given input.
struct OutputDirective; // see below
bool GenerateOutput(const std::vector<const FileDescriptor*>& parsed_files,
const OutputDirective& output_directive,
GeneratorContext* generator_context);
bool GeneratePluginOutput(
const std::vector<const FileDescriptor*>& parsed_files,
const std::string& plugin_name, const std::string& parameter,
GeneratorContext* generator_context, std::string* error);
// Implements --encode and --decode.
bool EncodeOrDecode(const DescriptorPool* pool);
// Implements the --descriptor_set_out option.
bool WriteDescriptorSet(
const std::vector<const FileDescriptor*>& parsed_files);
// Implements the --dependency_out option
bool GenerateDependencyManifestFile(
const std::vector<const FileDescriptor*>& parsed_files,
const GeneratorContextMap& output_directories,
DiskSourceTree* source_tree);
// Get all transitive dependencies of the given file (including the file
// itself), adding them to the given list of FileDescriptorProtos. The
// protos will be ordered such that every file is listed before any file that
// depends on it, so that you can call DescriptorPool::BuildFile() on them
// in order. Any files in *already_seen will not be added, and each file
// added will be inserted into *already_seen. If include_source_code_info is
// true then include the source code information in the FileDescriptorProtos.
// If include_json_name is true, populate the json_name field of
// FieldDescriptorProto for all fields.
static void GetTransitiveDependencies(
const FileDescriptor* file, bool include_json_name,
bool include_source_code_info,
std::set<const FileDescriptor*>* already_seen,
RepeatedPtrField<FileDescriptorProto>* output);
// Implements the --print_free_field_numbers. This function prints free field
// numbers into stdout for the message and it's nested message types in
// post-order, i.e. nested types first. Printed range are left-right
// inclusive, i.e. [a, b].
//
// Groups:
// For historical reasons, groups are considered to share the same
// field number space with the parent message, thus it will not print free
// field numbers for groups. The field numbers used in the groups are
// excluded in the free field numbers of the parent message.
//
// Extension Ranges:
// Extension ranges are considered ocuppied field numbers and they will not be
// listed as free numbers in the output.
void PrintFreeFieldNumbers(const Descriptor* descriptor);
// -----------------------------------------------------------------
// The name of the executable as invoked (i.e. argv[0]).
std::string executable_name_;
// Version info set with SetVersionInfo().
std::string version_info_;
// Registered generators.
struct GeneratorInfo {
std::string flag_name;
std::string option_flag_name;
CodeGenerator* generator;
std::string help_text;
};
typedef std::map<std::string, GeneratorInfo> GeneratorMap;
GeneratorMap generators_by_flag_name_;
GeneratorMap generators_by_option_name_;
// A map from generator names to the parameters specified using the option
// flag. For example, if the user invokes the compiler with:
// protoc --foo_out=outputdir --foo_opt=enable_bar ...
// Then there will be an entry ("--foo_out", "enable_bar") in this map.
std::map<std::string, std::string> generator_parameters_;
// Similar to generator_parameters_, but stores the parameters for plugins.
std::map<std::string, std::string> plugin_parameters_;
// See AllowPlugins(). If this is empty, plugins aren't allowed.
std::string plugin_prefix_;
// Maps specific plugin names to files. When executing a plugin, this map
// is searched first to find the plugin executable. If not found here, the
// PATH (or other OS-specific search strategy) is searched.
std::map<std::string, std::string> plugins_;
// Stuff parsed from command line.
enum Mode {
MODE_COMPILE, // Normal mode: parse .proto files and compile them.
MODE_ENCODE, // --encode: read text from stdin, write binary to stdout.
MODE_DECODE, // --decode: read binary from stdin, write text to stdout.
MODE_PRINT, // Print mode: print info of the given .proto files and exit.
};
Mode mode_ = MODE_COMPILE;
enum PrintMode {
PRINT_NONE, // Not in MODE_PRINT
PRINT_FREE_FIELDS, // --print_free_fields
};
PrintMode print_mode_ = PRINT_NONE;
enum ErrorFormat {
ERROR_FORMAT_GCC, // GCC error output format (default).
ERROR_FORMAT_MSVS // Visual Studio output (--error_format=msvs).
};
ErrorFormat error_format_ = ERROR_FORMAT_GCC;
// True if we should treat warnings as errors that fail the compilation.
bool fatal_warnings_ = false;
std::vector<std::pair<std::string, std::string> >
proto_path_; // Search path for proto files.
std::vector<std::string> input_files_; // Names of the input proto files.
// Names of proto files which are allowed to be imported. Used by build
// systems to enforce depend-on-what-you-import.
std::set<std::string> direct_dependencies_;
bool direct_dependencies_explicitly_set_ = false;
// If there's a violation of depend-on-what-you-import, this string will be
// presented to the user. "%s" will be replaced with the violating import.
std::string direct_dependencies_violation_msg_;
// output_directives_ lists all the files we are supposed to output and what
// generator to use for each.
struct OutputDirective {
std::string name; // E.g. "--foo_out"
CodeGenerator* generator; // NULL for plugins
std::string parameter;
std::string output_location;
};
std::vector<OutputDirective> output_directives_;
// When using --encode or --decode, this names the type we are encoding or
// decoding. (Empty string indicates --decode_raw.)
std::string codec_type_;
// If --descriptor_set_in was given, these are filenames containing
// parsed FileDescriptorSets to be used for loading protos. Otherwise, empty.
std::vector<std::string> descriptor_set_in_names_;
// If --descriptor_set_out was given, this is the filename to which the
// FileDescriptorSet should be written. Otherwise, empty.
std::string descriptor_set_out_name_;
// If --dependency_out was given, this is the path to the file where the
// dependency file will be written. Otherwise, empty.
std::string dependency_out_name_;
// True if --include_imports was given, meaning that we should
// write all transitive dependencies to the DescriptorSet. Otherwise, only
// the .proto files listed on the command-line are added.
bool imports_in_descriptor_set_;
// True if --include_source_info was given, meaning that we should not strip
// SourceCodeInfo from the DescriptorSet.
bool source_info_in_descriptor_set_ = false;
// Was the --disallow_services flag used?
bool disallow_services_ = false;
// When using --encode, this will be passed to SetSerializationDeterministic.
bool deterministic_output_ = false;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CommandLineInterface);
};
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This test insures that net/proto2/proto/descriptor.pb.{h,cc} match exactly
// what would be generated by the protocol compiler. These files are not
// generated automatically at build time because they are compiled into the
// protocol compiler itself. So, if they were auto-generated, you'd have a
// chicken-and-egg problem.
//
// If this test fails, run the script
// "generate_descriptor_proto.sh" and add
// descriptor.pb.{h,cc} to your changelist.
#include <map>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/compiler/cpp/generator.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/test_util2.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/stl_util.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
class MockErrorCollector : public MultiFileErrorCollector {
public:
MockErrorCollector() {}
~MockErrorCollector() override {}
std::string text_;
// implements ErrorCollector ---------------------------------------
void AddError(const std::string& filename, int line, int column,
const std::string& message) override {
strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", filename, line, column,
message);
}
};
class MockGeneratorContext : public GeneratorContext {
public:
void ExpectFileMatches(const std::string& virtual_filename,
const std::string& physical_filename) {
auto it = files_.find(virtual_filename);
ASSERT_TRUE(it != files_.end())
<< "Generator failed to generate file: " << virtual_filename;
std::string expected_contents = *it->second;
std::string actual_contents;
GOOGLE_CHECK_OK(
File::GetContents(TestUtil::TestSourceDir() + "/" + physical_filename,
&actual_contents, true))
<< physical_filename;
CleanStringLineEndings(&actual_contents, false);
#ifdef WRITE_FILES // Define to debug mismatched files.
GOOGLE_CHECK_OK(File::SetContents("/tmp/expected.cc", expected_contents,
true));
GOOGLE_CHECK_OK(
File::SetContents("/tmp/actual.cc", actual_contents, true));
#endif
ASSERT_EQ(expected_contents, actual_contents)
<< physical_filename
<< " needs to be regenerated. Please run "
"generate_descriptor_proto.sh. "
"Then add this file to your CL.";
}
// implements GeneratorContext --------------------------------------
io::ZeroCopyOutputStream* Open(const std::string& filename) override {
auto& map_slot = files_[filename];
map_slot.reset(new std::string);
return new io::StringOutputStream(map_slot.get());
}
private:
std::map<std::string, std::unique_ptr<std::string>> files_;
};
const char kDescriptorParameter[] = "dllexport_decl=PROTOBUF_EXPORT";
const char kPluginParameter[] = "dllexport_decl=PROTOC_EXPORT";
const char* test_protos[][2] = {
{"google/protobuf/descriptor", kDescriptorParameter},
{"google/protobuf/compiler/plugin", kPluginParameter},
};
TEST(BootstrapTest, GeneratedFilesMatch) {
// We need a mapping from the actual file to virtual and actual path
// of the data to compare to.
std::map<std::string, std::string> vpath_map;
std::map<std::string, std::string> rpath_map;
rpath_map["third_party/protobuf/test_messages_proto2"] =
"net/proto2/z_generated_example/test_messages_proto2";
rpath_map["third_party/protobuf/test_messages_proto3"] =
"net/proto2/z_generated_example/test_messages_proto3";
rpath_map["net/proto2/internal/proto2_weak"] =
"net/proto2/z_generated_example/proto2_weak";
DiskSourceTree source_tree;
source_tree.MapPath("", TestUtil::TestSourceDir());
for (auto file_parameter : test_protos) {
MockErrorCollector error_collector;
Importer importer(&source_tree, &error_collector);
const FileDescriptor* file =
importer.Import(file_parameter[0] + std::string(".proto"));
ASSERT_TRUE(file != nullptr)
<< "Can't import file " << file_parameter[0] + std::string(".proto")
<< "\n";
EXPECT_EQ("", error_collector.text_);
CppGenerator generator;
MockGeneratorContext context;
#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
generator.set_opensource_runtime(true);
generator.set_runtime_include_base(GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE);
#endif
std::string error;
ASSERT_TRUE(generator.Generate(file, file_parameter[1], &context, &error));
std::string vpath =
FindWithDefault(vpath_map, file_parameter[0], file_parameter[0]);
std::string rpath =
FindWithDefault(rpath_map, file_parameter[0], file_parameter[0]);
context.ExpectFileMatches(vpath + ".pb.cc", rpath + ".pb.cc");
context.ExpectFileMatches(vpath + ".pb.h", rpath + ".pb.h");
}
}
// test Generate in cpp_generator.cc
TEST(BootstrapTest, OptionNotExist) {
cpp::CppGenerator generator;
DescriptorPool pool;
GeneratorContext* generator_context = nullptr;
std::string parameter = "aaa";
std::string error;
ASSERT_FALSE(generator.Generate(
pool.FindFileByName("google/protobuf/descriptor.proto"), parameter,
generator_context, &error));
EXPECT_EQ(error, "Unknown generator option: " + parameter);
}
} // namespace
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,6 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_CPP_GENERATOR_H_
#define GOOGLE_PROTOBUF_COMPILER_CPP_CPP_GENERATOR_H_
#include <google/protobuf/compiler/cpp/generator.h>
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_CPP_GENERATOR_H_

View File

@@ -0,0 +1,438 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/enum.h>
#include <cstdint>
#include <limits>
#include <map>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/names.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
// The GOOGLE_ARRAYSIZE constant is the max enum value plus 1. If the max enum value
// is kint32max, GOOGLE_ARRAYSIZE will overflow. In such cases we should omit the
// generation of the GOOGLE_ARRAYSIZE constant.
bool ShouldGenerateArraySize(const EnumDescriptor* descriptor) {
int32_t max_value = descriptor->value(0)->number();
for (int i = 0; i < descriptor->value_count(); i++) {
if (descriptor->value(i)->number() > max_value) {
max_value = descriptor->value(i)->number();
}
}
return max_value != std::numeric_limits<int32_t>::max();
}
// Returns the number of unique numeric enum values. This is less than
// descriptor->value_count() when there are aliased values.
int CountUniqueValues(const EnumDescriptor* descriptor) {
std::set<int> values;
for (int i = 0; i < descriptor->value_count(); ++i) {
values.insert(descriptor->value(i)->number());
}
return values.size();
}
} // namespace
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
const std::map<std::string, std::string>& vars,
const Options& options)
: descriptor_(descriptor),
classname_(ClassName(descriptor, false)),
options_(options),
generate_array_size_(ShouldGenerateArraySize(descriptor)),
variables_(vars) {
variables_["classname"] = classname_;
variables_["classtype"] = QualifiedClassName(descriptor_, options);
variables_["short_name"] = descriptor_->name();
variables_["nested_name"] = descriptor_->name();
variables_["resolved_name"] = ResolveKeyword(descriptor_->name());
variables_["prefix"] =
(descriptor_->containing_type() == nullptr) ? "" : classname_ + "_";
}
EnumGenerator::~EnumGenerator() {}
void EnumGenerator::GenerateDefinition(io::Printer* printer) {
Formatter format(printer, variables_);
format("enum ${1$$classname$$}$ : int {\n", descriptor_);
format.Indent();
const EnumValueDescriptor* min_value = descriptor_->value(0);
const EnumValueDescriptor* max_value = descriptor_->value(0);
for (int i = 0; i < descriptor_->value_count(); i++) {
auto format_value = format;
format_value.Set("name", EnumValueName(descriptor_->value(i)));
// In C++, an value of -2147483648 gets interpreted as the negative of
// 2147483648, and since 2147483648 can't fit in an integer, this produces a
// compiler warning. This works around that issue.
format_value.Set("number", Int32ToString(descriptor_->value(i)->number()));
format_value.Set("deprecation",
DeprecatedAttribute(options_, descriptor_->value(i)));
if (i > 0) format_value(",\n");
format_value("${1$$prefix$$name$$}$ $deprecation$= $number$",
descriptor_->value(i));
if (descriptor_->value(i)->number() < min_value->number()) {
min_value = descriptor_->value(i);
}
if (descriptor_->value(i)->number() > max_value->number()) {
max_value = descriptor_->value(i);
}
}
if (descriptor_->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
// For new enum semantics: generate min and max sentinel values equal to
// INT32_MIN and INT32_MAX
if (descriptor_->value_count() > 0) format(",\n");
format(
"$classname$_$prefix$INT_MIN_SENTINEL_DO_NOT_USE_ = "
"std::numeric_limits<$int32$>::min(),\n"
"$classname$_$prefix$INT_MAX_SENTINEL_DO_NOT_USE_ = "
"std::numeric_limits<$int32$>::max()");
}
format.Outdent();
format("\n};\n");
format(
"$dllexport_decl $bool $classname$_IsValid(int value);\n"
"constexpr $classname$ ${1$$prefix$$short_name$_MIN$}$ = "
"$prefix$$2$;\n"
"constexpr $classname$ ${1$$prefix$$short_name$_MAX$}$ = "
"$prefix$$3$;\n",
descriptor_, EnumValueName(min_value), EnumValueName(max_value));
if (generate_array_size_) {
format(
"constexpr int ${1$$prefix$$short_name$_ARRAYSIZE$}$ = "
"$prefix$$short_name$_MAX + 1;\n\n",
descriptor_);
}
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
"$dllexport_decl $const ::$proto_ns$::EnumDescriptor* "
"$classname$_descriptor();\n");
}
// The _Name and _Parse functions. The lite implementation is table-based, so
// we make sure to keep the tables hidden in the .cc file.
if (!HasDescriptorMethods(descriptor_->file(), options_)) {
format("const std::string& $classname$_Name($classname$ value);\n");
}
// The _Name() function accepts the enum type itself but also any integral
// type.
format(
"template<typename T>\n"
"inline const std::string& $classname$_Name(T enum_t_value) {\n"
" static_assert(::std::is_same<T, $classname$>::value ||\n"
" ::std::is_integral<T>::value,\n"
" \"Incorrect type passed to function $classname$_Name.\");\n");
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
" return ::$proto_ns$::internal::NameOfEnum(\n"
" $classname$_descriptor(), enum_t_value);\n");
} else {
format(
" return $classname$_Name(static_cast<$classname$>(enum_t_value));\n");
}
format("}\n");
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
"inline bool $classname$_Parse(\n"
" ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
"value) "
"{\n"
" return ::$proto_ns$::internal::ParseNamedEnum<$classname$>(\n"
" $classname$_descriptor(), name, value);\n"
"}\n");
} else {
format(
"bool $classname$_Parse(\n"
" ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
"value);\n");
}
}
void EnumGenerator::GenerateGetEnumDescriptorSpecializations(
io::Printer* printer) {
Formatter format(printer, variables_);
format(
"template <> struct is_proto_enum< $classtype$> : ::std::true_type "
"{};\n");
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
"template <>\n"
"inline const EnumDescriptor* GetEnumDescriptor< $classtype$>() {\n"
" return $classtype$_descriptor();\n"
"}\n");
}
}
void EnumGenerator::GenerateSymbolImports(io::Printer* printer) const {
Formatter format(printer, variables_);
format("typedef $classname$ $resolved_name$;\n");
for (int j = 0; j < descriptor_->value_count(); j++) {
std::string deprecated_attr =
DeprecatedAttribute(options_, descriptor_->value(j));
format(
"$1$static constexpr $resolved_name$ ${2$$3$$}$ =\n"
" $classname$_$3$;\n",
deprecated_attr, descriptor_->value(j),
EnumValueName(descriptor_->value(j)));
}
format(
"static inline bool $nested_name$_IsValid(int value) {\n"
" return $classname$_IsValid(value);\n"
"}\n"
"static constexpr $resolved_name$ ${1$$nested_name$_MIN$}$ =\n"
" $classname$_$nested_name$_MIN;\n"
"static constexpr $resolved_name$ ${1$$nested_name$_MAX$}$ =\n"
" $classname$_$nested_name$_MAX;\n",
descriptor_);
if (generate_array_size_) {
format(
"static constexpr int ${1$$nested_name$_ARRAYSIZE$}$ =\n"
" $classname$_$nested_name$_ARRAYSIZE;\n",
descriptor_);
}
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
"static inline const ::$proto_ns$::EnumDescriptor*\n"
"$nested_name$_descriptor() {\n"
" return $classname$_descriptor();\n"
"}\n");
}
format(
"template<typename T>\n"
"static inline const std::string& $nested_name$_Name(T enum_t_value) {\n"
" static_assert(::std::is_same<T, $resolved_name$>::value ||\n"
" ::std::is_integral<T>::value,\n"
" \"Incorrect type passed to function $nested_name$_Name.\");\n"
" return $classname$_Name(enum_t_value);\n"
"}\n");
format(
"static inline bool "
"$nested_name$_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,\n"
" $resolved_name$* value) {\n"
" return $classname$_Parse(name, value);\n"
"}\n");
}
void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) {
Formatter format(printer, variables_);
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format(
"const ::$proto_ns$::EnumDescriptor* $classname$_descriptor() {\n"
" ::$proto_ns$::internal::AssignDescriptors(&$desc_table$);\n"
" return $file_level_enum_descriptors$[$1$];\n"
"}\n",
idx);
}
format(
"bool $classname$_IsValid(int value) {\n"
" switch (value) {\n");
// Multiple values may have the same number. Make sure we only cover
// each number once by first constructing a set containing all valid
// numbers, then printing a case statement for each element.
std::set<int> numbers;
for (int j = 0; j < descriptor_->value_count(); j++) {
const EnumValueDescriptor* value = descriptor_->value(j);
numbers.insert(value->number());
}
for (std::set<int>::iterator iter = numbers.begin(); iter != numbers.end();
++iter) {
format(" case $1$:\n", Int32ToString(*iter));
}
format(
" return true;\n"
" default:\n"
" return false;\n"
" }\n"
"}\n"
"\n");
if (!HasDescriptorMethods(descriptor_->file(), options_)) {
// In lite mode (where descriptors are unavailable), we generate separate
// tables for mapping between enum names and numbers. The _entries table
// contains the bulk of the data and is sorted by name, while
// _entries_by_number is sorted by number and just contains pointers into
// _entries. The two tables allow mapping from name to number and number to
// name, both in time logarithmic in the number of enum entries. This could
// probably be made faster, but for now the tables are intended to be simple
// and compact.
//
// Enums with allow_alias = true support multiple entries with the same
// numerical value. In cases where there are multiple names for the same
// number, we treat the first name appearing in the .proto file as the
// canonical one.
std::map<std::string, int> name_to_number;
std::map<int, std::string> number_to_canonical_name;
for (int i = 0; i < descriptor_->value_count(); i++) {
const EnumValueDescriptor* value = descriptor_->value(i);
name_to_number.emplace(value->name(), value->number());
// The same number may appear with multiple names, so we use emplace() to
// let the first name win.
number_to_canonical_name.emplace(value->number(), value->name());
}
format(
"static ::$proto_ns$::internal::ExplicitlyConstructed<std::string> "
"$classname$_strings[$1$] = {};\n\n",
CountUniqueValues(descriptor_));
// We concatenate all the names for a given enum into one big string
// literal. If instead we store an array of string literals, the linker
// seems to put all enum strings for a given .proto file in the same
// section, which hinders its ability to strip out unused strings.
format("static const char $classname$_names[] =");
for (const auto& p : name_to_number) {
format("\n \"$1$\"", p.first);
}
format(";\n\n");
format(
"static const ::$proto_ns$::internal::EnumEntry $classname$_entries[] "
"= {\n");
int i = 0;
std::map<int, int> number_to_index;
int data_index = 0;
for (const auto& p : name_to_number) {
format(" { {$classname$_names + $1$, $2$}, $3$ },\n", data_index,
p.first.size(), p.second);
if (number_to_canonical_name[p.second] == p.first) {
number_to_index.emplace(p.second, i);
}
++i;
data_index += p.first.size();
}
format(
"};\n"
"\n"
"static const int $classname$_entries_by_number[] = {\n");
for (const auto& p : number_to_index) {
format(" $1$, // $2$ -> $3$\n", p.second, p.first,
number_to_canonical_name[p.first]);
}
format(
"};\n"
"\n");
format(
"const std::string& $classname$_Name(\n"
" $classname$ value) {\n"
" static const bool dummy =\n"
" ::$proto_ns$::internal::InitializeEnumStrings(\n"
" $classname$_entries,\n"
" $classname$_entries_by_number,\n"
" $1$, $classname$_strings);\n"
" (void) dummy;\n"
" int idx = ::$proto_ns$::internal::LookUpEnumName(\n"
" $classname$_entries,\n"
" $classname$_entries_by_number,\n"
" $1$, value);\n"
" return idx == -1 ? ::$proto_ns$::internal::GetEmptyString() :\n"
" $classname$_strings[idx].get();\n"
"}\n",
CountUniqueValues(descriptor_));
format(
"bool $classname$_Parse(\n"
" ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, $classname$* "
"value) "
"{\n"
" int int_value;\n"
" bool success = ::$proto_ns$::internal::LookUpEnumValue(\n"
" $classname$_entries, $1$, name, &int_value);\n"
" if (success) {\n"
" *value = static_cast<$classname$>(int_value);\n"
" }\n"
" return success;\n"
"}\n",
descriptor_->value_count());
}
if (descriptor_->containing_type() != nullptr) {
std::string parent = ClassName(descriptor_->containing_type(), false);
// Before C++17, we must define the static constants which were
// declared in the header, to give the linker a place to put them.
// But MSVC++ pre-2015 and post-2017 (version 15.5+) insists that we not.
format(
"#if (__cplusplus < 201703) && "
"(!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))\n");
for (int i = 0; i < descriptor_->value_count(); i++) {
format("constexpr $classname$ $1$::$2$;\n", parent,
EnumValueName(descriptor_->value(i)));
}
format(
"constexpr $classname$ $1$::$nested_name$_MIN;\n"
"constexpr $classname$ $1$::$nested_name$_MAX;\n",
parent);
if (generate_array_size_) {
format("constexpr int $1$::$nested_name$_ARRAYSIZE;\n", parent);
}
format(
"#endif // (__cplusplus < 201703) && "
"(!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))\n");
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,106 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__
#include <map>
#include <set>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class EnumGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
EnumGenerator(const EnumDescriptor* descriptor,
const std::map<std::string, std::string>& vars,
const Options& options);
~EnumGenerator();
// Generate header code defining the enum. This code should be placed
// within the enum's package namespace, but NOT within any class, even for
// nested enums.
void GenerateDefinition(io::Printer* printer);
// Generate specialization of GetEnumDescriptor<MyEnum>().
// Precondition: in ::google::protobuf namespace.
void GenerateGetEnumDescriptorSpecializations(io::Printer* printer);
// For enums nested within a message, generate code to import all the enum's
// symbols (e.g. the enum type name, all its values, etc.) into the class's
// namespace. This should be placed inside the class definition in the
// header.
void GenerateSymbolImports(io::Printer* printer) const;
// Source file stuff.
// Generate non-inline methods related to the enum, such as IsValidValue().
// Goes in the .cc file. EnumDescriptors are stored in an array, idx is
// the index in this array that corresponds with this enum.
void GenerateMethods(int idx, io::Printer* printer);
private:
const EnumDescriptor* descriptor_;
const std::string classname_;
const Options& options_;
// whether to generate the *_ARRAYSIZE constant.
const bool generate_array_size_;
std::map<std::string, std::string> variables_;
friend class FileGenerator;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__

View File

@@ -0,0 +1,451 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/enum_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/field.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
void SetEnumVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonFieldVariables(descriptor, variables, options);
const EnumValueDescriptor* default_value = descriptor->default_value_enum();
(*variables)["type"] = QualifiedClassName(descriptor->enum_type(), options);
(*variables)["default"] = Int32ToString(default_value->number());
(*variables)["full_name"] = descriptor->full_name();
(*variables)["cached_byte_size_name"] = MakeVarintCachedSizeName(descriptor);
bool cold = ShouldSplit(descriptor, options);
(*variables)["cached_byte_size_field"] =
MakeVarintCachedSizeFieldName(descriptor, cold);
}
} // namespace
// ===================================================================
EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: FieldGenerator(descriptor, options) {
SetEnumVariables(descriptor, &variables_, options);
}
EnumFieldGenerator::~EnumFieldGenerator() {}
void EnumFieldGenerator::GeneratePrivateMembers(io::Printer* printer) const {
Formatter format(printer, variables_);
format("int $name$_;\n");
}
void EnumFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"$deprecated_attr$$type$ ${1$$name$$}$() const;\n"
"$deprecated_attr$void ${1$set_$name$$}$($type$ value);\n"
"private:\n"
"$type$ ${1$_internal_$name$$}$() const;\n"
"void ${1$_internal_set_$name$$}$($type$ value);\n"
"public:\n",
descriptor_);
}
void EnumFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$() const {\n"
" return static_cast< $type$ >($field$);\n"
"}\n"
"inline $type$ $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline void $classname$::_internal_set_$name$($type$ value) {\n");
if (!HasPreservingUnknownEnumSemantics(descriptor_)) {
format(" assert($type$_IsValid(value));\n");
}
format(
" $set_hasbit$\n"
" $field$ = value;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
"$maybe_prepare_split_message$"
" _internal_set_$name$(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
void EnumFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$ = $default$;\n");
}
void EnumFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->_internal_set_$name$(from._internal_$name$());\n");
}
void EnumFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("swap($field$, other->$field$);\n");
}
void EnumFieldGenerator::GenerateCopyConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$ = from.$field$;\n");
}
void EnumFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"target = stream->EnsureSpace(target);\n"
"target = ::_pbi::WireFormatLite::WriteEnumToArray(\n"
" $number$, this->_internal_$name$(), target);\n");
}
void EnumFieldGenerator::GenerateByteSize(io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"total_size += $tag_size$ +\n"
" ::_pbi::WireFormatLite::EnumSize(this->_internal_$name$());\n");
}
void EnumFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/$default$");
}
void EnumFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format("decltype(Impl_::Split::$name$_){$default$}");
return;
}
format("decltype($field$){$default$}");
}
void EnumFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){}");
}
// ===================================================================
EnumOneofFieldGenerator::EnumOneofFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: EnumFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {}
void EnumOneofFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$() const {\n"
" if (_internal_has_$name$()) {\n"
" return static_cast< $type$ >($field$);\n"
" }\n"
" return static_cast< $type$ >($default$);\n"
"}\n"
"inline $type$ $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline void $classname$::_internal_set_$name$($type$ value) {\n");
if (!HasPreservingUnknownEnumSemantics(descriptor_)) {
format(" assert($type$_IsValid(value));\n");
}
format(
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" }\n"
" $field$ = value;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" _internal_set_$name$(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
void EnumOneofFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$ = $default$;\n");
}
void EnumOneofFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void EnumOneofFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$ns$::_$classname$_default_instance_.$field$ = $default$;\n");
}
// ===================================================================
RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: FieldGenerator(descriptor, options) {
SetEnumVariables(descriptor, &variables_, options);
}
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {}
void RepeatedEnumFieldGenerator::GeneratePrivateMembers(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("::$proto_ns$::RepeatedField<int> $name$_;\n");
if (descriptor_->is_packed() &&
HasGeneratedMethods(descriptor_->file(), options_)) {
format("mutable std::atomic<int> $cached_byte_size_name$;\n");
}
}
void RepeatedEnumFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"private:\n"
"$type$ ${1$_internal_$name$$}$(int index) const;\n"
"void ${1$_internal_add_$name$$}$($type$ value);\n"
"::$proto_ns$::RepeatedField<int>* "
"${1$_internal_mutable_$name$$}$();\n"
"public:\n"
"$deprecated_attr$$type$ ${1$$name$$}$(int index) const;\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, $type$ value);\n"
"$deprecated_attr$void ${1$add_$name$$}$($type$ value);\n"
"$deprecated_attr$const ::$proto_ns$::RepeatedField<int>& "
"${1$$name$$}$() const;\n"
"$deprecated_attr$::$proto_ns$::RepeatedField<int>* "
"${1$mutable_$name$$}$();\n",
descriptor_);
}
void RepeatedEnumFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$(int index) const {\n"
" return static_cast< $type$ >($field$.Get(index));\n"
"}\n"
"inline $type$ $classname$::$name$(int index) const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n");
if (!HasPreservingUnknownEnumSemantics(descriptor_)) {
format(" assert($type$_IsValid(value));\n");
}
format(
" $field$.Set(index, value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::_internal_add_$name$($type$ value) {\n");
if (!HasPreservingUnknownEnumSemantics(descriptor_)) {
format(" assert($type$_IsValid(value));\n");
}
format(
" $field$.Add(value);\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
" _internal_add_$name$(value);\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline const ::$proto_ns$::RepeatedField<int>&\n"
"$classname$::$name$() const {\n"
"$annotate_list$"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $field$;\n"
"}\n"
"inline ::$proto_ns$::RepeatedField<int>*\n"
"$classname$::_internal_mutable_$name$() {\n"
" return &$field$;\n"
"}\n"
"inline ::$proto_ns$::RepeatedField<int>*\n"
"$classname$::mutable_$name$() {\n"
"$annotate_mutable_list$"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return _internal_mutable_$name$();\n"
"}\n");
}
void RepeatedEnumFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.Clear();\n");
}
void RepeatedEnumFieldGenerator::GenerateMergingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$.MergeFrom(from.$field$);\n");
}
void RepeatedEnumFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.InternalSwap(&other->$field$);\n");
}
void RepeatedEnumFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
// Not needed for repeated fields.
}
void RepeatedEnumFieldGenerator::GenerateDestructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.~RepeatedField();\n");
}
void RepeatedEnumFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (descriptor_->is_packed()) {
// Write the tag and the size.
format(
"{\n"
" int byte_size = "
"$cached_byte_size_field$.load(std::memory_order_relaxed);\n"
" if (byte_size > 0) {\n"
" target = stream->WriteEnumPacked(\n"
" $number$, $field$, byte_size, target);\n"
" }\n"
"}\n");
} else {
format(
"for (int i = 0, n = this->_internal_$name$_size(); i < n; i++) {\n"
" target = stream->EnsureSpace(target);\n"
" target = ::_pbi::WireFormatLite::WriteEnumToArray(\n"
" $number$, this->_internal_$name$(i), target);\n"
"}\n");
}
}
void RepeatedEnumFieldGenerator::GenerateByteSize(io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"{\n"
" size_t data_size = 0;\n"
" unsigned int count = static_cast<unsigned "
"int>(this->_internal_$name$_size());");
format.Indent();
format(
"for (unsigned int i = 0; i < count; i++) {\n"
" data_size += ::_pbi::WireFormatLite::EnumSize(\n"
" this->_internal_$name$(static_cast<int>(i)));\n"
"}\n");
if (descriptor_->is_packed()) {
format(
"if (data_size > 0) {\n"
" total_size += $tag_size$ +\n"
" "
"::_pbi::WireFormatLite::Int32Size(static_cast<$int32$>(data_size));\n"
"}\n"
"int cached_size = ::_pbi::ToCachedSize(data_size);\n"
"$cached_byte_size_field$.store(cached_size,\n"
" std::memory_order_relaxed);\n"
"total_size += data_size;\n");
} else {
format("total_size += ($tag_size$UL * count) + data_size;\n");
}
format.Outdent();
format("}\n");
}
void RepeatedEnumFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/{}");
if (descriptor_->is_packed() &&
HasGeneratedMethods(descriptor_->file(), options_)) {
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
void RepeatedEnumFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){arena}");
if (descriptor_->is_packed() &&
HasGeneratedMethods(descriptor_->file(), options_)) {
// std::atomic has no copy constructor, which prevents explicit aggregate
// initialization pre-C++17.
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
void RepeatedEnumFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){from.$field$}");
if (descriptor_->is_packed() &&
HasGeneratedMethods(descriptor_->file(), options_)) {
// std::atomic has no copy constructor.
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,125 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__
#include <map>
#include <string>
#include <google/protobuf/compiler/cpp/field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class EnumFieldGenerator : public FieldGenerator {
public:
EnumFieldGenerator(const FieldDescriptor* descriptor, const Options& options);
~EnumFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator);
};
class EnumOneofFieldGenerator : public EnumFieldGenerator {
public:
EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~EnumOneofFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumOneofFieldGenerator);
};
class RepeatedEnumFieldGenerator : public FieldGenerator {
public:
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~RepeatedEnumFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
void GenerateCopyConstructorCode(io::Printer* /*printer*/) const override {
GOOGLE_CHECK(!ShouldSplit(descriptor_, options_));
}
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__

View File

@@ -0,0 +1,193 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/extension.h>
#include <map>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: descriptor_(descriptor), options_(options), scc_analyzer_(scc_analyzer) {
// Construct type_traits_.
if (descriptor_->is_repeated()) {
type_traits_ = "Repeated";
}
switch (descriptor_->cpp_type()) {
case FieldDescriptor::CPPTYPE_ENUM:
type_traits_.append("EnumTypeTraits< ");
type_traits_.append(ClassName(descriptor_->enum_type(), true));
type_traits_.append(", ");
type_traits_.append(ClassName(descriptor_->enum_type(), true));
type_traits_.append("_IsValid>");
break;
case FieldDescriptor::CPPTYPE_STRING:
type_traits_.append("StringTypeTraits");
break;
case FieldDescriptor::CPPTYPE_MESSAGE:
type_traits_.append("MessageTypeTraits< ");
type_traits_.append(ClassName(descriptor_->message_type(), true));
type_traits_.append(" >");
break;
default:
type_traits_.append("PrimitiveTypeTraits< ");
type_traits_.append(PrimitiveTypeName(options_, descriptor_->cpp_type()));
type_traits_.append(" >");
break;
}
SetCommonVars(options, &variables_);
SetCommonMessageDataVariables(descriptor_->containing_type(), &variables_);
variables_["extendee"] =
QualifiedClassName(descriptor_->containing_type(), options_);
variables_["type_traits"] = type_traits_;
std::string name = descriptor_->name();
variables_["name"] = ResolveKeyword(name);
variables_["constant_name"] = FieldConstantName(descriptor_);
variables_["field_type"] =
StrCat(static_cast<int>(descriptor_->type()));
variables_["packed"] = descriptor_->is_packed() ? "true" : "false";
std::string scope =
IsScoped() ? ClassName(descriptor_->extension_scope(), false) + "::" : "";
variables_["scope"] = scope;
variables_["scoped_name"] = ExtensionName(descriptor_);
variables_["number"] = StrCat(descriptor_->number());
bool add_verify_fn =
// Only verify msgs.
descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
// Options say to verify.
ShouldVerify(descriptor_->message_type(), options_, scc_analyzer_) &&
ShouldVerify(descriptor_->containing_type(), options_, scc_analyzer_);
variables_["verify_fn"] =
add_verify_fn
? StrCat("&", FieldMessageTypeName(descriptor_, options_),
"::InternalVerify")
: "nullptr";
}
ExtensionGenerator::~ExtensionGenerator() {}
bool ExtensionGenerator::IsScoped() const {
return descriptor_->extension_scope() != nullptr;
}
void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) const {
Formatter format(printer, variables_);
// If this is a class member, it needs to be declared "static". Otherwise,
// it needs to be "extern". In the latter case, it also needs the DLL
// export/import specifier.
std::string qualifier;
if (!IsScoped()) {
qualifier = "extern";
if (!options_.dllexport_decl.empty()) {
qualifier = options_.dllexport_decl + " " + qualifier;
}
} else {
qualifier = "static";
}
format(
"static const int $constant_name$ = $number$;\n"
"$1$ ::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n"
" ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$ >\n"
" ${2$$name$$}$;\n",
qualifier, descriptor_);
}
void ExtensionGenerator::GenerateDefinition(io::Printer* printer) {
// If we are building for lite with implicit weak fields, we want to skip over
// any custom options (i.e. extensions of messages from descriptor.proto).
// This prevents the creation of any unnecessary linker references to the
// descriptor messages.
if (options_.lite_implicit_weak_fields &&
descriptor_->containing_type()->file()->name() ==
"net/proto2/proto/descriptor.proto") {
return;
}
Formatter format(printer, variables_);
std::string default_str;
// If this is a class member, it needs to be declared in its class scope.
if (descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
// We need to declare a global string which will contain the default value.
// We cannot declare it at class scope because that would require exposing
// it in the header which would be annoying for other reasons. So we
// replace :: with _ in the name and declare it as a global.
default_str =
StringReplace(variables_["scoped_name"], "::", "_", true) + "_default";
format("const std::string $1$($2$);\n", default_str,
DefaultValue(options_, descriptor_));
} else if (descriptor_->message_type()) {
// We have to initialize the default instance for extensions at registration
// time.
default_str =
FieldMessageTypeName(descriptor_, options_) + "::default_instance()";
} else {
default_str = DefaultValue(options_, descriptor_);
}
// Likewise, class members need to declare the field constant variable.
if (IsScoped()) {
format(
"#if !defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)\n"
"const int $scope$$constant_name$;\n"
"#endif\n");
}
format(
"PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 "
"::$proto_ns$::internal::ExtensionIdentifier< $extendee$,\n"
" ::$proto_ns$::internal::$type_traits$, $field_type$, $packed$>\n"
" $scoped_name$($constant_name$, $1$, $verify_fn$);\n",
default_str);
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,95 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__
#include <map>
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
class FieldDescriptor; // descriptor.h
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class MessageSCCAnalyzer;
// Generates code for an extension, which may be within the scope of some
// message or may be at file scope. This is much simpler than FieldGenerator
// since extensions are just simple identifiers with interesting types.
class ExtensionGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit ExtensionGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~ExtensionGenerator();
// Header stuff.
void GenerateDeclaration(io::Printer* printer) const;
// Source file stuff.
void GenerateDefinition(io::Printer* printer);
bool IsScoped() const;
private:
const FieldDescriptor* descriptor_;
std::string type_traits_;
Options options_;
MessageSCCAnalyzer* scc_analyzer_;
std::map<std::string, std::string> variables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__

View File

@@ -0,0 +1,421 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/field.h>
#include <cstdint>
#include <memory>
#include <string>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/primitive_field.h>
#include <google/protobuf/compiler/cpp/string_field.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/compiler/cpp/enum_field.h>
#include <google/protobuf/compiler/cpp/map_field.h>
#include <google/protobuf/compiler/cpp/message_field.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
using internal::WireFormat;
namespace {
void MaySetAnnotationVariable(const Options& options,
StringPiece annotation_name,
StringPiece substitute_template_prefix,
StringPiece prepared_template,
int field_index, StringPiece access_type,
std::map<std::string, std::string>* variables) {
if (options.field_listener_options.forbidden_field_listener_events.count(
std::string(annotation_name)))
return;
(*variables)[StrCat("annotate_", annotation_name)] = strings::Substitute(
StrCat(substitute_template_prefix, prepared_template, ");\n"),
field_index, access_type);
}
std::string GenerateTemplateForOneofString(const FieldDescriptor* descriptor,
StringPiece proto_ns,
StringPiece field_member) {
std::string field_name = google::protobuf::compiler::cpp::FieldName(descriptor);
std::string field_pointer =
descriptor->options().ctype() == google::protobuf::FieldOptions::STRING
? "$0.UnsafeGetPointer()"
: "$0";
if (descriptor->default_value_string().empty()) {
return strings::Substitute(StrCat("_internal_has_", field_name, "() ? ",
field_pointer, ": nullptr"),
field_member);
}
if (descriptor->options().ctype() == google::protobuf::FieldOptions::STRING_PIECE) {
return strings::Substitute(StrCat("_internal_has_", field_name, "() ? ",
field_pointer, ": nullptr"),
field_member);
}
std::string default_value_pointer =
descriptor->options().ctype() == google::protobuf::FieldOptions::STRING
? "&$1.get()"
: "&$1";
return strings::Substitute(
StrCat("_internal_has_", field_name, "() ? ", field_pointer, " : ",
default_value_pointer),
field_member, MakeDefaultFieldName(descriptor));
}
std::string GenerateTemplateForSingleString(const FieldDescriptor* descriptor,
StringPiece field_member) {
if (descriptor->default_value_string().empty()) {
return StrCat("&", field_member);
}
if (descriptor->options().ctype() == google::protobuf::FieldOptions::STRING) {
return strings::Substitute(
"$0.IsDefault() ? &$1.get() : $0.UnsafeGetPointer()", field_member,
MakeDefaultFieldName(descriptor));
}
return StrCat("&", field_member);
}
} // namespace
void AddAccessorAnnotations(const FieldDescriptor* descriptor,
const Options& options,
std::map<std::string, std::string>* variables) {
// Can be expanded to include more specific calls, for example, for arena or
// clear calls.
static constexpr const char* kAccessorsAnnotations[] = {
"annotate_add", "annotate_get", "annotate_has",
"annotate_list", "annotate_mutable", "annotate_mutable_list",
"annotate_release", "annotate_set", "annotate_size",
"annotate_clear", "annotate_add_mutable",
};
for (size_t i = 0; i < GOOGLE_ARRAYSIZE(kAccessorsAnnotations); ++i) {
(*variables)[kAccessorsAnnotations[i]] = "";
}
if (options.annotate_accessor) {
for (size_t i = 0; i < GOOGLE_ARRAYSIZE(kAccessorsAnnotations); ++i) {
(*variables)[kAccessorsAnnotations[i]] = StrCat(
" ", FieldName(descriptor), "_AccessedNoStrip = true;\n");
}
}
if (!options.field_listener_options.inject_field_listener_events) {
return;
}
if (descriptor->file()->options().optimize_for() ==
google::protobuf::FileOptions::LITE_RUNTIME) {
return;
}
std::string field_member = (*variables)["field"];
const google::protobuf::OneofDescriptor* oneof_member =
descriptor->real_containing_oneof();
const std::string proto_ns = (*variables)["proto_ns"];
const std::string substitute_template_prefix =
StrCat(" ", (*variables)["tracker"], ".$1<$0>(this, ");
std::string prepared_template;
// Flat template is needed if the prepared one is introspecting the values
// inside the returned values, for example, for repeated fields and maps.
std::string prepared_flat_template;
std::string prepared_add_template;
// TODO(b/190614678): Support fields with type Message or Map.
if (descriptor->is_repeated() && !descriptor->is_map()) {
if (descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_GROUP) {
prepared_template = strings::Substitute("&$0.Get(index)", field_member);
prepared_add_template =
strings::Substitute("&$0.Get($0.size() - 1)", field_member);
} else {
prepared_template = "nullptr";
prepared_add_template = "nullptr";
}
} else if (descriptor->is_map()) {
prepared_template = "nullptr";
} else if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
!IsExplicitLazy(descriptor)) {
prepared_template = "nullptr";
} else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
if (oneof_member) {
prepared_template = GenerateTemplateForOneofString(
descriptor, (*variables)["proto_ns"], field_member);
} else {
prepared_template =
GenerateTemplateForSingleString(descriptor, field_member);
}
} else {
prepared_template = StrCat("&", field_member);
}
if (descriptor->is_repeated() && !descriptor->is_map() &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_GROUP) {
prepared_flat_template = StrCat("&", field_member);
} else {
prepared_flat_template = prepared_template;
}
MaySetAnnotationVariable(options, "get", substitute_template_prefix,
prepared_template, descriptor->index(), "OnGet",
variables);
MaySetAnnotationVariable(options, "set", substitute_template_prefix,
prepared_template, descriptor->index(), "OnSet",
variables);
MaySetAnnotationVariable(options, "has", substitute_template_prefix,
prepared_template, descriptor->index(), "OnHas",
variables);
MaySetAnnotationVariable(options, "mutable", substitute_template_prefix,
prepared_template, descriptor->index(), "OnMutable",
variables);
MaySetAnnotationVariable(options, "release", substitute_template_prefix,
prepared_template, descriptor->index(), "OnRelease",
variables);
MaySetAnnotationVariable(options, "clear", substitute_template_prefix,
prepared_flat_template, descriptor->index(),
"OnClear", variables);
MaySetAnnotationVariable(options, "size", substitute_template_prefix,
prepared_flat_template, descriptor->index(),
"OnSize", variables);
MaySetAnnotationVariable(options, "list", substitute_template_prefix,
prepared_flat_template, descriptor->index(),
"OnList", variables);
MaySetAnnotationVariable(options, "mutable_list", substitute_template_prefix,
prepared_flat_template, descriptor->index(),
"OnMutableList", variables);
MaySetAnnotationVariable(options, "add", substitute_template_prefix,
prepared_add_template, descriptor->index(), "OnAdd",
variables);
MaySetAnnotationVariable(options, "add_mutable", substitute_template_prefix,
prepared_add_template, descriptor->index(),
"OnAddMutable", variables);
}
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonVars(options, variables);
SetCommonMessageDataVariables(descriptor->containing_type(), variables);
(*variables)["ns"] = Namespace(descriptor, options);
(*variables)["name"] = FieldName(descriptor);
(*variables)["index"] = StrCat(descriptor->index());
(*variables)["number"] = StrCat(descriptor->number());
(*variables)["classname"] = ClassName(FieldScope(descriptor), false);
(*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type());
bool split = ShouldSplit(descriptor, options);
(*variables)["field"] = FieldMemberName(descriptor, split);
(*variables)["tag_size"] = StrCat(
WireFormat::TagSize(descriptor->number(), descriptor->type()));
(*variables)["deprecated_attr"] = DeprecatedAttribute(options, descriptor);
(*variables)["set_hasbit"] = "";
(*variables)["clear_hasbit"] = "";
(*variables)["maybe_prepare_split_message"] =
split ? " PrepareSplitMessageForWrite();\n" : "";
AddAccessorAnnotations(descriptor, options, variables);
// These variables are placeholders to pick out the beginning and ends of
// identifiers for annotations (when doing so with existing variables would
// be ambiguous or impossible). They should never be set to anything but the
// empty string.
(*variables)["{"] = "";
(*variables)["}"] = "";
}
void FieldGenerator::SetHasBitIndex(int32_t has_bit_index) {
if (!HasHasbit(descriptor_)) {
GOOGLE_CHECK_EQ(has_bit_index, -1);
return;
}
variables_["set_hasbit"] = StrCat(
variables_["has_bits"], "[", has_bit_index / 32, "] |= 0x",
strings::Hex(1u << (has_bit_index % 32), strings::ZERO_PAD_8), "u;");
variables_["clear_hasbit"] = StrCat(
variables_["has_bits"], "[", has_bit_index / 32, "] &= ~0x",
strings::Hex(1u << (has_bit_index % 32), strings::ZERO_PAD_8), "u;");
}
void FieldGenerator::SetInlinedStringIndex(int32_t inlined_string_index) {
if (!IsStringInlined(descriptor_, options_)) {
GOOGLE_CHECK_EQ(inlined_string_index, -1);
return;
}
// The first bit is the tracking bit for on demand registering ArenaDtor.
GOOGLE_CHECK_GT(inlined_string_index, 0)
<< "_inlined_string_donated_'s bit 0 is reserved for arena dtor tracking";
variables_["inlined_string_donated"] = StrCat(
"(", variables_["inlined_string_donated_array"], "[",
inlined_string_index / 32, "] & 0x",
strings::Hex(1u << (inlined_string_index % 32), strings::ZERO_PAD_8),
"u) != 0;");
variables_["donating_states_word"] =
StrCat(variables_["inlined_string_donated_array"], "[",
inlined_string_index / 32, "]");
variables_["mask_for_undonate"] = StrCat(
"~0x", strings::Hex(1u << (inlined_string_index % 32), strings::ZERO_PAD_8),
"u");
}
void FieldGenerator::GenerateAggregateInitializer(io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format("decltype(Impl_::Split::$name$_){arena}");
return;
}
format("decltype($field$){arena}");
}
void FieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/{}");
}
void FieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){from.$field$}");
}
void FieldGenerator::GenerateCopyConstructorCode(io::Printer* printer) const {
if (ShouldSplit(descriptor_, options_)) {
// There is no copy constructor for the `Split` struct, so we need to copy
// the value here.
Formatter format(printer, variables_);
format("$field$ = from.$field$;\n");
}
}
void SetCommonOneofFieldVariables(
const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables) {
const std::string prefix = descriptor->containing_oneof()->name() + "_.";
(*variables)["oneof_name"] = descriptor->containing_oneof()->name();
}
FieldGenerator::~FieldGenerator() {}
FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: descriptor_(descriptor), field_generators_(descriptor->field_count()) {
// Construct all the FieldGenerators.
for (int i = 0; i < descriptor->field_count(); i++) {
field_generators_[i].reset(
MakeGenerator(descriptor->field(i), options, scc_analyzer));
}
}
FieldGenerator* FieldGeneratorMap::MakeGoogleInternalGenerator(
const FieldDescriptor* field, const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
return nullptr;
}
FieldGenerator* FieldGeneratorMap::MakeGenerator(
const FieldDescriptor* field, const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
FieldGenerator* generator =
MakeGoogleInternalGenerator(field, options, scc_analyzer);
if (generator) {
return generator;
}
if (field->is_repeated()) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
if (field->is_map()) {
return new MapFieldGenerator(field, options, scc_analyzer);
} else {
return new RepeatedMessageFieldGenerator(field, options,
scc_analyzer);
}
case FieldDescriptor::CPPTYPE_STRING:
return new RepeatedStringFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_ENUM:
return new RepeatedEnumFieldGenerator(field, options);
default:
return new RepeatedPrimitiveFieldGenerator(field, options);
}
} else if (field->real_containing_oneof()) {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
return new MessageOneofFieldGenerator(field, options, scc_analyzer);
case FieldDescriptor::CPPTYPE_STRING:
return new StringOneofFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_ENUM:
return new EnumOneofFieldGenerator(field, options);
default:
return new PrimitiveOneofFieldGenerator(field, options);
}
} else {
switch (field->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
return new MessageFieldGenerator(field, options, scc_analyzer);
case FieldDescriptor::CPPTYPE_STRING:
return new StringFieldGenerator(field, options);
case FieldDescriptor::CPPTYPE_ENUM:
return new EnumFieldGenerator(field, options);
default:
return new PrimitiveFieldGenerator(field, options);
}
}
}
FieldGeneratorMap::~FieldGeneratorMap() {}
const FieldGenerator& FieldGeneratorMap::get(
const FieldDescriptor* field) const {
GOOGLE_CHECK_EQ(field->containing_type(), descriptor_);
return *field_generators_[field->index()];
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,270 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Helper function: set variables in the map that are the same for all
// field code generators.
// ['name', 'index', 'number', 'classname', 'declared_type', 'tag_size',
// 'deprecation'].
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options);
void SetCommonOneofFieldVariables(
const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables);
class FieldGenerator {
public:
explicit FieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: descriptor_(descriptor), options_(options) {}
virtual ~FieldGenerator();
virtual void GenerateSerializeWithCachedSizes(
io::Printer* printer) const final{};
// Generate lines of code declaring members fields of the message class
// needed to represent this field. These are placed inside the message
// class.
virtual void GeneratePrivateMembers(io::Printer* printer) const = 0;
// Generate static default variable for this field. These are placed inside
// the message class. Most field types don't need this, so the default
// implementation is empty.
virtual void GenerateStaticMembers(io::Printer* /*printer*/) const {}
// Generate prototypes for all of the accessor functions related to this
// field. These are placed inside the class definition.
virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0;
// Generate inline definitions of accessor functions for this field.
// These are placed inside the header after all class definitions.
virtual void GenerateInlineAccessorDefinitions(
io::Printer* printer) const = 0;
// Generate definitions of accessors that aren't inlined. These are
// placed somewhere in the .cc file.
// Most field types don't need this, so the default implementation is empty.
virtual void GenerateNonInlineAccessorDefinitions(
io::Printer* /*printer*/) const {}
// Generate declarations of accessors that are for internal purposes only.
// Most field types don't need this, so the default implementation is empty.
virtual void GenerateInternalAccessorDefinitions(
io::Printer* /*printer*/) const {}
// Generate definitions of accessors that are for internal purposes only.
// Most field types don't need this, so the default implementation is empty.
virtual void GenerateInternalAccessorDeclarations(
io::Printer* /*printer*/) const {}
// Generate lines of code (statements, not declarations) which clear the
// field. This is used to define the clear_$name$() method
virtual void GenerateClearingCode(io::Printer* printer) const = 0;
// Generate lines of code (statements, not declarations) which clear the
// field as part of the Clear() method for the whole message. For message
// types which have field presence bits, MessageGenerator::GenerateClear
// will have already checked the presence bits.
//
// Since most field types can re-use GenerateClearingCode, this method is
// not pure virtual.
virtual void GenerateMessageClearingCode(io::Printer* printer) const {
GenerateClearingCode(printer);
}
// Generate lines of code (statements, not declarations) which merges the
// contents of the field from the current message to the target message,
// which is stored in the generated code variable "from".
// This is used to fill in the MergeFrom method for the whole message.
// Details of this usage can be found in message.cc under the
// GenerateMergeFrom method.
virtual void GenerateMergingCode(io::Printer* printer) const = 0;
// Generates a copy constructor
virtual void GenerateCopyConstructorCode(io::Printer* printer) const;
// Generate lines of code (statements, not declarations) which swaps
// this field and the corresponding field of another message, which
// is stored in the generated code variable "other". This is used to
// define the Swap method. Details of usage can be found in
// message.cc under the GenerateSwap method.
virtual void GenerateSwappingCode(io::Printer* printer) const = 0;
// Generate initialization code for private members declared by
// GeneratePrivateMembers(). These go into the message class's SharedCtor()
// method, invoked by each of the generated constructors.
virtual void GenerateConstructorCode(io::Printer* printer) const = 0;
// Generate initialization code for private members in the cold struct.
virtual void GenerateCreateSplitMessageCode(io::Printer* printer) const {}
// Generate any code that needs to go in the class's SharedDtor() method,
// invoked by the destructor.
// Most field types don't need this, so the default implementation is empty.
virtual void GenerateDestructorCode(io::Printer* /*printer*/) const {}
// Generate a manual destructor invocation for use when the message is on an
// arena. The code that this method generates will be executed inside a
// shared-for-the-whole-message-class method registered with
// OwnDestructor().
virtual void GenerateArenaDestructorCode(io::Printer* printer) const {
GOOGLE_CHECK(NeedsArenaDestructor() == ArenaDtorNeeds::kNone)
<< descriptor_->cpp_type_name();
}
// Generate initialization code for private members declared by
// GeneratePrivateMembers(). These go into the SharedCtor's
// aggregate initialization of the _impl_ struct and must follow the syntax
// (e.g. `decltype($field$){$default$}`). Does not include `:` or `,`
// separators. Default values should be specified here when possible.
//
// Note: We use `decltype($field$)` for both explicit construction and the
// fact that it's self-documenting. Pre-C++17, copy elision isn't guaranteed
// in aggregate initialization so a valid copy/move constructor must exist
// (even though it's not used). Because of this, we need to comment out the
// decltype and fallback to implicit construction.
virtual void GenerateAggregateInitializer(io::Printer* printer) const;
// Generate constinit initialization code for private members declared by
// GeneratePrivateMembers(). These go into the constexpr constructor's
// aggregate initialization of the _impl_ struct and must follow the syntax
// (e.g. `/*decltype($field$)*/{}`, see above). Does not
// include `:` or `,` separators.
virtual void GenerateConstexprAggregateInitializer(
io::Printer* printer) const;
// Generate copy initialization code for private members declared by
// GeneratePrivateMembers(). These go into the copy constructor's
// aggregate initialization of the _impl_ struct and must follow the syntax
// (e.g. `decltype($field$){from.$field$}`, see above). Does not
// include `:` or `,` separators.
virtual void GenerateCopyAggregateInitializer(io::Printer* printer) const;
// Generate lines to serialize this field directly to the array "target",
// which are placed within the message's SerializeWithCachedSizesToArray()
// method. This must also advance "target" past the written bytes.
virtual void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const = 0;
// Generate lines to compute the serialized size of this field, which
// are placed in the message's ByteSize() method.
virtual void GenerateByteSize(io::Printer* printer) const = 0;
// Generates lines to call IsInitialized() for eligible message fields. Non
// message fields won't need to override this function.
virtual void GenerateIsInitialized(io::Printer* printer) const {}
virtual bool IsInlined() const { return false; }
virtual ArenaDtorNeeds NeedsArenaDestructor() const {
return ArenaDtorNeeds::kNone;
}
void SetHasBitIndex(int32_t has_bit_index);
void SetInlinedStringIndex(int32_t inlined_string_index);
protected:
const FieldDescriptor* descriptor_;
const Options& options_;
std::map<std::string, std::string> variables_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator);
};
// Convenience class which constructs FieldGenerators for a Descriptor.
class FieldGeneratorMap {
public:
FieldGeneratorMap(const Descriptor* descriptor, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~FieldGeneratorMap();
const FieldGenerator& get(const FieldDescriptor* field) const;
void SetHasBitIndices(const std::vector<int>& has_bit_indices_) {
for (int i = 0; i < descriptor_->field_count(); ++i) {
field_generators_[i]->SetHasBitIndex(has_bit_indices_[i]);
}
}
void SetInlinedStringIndices(const std::vector<int>& inlined_string_indices) {
for (int i = 0; i < descriptor_->field_count(); ++i) {
field_generators_[i]->SetInlinedStringIndex(inlined_string_indices[i]);
}
}
private:
const Descriptor* descriptor_;
std::vector<std::unique_ptr<FieldGenerator>> field_generators_;
static FieldGenerator* MakeGoogleInternalGenerator(
const FieldDescriptor* field, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
static FieldGenerator* MakeGenerator(const FieldDescriptor* field,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__
#include <algorithm>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/cpp/field.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/scc.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
class FileDescriptor; // descriptor.h
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class EnumGenerator; // enum.h
class MessageGenerator; // message.h
class ServiceGenerator; // service.h
class ExtensionGenerator; // extension.h
class FileGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
FileGenerator(const FileDescriptor* file, const Options& options);
~FileGenerator();
// Shared code between the two header generators below.
void GenerateHeader(io::Printer* printer);
// info_path, if non-empty, should be the path (relative to printer's
// output) to the metadata file describing this proto header.
void GenerateProtoHeader(io::Printer* printer, const std::string& info_path);
// info_path, if non-empty, should be the path (relative to printer's
// output) to the metadata file describing this PB header.
void GeneratePBHeader(io::Printer* printer, const std::string& info_path);
void GenerateSource(io::Printer* printer);
// The following member functions are used when the lite_implicit_weak_fields
// option is set. In this mode the code is organized a bit differently to
// promote better linker stripping of unused code. In particular, we generate
// one .cc file per message, one .cc file per extension, and a main pb.cc file
// containing everything else.
int NumMessages() const { return message_generators_.size(); }
int NumExtensions() const { return extension_generators_.size(); }
// Generates the source file for one message.
void GenerateSourceForMessage(int idx, io::Printer* printer);
// Generates the source file for one extension.
void GenerateSourceForExtension(int idx, io::Printer* printer);
// Generates a source file containing everything except messages and
// extensions.
void GenerateGlobalSource(io::Printer* printer);
private:
// Internal type used by GenerateForwardDeclarations (defined in file.cc).
class ForwardDeclarations;
struct CrossFileReferences;
void IncludeFile(const std::string& google3_name, io::Printer* printer) {
DoIncludeFile(google3_name, false, printer);
}
void IncludeFileAndExport(const std::string& google3_name,
io::Printer* printer) {
DoIncludeFile(google3_name, true, printer);
}
void DoIncludeFile(const std::string& google3_name, bool do_export,
io::Printer* printer);
std::string CreateHeaderInclude(const std::string& basename,
const FileDescriptor* file);
void GetCrossFileReferencesForField(const FieldDescriptor* field,
CrossFileReferences* refs);
void GetCrossFileReferencesForFile(const FileDescriptor* file,
CrossFileReferences* refs);
void GenerateInternalForwardDeclarations(const CrossFileReferences& refs,
io::Printer* printer);
void GenerateSourceIncludes(io::Printer* printer);
void GenerateSourcePrelude(io::Printer* printer);
void GenerateSourceDefaultInstance(int idx, io::Printer* printer);
void GenerateInitForSCC(const SCC* scc, const CrossFileReferences& refs,
io::Printer* printer);
void GenerateReflectionInitializationCode(io::Printer* printer);
// For other imports, generates their forward-declarations.
void GenerateForwardDeclarations(io::Printer* printer);
// Generates top or bottom of a header file.
void GenerateTopHeaderGuard(io::Printer* printer, bool pb_h);
void GenerateBottomHeaderGuard(io::Printer* printer, bool pb_h);
// Generates #include directives.
void GenerateLibraryIncludes(io::Printer* printer);
void GenerateDependencyIncludes(io::Printer* printer);
// Generate a pragma to pull in metadata using the given info_path (if
// non-empty). info_path should be relative to printer's output.
void GenerateMetadataPragma(io::Printer* printer,
const std::string& info_path);
// Generates a couple of different pieces before definitions:
void GenerateGlobalStateFunctionDeclarations(io::Printer* printer);
// Generates types for classes.
void GenerateMessageDefinitions(io::Printer* printer);
void GenerateEnumDefinitions(io::Printer* printer);
// Generates generic service definitions.
void GenerateServiceDefinitions(io::Printer* printer);
// Generates extension identifiers.
void GenerateExtensionIdentifiers(io::Printer* printer);
// Generates inline function definitions.
void GenerateInlineFunctionDefinitions(io::Printer* printer);
void GenerateProto2NamespaceEnumSpecializations(io::Printer* printer);
// Sometimes the names we use in a .proto file happen to be defined as
// macros on some platforms (e.g., macro/minor used in plugin.proto are
// defined as macros in sys/types.h on FreeBSD and a few other platforms).
// To make the generated code compile on these platforms, we either have to
// undef the macro for these few platforms, or rename the field name for all
// platforms. Since these names are part of protobuf public API, renaming is
// generally a breaking change so we prefer the #undef approach.
void GenerateMacroUndefs(io::Printer* printer);
bool IsDepWeak(const FileDescriptor* dep) const {
if (weak_deps_.count(dep) != 0) {
GOOGLE_CHECK(!options_.opensource_runtime);
return true;
}
return false;
}
std::set<const FileDescriptor*> weak_deps_;
const FileDescriptor* file_;
const Options options_;
MessageSCCAnalyzer scc_analyzer_;
std::map<std::string, std::string> variables_;
// Contains the post-order walk of all the messages (and child messages) in
// this file. If you need a pre-order walk just reverse iterate.
std::vector<std::unique_ptr<MessageGenerator>> message_generators_;
std::vector<std::unique_ptr<EnumGenerator>> enum_generators_;
std::vector<std::unique_ptr<ServiceGenerator>> service_generators_;
std::vector<std::unique_ptr<ExtensionGenerator>> extension_generators_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__

View File

@@ -0,0 +1,279 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/generator.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/compiler/cpp/file.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
CppGenerator::CppGenerator() {}
CppGenerator::~CppGenerator() {}
namespace {
std::string NumberedCcFileName(const std::string& basename, int number) {
return StrCat(basename, ".out/", number, ".cc");
}
} // namespace
bool CppGenerator::Generate(const FileDescriptor* file,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const {
std::vector<std::pair<std::string, std::string> > options;
ParseGeneratorParameter(parameter, &options);
// -----------------------------------------------------------------
// parse generator options
// If the dllexport_decl option is passed to the compiler, we need to write
// it in front of every symbol that should be exported if this .proto is
// compiled into a Windows DLL. E.g., if the user invokes the protocol
// compiler as:
// protoc --cpp_out=dllexport_decl=FOO_EXPORT:outdir foo.proto
// then we'll define classes like this:
// class FOO_EXPORT Foo {
// ...
// }
// FOO_EXPORT is a macro which should expand to __declspec(dllexport) or
// __declspec(dllimport) depending on what is being compiled.
//
// If the proto_h option is passed to the compiler, we will generate all
// classes and enums so that they can be forward-declared from files that
// need them from imports.
//
// If the lite option is passed to the compiler, we will generate the
// current files and all transitive dependencies using the LITE runtime.
Options file_options;
file_options.opensource_runtime = opensource_runtime_;
file_options.runtime_include_base = runtime_include_base_;
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "dllexport_decl") {
file_options.dllexport_decl = options[i].second;
} else if (options[i].first == "safe_boundary_check") {
file_options.safe_boundary_check = true;
} else if (options[i].first == "annotate_headers") {
file_options.annotate_headers = true;
} else if (options[i].first == "annotation_pragma_name") {
file_options.annotation_pragma_name = options[i].second;
} else if (options[i].first == "annotation_guard_name") {
file_options.annotation_guard_name = options[i].second;
} else if (options[i].first == "speed") {
file_options.enforce_mode = EnforceOptimizeMode::kSpeed;
} else if (options[i].first == "code_size") {
file_options.enforce_mode = EnforceOptimizeMode::kCodeSize;
} else if (options[i].first == "lite") {
file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
} else if (options[i].first == "lite_implicit_weak_fields") {
file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
file_options.lite_implicit_weak_fields = true;
if (!options[i].second.empty()) {
file_options.num_cc_files =
strto32(options[i].second.c_str(), nullptr, 10);
}
} else if (options[i].first == "proto_h") {
file_options.proto_h = true;
} else if (options[i].first == "annotate_accessor") {
file_options.annotate_accessor = true;
} else if (options[i].first == "inject_field_listener_events") {
file_options.field_listener_options.inject_field_listener_events = true;
} else if (options[i].first == "forbidden_field_listener_events") {
std::size_t pos = 0;
do {
std::size_t next_pos = options[i].second.find_first_of("+", pos);
if (next_pos == std::string::npos) {
next_pos = options[i].second.size();
}
if (next_pos > pos)
file_options.field_listener_options.forbidden_field_listener_events
.insert(options[i].second.substr(pos, next_pos - pos));
pos = next_pos + 1;
} while (pos < options[i].second.size());
} else if (options[i].first == "verified_lazy") {
file_options.unverified_lazy = false;
} else if (options[i].first == "unverified_lazy_message_sets") {
file_options.unverified_lazy_message_sets = true;
} else if (options[i].first == "message_owned_arena_trial") {
file_options.message_owned_arena_trial = true;
} else if (options[i].first == "force_eagerly_verified_lazy") {
file_options.force_eagerly_verified_lazy = true;
} else if (options[i].first == "experimental_tail_call_table_mode") {
if (options[i].second == "never") {
file_options.tctable_mode = Options::kTCTableNever;
} else if (options[i].second == "guarded") {
file_options.tctable_mode = Options::kTCTableGuarded;
} else if (options[i].second == "always") {
file_options.tctable_mode = Options::kTCTableAlways;
} else {
*error = "Unknown value for experimental_tail_call_table_mode: " +
options[i].second;
return false;
}
} else {
*error = "Unknown generator option: " + options[i].first;
return false;
}
}
// The safe_boundary_check option controls behavior for Google-internal
// protobuf APIs.
if (file_options.safe_boundary_check && file_options.opensource_runtime) {
*error =
"The safe_boundary_check option is not supported outside of Google.";
return false;
}
// -----------------------------------------------------------------
std::string basename = StripProto(file->name());
if (MaybeBootstrap(file_options, generator_context, file_options.bootstrap,
&basename)) {
return true;
}
FileGenerator file_generator(file, file_options);
// Generate header(s).
if (file_options.proto_h) {
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".proto.h"));
GeneratedCodeInfo annotations;
io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
&annotations);
std::string info_path = basename + ".proto.h.meta";
io::Printer printer(
output.get(), '$',
file_options.annotate_headers ? &annotation_collector : nullptr);
file_generator.GenerateProtoHeader(
&printer, file_options.annotate_headers ? info_path : "");
if (file_options.annotate_headers) {
std::unique_ptr<io::ZeroCopyOutputStream> info_output(
generator_context->Open(info_path));
annotations.SerializeToZeroCopyStream(info_output.get());
}
}
{
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".pb.h"));
GeneratedCodeInfo annotations;
io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
&annotations);
std::string info_path = basename + ".pb.h.meta";
io::Printer printer(
output.get(), '$',
file_options.annotate_headers ? &annotation_collector : nullptr);
file_generator.GeneratePBHeader(
&printer, file_options.annotate_headers ? info_path : "");
if (file_options.annotate_headers) {
std::unique_ptr<io::ZeroCopyOutputStream> info_output(
generator_context->Open(info_path));
annotations.SerializeToZeroCopyStream(info_output.get());
}
}
// Generate cc file(s).
if (UsingImplicitWeakFields(file, file_options)) {
{
// This is the global .cc file, containing
// enum/services/tables/reflection
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".pb.cc"));
io::Printer printer(output.get(), '$');
file_generator.GenerateGlobalSource(&printer);
}
int num_cc_files =
file_generator.NumMessages() + file_generator.NumExtensions();
// If we're using implicit weak fields then we allow the user to
// optionally specify how many files to generate, not counting the global
// pb.cc file. If we have more files than messages, then some files will
// be generated as empty placeholders.
if (file_options.num_cc_files > 0) {
GOOGLE_CHECK_LE(num_cc_files, file_options.num_cc_files)
<< "There must be at least as many numbered .cc files as messages "
"and extensions.";
num_cc_files = file_options.num_cc_files;
}
int cc_file_number = 0;
for (int i = 0; i < file_generator.NumMessages(); i++) {
std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open(
NumberedCcFileName(basename, cc_file_number++)));
io::Printer printer(output.get(), '$');
file_generator.GenerateSourceForMessage(i, &printer);
}
for (int i = 0; i < file_generator.NumExtensions(); i++) {
std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open(
NumberedCcFileName(basename, cc_file_number++)));
io::Printer printer(output.get(), '$');
file_generator.GenerateSourceForExtension(i, &printer);
}
// Create empty placeholder files if necessary to match the expected number
// of files.
for (; cc_file_number < num_cc_files; ++cc_file_number) {
std::unique_ptr<io::ZeroCopyOutputStream> output(generator_context->Open(
NumberedCcFileName(basename, cc_file_number)));
}
} else {
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".pb.cc"));
io::Printer printer(output.get(), '$');
file_generator.GenerateSource(&printer);
}
return true;
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,107 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Generates C++ code for a given .proto file.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// CodeGenerator implementation which generates a C++ source file and
// header. If you create your own protocol compiler binary and you want
// it to support C++ output, you can do so by registering an instance of this
// CodeGenerator with the CommandLineInterface in your main() function.
class PROTOC_EXPORT CppGenerator : public CodeGenerator {
public:
CppGenerator();
~CppGenerator() override;
enum class Runtime {
kGoogle3, // Use the internal google3 runtime.
kOpensource, // Use the open-source runtime.
// Use the open-source runtime with google3 #include paths. We make these
// absolute to avoid ambiguity, so the runtime will be #included like:
// #include "third_party/protobuf/.../google/protobuf/message.h"
kOpensourceGoogle3
};
void set_opensource_runtime(bool opensource) {
opensource_runtime_ = opensource;
}
// If set to a non-empty string, generated code will do:
// #include "<BASE>/google/protobuf/message.h"
// instead of:
// #include <google/protobuf/message.h>
// This has no effect if opensource_runtime = false.
void set_runtime_include_base(const std::string& base) {
runtime_include_base_ = base;
}
// implements CodeGenerator ----------------------------------------
bool Generate(const FileDescriptor* file, const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
// We don't fully support this yet, but this is needed to unblock the tests,
// and we will have full support before the experimental flag is removed.
return FEATURE_PROTO3_OPTIONAL;
}
private:
bool opensource_runtime_ = true;
std::string runtime_include_base_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CppGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,338 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/cpp/map_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
bool IsProto3Field(const FieldDescriptor* field_descriptor) {
const FileDescriptor* file_descriptor = field_descriptor->file();
return file_descriptor->syntax() == FileDescriptor::SYNTAX_PROTO3;
}
void SetMessageVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonFieldVariables(descriptor, variables, options);
(*variables)["type"] = ClassName(descriptor->message_type(), false);
(*variables)["full_name"] = descriptor->full_name();
const FieldDescriptor* key = descriptor->message_type()->map_key();
const FieldDescriptor* val = descriptor->message_type()->map_value();
(*variables)["key_cpp"] = PrimitiveTypeName(options, key->cpp_type());
switch (val->cpp_type()) {
case FieldDescriptor::CPPTYPE_MESSAGE:
(*variables)["val_cpp"] = FieldMessageTypeName(val, options);
break;
case FieldDescriptor::CPPTYPE_ENUM:
(*variables)["val_cpp"] = ClassName(val->enum_type(), true);
break;
default:
(*variables)["val_cpp"] = PrimitiveTypeName(options, val->cpp_type());
}
(*variables)["key_wire_type"] =
"TYPE_" + ToUpper(DeclaredTypeMethodName(key->type()));
(*variables)["val_wire_type"] =
"TYPE_" + ToUpper(DeclaredTypeMethodName(val->type()));
(*variables)["map_classname"] = ClassName(descriptor->message_type(), false);
(*variables)["number"] = StrCat(descriptor->number());
(*variables)["tag"] = StrCat(internal::WireFormat::MakeTag(descriptor));
if (HasDescriptorMethods(descriptor->file(), options)) {
(*variables)["lite"] = "";
} else {
(*variables)["lite"] = "Lite";
}
}
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: FieldGenerator(descriptor, options),
has_required_fields_(
scc_analyzer->HasRequiredFields(descriptor->message_type())) {
SetMessageVariables(descriptor, &variables_, options);
}
MapFieldGenerator::~MapFieldGenerator() {}
void MapFieldGenerator::GeneratePrivateMembers(io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"::$proto_ns$::internal::MapField$lite$<\n"
" $map_classname$,\n"
" $key_cpp$, $val_cpp$,\n"
" ::$proto_ns$::internal::WireFormatLite::$key_wire_type$,\n"
" ::$proto_ns$::internal::WireFormatLite::$val_wire_type$> "
"$name$_;\n");
}
void MapFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"private:\n"
"const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n"
" ${1$_internal_$name$$}$() const;\n"
"::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n"
" ${1$_internal_mutable_$name$$}$();\n"
"public:\n"
"$deprecated_attr$const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n"
" ${1$$name$$}$() const;\n"
"$deprecated_attr$::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n"
" ${1$mutable_$name$$}$();\n",
descriptor_);
}
void MapFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n"
"$classname$::_internal_$name$() const {\n"
" return $field$.GetMap();\n"
"}\n"
"inline const ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >&\n"
"$classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_map:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n"
"$classname$::_internal_mutable_$name$() {\n"
"$maybe_prepare_split_message$"
" return $field$.MutableMap();\n"
"}\n"
"inline ::$proto_ns$::Map< $key_cpp$, $val_cpp$ >*\n"
"$classname$::mutable_$name$() {\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable_map:$full_name$)\n"
" return _internal_mutable_$name$();\n"
"}\n");
}
void MapFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.Clear();\n");
}
void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$.MergeFrom(from.$field$);\n");
}
void MapFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.InternalSwap(&other->$field$);\n");
}
void MapFieldGenerator::GenerateCopyConstructorCode(
io::Printer* printer) const {
GenerateConstructorCode(printer);
GenerateMergingCode(printer);
}
static void GenerateSerializationLoop(Formatter& format, bool string_key,
bool string_value,
bool is_deterministic) {
if (is_deterministic) {
format(
"for (const auto& entry : "
"::_pbi::MapSorter$1$<MapType>(map_field)) {\n",
(string_key ? "Ptr" : "Flat"));
} else {
format("for (const auto& entry : map_field) {\n");
}
{
auto loop_scope = format.ScopedIndent();
format(
"target = WireHelper::InternalSerialize($number$, "
"entry.first, entry.second, target, stream);\n");
if (string_key || string_value) {
format("check_utf8(entry);\n");
}
}
format("}\n");
}
void MapFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("if (!this->_internal_$name$().empty()) {\n");
format.Indent();
const FieldDescriptor* key_field = descriptor_->message_type()->map_key();
const FieldDescriptor* value_field = descriptor_->message_type()->map_value();
const bool string_key = key_field->type() == FieldDescriptor::TYPE_STRING;
const bool string_value = value_field->type() == FieldDescriptor::TYPE_STRING;
format(
"using MapType = ::_pb::Map<$key_cpp$, $val_cpp$>;\n"
"using WireHelper = $map_classname$::Funcs;\n"
"const auto& map_field = this->_internal_$name$();\n");
bool utf8_check = string_key || string_value;
if (utf8_check) {
format("auto check_utf8 = [](const MapType::value_type& entry) {\n");
{
auto check_scope = format.ScopedIndent();
// p may be unused when GetUtf8CheckMode evaluates to kNone,
// thus disabling the validation.
format("(void)entry;\n");
if (string_key) {
GenerateUtf8CheckCodeForString(
key_field, options_, false,
"entry.first.data(), static_cast<int>(entry.first.length()),\n",
format);
}
if (string_value) {
GenerateUtf8CheckCodeForString(
value_field, options_, false,
"entry.second.data(), static_cast<int>(entry.second.length()),\n",
format);
}
}
format("};\n");
}
format(
"\n"
"if (stream->IsSerializationDeterministic() && "
"map_field.size() > 1) {\n");
{
auto deterministic_scope = format.ScopedIndent();
GenerateSerializationLoop(format, string_key, string_value, true);
}
format("} else {\n");
{
auto map_order_scope = format.ScopedIndent();
GenerateSerializationLoop(format, string_key, string_value, false);
}
format("}\n");
format.Outdent();
format("}\n");
}
void MapFieldGenerator::GenerateByteSize(io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"total_size += $tag_size$ *\n"
" "
"::$proto_ns$::internal::FromIntSize(this->_internal_$name$_size());\n"
"for (::$proto_ns$::Map< $key_cpp$, $val_cpp$ >::const_iterator\n"
" it = this->_internal_$name$().begin();\n"
" it != this->_internal_$name$().end(); ++it) {\n"
" total_size += $map_classname$::Funcs::ByteSizeLong(it->first, "
"it->second);\n"
"}\n");
}
void MapFieldGenerator::GenerateIsInitialized(io::Printer* printer) const {
if (!has_required_fields_) return;
Formatter format(printer, variables_);
format(
"if (!::$proto_ns$::internal::AllAreInitialized($field$)) return "
"false;\n");
}
void MapFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (HasDescriptorMethods(descriptor_->file(), options_)) {
format("/*decltype($field$)*/{::_pbi::ConstantInitialized()}");
} else {
format("/*decltype($field$)*/{}");
}
}
void MapFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
// MapField has no move constructor, which prevents explicit aggregate
// initialization pre-C++17.
format("/*decltype($field$)*/{}");
}
void MapFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format(
"/*decltype($classname$::Split::$name$_)*/"
"{::_pbi::ArenaInitialized(), arena}");
return;
}
// MapField has no move constructor.
format("/*decltype($field$)*/{::_pbi::ArenaInitialized(), arena}");
}
void MapFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format("$cached_split_ptr$->$name$_.Destruct();\n");
format("$cached_split_ptr$->$name$_.~MapField$lite$();\n");
return;
}
format("$field$.Destruct();\n");
format("$field$.~MapField$lite$();\n");
}
void MapFieldGenerator::GenerateArenaDestructorCode(
io::Printer* printer) const {
if (NeedsArenaDestructor() == ArenaDtorNeeds::kNone) {
return;
}
Formatter format(printer, variables_);
// _this is the object being destructed (we are inside a static method here).
format("_this->$field$.Destruct();\n");
}
ArenaDtorNeeds MapFieldGenerator::NeedsArenaDestructor() const {
return HasDescriptorMethods(descriptor_->file(), options_)
? ArenaDtorNeeds::kRequired
: ArenaDtorNeeds::kNone;
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,83 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MAP_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MAP_FIELD_H__
#include <map>
#include <string>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/message_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class MapFieldGenerator : public FieldGenerator {
public:
MapFieldGenerator(const FieldDescriptor* descriptor, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~MapFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateIsInitialized(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateArenaDestructorCode(io::Printer* printer) const override;
ArenaDtorNeeds NeedsArenaDestructor() const override;
private:
const bool has_required_fields_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MAP_FIELD_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__
#include <cstdint>
#include <memory>
#include <set>
#include <string>
#include <google/protobuf/compiler/cpp/field.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/message_layout_helper.h>
#include <google/protobuf/compiler/cpp/options.h>
#include <google/protobuf/compiler/cpp/parse_function_generator.h>
namespace google {
namespace protobuf {
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class EnumGenerator; // enum.h
class ExtensionGenerator; // extension.h
class MessageGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
MessageGenerator(const Descriptor* descriptor,
const std::map<std::string, std::string>& vars,
int index_in_file_messages, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~MessageGenerator();
// Append the two types of nested generators to the corresponding vector.
void AddGenerators(
std::vector<std::unique_ptr<EnumGenerator>>* enum_generators,
std::vector<std::unique_ptr<ExtensionGenerator>>* extension_generators);
// Generate definitions for this class and all its nested types.
void GenerateClassDefinition(io::Printer* printer);
// Generate definitions of inline methods (placed at the end of the header
// file).
void GenerateInlineMethods(io::Printer* printer);
// Source file stuff.
// Generate all non-inline methods for this class.
void GenerateClassMethods(io::Printer* printer);
// Generate source file code that should go outside any namespace.
void GenerateSourceInProto2Namespace(io::Printer* printer);
private:
// Generate declarations and definitions of accessors for fields.
void GenerateFieldAccessorDeclarations(io::Printer* printer);
void GenerateFieldAccessorDefinitions(io::Printer* printer);
// Generate the field offsets array. Returns the a pair of the total number
// of entries generated and the index of the first has_bit entry.
std::pair<size_t, size_t> GenerateOffsets(io::Printer* printer);
void GenerateSchema(io::Printer* printer, int offset, int has_offset);
// Generate constructors and destructor.
void GenerateStructors(io::Printer* printer);
// The compiler typically generates multiple copies of each constructor and
// destructor: http://gcc.gnu.org/bugs.html#nonbugs_cxx
// Placing common code in a separate method reduces the generated code size.
//
// Generate the shared constructor code.
void GenerateSharedConstructorCode(io::Printer* printer);
// Generate the shared destructor code.
void GenerateSharedDestructorCode(io::Printer* printer);
// Generate the arena-specific destructor code.
void GenerateArenaDestructorCode(io::Printer* printer);
// Generate the constexpr constructor for constant initialization of the
// default instance.
void GenerateConstexprConstructor(io::Printer* printer);
void GenerateCreateSplitMessage(io::Printer* printer);
void GenerateInitDefaultSplitInstance(io::Printer* printer);
// Generate standard Message methods.
void GenerateClear(io::Printer* printer);
void GenerateOneofClear(io::Printer* printer);
void GenerateVerify(io::Printer* printer);
void GenerateSerializeWithCachedSizes(io::Printer* printer);
void GenerateSerializeWithCachedSizesToArray(io::Printer* printer);
void GenerateSerializeWithCachedSizesBody(io::Printer* printer);
void GenerateSerializeWithCachedSizesBodyShuffled(io::Printer* printer);
void GenerateByteSize(io::Printer* printer);
void GenerateMergeFrom(io::Printer* printer);
void GenerateClassSpecificMergeImpl(io::Printer* printer);
void GenerateCopyFrom(io::Printer* printer);
void GenerateSwap(io::Printer* printer);
void GenerateIsInitialized(io::Printer* printer);
// Helpers for GenerateSerializeWithCachedSizes().
//
// cached_has_bit_index maintains that:
// cached_has_bits = _has_bits_[cached_has_bit_index]
// for cached_has_bit_index >= 0
void GenerateSerializeOneField(io::Printer* printer,
const FieldDescriptor* field,
int cached_has_bits_index);
// Generate a switch statement to serialize 2+ fields from the same oneof.
// Or, if fields.size() == 1, just call GenerateSerializeOneField().
void GenerateSerializeOneofFields(
io::Printer* printer, const std::vector<const FieldDescriptor*>& fields);
void GenerateSerializeOneExtensionRange(
io::Printer* printer, const Descriptor::ExtensionRange* range);
// Generates has_foo() functions and variables for singular field has-bits.
void GenerateSingularFieldHasBits(const FieldDescriptor* field,
Formatter format);
// Generates has_foo() functions and variables for oneof field has-bits.
void GenerateOneofHasBits(io::Printer* printer);
// Generates has_foo_bar() functions for oneof members.
void GenerateOneofMemberHasBits(const FieldDescriptor* field,
const Formatter& format);
// Generates the clear_foo() method for a field.
void GenerateFieldClear(const FieldDescriptor* field, bool is_inline,
Formatter format);
// Generates the body of the message's copy constructor.
void GenerateCopyConstructorBody(io::Printer* printer) const;
// Returns the level that this message needs ArenaDtor. If the message has
// a field that is not arena-exclusive, it needs an ArenaDtor
// (go/proto-destructor).
//
// - Returning kNone means we don't need to generate ArenaDtor.
// - Returning kOnDemand means we need to generate ArenaDtor, but don't need
// to register ArenaDtor at construction. Such as when the message's
// ArenaDtor code is only for destructing inlined string.
// - Returning kRequired means we meed to generate ArenaDtor and register it
// at construction.
ArenaDtorNeeds NeedsArenaDestructor() const;
size_t HasBitsSize() const;
size_t InlinedStringDonatedSize() const;
int HasBitIndex(const FieldDescriptor* a) const;
int HasByteIndex(const FieldDescriptor* a) const;
int HasWordIndex(const FieldDescriptor* a) const;
bool SameHasByte(const FieldDescriptor* a, const FieldDescriptor* b) const;
std::vector<uint32_t> RequiredFieldsBitMask() const;
const Descriptor* descriptor_;
int index_in_file_messages_;
std::string classname_;
Options options_;
FieldGeneratorMap field_generators_;
// optimized_order_ is the order we layout the message's fields in the
// class. This is reused to initialize the fields in-order for cache
// efficiency.
//
// optimized_order_ excludes oneof fields and weak fields.
std::vector<const FieldDescriptor*> optimized_order_;
std::vector<int> has_bit_indices_;
int max_has_bit_index_;
// A map from field index to inlined_string index. For non-inlined-string
// fields, the element is -1. If there is no inlined string in the message,
// this is empty.
std::vector<int> inlined_string_indices_;
// The count of inlined_string fields in the message.
int max_inlined_string_index_;
std::vector<const EnumGenerator*> enum_generators_;
std::vector<const ExtensionGenerator*> extension_generators_;
int num_required_fields_;
int num_weak_fields_;
std::unique_ptr<MessageLayoutHelper> message_layout_helper_;
std::unique_ptr<ParseFunctionGenerator> parse_function_generator_;
MessageSCCAnalyzer* scc_analyzer_;
std::map<std::string, std::string> variables_;
friend class FileGenerator;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__

View File

@@ -0,0 +1,958 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/message_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/compiler/cpp/field.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
std::string ReinterpretCast(const std::string& type,
const std::string& expression,
bool implicit_weak_field) {
if (implicit_weak_field) {
return "reinterpret_cast< " + type + " >(" + expression + ")";
} else {
return expression;
}
}
void SetMessageVariables(const FieldDescriptor* descriptor,
const Options& options, bool implicit_weak,
std::map<std::string, std::string>* variables) {
SetCommonFieldVariables(descriptor, variables, options);
(*variables)["type"] = FieldMessageTypeName(descriptor, options);
(*variables)["casted_member"] = ReinterpretCast(
(*variables)["type"] + "*", (*variables)["field"], implicit_weak);
(*variables)["casted_member_const"] =
ReinterpretCast("const " + (*variables)["type"] + "&",
"*" + (*variables)["field"], implicit_weak);
(*variables)["type_default_instance"] =
QualifiedDefaultInstanceName(descriptor->message_type(), options);
(*variables)["type_default_instance_ptr"] = ReinterpretCast(
"const ::PROTOBUF_NAMESPACE_ID::MessageLite*",
QualifiedDefaultInstancePtr(descriptor->message_type(), options),
implicit_weak);
(*variables)["type_reference_function"] =
implicit_weak ? (" ::" + (*variables)["proto_ns"] +
"::internal::StrongReference(reinterpret_cast<const " +
(*variables)["type"] + "&>(\n" +
(*variables)["type_default_instance"] + "));\n")
: "";
// NOTE: Escaped here to unblock proto1->proto2 migration.
// TODO(liujisi): Extend this to apply for other conflicting methods.
(*variables)["release_name"] =
SafeFunctionName(descriptor->containing_type(), descriptor, "release_");
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
// ===================================================================
MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: FieldGenerator(descriptor, options),
implicit_weak_field_(
IsImplicitWeakField(descriptor, options, scc_analyzer)),
has_required_fields_(
scc_analyzer->HasRequiredFields(descriptor->message_type())) {
SetMessageVariables(descriptor, options, implicit_weak_field_, &variables_);
}
MessageFieldGenerator::~MessageFieldGenerator() {}
void MessageFieldGenerator::GeneratePrivateMembers(io::Printer* printer) const {
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format("::$proto_ns$::MessageLite* $name$_;\n");
} else {
format("$type$* $name$_;\n");
}
}
void MessageFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (IsFieldStripped(descriptor_, options_)) {
format(
"$deprecated_attr$const $type$& ${1$$name$$}$() const { "
"__builtin_trap(); }\n"
"PROTOBUF_NODISCARD $deprecated_attr$$type$* "
"${1$$release_name$$}$() { "
"__builtin_trap(); }\n"
"$deprecated_attr$$type$* ${1$mutable_$name$$}$() { "
"__builtin_trap(); }\n"
"$deprecated_attr$void ${1$set_allocated_$name$$}$"
"($type$* $name$) { __builtin_trap(); }\n"
"$deprecated_attr$void "
"${1$unsafe_arena_set_allocated_$name$$}$(\n"
" $type$* $name$) { __builtin_trap(); }\n"
"$deprecated_attr$$type$* ${1$unsafe_arena_release_$name$$}$() { "
"__builtin_trap(); }\n",
descriptor_);
return;
}
format(
"$deprecated_attr$const $type$& ${1$$name$$}$() const;\n"
"PROTOBUF_NODISCARD $deprecated_attr$$type$* "
"${1$$release_name$$}$();\n"
"$deprecated_attr$$type$* ${1$mutable_$name$$}$();\n"
"$deprecated_attr$void ${1$set_allocated_$name$$}$"
"($type$* $name$);\n",
descriptor_);
if (!IsFieldStripped(descriptor_, options_)) {
format(
"private:\n"
"const $type$& ${1$_internal_$name$$}$() const;\n"
"$type$* ${1$_internal_mutable_$name$$}$();\n"
"public:\n",
descriptor_);
}
format(
"$deprecated_attr$void "
"${1$unsafe_arena_set_allocated_$name$$}$(\n"
" $type$* $name$);\n"
"$deprecated_attr$$type$* ${1$unsafe_arena_release_$name$$}$();\n",
descriptor_);
}
void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const {}
void MessageFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline const $type$& $classname$::_internal_$name$() const {\n"
"$type_reference_function$"
" const $type$* p = $casted_member$;\n"
" return p != nullptr ? *p : reinterpret_cast<const $type$&>(\n"
" $type_default_instance$);\n"
"}\n"
"inline const $type$& $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n");
format(
"inline void $classname$::unsafe_arena_set_allocated_$name$(\n"
" $type$* $name$) {\n"
"$maybe_prepare_split_message$"
// If we're not on an arena, free whatever we were holding before.
// (If we are on arena, we can just forget the earlier pointer.)
" if (GetArenaForAllocation() == nullptr) {\n"
" delete reinterpret_cast<::$proto_ns$::MessageLite*>($field$);\n"
" }\n");
if (implicit_weak_field_) {
format(
" $field$ = reinterpret_cast<::$proto_ns$::MessageLite*>($name$);\n");
} else {
format(" $field$ = $name$;\n");
}
format(
" if ($name$) {\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_unsafe_arena_set_allocated"
":$full_name$)\n"
"}\n");
format(
"inline $type$* $classname$::$release_name$() {\n"
"$type_reference_function$"
"$annotate_release$"
"$maybe_prepare_split_message$"
" $clear_hasbit$\n"
" $type$* temp = $casted_member$;\n"
" $field$ = nullptr;\n"
"#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE\n"
" auto* old = reinterpret_cast<::$proto_ns$::MessageLite*>(temp);\n"
" temp = ::$proto_ns$::internal::DuplicateIfNonNull(temp);\n"
" if (GetArenaForAllocation() == nullptr) { delete old; }\n"
"#else // PROTOBUF_FORCE_COPY_IN_RELEASE\n"
" if (GetArenaForAllocation() != nullptr) {\n"
" temp = ::$proto_ns$::internal::DuplicateIfNonNull(temp);\n"
" }\n"
"#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE\n"
" return temp;\n"
"}\n"
"inline $type$* $classname$::unsafe_arena_release_$name$() {\n"
"$annotate_release$"
" // @@protoc_insertion_point(field_release:$full_name$)\n"
"$type_reference_function$"
"$maybe_prepare_split_message$"
" $clear_hasbit$\n"
" $type$* temp = $casted_member$;\n"
" $field$ = nullptr;\n"
" return temp;\n"
"}\n");
format(
"inline $type$* $classname$::_internal_mutable_$name$() {\n"
"$type_reference_function$"
" $set_hasbit$\n"
" if ($field$ == nullptr) {\n"
" auto* p = CreateMaybeMessage<$type$>(GetArenaForAllocation());\n");
if (implicit_weak_field_) {
format(" $field$ = reinterpret_cast<::$proto_ns$::MessageLite*>(p);\n");
} else {
format(" $field$ = p;\n");
}
format(
" }\n"
" return $casted_member$;\n"
"}\n"
"inline $type$* $classname$::mutable_$name$() {\n"
// TODO(b/122856539): add tests to make sure all write accessors are able
// to prepare split message allocation.
"$maybe_prepare_split_message$"
" $type$* _msg = _internal_mutable_$name$();\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return _msg;\n"
"}\n");
// We handle the most common case inline, and delegate less common cases to
// the slow fallback function.
format(
"inline void $classname$::set_allocated_$name$($type$* $name$) {\n"
" ::$proto_ns$::Arena* message_arena = GetArenaForAllocation();\n");
format(
"$maybe_prepare_split_message$"
" if (message_arena == nullptr) {\n");
if (IsCrossFileMessage(descriptor_)) {
format(
" delete reinterpret_cast< ::$proto_ns$::MessageLite*>($field$);\n");
} else {
format(" delete $field$;\n");
}
format(
" }\n"
" if ($name$) {\n");
if (IsCrossFileMessage(descriptor_)) {
// We have to read the arena through the virtual method, because the type
// isn't defined in this file.
format(
" ::$proto_ns$::Arena* submessage_arena =\n"
" ::$proto_ns$::Arena::InternalGetOwningArena(\n"
" reinterpret_cast<::$proto_ns$::MessageLite*>("
"$name$));\n");
} else {
format(
" ::$proto_ns$::Arena* submessage_arena =\n"
" ::$proto_ns$::Arena::InternalGetOwningArena("
"$name$);\n");
}
format(
" if (message_arena != submessage_arena) {\n"
" $name$ = ::$proto_ns$::internal::GetOwnedMessage(\n"
" message_arena, $name$, submessage_arena);\n"
" }\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n");
if (implicit_weak_field_) {
format(" $field$ = reinterpret_cast<MessageLite*>($name$);\n");
} else {
format(" $field$ = $name$;\n");
}
format(
"$annotate_set$"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
void MessageFieldGenerator::GenerateInternalAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format(
"static const ::$proto_ns$::MessageLite& $name$("
"const $classname$* msg);\n"
"static ::$proto_ns$::MessageLite* mutable_$name$("
"$classname$* msg);\n");
} else {
format("static const $type$& $name$(const $classname$* msg);\n");
}
}
void MessageFieldGenerator::GenerateInternalAccessorDefinitions(
io::Printer* printer) const {
// In theory, these accessors could be inline in _Internal. However, in
// practice, the linker is then not able to throw them out making implicit
// weak dependencies not work at all.
Formatter format(printer, variables_);
if (implicit_weak_field_) {
// These private accessors are used by MergeFrom and
// MergePartialFromCodedStream, and their purpose is to provide access to
// the field without creating a strong dependency on the message type.
format(
"const ::$proto_ns$::MessageLite& $classname$::_Internal::$name$(\n"
" const $classname$* msg) {\n"
" if (msg->$field$ != nullptr) {\n"
" return *msg->$field$;\n"
" } else {\n"
" return *$type_default_instance_ptr$;\n"
" }\n"
"}\n");
format(
"::$proto_ns$::MessageLite*\n"
"$classname$::_Internal::mutable_$name$($classname$* msg) {\n");
if (HasHasbit(descriptor_)) {
format(" msg->$set_hasbit$\n");
}
if (descriptor_->real_containing_oneof() == nullptr) {
format(" if (msg->$field$ == nullptr) {\n");
} else {
format(
" if (!msg->_internal_has_$name$()) {\n"
" msg->clear_$oneof_name$();\n"
" msg->set_has_$name$();\n");
}
format(
" msg->$field$ = $type_default_instance_ptr$->New(\n"
" msg->GetArenaForAllocation());\n"
" }\n"
" return msg->$field$;\n"
"}\n");
} else {
// This inline accessor directly returns member field and is used in
// Serialize such that AFDO profile correctly captures access information to
// message fields under serialize.
format(
"const $type$&\n"
"$classname$::_Internal::$name$(const $classname$* msg) {\n"
" return *msg->$field$;\n"
"}\n");
}
}
void MessageFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (!HasHasbit(descriptor_)) {
// If we don't have has-bits, message presence is indicated only by ptr !=
// nullptr. Thus on clear, we need to delete the object.
format(
"if (GetArenaForAllocation() == nullptr && $field$ != nullptr) {\n"
" delete $field$;\n"
"}\n"
"$field$ = nullptr;\n");
} else {
format("if ($field$ != nullptr) $field$->Clear();\n");
}
}
void MessageFieldGenerator::GenerateMessageClearingCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (!HasHasbit(descriptor_)) {
// If we don't have has-bits, message presence is indicated only by ptr !=
// nullptr. Thus on clear, we need to delete the object.
format(
"if (GetArenaForAllocation() == nullptr && $field$ != nullptr) {\n"
" delete $field$;\n"
"}\n"
"$field$ = nullptr;\n");
} else {
format(
"$DCHK$($field$ != nullptr);\n"
"$field$->Clear();\n");
}
}
void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format(
"_Internal::mutable_$name$(_this)->CheckTypeAndMergeFrom(\n"
" _Internal::$name$(&from));\n");
} else {
format(
"_this->_internal_mutable_$name$()->$type$::MergeFrom(\n"
" from._internal_$name$());\n");
}
}
void MessageFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format("swap($field$, other->$field$);\n");
}
void MessageFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (options_.opensource_runtime) {
// TODO(gerbens) Remove this when we don't need to destruct default
// instances. In google3 a default instance will never get deleted so we
// don't need to worry about that but in opensource protobuf default
// instances are deleted in shutdown process and we need to take special
// care when handling them.
format("if (this != internal_default_instance()) ");
}
if (ShouldSplit(descriptor_, options_)) {
format("delete $cached_split_ptr$->$name$_;\n");
return;
}
format("delete $field$;\n");
}
void MessageFieldGenerator::GenerateCopyConstructorCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format(
"if (from._internal_has_$name$()) {\n"
" _this->$field$ = new $type$(*from.$field$);\n"
"}\n");
}
void MessageFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) {
format(
"target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$($number$, _Internal::$name$(this),\n"
" _Internal::$name$(this).GetCachedSize(), target, stream);\n");
} else {
format(
"target = stream->EnsureSpace(target);\n"
"target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$(\n"
" $number$, _Internal::$name$(this), target, stream);\n");
}
}
void MessageFieldGenerator::GenerateByteSize(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format(
"total_size += $tag_size$ +\n"
" ::$proto_ns$::internal::WireFormatLite::$declared_type$Size(\n"
" *$field$);\n");
}
void MessageFieldGenerator::GenerateIsInitialized(io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
if (!has_required_fields_) return;
Formatter format(printer, variables_);
format(
"if (_internal_has_$name$()) {\n"
" if (!$field$->IsInitialized()) return false;\n"
"}\n");
}
void MessageFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/nullptr");
}
void MessageFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){nullptr}");
}
void MessageFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format("decltype(Impl_::Split::$name$_){nullptr}");
return;
}
format("decltype($field$){nullptr}");
}
// ===================================================================
MessageOneofFieldGenerator::MessageOneofFieldGenerator(
const FieldDescriptor* descriptor, const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: MessageFieldGenerator(descriptor, options, scc_analyzer) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {}
void MessageOneofFieldGenerator::GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"void $classname$::set_allocated_$name$($type$* $name$) {\n"
" ::$proto_ns$::Arena* message_arena = GetArenaForAllocation();\n"
" clear_$oneof_name$();\n"
" if ($name$) {\n");
if (descriptor_->file() != descriptor_->message_type()->file()) {
// We have to read the arena through the virtual method, because the type
// isn't defined in this file.
format(
" ::$proto_ns$::Arena* submessage_arena =\n"
" ::$proto_ns$::Arena::InternalGetOwningArena(\n"
" reinterpret_cast<::$proto_ns$::MessageLite*>("
"$name$));\n");
} else {
format(
" ::$proto_ns$::Arena* submessage_arena =\n"
" ::$proto_ns$::Arena::InternalGetOwningArena($name$);\n");
}
format(
" if (message_arena != submessage_arena) {\n"
" $name$ = ::$proto_ns$::internal::GetOwnedMessage(\n"
" message_arena, $name$, submessage_arena);\n"
" }\n"
" set_has_$name$();\n"
" $field$ = $name$;\n"
" }\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
void MessageOneofFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$* $classname$::$release_name$() {\n"
"$annotate_release$"
" // @@protoc_insertion_point(field_release:$full_name$)\n"
"$type_reference_function$"
" if (_internal_has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" $type$* temp = $casted_member$;\n"
" if (GetArenaForAllocation() != nullptr) {\n"
" temp = ::$proto_ns$::internal::DuplicateIfNonNull(temp);\n"
" }\n"
" $field$ = nullptr;\n"
" return temp;\n"
" } else {\n"
" return nullptr;\n"
" }\n"
"}\n");
format(
"inline const $type$& $classname$::_internal_$name$() const {\n"
"$type_reference_function$"
" return _internal_has_$name$()\n"
" ? $casted_member_const$\n"
" : reinterpret_cast< $type$&>($type_default_instance$);\n"
"}\n"
"inline const $type$& $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline $type$* $classname$::unsafe_arena_release_$name$() {\n"
"$annotate_release$"
" // @@protoc_insertion_point(field_unsafe_arena_release"
":$full_name$)\n"
"$type_reference_function$"
" if (_internal_has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" $type$* temp = $casted_member$;\n"
" $field$ = nullptr;\n"
" return temp;\n"
" } else {\n"
" return nullptr;\n"
" }\n"
"}\n"
"inline void $classname$::unsafe_arena_set_allocated_$name$"
"($type$* $name$) {\n"
// We rely on the oneof clear method to free the earlier contents of
// this oneof. We can directly use the pointer we're given to set the
// new value.
" clear_$oneof_name$();\n"
" if ($name$) {\n"
" set_has_$name$();\n");
if (implicit_weak_field_) {
format(
" $field$ = "
"reinterpret_cast<::$proto_ns$::MessageLite*>($name$);\n");
} else {
format(" $field$ = $name$;\n");
}
format(
" }\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_unsafe_arena_set_allocated:"
"$full_name$)\n"
"}\n"
"inline $type$* $classname$::_internal_mutable_$name$() {\n"
"$type_reference_function$"
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n");
if (implicit_weak_field_) {
format(
" $field$ = "
"reinterpret_cast<::$proto_ns$::MessageLite*>(CreateMaybeMessage< "
"$type$ >(GetArenaForAllocation()));\n");
} else {
format(
" $field$ = CreateMaybeMessage< $type$ "
">(GetArenaForAllocation());\n");
}
format(
" }\n"
" return $casted_member$;\n"
"}\n"
"inline $type$* $classname$::mutable_$name$() {\n"
" $type$* _msg = _internal_mutable_$name$();\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return _msg;\n"
"}\n");
}
void MessageOneofFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format(
"if (GetArenaForAllocation() == nullptr) {\n"
" delete $field$;\n"
"}\n");
}
void MessageOneofFieldGenerator::GenerateMessageClearingCode(
io::Printer* printer) const {
GenerateClearingCode(printer);
}
void MessageOneofFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void MessageOneofFieldGenerator::GenerateDestructorCode(
io::Printer* printer) const {
// We inherit from MessageFieldGenerator, so we need to override the default
// behavior.
}
void MessageOneofFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
// Don't print any constructor code. The field is in a union. We allocate
// space only when this field is used.
}
void MessageOneofFieldGenerator::GenerateIsInitialized(
io::Printer* printer) const {
if (!has_required_fields_) return;
Formatter format(printer, variables_);
format(
"if (_internal_has_$name$()) {\n"
" if (!$field$->IsInitialized()) return false;\n"
"}\n");
}
// ===================================================================
RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator(
const FieldDescriptor* descriptor, const Options& options,
MessageSCCAnalyzer* scc_analyzer)
: FieldGenerator(descriptor, options),
implicit_weak_field_(
IsImplicitWeakField(descriptor, options, scc_analyzer)),
has_required_fields_(
scc_analyzer->HasRequiredFields(descriptor->message_type())) {
SetMessageVariables(descriptor, options, implicit_weak_field_, &variables_);
}
RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {}
void RepeatedMessageFieldGenerator::GeneratePrivateMembers(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format("::$proto_ns$::WeakRepeatedPtrField< $type$ > $name$_;\n");
} else {
format("::$proto_ns$::RepeatedPtrField< $type$ > $name$_;\n");
}
}
void RepeatedMessageFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (IsFieldStripped(descriptor_, options_)) {
format(
"$deprecated_attr$$type$* ${1$mutable_$name$$}$(int index) { "
"__builtin_trap(); }\n"
"$deprecated_attr$::$proto_ns$::RepeatedPtrField< $type$ >*\n"
" ${1$mutable_$name$$}$() { __builtin_trap(); }\n"
"$deprecated_attr$const $type$& ${1$$name$$}$(int index) const { "
"__builtin_trap(); }\n"
"$deprecated_attr$$type$* ${1$add_$name$$}$() { "
"__builtin_trap(); }\n"
"$deprecated_attr$const ::$proto_ns$::RepeatedPtrField< $type$ >&\n"
" ${1$$name$$}$() const { __builtin_trap(); }\n",
descriptor_);
return;
}
format(
"$deprecated_attr$$type$* ${1$mutable_$name$$}$(int index);\n"
"$deprecated_attr$::$proto_ns$::RepeatedPtrField< $type$ >*\n"
" ${1$mutable_$name$$}$();\n",
descriptor_);
if (!IsFieldStripped(descriptor_, options_)) {
format(
"private:\n"
"const $type$& ${1$_internal_$name$$}$(int index) const;\n"
"$type$* ${1$_internal_add_$name$$}$();\n"
"public:\n",
descriptor_);
}
format(
"$deprecated_attr$const $type$& ${1$$name$$}$(int index) const;\n"
"$deprecated_attr$$type$* ${1$add_$name$$}$();\n"
"$deprecated_attr$const ::$proto_ns$::RepeatedPtrField< $type$ >&\n"
" ${1$$name$$}$() const;\n",
descriptor_);
}
void RepeatedMessageFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format.Set("weak", implicit_weak_field_ ? ".weak" : "");
format(
"inline $type$* $classname$::mutable_$name$(int index) {\n"
"$annotate_mutable$"
// TODO(dlj): move insertion points
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
"$type_reference_function$"
" return $field$$weak$.Mutable(index);\n"
"}\n"
"inline ::$proto_ns$::RepeatedPtrField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
"$annotate_mutable_list$"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
"$type_reference_function$"
" return &$field$$weak$;\n"
"}\n");
if (options_.safe_boundary_check) {
format(
"inline const $type$& $classname$::_internal_$name$(int index) const "
"{\n"
" return $field$$weak$.InternalCheckedGet(index,\n"
" reinterpret_cast<const $type$&>($type_default_instance$));\n"
"}\n");
} else {
format(
"inline const $type$& $classname$::_internal_$name$(int index) const "
"{\n"
"$type_reference_function$"
" return $field$$weak$.Get(index);\n"
"}\n");
}
format(
"inline const $type$& $classname$::$name$(int index) const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$(index);\n"
"}\n"
"inline $type$* $classname$::_internal_add_$name$() {\n"
" return $field$$weak$.Add();\n"
"}\n"
"inline $type$* $classname$::add_$name$() {\n"
" $type$* _add = _internal_add_$name$();\n"
"$annotate_add_mutable$"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
" return _add;\n"
"}\n");
format(
"inline const ::$proto_ns$::RepeatedPtrField< $type$ >&\n"
"$classname$::$name$() const {\n"
"$annotate_list$"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
"$type_reference_function$"
" return $field$$weak$;\n"
"}\n");
}
void RepeatedMessageFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format("$field$.Clear();\n");
}
void RepeatedMessageFieldGenerator::GenerateMergingCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format("_this->$field$.MergeFrom(from.$field$);\n");
}
void RepeatedMessageFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format("$field$.InternalSwap(&other->$field$);\n");
}
void RepeatedMessageFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
// Not needed for repeated fields.
}
void RepeatedMessageFieldGenerator::GenerateDestructorCode(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format("$field$.~WeakRepeatedPtrField();\n");
} else {
format("$field$.~RepeatedPtrField();\n");
}
}
void RepeatedMessageFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format(
"for (auto it = this->$field$.pointer_begin(),\n"
" end = this->$field$.pointer_end(); it < end; ++it) {\n");
if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) {
format(
" target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$($number$, "
"**it, (**it).GetCachedSize(), target, stream);\n");
} else {
format(
" target = stream->EnsureSpace(target);\n"
" target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$($number$, **it, target, "
"stream);\n");
}
format("}\n");
} else {
format(
"for (unsigned i = 0,\n"
" n = static_cast<unsigned>(this->_internal_$name$_size());"
" i < n; i++) {\n");
if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) {
format(
" const auto& repfield = this->_internal_$name$(i);\n"
" target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$($number$, "
"repfield, repfield.GetCachedSize(), target, stream);\n"
"}\n");
} else {
format(
" target = stream->EnsureSpace(target);\n"
" target = ::$proto_ns$::internal::WireFormatLite::\n"
" InternalWrite$declared_type$($number$, "
"this->_internal_$name$(i), target, stream);\n"
"}\n");
}
}
}
void RepeatedMessageFieldGenerator::GenerateByteSize(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
Formatter format(printer, variables_);
format(
"total_size += $tag_size$UL * this->_internal_$name$_size();\n"
"for (const auto& msg : this->$field$) {\n"
" total_size +=\n"
" ::$proto_ns$::internal::WireFormatLite::$declared_type$Size(msg);\n"
"}\n");
}
void RepeatedMessageFieldGenerator::GenerateIsInitialized(
io::Printer* printer) const {
GOOGLE_CHECK(!IsFieldStripped(descriptor_, options_));
if (!has_required_fields_) return;
Formatter format(printer, variables_);
if (implicit_weak_field_) {
format(
"if (!::$proto_ns$::internal::AllAreInitializedWeak($field$.weak))\n"
" return false;\n");
} else {
format(
"if (!::$proto_ns$::internal::AllAreInitialized($field$))\n"
" return false;\n");
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,148 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__
#include <map>
#include <string>
#include <google/protobuf/compiler/cpp/field.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class MessageFieldGenerator : public FieldGenerator {
public:
MessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~MessageFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const override;
void GenerateInternalAccessorDeclarations(
io::Printer* printer) const override;
void GenerateInternalAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMessageClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateIsInitialized(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
protected:
const bool implicit_weak_field_;
const bool has_required_fields_;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator);
};
class MessageOneofFieldGenerator : public MessageFieldGenerator {
public:
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~MessageOneofFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
// MessageFieldGenerator, from which we inherit, overrides this so we need to
// override it as well.
void GenerateMessageClearingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
void GenerateIsInitialized(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator);
};
class RepeatedMessageFieldGenerator : public FieldGenerator {
public:
RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
~RepeatedMessageFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
void GenerateCopyConstructorCode(io::Printer* printer) const override {}
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateIsInitialized(io::Printer* printer) const override;
private:
const bool implicit_weak_field_;
const bool has_required_fields_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__

View File

@@ -0,0 +1,64 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: seongkim@google.com (Seong Beom Kim)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class MessageSCCAnalyzer;
// Provides an abstract interface to optimize message layout
// by rearranging the fields of a message.
class MessageLayoutHelper {
public:
virtual ~MessageLayoutHelper() {}
virtual void OptimizeLayout(std::vector<const FieldDescriptor*>* fields,
const Options& options,
MessageSCCAnalyzer* scc_analyzer) = 0;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__

View File

@@ -0,0 +1,272 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/unittest.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
namespace cpp_unittest {
#if !defined(GOOGLE_CHECK_MESSAGE_SIZE)
#define GOOGLE_CHECK_MESSAGE_SIZE(t, expected)
#endif
// Mock structures to lock down the size of messages in a platform-independent
// way. The commented sizes only apply when build with clang x86_64.
struct MockMessageBase {
virtual ~MockMessageBase() = default; // 8 bytes vtable
void* internal_metadata; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockMessageBase, 16);
struct MockZeroFieldsBase : public MockMessageBase {
int cached_size; // 4 bytes
// + 4 bytes padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockZeroFieldsBase, 24);
struct MockExtensionSet {
void* arena; // 8 bytes
int16_t capacity; // 4 bytes
int16_t size; // 4 bytes
void* data; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockExtensionSet, 24);
struct MockRepeatedPtrField {
void* arena; // 8 bytes
int current_size; // 4 bytes
int total_size; // 4 bytes
void* data; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockRepeatedPtrField, 24);
struct MockRepeatedField {
int current_size; // 4 bytes
int total_size; // 4 bytes
void* data; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockRepeatedField, 16);
TEST(GeneratedMessageTest, MockSizes) {
// Consistency checks -- if these fail, the tests below will definitely fail.
GOOGLE_CHECK_EQ(sizeof(MessageLite), sizeof(MockMessageBase));
GOOGLE_CHECK_EQ(sizeof(Message), sizeof(MockMessageBase));
GOOGLE_CHECK_EQ(sizeof(internal::ZeroFieldsBase), sizeof(MockZeroFieldsBase));
GOOGLE_CHECK_EQ(sizeof(internal::ExtensionSet), sizeof(MockExtensionSet));
GOOGLE_CHECK_EQ(sizeof(RepeatedPtrField<std::string>), sizeof(MockRepeatedPtrField));
GOOGLE_CHECK_EQ(sizeof(RepeatedField<int>), sizeof(MockRepeatedField));
}
TEST(GeneratedMessageTest, EmptyMessageSize) {
EXPECT_EQ(sizeof(protobuf_unittest::TestEmptyMessage),
sizeof(MockZeroFieldsBase));
}
TEST(GeneratedMessageTest, ReservedSize) {
EXPECT_EQ(sizeof(protobuf_unittest::TestReservedFields),
sizeof(MockZeroFieldsBase));
}
TEST(GeneratedMessageTest, EmptyMessageWithExtensionsSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
MockExtensionSet extensions; // 24 bytes
int cached_size; // 4 bytes
// + 4 bytes of padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 48);
EXPECT_EQ(sizeof(protobuf_unittest::TestEmptyMessageWithExtensions),
sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, RecursiveMessageSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
void* a; // 8 bytes
int32_t i; // 4 bytes
// + 4 bytes padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 40);
EXPECT_EQ(sizeof(protobuf_unittest::TestRecursiveMessage),
sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, OneStringSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
void* data; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
EXPECT_EQ(sizeof(protobuf_unittest::OneString), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, MoreStringSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
MockRepeatedPtrField data; // 24 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 48);
EXPECT_EQ(sizeof(protobuf_unittest::MoreString), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, Int32MessageSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
int32_t data; // 4 bytes
// + 4 bytes padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
EXPECT_EQ(sizeof(protobuf_unittest::Int32Message), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, Int64MessageSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
int64_t data; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
EXPECT_EQ(sizeof(protobuf_unittest::Int64Message), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, BoolMessageSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
bool data; // 1 byte
// + 3 bytes padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
EXPECT_EQ(sizeof(protobuf_unittest::BoolMessage), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, OneofSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
void* foo; // 8 bytes
int cached_size; // 4 bytes
uint32_t oneof_case[1]; // 4 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 32);
EXPECT_EQ(sizeof(protobuf_unittest::TestOneof), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, Oneof2Size) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
void* baz_string; // 8 bytes
int32_t baz_int; // 4 bytes
// + 4 bytes padding
void* foo; // 8 bytes
void* bar; // 8 bytes
uint32_t oneof_case[2]; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 64);
EXPECT_EQ(sizeof(protobuf_unittest::TestOneof2), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, FieldOrderingsSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
MockExtensionSet extensions; // 24 bytes
void* my_string; // 8 bytes
void* optional_nested_message; // 8 bytes
int64_t my_int; // 8 bytes
float my_float; // 4 bytes
// + 4 bytes of padding
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 80);
EXPECT_EQ(sizeof(protobuf_unittest::TestFieldOrderings), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, TestMessageSize) {
// We expect the message to contain (not in this order):
struct MockGenerated : public MockMessageBase { // 16 bytes
int has_bits[1]; // 4 bytes
int cached_size; // 4 bytes
void* m4; // 8 bytes
int64_t m2; // 8 bytes
bool m1; // 1 bytes
bool m3; // 1 bytes
// + 2 bytes padding
int m5; // 4 bytes
int64_t m6; // 8 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 56);
EXPECT_EQ(sizeof(protobuf_unittest::TestMessageSize), sizeof(MockGenerated));
}
TEST(GeneratedMessageTest, PackedTypesSize) {
struct MockGenerated : public MockMessageBase { // 16 bytes
MockRepeatedField packed_int32; // 16 bytes
int packed_int32_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_int64; // 16 bytes
int packed_int64_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_uint32; // 16 bytes
int packed_uint32_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_uint64; // 16 bytes
int packed_uint64_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_sint32; // 16 bytes
int packed_sint32_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_sint64; // 16 bytes
int packed_sint64_cached_byte_size; // 4 bytes + 4 bytes padding
MockRepeatedField packed_fixed32; // 16 bytes
MockRepeatedField packed_fixed64; // 16 bytes
MockRepeatedField packed_sfixed32; // 16 bytes
MockRepeatedField packed_sfixed64; // 16 bytes
MockRepeatedField packed_float; // 16 bytes
MockRepeatedField packed_double; // 16 bytes
MockRepeatedField packed_bool; // 16 bytes
MockRepeatedField packed_enum; // 16 bytes
int packed_enum_cached_byte_size; // 4 bytes
int cached_size; // 4 bytes
};
GOOGLE_CHECK_MESSAGE_SIZE(MockGenerated, 16 * 15 + 8 * 6 + 8);
EXPECT_EQ(sizeof(protobuf_unittest::TestPackedTypes), sizeof(MockGenerated));
}
} // namespace cpp_unittest
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,161 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/compiler/cpp/generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/compiler/annotation_test_util.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace atu = annotation_test_util;
namespace {
class CppMetadataTest : public ::testing::Test {
public:
// Tries to capture a FileDescriptorProto, GeneratedCodeInfo, and output
// code from the previously added file with name `filename`. Returns true on
// success. If pb_h is non-null, expects a .pb.h and a .pb.h.meta (copied to
// pb_h and pb_h_info respecfively); similarly for proto_h and proto_h_info.
bool CaptureMetadata(const std::string& filename, FileDescriptorProto* file,
std::string* pb_h, GeneratedCodeInfo* pb_h_info,
std::string* proto_h, GeneratedCodeInfo* proto_h_info,
std::string* pb_cc) {
CommandLineInterface cli;
CppGenerator cpp_generator;
cli.RegisterGenerator("--cpp_out", &cpp_generator, "");
std::string cpp_out =
"--cpp_out=annotate_headers=true,"
"annotation_pragma_name=pragma_name,"
"annotation_guard_name=guard_name:" +
TestTempDir();
const bool result = atu::RunProtoCompiler(filename, cpp_out, &cli, file);
if (!result) {
return result;
}
std::string output_base = TestTempDir() + "/" + StripProto(filename);
if (pb_cc != nullptr) {
GOOGLE_CHECK_OK(
File::GetContents(output_base + ".pb.cc", pb_cc, true));
}
if (pb_h != nullptr && pb_h_info != nullptr) {
GOOGLE_CHECK_OK(
File::GetContents(output_base + ".pb.h", pb_h, true));
if (!atu::DecodeMetadata(output_base + ".pb.h.meta", pb_h_info)) {
return false;
}
}
if (proto_h != nullptr && proto_h_info != nullptr) {
GOOGLE_CHECK_OK(File::GetContents(output_base + ".proto.h", proto_h,
true));
if (!atu::DecodeMetadata(output_base + ".proto.h.meta", proto_h_info)) {
return false;
}
}
return true;
}
};
const char kSmallTestFile[] =
"syntax = \"proto2\";\n"
"package foo;\n"
"enum Enum { VALUE = 0; }\n"
"message Message { }\n";
TEST_F(CppMetadataTest, CapturesEnumNames) {
FileDescriptorProto file;
GeneratedCodeInfo info;
std::string pb_h;
atu::AddFile("test.proto", kSmallTestFile);
EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
nullptr, nullptr));
EXPECT_EQ("Enum", file.enum_type(0).name());
std::vector<int> enum_path;
enum_path.push_back(FileDescriptorProto::kEnumTypeFieldNumber);
enum_path.push_back(0);
const GeneratedCodeInfo::Annotation* enum_annotation =
atu::FindAnnotationOnPath(info, "test.proto", enum_path);
EXPECT_TRUE(nullptr != enum_annotation);
EXPECT_TRUE(atu::AnnotationMatchesSubstring(pb_h, enum_annotation, "Enum"));
}
TEST_F(CppMetadataTest, AddsPragma) {
FileDescriptorProto file;
GeneratedCodeInfo info;
std::string pb_h;
atu::AddFile("test.proto", kSmallTestFile);
EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
nullptr, nullptr));
EXPECT_TRUE(pb_h.find("#ifdef guard_name") != std::string::npos);
EXPECT_TRUE(pb_h.find("#pragma pragma_name \"test.pb.h.meta\"") !=
std::string::npos);
}
TEST_F(CppMetadataTest, CapturesMessageNames) {
FileDescriptorProto file;
GeneratedCodeInfo info;
std::string pb_h;
atu::AddFile("test.proto", kSmallTestFile);
EXPECT_TRUE(CaptureMetadata("test.proto", &file, &pb_h, &info, nullptr,
nullptr, nullptr));
EXPECT_EQ("Message", file.message_type(0).name());
std::vector<int> message_path;
message_path.push_back(FileDescriptorProto::kMessageTypeFieldNumber);
message_path.push_back(0);
const GeneratedCodeInfo::Annotation* message_annotation =
atu::FindAnnotationOnPath(info, "test.proto", message_path);
EXPECT_TRUE(nullptr != message_annotation);
EXPECT_TRUE(
atu::AnnotationMatchesSubstring(pb_h, message_annotation, "Message"));
}
} // namespace
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,169 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
#include <gtest/gtest.h>
#if LANG_CXX11
#include <type_traits>
#endif
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
namespace cpp_unittest {
// Moves are enabled only when compiling with a C++11 compiler or newer.
#if LANG_CXX11
TEST(MovableMessageTest, MoveConstructor) {
protobuf_unittest::TestAllTypes message1;
TestUtil::SetAllFields(&message1);
const auto* nested = &message1.optional_nested_message();
protobuf_unittest::TestAllTypes message2(std::move(message1));
TestUtil::ExpectAllFieldsSet(message2);
// Check if the optional_nested_message was actually moved (and not just
// copied).
EXPECT_EQ(nested, &message2.optional_nested_message());
EXPECT_NE(nested, &message1.optional_nested_message());
}
TEST(MovableMessageTest, MoveAssignmentOperator) {
protobuf_unittest::TestAllTypes message1;
TestUtil::SetAllFields(&message1);
const auto* nested = &message1.optional_nested_message();
protobuf_unittest::TestAllTypes message2;
message2 = std::move(message1);
TestUtil::ExpectAllFieldsSet(message2);
// Check if the optional_nested_message was actually moved (and not just
// copied).
EXPECT_EQ(nested, &message2.optional_nested_message());
EXPECT_NE(nested, &message1.optional_nested_message());
}
TEST(MovableMessageTest, SelfMoveAssignment) {
// The `self` reference is necessary to defeat -Wself-move.
protobuf_unittest::TestAllTypes message, &self = message;
TestUtil::SetAllFields(&message);
message = std::move(self);
TestUtil::ExpectAllFieldsSet(message);
}
TEST(MovableMessageTest, MoveSameArena) {
Arena arena;
auto* message1_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena);
TestUtil::SetAllFields(message1_on_arena);
const auto* nested = &message1_on_arena->optional_nested_message();
auto* message2_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena);
// Moving messages on the same arena should lead to swapped pointers.
*message2_on_arena = std::move(*message1_on_arena);
EXPECT_EQ(nested, &message2_on_arena->optional_nested_message());
}
TEST(MovableMessageTest, MoveDifferentArenas) {
Arena arena1, arena2;
auto* message1_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena1);
TestUtil::SetAllFields(message1_on_arena);
const auto* nested = &message1_on_arena->optional_nested_message();
auto* message2_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena2);
// Moving messages on two different arenas should lead to a copy.
*message2_on_arena = std::move(*message1_on_arena);
EXPECT_NE(nested, &message2_on_arena->optional_nested_message());
TestUtil::ExpectAllFieldsSet(*message1_on_arena);
TestUtil::ExpectAllFieldsSet(*message2_on_arena);
}
TEST(MovableMessageTest, MoveFromArena) {
Arena arena;
auto* message1_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena);
TestUtil::SetAllFields(message1_on_arena);
const auto* nested = &message1_on_arena->optional_nested_message();
protobuf_unittest::TestAllTypes message2;
// Moving from a message on the arena should lead to a copy.
message2 = std::move(*message1_on_arena);
EXPECT_NE(nested, &message2.optional_nested_message());
TestUtil::ExpectAllFieldsSet(*message1_on_arena);
TestUtil::ExpectAllFieldsSet(message2);
}
TEST(MovableMessageTest, MoveToArena) {
Arena arena;
protobuf_unittest::TestAllTypes message1;
TestUtil::SetAllFields(&message1);
const auto* nested = &message1.optional_nested_message();
auto* message2_on_arena =
Arena::CreateMessage<protobuf_unittest::TestAllTypes>(&arena);
// Moving to a message on the arena should lead to a copy.
*message2_on_arena = std::move(message1);
EXPECT_NE(nested, &message2_on_arena->optional_nested_message());
TestUtil::ExpectAllFieldsSet(message1);
TestUtil::ExpectAllFieldsSet(*message2_on_arena);
}
TEST(MovableMessageTest, Noexcept) {
EXPECT_TRUE(
std::is_nothrow_move_constructible<protobuf_unittest::TestAllTypes>());
EXPECT_TRUE(std::is_nothrow_move_assignable<protobuf_unittest::TestAllTypes>());
}
#endif // LANG_CXX11
} // namespace cpp_unittest
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,97 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_NAMES_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_NAMES_H__
#include <string>
// Must be included last.
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
class Descriptor;
class EnumDescriptor;
class EnumValueDescriptor;
class FieldDescriptor;
namespace compiler {
namespace cpp {
// Returns the unqualified C++ name.
//
// For example, if you had:
// package foo.bar;
// message Baz { message Moo {} }
// Then the non-qualified version would be:
// Baz_Moo
std::string ClassName(const Descriptor* descriptor);
std::string ClassName(const EnumDescriptor* enum_descriptor);
// Returns the fully qualified C++ name.
//
// For example, if you had:
// package foo.bar;
// message Baz { message Moo {} }
// Then the qualified ClassName for Moo would be:
// ::foo::bar::Baz_Moo
std::string QualifiedClassName(const Descriptor* d);
std::string QualifiedClassName(const EnumDescriptor* d);
std::string QualifiedExtensionName(const FieldDescriptor* d);
// Get the (unqualified) name that should be used for this field in C++ code.
// The name is coerced to lower-case to emulate proto1 behavior. People
// should be using lowercase-with-underscores style for proto field names
// anyway, so normally this just returns field->name().
std::string FieldName(const FieldDescriptor* field);
// Requires that this field is in a oneof. Returns the (unqualified) case
// constant for this field.
std::string OneofCaseConstantName(const FieldDescriptor* field);
// Returns the quafilied case constant for this field.
std::string QualifiedOneofCaseConstantName(const FieldDescriptor* field);
// Get the (unqualified) name that should be used for this enum value in C++
// code.
std::string EnumValueName(const EnumValueDescriptor* enum_value);
// Strips ".proto" or ".protodevel" from the end of a filename.
PROTOC_EXPORT std::string StripProto(const std::string& filename);
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_NAMES_H__

View File

@@ -0,0 +1,101 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: rennie@google.com (Jeffrey Rennie)
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__
#include <set>
#include <string>
namespace google {
namespace protobuf {
namespace compiler {
class AccessInfoMap;
namespace cpp {
enum class EnforceOptimizeMode {
kNoEnforcement, // Use the runtime specified by the file specific options.
kSpeed, // Full runtime with a generated code implementation.
kCodeSize, // Full runtime with a reflective implementation.
kLiteRuntime,
};
struct FieldListenerOptions {
bool inject_field_listener_events = false;
std::set<std::string> forbidden_field_listener_events;
};
// Generator options (see generator.cc for a description of each):
struct Options {
const AccessInfoMap* access_info_map = nullptr;
std::string dllexport_decl;
std::string runtime_include_base;
std::string annotation_pragma_name;
std::string annotation_guard_name;
FieldListenerOptions field_listener_options;
EnforceOptimizeMode enforce_mode = EnforceOptimizeMode::kNoEnforcement;
enum {
kTCTableNever,
kTCTableGuarded,
kTCTableAlways
} tctable_mode = kTCTableNever;
int num_cc_files = 0;
bool safe_boundary_check = false;
bool proto_h = false;
bool transitive_pb_h = true;
bool annotate_headers = false;
bool lite_implicit_weak_fields = false;
bool bootstrap = false;
bool opensource_runtime = false;
bool annotate_accessor = false;
bool unused_field_stripping = false;
bool unverified_lazy_message_sets = false;
bool unverified_lazy = false;
bool profile_driven_inline_string = true;
bool message_owned_arena_trial = false;
bool force_split = false;
#ifdef PROTOBUF_STABLE_EXPERIMENTS
bool force_eagerly_verified_lazy = true;
bool force_inline_string = true;
#else // PROTOBUF_STABLE_EXPERIMENTS
bool force_eagerly_verified_lazy = false;
bool force_inline_string = false;
#endif // !PROTOBUF_STABLE_EXPERIMENTS
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_OPTIONS_H__

View File

@@ -0,0 +1,228 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/cpp/padding_optimizer.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
// FieldGroup is just a helper for PaddingOptimizer below. It holds a vector of
// fields that are grouped together because they have compatible alignment, and
// a preferred location in the final field ordering.
class FieldGroup {
public:
FieldGroup() : preferred_location_(0) {}
// A group with a single field.
FieldGroup(double preferred_location, const FieldDescriptor* field)
: preferred_location_(preferred_location), fields_(1, field) {}
// Append the fields in 'other' to this group.
void Append(const FieldGroup& other) {
if (other.fields_.empty()) {
return;
}
// Preferred location is the average among all the fields, so we weight by
// the number of fields on each FieldGroup object.
preferred_location_ = (preferred_location_ * fields_.size() +
(other.preferred_location_ * other.fields_.size())) /
(fields_.size() + other.fields_.size());
fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end());
}
void SetPreferredLocation(double location) { preferred_location_ = location; }
const std::vector<const FieldDescriptor*>& fields() const { return fields_; }
// FieldGroup objects sort by their preferred location.
bool operator<(const FieldGroup& other) const {
return preferred_location_ < other.preferred_location_;
}
private:
// "preferred_location_" is an estimate of where this group should go in the
// final list of fields. We compute this by taking the average index of each
// field in this group in the original ordering of fields. This is very
// approximate, but should put this group close to where its member fields
// originally went.
double preferred_location_;
std::vector<const FieldDescriptor*> fields_;
// We rely on the default copy constructor and operator= so this type can be
// used in a vector.
};
} // namespace
// Reorder 'fields' so that if the fields are output into a c++ class in the new
// order, fields of similar family (see below) are together and within each
// family, alignment padding is minimized.
//
// We try to do this while keeping each field as close as possible to its field
// number order so that we don't reduce cache locality much for function that
// access each field in order. Originally, OptimizePadding used declaration
// order for its decisions, but generated code minus the serializer/parsers uses
// the output of OptimizePadding as well (stored in
// MessageGenerator::optimized_order_). Since the serializers use field number
// order, we use that as a tie-breaker.
//
// We classify each field into a particular "family" of fields, that we perform
// the same operation on in our generated functions.
//
// REPEATED is placed first, as the C++ compiler automatically initializes
// these fields in layout order.
//
// STRING is grouped next, as our Clear/SharedCtor/SharedDtor walks it and
// calls ArenaStringPtr::Destroy on each.
//
// LAZY_MESSAGE is grouped next, as it interferes with the ability to memset
// non-repeated fields otherwise.
//
// MESSAGE is grouped next, as our Clear/SharedDtor code walks it and calls
// delete on each. We initialize these fields with a NULL pointer (see
// MessageFieldGenerator::GenerateConstructorCode), which allows them to be
// memset.
//
// ZERO_INITIALIZABLE is memset in Clear/SharedCtor
//
// OTHER these fields are initialized one-by-one.
void PaddingOptimizer::OptimizeLayout(
std::vector<const FieldDescriptor*>* fields, const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
// The sorted numeric order of Family determines the declaration order in the
// memory layout.
enum Family {
REPEATED = 0,
STRING = 1,
// Laying out LAZY_MESSAGE before MESSAGE allows a single memset to zero
// MESSAGE and ZERO_INITIALIZABLE fields together.
LAZY_MESSAGE = 2,
MESSAGE = 3,
ZERO_INITIALIZABLE = 4,
OTHER = 5,
kMaxFamily
};
// First divide fields into those that align to 1 byte, 4 bytes or 8 bytes.
std::vector<FieldGroup> aligned_to_1[kMaxFamily];
std::vector<FieldGroup> aligned_to_4[kMaxFamily];
std::vector<FieldGroup> aligned_to_8[kMaxFamily];
for (int i = 0; i < fields->size(); ++i) {
const FieldDescriptor* field = (*fields)[i];
Family f = OTHER;
if (field->is_repeated()) {
f = REPEATED;
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
f = STRING;
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
f = MESSAGE;
if (IsLazy(field, options, scc_analyzer)) {
f = LAZY_MESSAGE;
}
} else if (CanInitializeByZeroing(field)) {
f = ZERO_INITIALIZABLE;
}
const int j = field->number();
switch (EstimateAlignmentSize(field)) {
case 1:
aligned_to_1[f].push_back(FieldGroup(j, field));
break;
case 4:
aligned_to_4[f].push_back(FieldGroup(j, field));
break;
case 8:
aligned_to_8[f].push_back(FieldGroup(j, field));
break;
default:
GOOGLE_LOG(FATAL) << "Unknown alignment size " << EstimateAlignmentSize(field)
<< "for a field " << field->full_name() << ".";
}
}
// For each family, group fields to optimize padding.
for (int f = 0; f < kMaxFamily; f++) {
// Now group fields aligned to 1 byte into sets of 4, and treat those like a
// single field aligned to 4 bytes.
for (int i = 0; i < aligned_to_1[f].size(); i += 4) {
FieldGroup field_group;
for (int j = i; j < aligned_to_1[f].size() && j < i + 4; ++j) {
field_group.Append(aligned_to_1[f][j]);
}
aligned_to_4[f].push_back(field_group);
}
// Sort by preferred location to keep fields as close to their field number
// order as possible. Using stable_sort ensures that the output is
// consistent across runs.
std::stable_sort(aligned_to_4[f].begin(), aligned_to_4[f].end());
// Now group fields aligned to 4 bytes (or the 4-field groups created above)
// into pairs, and treat those like a single field aligned to 8 bytes.
for (int i = 0; i < aligned_to_4[f].size(); i += 2) {
FieldGroup field_group;
for (int j = i; j < aligned_to_4[f].size() && j < i + 2; ++j) {
field_group.Append(aligned_to_4[f][j]);
}
if (i == aligned_to_4[f].size() - 1) {
if (f == OTHER) {
// Move incomplete 4-byte block to the beginning. This is done to
// pair with the (possible) leftover blocks from the
// ZERO_INITIALIZABLE family.
field_group.SetPreferredLocation(-1);
} else {
// Move incomplete 4-byte block to the end.
field_group.SetPreferredLocation(double{FieldDescriptor::kMaxNumber});
}
}
aligned_to_8[f].push_back(field_group);
}
// Sort by preferred location.
std::stable_sort(aligned_to_8[f].begin(), aligned_to_8[f].end());
}
// Now pull out all the FieldDescriptors in order.
fields->clear();
for (int f = 0; f < kMaxFamily; ++f) {
for (int i = 0; i < aligned_to_8[f].size(); ++i) {
fields->insert(fields->end(), aligned_to_8[f][i].fields().begin(),
aligned_to_8[f][i].fields().end());
}
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,65 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: seongkim@google.com (Seong Beom Kim)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__
#include <google/protobuf/compiler/cpp/message_layout_helper.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Rearranges the fields of a message to minimize padding.
// Fields are grouped by the type and the size.
// For example, grouping four boolean fields and one int32
// field results in zero padding overhead. See OptimizeLayout's
// comment for details.
class PaddingOptimizer : public MessageLayoutHelper {
public:
PaddingOptimizer() {}
~PaddingOptimizer() override {}
void OptimizeLayout(std::vector<const FieldDescriptor*>* fields,
const Options& options,
MessageSCCAnalyzer* scc_analyzer) override;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,180 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__
#include <map>
#include <string>
#include <vector>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Helper class for generating tailcall parsing functions.
struct TailCallTableInfo {
TailCallTableInfo(const Descriptor* descriptor, const Options& options,
const std::vector<const FieldDescriptor*>& ordered_fields,
const std::vector<int>& has_bit_indices,
const std::vector<int>& inlined_string_indices,
MessageSCCAnalyzer* scc_analyzer);
// Fields parsed by the table fast-path.
struct FastFieldInfo {
std::string func_name;
const FieldDescriptor* field;
uint16_t coded_tag;
uint8_t hasbit_idx;
uint8_t aux_idx;
};
std::vector<FastFieldInfo> fast_path_fields;
// Fields parsed by mini parsing routines.
struct FieldEntryInfo {
const FieldDescriptor* field;
int hasbit_idx;
int inlined_string_idx;
uint16_t aux_idx;
// True for enums entirely covered by the start/length fields of FieldAux:
bool is_enum_range;
};
std::vector<FieldEntryInfo> field_entries;
std::vector<std::string> aux_entries;
// Fields parsed by generated fallback function.
std::vector<const FieldDescriptor*> fallback_fields;
// Table size.
int table_size_log2;
// Mask for has-bits of required fields.
uint32_t has_hasbits_required_mask;
// True if a generated fallback function is required instead of generic.
bool use_generated_fallback;
};
// ParseFunctionGenerator generates the _InternalParse function for a message
// (and any associated supporting members).
class ParseFunctionGenerator {
public:
ParseFunctionGenerator(const Descriptor* descriptor, int max_has_bit_index,
const std::vector<int>& has_bit_indices,
const std::vector<int>& inlined_string_indices,
const Options& options,
MessageSCCAnalyzer* scc_analyzer,
const std::map<std::string, std::string>& vars);
// Emits class-level method declarations to `printer`:
void GenerateMethodDecls(io::Printer* printer);
// Emits out-of-class method implementation definitions to `printer`:
void GenerateMethodImpls(io::Printer* printer);
// Emits class-level data member declarations to `printer`:
void GenerateDataDecls(io::Printer* printer);
// Emits out-of-class data member definitions to `printer`:
void GenerateDataDefinitions(io::Printer* printer);
private:
// Returns true if tailcall table code should be generated.
bool should_generate_tctable() const;
// Returns true if tailcall table code should be generated, but inside an
// #ifdef guard.
bool should_generate_guarded_tctable() const {
return should_generate_tctable() &&
options_.tctable_mode == Options::kTCTableGuarded;
}
// Generates a tail-calling `_InternalParse` function.
void GenerateTailcallParseFunction(Formatter& format);
// Generates a fallback function for tailcall table-based parsing.
void GenerateTailcallFallbackFunction(Formatter& format);
// Generates a looping `_InternalParse` function.
void GenerateLoopingParseFunction(Formatter& format);
// Generates the tail-call table definition.
void GenerateTailCallTable(Formatter& format);
void GenerateFastFieldEntries(Formatter& format);
void GenerateFieldEntries(Formatter& format);
int CalculateFieldNamesSize() const;
void GenerateFieldNames(Formatter& format);
// Generates parsing code for an `ArenaString` field.
void GenerateArenaString(Formatter& format, const FieldDescriptor* field);
// Generates parsing code for a string-typed field.
void GenerateStrings(Formatter& format, const FieldDescriptor* field,
bool check_utf8);
// Generates parsing code for a length-delimited field (strings, messages,
// etc.).
void GenerateLengthDelim(Formatter& format, const FieldDescriptor* field);
// Generates the parsing code for a known field.
void GenerateFieldBody(Formatter& format,
google::protobuf::internal::WireFormatLite::WireType wiretype,
const FieldDescriptor* field);
// Generates code to parse the next field from the input stream.
void GenerateParseIterationBody(
Formatter& format, const Descriptor* descriptor,
const std::vector<const FieldDescriptor*>& fields);
// Generates a `switch` statement to parse each of `fields`.
void GenerateFieldSwitch(Formatter& format,
const std::vector<const FieldDescriptor*>& fields);
const Descriptor* descriptor_;
MessageSCCAnalyzer* scc_analyzer_;
const Options& options_;
std::map<std::string, std::string> variables_;
std::unique_ptr<TailCallTableInfo> tc_table_info_;
std::vector<int> inlined_string_indices_;
const std::vector<const FieldDescriptor*> ordered_fields_;
int num_hasbits_;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__

View File

@@ -0,0 +1,235 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
//
// TODO(kenton): Share code with the versions of this test in other languages?
// It seemed like parameterizing it would add more complexity than it is
// worth.
#include <memory>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/compiler/cpp/generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
class TestGenerator : public CodeGenerator {
public:
TestGenerator() {}
~TestGenerator() override {}
bool Generate(const FileDescriptor* file, const std::string& parameter,
GeneratorContext* context, std::string* error) const override {
TryInsert("test.pb.h", "includes", context);
TryInsert("test.pb.h", "namespace_scope", context);
TryInsert("test.pb.h", "global_scope", context);
TryInsert("test.pb.h", "class_scope:foo.Bar", context);
TryInsert("test.pb.h", "class_scope:foo.Bar.Baz", context);
TryInsert("test.pb.cc", "includes", context);
TryInsert("test.pb.cc", "namespace_scope", context);
TryInsert("test.pb.cc", "global_scope", context);
// Check field accessors for an optional int32:
TryInsert("test.pb.h", "field_get:foo.Bar.optInt", context);
TryInsert("test.pb.h", "field_set:foo.Bar.optInt", context);
// Check field accessors for a repeated int32:
TryInsert("test.pb.h", "field_get:foo.Bar.repeatedInt", context);
TryInsert("test.pb.h", "field_set:foo.Bar.repeatedInt", context);
// Check field accessors for a required string:
TryInsert("test.pb.h", "field_get:foo.Bar.requiredString", context);
TryInsert("test.pb.h", "field_set:foo.Bar.requiredString", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.requiredString", context);
TryInsert("test.pb.h", "field_set_allocated:foo.Bar.requiredString",
context);
// Check field accessors for a repeated string:
TryInsert("test.pb.h", "field_get:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_set:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_set_char:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_set_pointer:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_set_char:foo.Bar.repeatedString", context);
TryInsert("test.pb.h", "field_set_pointer:foo.Bar.repeatedString", context);
// Check field accessors for an int inside oneof{}:
TryInsert("test.pb.h", "field_get:foo.Bar.oneOfInt", context);
TryInsert("test.pb.h", "field_set:foo.Bar.oneOfInt", context);
// Check field accessors for a string inside oneof{}:
TryInsert("test.pb.h", "field_get:foo.Bar.oneOfString", context);
TryInsert("test.pb.h", "field_set:foo.Bar.oneOfString", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfString", context);
TryInsert("test.pb.h", "field_set_allocated:foo.Bar.oneOfString", context);
// Check field accessors for an optional message:
TryInsert("test.pb.h", "field_get:foo.Bar.optMessage", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.optMessage", context);
TryInsert("test.pb.h", "field_set_allocated:foo.Bar.optMessage", context);
// Check field accessors for a repeated message:
TryInsert("test.pb.h", "field_add:foo.Bar.repeatedMessage", context);
TryInsert("test.pb.h", "field_get:foo.Bar.repeatedMessage", context);
TryInsert("test.pb.h", "field_list:foo.Bar.repeatedMessage", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedMessage", context);
TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedMessage",
context);
// Check field accessors for a message inside oneof{}:
TryInsert("test.pb.h", "field_get:foo.Bar.oneOfMessage", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfMessage", context);
TryInsert("test.pb.cc", "field_set_allocated:foo.Bar.oneOfMessage",
context);
// Check field accessors for an optional enum:
TryInsert("test.pb.h", "field_get:foo.Bar.optEnum", context);
TryInsert("test.pb.h", "field_set:foo.Bar.optEnum", context);
// Check field accessors for a repeated enum:
TryInsert("test.pb.h", "field_get:foo.Bar.repeatedEnum", context);
TryInsert("test.pb.h", "field_set:foo.Bar.repeatedEnum", context);
TryInsert("test.pb.h", "field_add:foo.Bar.repeatedEnum", context);
TryInsert("test.pb.h", "field_list:foo.Bar.repeatedEnum", context);
TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedEnum", context);
// Check field accessors for an enum inside oneof{}:
TryInsert("test.pb.h", "field_get:foo.Bar.oneOfEnum", context);
TryInsert("test.pb.h", "field_set:foo.Bar.oneOfEnum", context);
// Check field accessors for a required cord:
TryInsert("test.pb.h", "field_get:foo.Bar.requiredCord", context);
TryInsert("test.pb.h", "field_set:foo.Bar.requiredCord", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.requiredCord", context);
// Check field accessors for a repeated cord:
TryInsert("test.pb.h", "field_get:foo.Bar.repeatedCord", context);
TryInsert("test.pb.h", "field_set:foo.Bar.repeatedCord", context);
TryInsert("test.pb.h", "field_add:foo.Bar.repeatedCord", context);
TryInsert("test.pb.h", "field_list:foo.Bar.repeatedCord", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.repeatedCord", context);
TryInsert("test.pb.h", "field_mutable_list:foo.Bar.repeatedCord", context);
// Check field accessors for a cord inside oneof{}:
TryInsert("test.pb.h", "field_get:foo.Bar.oneOfCord", context);
TryInsert("test.pb.h", "field_set:foo.Bar.oneOfCord", context);
TryInsert("test.pb.h", "field_mutable:foo.Bar.oneOfCord", context);
return true;
}
void TryInsert(const std::string& filename,
const std::string& insertion_point,
GeneratorContext* context) const {
std::unique_ptr<io::ZeroCopyOutputStream> output(
context->OpenForInsert(filename, insertion_point));
io::Printer printer(output.get(), '$');
printer.Print("// inserted $name$\n", "name", insertion_point);
}
};
// This test verifies that all the expected insertion points exist. It does
// not verify that they are correctly-placed; that would require actually
// compiling the output which is a bit more than I care to do for this test.
TEST(CppPluginTest, PluginTest) {
GOOGLE_CHECK_OK(File::SetContents(TestTempDir() + "/test.proto",
"syntax = \"proto2\";\n"
"package foo;\n"
"\n"
"enum Thud { VALUE = 0; }\n"
"\n"
"message Bar {\n"
" message Baz {}\n"
" optional int32 optInt = 1;\n"
" repeated int32 repeatedInt = 2;\n"
"\n"
" required string requiredString = 3;\n"
" repeated string repeatedString = 4;\n"
"\n"
" optional Baz optMessage = 6;\n"
" repeated Baz repeatedMessage = 7;\n"
"\n"
" optional Thud optEnum = 8;\n"
" repeated Thud repeatedEnum = 9;\n"
"\n"
" required string requiredCord = 10 [\n"
" ctype = CORD\n"
" ];\n"
" repeated string repeatedCord = 11 [\n"
" ctype = CORD\n"
" ];\n"
"\n"
" oneof Moo {\n"
" int64 oneOfInt = 20;\n"
" string oneOfString = 21;\n"
" Baz oneOfMessage = 22;\n"
" Thud oneOfEnum = 23;"
" string oneOfCord = 24 [\n"
" ctype = CORD\n"
" ];\n"
" }\n"
"}\n",
true));
CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
CppGenerator cpp_generator;
TestGenerator test_generator;
cli.RegisterGenerator("--cpp_out", &cpp_generator, "");
cli.RegisterGenerator("--test_out", &test_generator, "");
std::string proto_path = "-I" + TestTempDir();
std::string cpp_out = "--cpp_out=" + TestTempDir();
std::string test_out = "--test_out=" + TestTempDir();
const char* argv[] = {"protoc", proto_path.c_str(), cpp_out.c_str(),
test_out.c_str(), "test.proto"};
EXPECT_EQ(0, cli.Run(5, argv));
}
} // namespace
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,539 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/primitive_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
using internal::WireFormatLite;
namespace {
// For encodings with fixed sizes, returns that size in bytes. Otherwise
// returns -1.
int FixedSize(FieldDescriptor::Type type) {
switch (type) {
case FieldDescriptor::TYPE_INT32:
return -1;
case FieldDescriptor::TYPE_INT64:
return -1;
case FieldDescriptor::TYPE_UINT32:
return -1;
case FieldDescriptor::TYPE_UINT64:
return -1;
case FieldDescriptor::TYPE_SINT32:
return -1;
case FieldDescriptor::TYPE_SINT64:
return -1;
case FieldDescriptor::TYPE_FIXED32:
return WireFormatLite::kFixed32Size;
case FieldDescriptor::TYPE_FIXED64:
return WireFormatLite::kFixed64Size;
case FieldDescriptor::TYPE_SFIXED32:
return WireFormatLite::kSFixed32Size;
case FieldDescriptor::TYPE_SFIXED64:
return WireFormatLite::kSFixed64Size;
case FieldDescriptor::TYPE_FLOAT:
return WireFormatLite::kFloatSize;
case FieldDescriptor::TYPE_DOUBLE:
return WireFormatLite::kDoubleSize;
case FieldDescriptor::TYPE_BOOL:
return WireFormatLite::kBoolSize;
case FieldDescriptor::TYPE_ENUM:
return -1;
case FieldDescriptor::TYPE_STRING:
return -1;
case FieldDescriptor::TYPE_BYTES:
return -1;
case FieldDescriptor::TYPE_GROUP:
return -1;
case FieldDescriptor::TYPE_MESSAGE:
return -1;
// No default because we want the compiler to complain if any new
// types are added.
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return -1;
}
void SetPrimitiveVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonFieldVariables(descriptor, variables, options);
(*variables)["type"] = PrimitiveTypeName(options, descriptor->cpp_type());
(*variables)["default"] = DefaultValue(options, descriptor);
(*variables)["cached_byte_size_name"] = MakeVarintCachedSizeName(descriptor);
bool cold = ShouldSplit(descriptor, options);
(*variables)["cached_byte_size_field"] =
MakeVarintCachedSizeFieldName(descriptor, cold);
(*variables)["tag"] = StrCat(internal::WireFormat::MakeTag(descriptor));
int fixed_size = FixedSize(descriptor->type());
if (fixed_size != -1) {
(*variables)["fixed_size"] = StrCat(fixed_size);
}
(*variables)["wire_format_field_type"] = FieldDescriptorProto_Type_Name(
static_cast<FieldDescriptorProto_Type>(descriptor->type()));
(*variables)["full_name"] = descriptor->full_name();
}
} // namespace
// ===================================================================
PrimitiveFieldGenerator::PrimitiveFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: FieldGenerator(descriptor, options) {
SetPrimitiveVariables(descriptor, &variables_, options);
}
PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {}
void PrimitiveFieldGenerator::GeneratePrivateMembers(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$type$ $name$_;\n");
}
void PrimitiveFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"$deprecated_attr$$type$ ${1$$name$$}$() const;\n"
"$deprecated_attr$void ${1$set_$name$$}$($type$ value);\n"
"private:\n"
"$type$ ${1$_internal_$name$$}$() const;\n"
"void ${1$_internal_set_$name$$}$($type$ value);\n"
"public:\n",
descriptor_);
}
void PrimitiveFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$() const {\n"
" return $field$;\n"
"}\n"
"inline $type$ $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline void $classname$::_internal_set_$name$($type$ value) {\n"
" $set_hasbit$\n"
" $field$ = value;\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
"$maybe_prepare_split_message$"
" _internal_set_$name$(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
void PrimitiveFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$ = $default$;\n");
}
void PrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->_internal_set_$name$(from._internal_$name$());\n");
}
void PrimitiveFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
format("swap($field$, other->$field$);\n");
}
void PrimitiveFieldGenerator::GenerateCopyConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$ = from.$field$;\n");
}
void PrimitiveFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"target = stream->EnsureSpace(target);\n"
"target = "
"::_pbi::WireFormatLite::Write$declared_type$ToArray("
"$number$, this->_internal_$name$(), target);\n");
}
void PrimitiveFieldGenerator::GenerateByteSize(io::Printer* printer) const {
Formatter format(printer, variables_);
int fixed_size = FixedSize(descriptor_->type());
if (fixed_size == -1) {
if (internal::WireFormat::TagSize(descriptor_->number(),
descriptor_->type()) == 1) {
// Adding one is very common and it turns out it can be done for
// free inside of WireFormatLite, so we can save an instruction here.
format(
"total_size += ::_pbi::WireFormatLite::"
"$declared_type$SizePlusOne(this->_internal_$name$());\n");
} else {
format(
"total_size += $tag_size$ +\n"
" ::_pbi::WireFormatLite::$declared_type$Size(\n"
" this->_internal_$name$());\n");
}
} else {
format("total_size += $tag_size$ + $fixed_size$;\n");
}
}
void PrimitiveFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/$default$");
}
void PrimitiveFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
format("decltype(Impl_::Split::$name$_){$default$}");
return;
}
format("decltype($field$){$default$}");
}
void PrimitiveFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){}");
}
// ===================================================================
PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: PrimitiveFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {}
void PrimitiveOneofFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$() const {\n"
" if (_internal_has_$name$()) {\n"
" return $field$;\n"
" }\n"
" return $default$;\n"
"}\n"
"inline void $classname$::_internal_set_$name$($type$ value) {\n"
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" }\n"
" $field$ = value;\n"
"}\n"
"inline $type$ $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline void $classname$::set_$name$($type$ value) {\n"
" _internal_set_$name$(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
}
void PrimitiveOneofFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$ = $default$;\n");
}
void PrimitiveOneofFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void PrimitiveOneofFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$ns$::_$classname$_default_instance_.$field$ = $default$;\n");
}
// ===================================================================
RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: FieldGenerator(descriptor, options) {
SetPrimitiveVariables(descriptor, &variables_, options);
if (descriptor->is_packed()) {
variables_["packed_reader"] = "ReadPackedPrimitive";
variables_["repeated_reader"] = "ReadRepeatedPrimitiveNoInline";
} else {
variables_["packed_reader"] = "ReadPackedPrimitiveNoInline";
variables_["repeated_reader"] = "ReadRepeatedPrimitive";
}
}
RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {}
void RepeatedPrimitiveFieldGenerator::GeneratePrivateMembers(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("::$proto_ns$::RepeatedField< $type$ > $name$_;\n");
if (descriptor_->is_packed() && FixedSize(descriptor_->type()) == -1 &&
HasGeneratedMethods(descriptor_->file(), options_)) {
format("mutable std::atomic<int> $cached_byte_size_name$;\n");
}
}
void RepeatedPrimitiveFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"private:\n"
"$type$ ${1$_internal_$name$$}$(int index) const;\n"
"const ::$proto_ns$::RepeatedField< $type$ >&\n"
" ${1$_internal_$name$$}$() const;\n"
"void ${1$_internal_add_$name$$}$($type$ value);\n"
"::$proto_ns$::RepeatedField< $type$ >*\n"
" ${1$_internal_mutable_$name$$}$();\n"
"public:\n"
"$deprecated_attr$$type$ ${1$$name$$}$(int index) const;\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, $type$ value);\n"
"$deprecated_attr$void ${1$add_$name$$}$($type$ value);\n"
"$deprecated_attr$const ::$proto_ns$::RepeatedField< $type$ >&\n"
" ${1$$name$$}$() const;\n"
"$deprecated_attr$::$proto_ns$::RepeatedField< $type$ >*\n"
" ${1$mutable_$name$$}$();\n",
descriptor_);
}
void RepeatedPrimitiveFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline $type$ $classname$::_internal_$name$(int index) const {\n"
" return $field$.Get(index);\n"
"}\n"
"inline $type$ $classname$::$name$(int index) const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, $type$ value) {\n"
"$annotate_set$"
" $field$.Set(index, value);\n"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::_internal_add_$name$($type$ value) {\n"
" $field$.Add(value);\n"
"}\n"
"inline void $classname$::add_$name$($type$ value) {\n"
" _internal_add_$name$(value);\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline const ::$proto_ns$::RepeatedField< $type$ >&\n"
"$classname$::_internal_$name$() const {\n"
" return $field$;\n"
"}\n"
"inline const ::$proto_ns$::RepeatedField< $type$ >&\n"
"$classname$::$name$() const {\n"
"$annotate_list$"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"inline ::$proto_ns$::RepeatedField< $type$ >*\n"
"$classname$::_internal_mutable_$name$() {\n"
" return &$field$;\n"
"}\n"
"inline ::$proto_ns$::RepeatedField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
"$annotate_mutable_list$"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return _internal_mutable_$name$();\n"
"}\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.Clear();\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateMergingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$.MergeFrom(from.$field$);\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.InternalSwap(&other->$field$);\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateDestructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.~RepeatedField();\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (descriptor_->is_packed()) {
if (FixedSize(descriptor_->type()) == -1) {
format(
"{\n"
" int byte_size = "
"$cached_byte_size_field$.load(std::memory_order_relaxed);\n"
" if (byte_size > 0) {\n"
" target = stream->Write$declared_type$Packed(\n"
" $number$, _internal_$name$(), byte_size, target);\n"
" }\n"
"}\n");
} else {
format(
"if (this->_internal_$name$_size() > 0) {\n"
" target = stream->WriteFixedPacked($number$, _internal_$name$(), "
"target);\n"
"}\n");
}
} else {
format(
"for (int i = 0, n = this->_internal_$name$_size(); i < n; i++) {\n"
" target = stream->EnsureSpace(target);\n"
" target = ::_pbi::WireFormatLite::"
"Write$declared_type$ToArray($number$, this->_internal_$name$(i), "
"target);\n"
"}\n");
}
}
void RepeatedPrimitiveFieldGenerator::GenerateByteSize(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("{\n");
format.Indent();
int fixed_size = FixedSize(descriptor_->type());
if (fixed_size == -1) {
format(
"size_t data_size = ::_pbi::WireFormatLite::\n"
" $declared_type$Size(this->$field$);\n");
} else {
format(
"unsigned int count = static_cast<unsigned "
"int>(this->_internal_$name$_size());\n"
"size_t data_size = $fixed_size$UL * count;\n");
}
if (descriptor_->is_packed()) {
format(
"if (data_size > 0) {\n"
" total_size += $tag_size$ +\n"
" "
"::_pbi::WireFormatLite::Int32Size(static_cast<$int32$>(data_size));\n"
"}\n");
if (FixedSize(descriptor_->type()) == -1) {
format(
"int cached_size = ::_pbi::ToCachedSize(data_size);\n"
"$cached_byte_size_field$.store(cached_size,\n"
" std::memory_order_relaxed);\n");
}
format("total_size += data_size;\n");
} else {
format(
"total_size += $tag_size$ *\n"
" "
"::_pbi::FromIntSize(this->_internal_$name$_size());\n"
"total_size += data_size;\n");
}
format.Outdent();
format("}\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("/*decltype($field$)*/{}");
if (descriptor_->is_packed() && FixedSize(descriptor_->type()) == -1 &&
HasGeneratedMethods(descriptor_->file(), options_)) {
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
void RepeatedPrimitiveFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){arena}");
if (descriptor_->is_packed() && FixedSize(descriptor_->type()) == -1 &&
HasGeneratedMethods(descriptor_->file(), options_)) {
// std::atomic has no move constructor, which prevents explicit aggregate
// initialization pre-C++17.
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
void RepeatedPrimitiveFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){from.$field$}");
if (descriptor_->is_packed() && FixedSize(descriptor_->type()) == -1 &&
HasGeneratedMethods(descriptor_->file(), options_)) {
// std::atomic has no move constructor.
format("\n, /*decltype($cached_byte_size_field$)*/{0}");
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,126 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__
#include <map>
#include <string>
#include <google/protobuf/compiler/cpp/field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class PrimitiveFieldGenerator : public FieldGenerator {
public:
PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~PrimitiveFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator);
};
class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
public:
PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~PrimitiveOneofFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveOneofFieldGenerator);
};
class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
public:
RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~RepeatedPrimitiveFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* /*printer*/) const override {
GOOGLE_CHECK(!ShouldSplit(descriptor_, options_));
}
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__

View File

@@ -0,0 +1,328 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/service.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
void InitMethodVariables(const MethodDescriptor* method, const Options& options,
Formatter* format) {
format->Set("name", method->name());
format->Set("input_type", QualifiedClassName(method->input_type(), options));
format->Set("output_type",
QualifiedClassName(method->output_type(), options));
}
} // namespace
ServiceGenerator::ServiceGenerator(
const ServiceDescriptor* descriptor,
const std::map<std::string, std::string>& vars, const Options& options)
: descriptor_(descriptor), vars_(vars), options_(options) {
vars_["classname"] = descriptor_->name();
vars_["full_name"] = descriptor_->full_name();
}
ServiceGenerator::~ServiceGenerator() {}
void ServiceGenerator::GenerateDeclarations(io::Printer* printer) {
Formatter format(printer, vars_);
// Forward-declare the stub type.
format(
"class $classname$_Stub;\n"
"\n");
GenerateInterface(printer);
GenerateStubDefinition(printer);
}
void ServiceGenerator::GenerateInterface(io::Printer* printer) {
Formatter format(printer, vars_);
format(
"class $dllexport_decl $$classname$ : public ::$proto_ns$::Service {\n"
" protected:\n"
" // This class should be treated as an abstract interface.\n"
" inline $classname$() {};\n"
" public:\n"
" virtual ~$classname$();\n");
printer->Indent();
format(
"\n"
"typedef $classname$_Stub Stub;\n"
"\n"
"static const ::$proto_ns$::ServiceDescriptor* descriptor();\n"
"\n");
GenerateMethodSignatures(VIRTUAL, printer);
format(
"\n"
"// implements Service ----------------------------------------------\n"
"\n"
"const ::$proto_ns$::ServiceDescriptor* GetDescriptor();\n"
"void CallMethod(const ::$proto_ns$::MethodDescriptor* method,\n"
" ::$proto_ns$::RpcController* controller,\n"
" const ::$proto_ns$::Message* request,\n"
" ::$proto_ns$::Message* response,\n"
" ::google::protobuf::Closure* done);\n"
"const ::$proto_ns$::Message& GetRequestPrototype(\n"
" const ::$proto_ns$::MethodDescriptor* method) const;\n"
"const ::$proto_ns$::Message& GetResponsePrototype(\n"
" const ::$proto_ns$::MethodDescriptor* method) const;\n");
printer->Outdent();
format(
"\n"
" private:\n"
" GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$);\n"
"};\n"
"\n");
}
void ServiceGenerator::GenerateStubDefinition(io::Printer* printer) {
Formatter format(printer, vars_);
format(
"class $dllexport_decl $$classname$_Stub : public $classname$ {\n"
" public:\n");
printer->Indent();
format(
"$classname$_Stub(::$proto_ns$::RpcChannel* channel);\n"
"$classname$_Stub(::$proto_ns$::RpcChannel* channel,\n"
" ::$proto_ns$::Service::ChannelOwnership ownership);\n"
"~$classname$_Stub();\n"
"\n"
"inline ::$proto_ns$::RpcChannel* channel() { return channel_; }\n"
"\n"
"// implements $classname$ ------------------------------------------\n"
"\n");
GenerateMethodSignatures(NON_VIRTUAL, printer);
printer->Outdent();
format(
" private:\n"
" ::$proto_ns$::RpcChannel* channel_;\n"
" bool owns_channel_;\n"
" GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$_Stub);\n"
"};\n"
"\n");
}
void ServiceGenerator::GenerateMethodSignatures(VirtualOrNon virtual_or_non,
io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
Formatter format(printer, vars_);
InitMethodVariables(method, options_, &format);
format.Set("virtual", virtual_or_non == VIRTUAL ? "virtual " : "");
format(
"$virtual$void $name$(::$proto_ns$::RpcController* controller,\n"
" const $input_type$* request,\n"
" $output_type$* response,\n"
" ::google::protobuf::Closure* done);\n");
}
}
// ===================================================================
void ServiceGenerator::GenerateImplementation(io::Printer* printer) {
Formatter format(printer, vars_);
format(
"$classname$::~$classname$() {}\n"
"\n"
"const ::$proto_ns$::ServiceDescriptor* $classname$::descriptor() {\n"
" "
"::$proto_ns$::internal::AssignDescriptors(&$desc_table$);\n"
" return $file_level_service_descriptors$[$1$];\n"
"}\n"
"\n"
"const ::$proto_ns$::ServiceDescriptor* $classname$::GetDescriptor() {\n"
" return descriptor();\n"
"}\n"
"\n",
index_in_metadata_);
// Generate methods of the interface.
GenerateNotImplementedMethods(printer);
GenerateCallMethod(printer);
GenerateGetPrototype(REQUEST, printer);
GenerateGetPrototype(RESPONSE, printer);
// Generate stub implementation.
format(
"$classname$_Stub::$classname$_Stub(::$proto_ns$::RpcChannel* channel)\n"
" : channel_(channel), owns_channel_(false) {}\n"
"$classname$_Stub::$classname$_Stub(\n"
" ::$proto_ns$::RpcChannel* channel,\n"
" ::$proto_ns$::Service::ChannelOwnership ownership)\n"
" : channel_(channel),\n"
" owns_channel_(ownership == "
"::$proto_ns$::Service::STUB_OWNS_CHANNEL) "
"{}\n"
"$classname$_Stub::~$classname$_Stub() {\n"
" if (owns_channel_) delete channel_;\n"
"}\n"
"\n");
GenerateStubMethods(printer);
}
void ServiceGenerator::GenerateNotImplementedMethods(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
Formatter format(printer, vars_);
InitMethodVariables(method, options_, &format);
format(
"void $classname$::$name$(::$proto_ns$::RpcController* controller,\n"
" const $input_type$*,\n"
" $output_type$*,\n"
" ::google::protobuf::Closure* done) {\n"
" controller->SetFailed(\"Method $name$() not implemented.\");\n"
" done->Run();\n"
"}\n"
"\n");
}
}
void ServiceGenerator::GenerateCallMethod(io::Printer* printer) {
Formatter format(printer, vars_);
format(
"void $classname$::CallMethod(const ::$proto_ns$::MethodDescriptor* "
"method,\n"
" ::$proto_ns$::RpcController* controller,\n"
" const ::$proto_ns$::Message* request,\n"
" ::$proto_ns$::Message* response,\n"
" ::google::protobuf::Closure* done) {\n"
" GOOGLE_DCHECK_EQ(method->service(), $file_level_service_descriptors$[$1$]);\n"
" switch(method->index()) {\n",
index_in_metadata_);
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
Formatter format_method(printer, vars_);
InitMethodVariables(method, options_, &format_method);
// Note: down_cast does not work here because it only works on pointers,
// not references.
format_method(
" case $1$:\n"
" $name$(controller,\n"
" ::$proto_ns$::internal::DownCast<const $input_type$*>(\n"
" request),\n"
" ::$proto_ns$::internal::DownCast<$output_type$*>(\n"
" response),\n"
" done);\n"
" break;\n",
i);
}
format(
" default:\n"
" GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n"
" break;\n"
" }\n"
"}\n"
"\n");
}
void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
io::Printer* printer) {
Formatter format(printer, vars_);
if (which == REQUEST) {
format("const ::$proto_ns$::Message& $classname$::GetRequestPrototype(\n");
} else {
format("const ::$proto_ns$::Message& $classname$::GetResponsePrototype(\n");
}
format(
" const ::$proto_ns$::MethodDescriptor* method) const {\n"
" GOOGLE_DCHECK_EQ(method->service(), descriptor());\n"
" switch(method->index()) {\n");
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
const Descriptor* type =
(which == REQUEST) ? method->input_type() : method->output_type();
format(
" case $1$:\n"
" return $2$::default_instance();\n",
i, QualifiedClassName(type, options_));
}
format(
" default:\n"
" GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n"
" return *::$proto_ns$::MessageFactory::generated_factory()\n"
" ->GetPrototype(method->$1$_type());\n"
" }\n"
"}\n"
"\n",
which == REQUEST ? "input" : "output");
}
void ServiceGenerator::GenerateStubMethods(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); i++) {
const MethodDescriptor* method = descriptor_->method(i);
Formatter format(printer, vars_);
InitMethodVariables(method, options_, &format);
format(
"void $classname$_Stub::$name$(::$proto_ns$::RpcController* "
"controller,\n"
" const $input_type$* request,\n"
" $output_type$* response,\n"
" ::google::protobuf::Closure* done) {\n"
" channel_->CallMethod(descriptor()->method($1$),\n"
" controller, request, response, done);\n"
"}\n",
i);
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,123 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__
#include <map>
#include <string>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/cpp/options.h>
namespace google {
namespace protobuf {
namespace io {
class Printer; // printer.h
}
} // namespace protobuf
} // namespace google
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class ServiceGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
explicit ServiceGenerator(const ServiceDescriptor* descriptor,
const std::map<std::string, std::string>& vars,
const Options& options);
~ServiceGenerator();
// Header stuff.
// Generate the class definitions for the service's interface and the
// stub implementation.
void GenerateDeclarations(io::Printer* printer);
// Source file stuff.
// Generate implementations of everything declared by
// GenerateDeclarations().
void GenerateImplementation(io::Printer* printer);
private:
enum RequestOrResponse { REQUEST, RESPONSE };
enum VirtualOrNon { VIRTUAL, NON_VIRTUAL };
// Header stuff.
// Generate the service abstract interface.
void GenerateInterface(io::Printer* printer);
// Generate the stub class definition.
void GenerateStubDefinition(io::Printer* printer);
// Prints signatures for all methods in the
void GenerateMethodSignatures(VirtualOrNon virtual_or_non,
io::Printer* printer);
// Source file stuff.
// Generate the default implementations of the service methods, which
// produce a "not implemented" error.
void GenerateNotImplementedMethods(io::Printer* printer);
// Generate the CallMethod() method of the service.
void GenerateCallMethod(io::Printer* printer);
// Generate the Get{Request,Response}Prototype() methods.
void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer);
// Generate the stub's implementations of the service methods.
void GenerateStubMethods(io::Printer* printer);
const ServiceDescriptor* descriptor_;
std::map<std::string, std::string> vars_;
const Options& options_;
int index_in_metadata_;
friend class FileGenerator;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__

View File

@@ -0,0 +1,957 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/string_field.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/cpp/helpers.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
void SetStringVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonFieldVariables(descriptor, variables, options);
const std::string kNS = "::" + (*variables)["proto_ns"] + "::internal::";
const std::string kArenaStringPtr = kNS + "ArenaStringPtr";
(*variables)["default"] = DefaultValue(options, descriptor);
(*variables)["default_length"] =
StrCat(descriptor->default_value_string().length());
(*variables)["default_variable_name"] = MakeDefaultName(descriptor);
(*variables)["default_variable_field"] = MakeDefaultFieldName(descriptor);
if (descriptor->default_value_string().empty()) {
(*variables)["default_string"] = kNS + "GetEmptyStringAlreadyInited()";
(*variables)["default_value"] = "&" + (*variables)["default_string"];
(*variables)["lazy_variable_args"] = "";
} else {
(*variables)["lazy_variable"] =
StrCat(QualifiedClassName(descriptor->containing_type(), options),
"::", MakeDefaultFieldName(descriptor));
(*variables)["default_string"] = (*variables)["lazy_variable"] + ".get()";
(*variables)["default_value"] = "nullptr";
(*variables)["lazy_variable_args"] = (*variables)["lazy_variable"] + ", ";
}
(*variables)["pointer_type"] =
descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char";
(*variables)["setter"] =
descriptor->type() == FieldDescriptor::TYPE_BYTES ? "SetBytes" : "Set";
(*variables)["null_check"] = (*variables)["DCHK"] + "(value != nullptr);\n";
// NOTE: Escaped here to unblock proto1->proto2 migration.
// TODO(liujisi): Extend this to apply for other conflicting methods.
(*variables)["release_name"] =
SafeFunctionName(descriptor->containing_type(), descriptor, "release_");
(*variables)["full_name"] = descriptor->full_name();
if (options.opensource_runtime) {
(*variables)["string_piece"] = "::std::string";
} else {
(*variables)["string_piece"] = "::StringPiece";
}
}
} // namespace
// ===================================================================
StringFieldGenerator::StringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: FieldGenerator(descriptor, options),
inlined_(IsStringInlined(descriptor, options)) {
SetStringVariables(descriptor, &variables_, options);
}
StringFieldGenerator::~StringFieldGenerator() {}
void StringFieldGenerator::GeneratePrivateMembers(io::Printer* printer) const {
Formatter format(printer, variables_);
if (!inlined_) {
format("::$proto_ns$::internal::ArenaStringPtr $name$_;\n");
} else {
// Skips the automatic destruction; rather calls it explicitly if
// allocating arena is null. This is required to support message-owned
// arena (go/path-to-arenas) where a root proto is destroyed but
// InlinedStringField may have arena-allocated memory.
format("::$proto_ns$::internal::InlinedStringField $name$_;\n");
}
}
void StringFieldGenerator::GenerateStaticMembers(io::Printer* printer) const {
Formatter format(printer, variables_);
if (!descriptor_->default_value_string().empty()) {
format(
"static const ::$proto_ns$::internal::LazyString"
" $default_variable_name$;\n");
}
if (inlined_) {
// `_init_inline_xxx` is used for initializing default instances.
format("static std::true_type _init_inline_$name$_;\n");
}
}
void StringFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
// If we're using StringFieldGenerator for a field with a ctype, it's
// because that ctype isn't actually implemented. In particular, this is
// true of ctype=CORD and ctype=STRING_PIECE in the open source release.
// We aren't releasing Cord because it has too many Google-specific
// dependencies and we aren't releasing StringPiece because it's hardly
// useful outside of Google and because it would get confusing to have
// multiple instances of the StringPiece class in different libraries (PCRE
// already includes it for their C++ bindings, which came from Google).
//
// In any case, we make all the accessors private while still actually
// using a string to represent the field internally. This way, we can
// guarantee that if we do ever implement the ctype, it won't break any
// existing users who might be -- for whatever reason -- already using .proto
// files that applied the ctype. The field can still be accessed via the
// reflection interface since the reflection interface is independent of
// the string's underlying representation.
bool unknown_ctype = descriptor_->options().ctype() !=
EffectiveStringCType(descriptor_, options_);
if (unknown_ctype) {
format.Outdent();
format(
" private:\n"
" // Hidden due to unknown ctype option.\n");
format.Indent();
}
format(
"$deprecated_attr$const std::string& ${1$$name$$}$() const;\n"
"template <typename ArgT0 = const std::string&, typename... ArgT>\n"
"$deprecated_attr$void ${1$set_$name$$}$(ArgT0&& arg0, ArgT... args);\n",
descriptor_);
format(
"$deprecated_attr$std::string* ${1$mutable_$name$$}$();\n"
"PROTOBUF_NODISCARD $deprecated_attr$std::string* "
"${1$$release_name$$}$();\n"
"$deprecated_attr$void ${1$set_allocated_$name$$}$(std::string* "
"$name$);\n",
descriptor_);
format(
"private:\n"
"const std::string& _internal_$name$() const;\n"
"inline PROTOBUF_ALWAYS_INLINE void "
"_internal_set_$name$(const std::string& value);\n"
"std::string* _internal_mutable_$name$();\n");
if (inlined_) {
format(
"inline PROTOBUF_ALWAYS_INLINE bool _internal_$name$_donated() "
"const;\n");
}
format("public:\n");
if (unknown_ctype) {
format.Outdent();
format(" public:\n");
format.Indent();
}
}
void StringFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline const std::string& $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n");
if (!descriptor_->default_value_string().empty()) {
format(
" if ($field$.IsDefault()) return "
"$default_variable_field$.get();\n");
}
format(
" return _internal_$name$();\n"
"}\n");
if (!inlined_) {
format(
"template <typename ArgT0, typename... ArgT>\n"
"inline PROTOBUF_ALWAYS_INLINE\n"
"void $classname$::set_$name$(ArgT0&& arg0, ArgT... args) {\n"
"$maybe_prepare_split_message$"
" $set_hasbit$\n"
" $field$.$setter$(static_cast<ArgT0 &&>(arg0),"
" args..., GetArenaForAllocation());\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n");
} else {
format(
"template <typename ArgT0, typename... ArgT>\n"
"inline PROTOBUF_ALWAYS_INLINE\n"
"void $classname$::set_$name$(ArgT0&& arg0, ArgT... args) {\n"
"$maybe_prepare_split_message$"
" $set_hasbit$\n"
" $field$.$setter$(static_cast<ArgT0 &&>(arg0),"
" args..., GetArenaForAllocation(), _internal_$name$_donated(), "
"&$donating_states_word$, $mask_for_undonate$, this);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline bool $classname$::_internal_$name$_donated() const {\n"
" bool value = $inlined_string_donated$\n"
" return value;\n"
"}\n");
}
format(
"inline std::string* $classname$::mutable_$name$() {\n"
"$maybe_prepare_split_message$"
" std::string* _s = _internal_mutable_$name$();\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return _s;\n"
"}\n"
"inline const std::string& $classname$::_internal_$name$() const {\n"
" return $field$.Get();\n"
"}\n"
"inline void $classname$::_internal_set_$name$(const std::string& "
"value) {\n"
" $set_hasbit$\n");
if (!inlined_) {
format(
" $field$.Set(value, GetArenaForAllocation());\n"
"}\n");
} else {
format(
" $field$.Set(value, GetArenaForAllocation(),\n"
" _internal_$name$_donated(), &$donating_states_word$, "
"$mask_for_undonate$, this);\n"
"}\n");
}
format(
"inline std::string* $classname$::_internal_mutable_$name$() {\n"
" $set_hasbit$\n");
if (!inlined_) {
format(
" return $field$.Mutable($lazy_variable_args$"
"GetArenaForAllocation());\n"
"}\n");
} else {
format(
" return $field$.Mutable($lazy_variable_args$"
"GetArenaForAllocation(), _internal_$name$_donated(), "
"&$donating_states_word$, $mask_for_undonate$, this);\n"
"}\n");
}
format(
"inline std::string* $classname$::$release_name$() {\n"
"$annotate_release$"
"$maybe_prepare_split_message$"
" // @@protoc_insertion_point(field_release:$full_name$)\n");
if (HasHasbit(descriptor_)) {
format(
" if (!_internal_has_$name$()) {\n"
" return nullptr;\n"
" }\n"
" $clear_hasbit$\n");
if (!inlined_) {
format(" auto* p = $field$.Release();\n");
if (descriptor_->default_value_string().empty()) {
format(
"#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING\n"
" if ($field$.IsDefault()) {\n"
" $field$.Set(\"\", GetArenaForAllocation());\n"
" }\n"
"#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING\n");
}
format(" return p;\n");
} else {
format(
" return $field$.Release(GetArenaForAllocation(), "
"_internal_$name$_donated());\n");
}
} else {
format(" return $field$.Release();\n");
}
format(
"}\n"
"inline void $classname$::set_allocated_$name$(std::string* $name$) {\n"
"$maybe_prepare_split_message$"
" if ($name$ != nullptr) {\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n");
if (!inlined_) {
format(" $field$.SetAllocated($name$, GetArenaForAllocation());\n");
if (descriptor_->default_value_string().empty()) {
format(
"#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING\n"
" if ($field$.IsDefault()) {\n"
" $field$.Set(\"\", GetArenaForAllocation());\n"
" }\n"
"#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING\n");
}
} else {
// Currently, string fields with default value can't be inlined.
format(
" $field$.SetAllocated(nullptr, $name$, GetArenaForAllocation(), "
"_internal_$name$_donated(), &$donating_states_word$, "
"$mask_for_undonate$, this);\n");
}
format(
"$annotate_set$"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
void StringFieldGenerator::GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (!descriptor_->default_value_string().empty()) {
format(
"const ::$proto_ns$::internal::LazyString "
"$classname$::$default_variable_field$"
"{{{$default$, $default_length$}}, {nullptr}};\n");
}
}
void StringFieldGenerator::GenerateClearingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (descriptor_->default_value_string().empty()) {
format("$field$.ClearToEmpty();\n");
} else {
GOOGLE_DCHECK(!inlined_);
format(
"$field$.ClearToDefault($lazy_variable$, GetArenaForAllocation());\n");
}
}
void StringFieldGenerator::GenerateMessageClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
// Two-dimension specialization here: supporting arenas, field presence, or
// not, and default value is the empty string or not. Complexity here ensures
// the minimal number of branches / amount of extraneous code at runtime
// (given that the below methods are inlined one-liners)!
// If we have a hasbit, then the Clear() method of the protocol buffer
// will have checked that this field is set. If so, we can avoid redundant
// checks against the default variable.
const bool must_be_present = HasHasbit(descriptor_);
if (inlined_ && must_be_present) {
// Calling mutable_$name$() gives us a string reference and sets the has bit
// for $name$ (in proto2). We may get here when the string field is inlined
// but the string's contents have not been changed by the user, so we cannot
// make an assertion about the contents of the string and could never make
// an assertion about the string instance.
//
// For non-inlined strings, we distinguish from non-default by comparing
// instances, rather than contents.
format("$DCHK$(!$field$.IsDefault());\n");
}
if (descriptor_->default_value_string().empty()) {
if (must_be_present) {
format("$field$.ClearNonDefaultToEmpty();\n");
} else {
format("$field$.ClearToEmpty();\n");
}
} else {
// Clear to a non-empty default is more involved, as we try to use the
// Arena if one is present and may need to reallocate the string.
format(
"$field$.ClearToDefault($lazy_variable$, GetArenaForAllocation());\n ");
}
}
void StringFieldGenerator::GenerateMergingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
// TODO(gpike): improve this
format("_this->_internal_set_$name$(from._internal_$name$());\n");
}
void StringFieldGenerator::GenerateSwappingCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (!inlined_) {
format(
"::$proto_ns$::internal::ArenaStringPtr::InternalSwap(\n"
" &$field$, lhs_arena,\n"
" &other->$field$, rhs_arena\n"
");\n");
} else {
format(
"::$proto_ns$::internal::InlinedStringField::InternalSwap(\n"
" &$field$, lhs_arena, "
"($inlined_string_donated_array$[0] & 0x1u) == 0, this,\n"
" &other->$field$, rhs_arena, "
"(other->$inlined_string_donated_array$[0] & 0x1u) == 0, other);\n");
}
}
void StringFieldGenerator::GenerateConstructorCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (inlined_ && descriptor_->default_value_string().empty()) {
return;
}
GOOGLE_DCHECK(!inlined_);
format("$field$.InitDefault();\n");
if (IsString(descriptor_, options_) &&
descriptor_->default_value_string().empty()) {
format(
"#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING\n"
" $field$.Set(\"\", GetArenaForAllocation());\n"
"#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING\n");
}
}
void StringFieldGenerator::GenerateCreateSplitMessageCode(
io::Printer* printer) const {
GOOGLE_CHECK(ShouldSplit(descriptor_, options_));
GOOGLE_CHECK(!inlined_);
Formatter format(printer, variables_);
format("ptr->$name$_.InitDefault();\n");
if (IsString(descriptor_, options_) &&
descriptor_->default_value_string().empty()) {
format(
"#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING\n"
" ptr->$name$_.Set(\"\", GetArenaForAllocation());\n"
"#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING\n");
}
}
void StringFieldGenerator::GenerateCopyConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
GenerateConstructorCode(printer);
if (inlined_) {
format("new (&_this->$field$) ::_pbi::InlinedStringField();\n");
}
if (HasHasbit(descriptor_)) {
format("if (from._internal_has_$name$()) {\n");
} else {
format("if (!from._internal_$name$().empty()) {\n");
}
format.Indent();
if (!inlined_) {
format(
"_this->$field$.Set(from._internal_$name$(), \n"
" _this->GetArenaForAllocation());\n");
} else {
format(
"_this->$field$.Set(from._internal_$name$(),\n"
" _this->GetArenaForAllocation(), _this->_internal_$name$_donated(), "
"&_this->$donating_states_word$, $mask_for_undonate$, _this);\n");
}
format.Outdent();
format("}\n");
}
void StringFieldGenerator::GenerateDestructorCode(io::Printer* printer) const {
Formatter format(printer, variables_);
if (!inlined_) {
if (ShouldSplit(descriptor_, options_)) {
format("$cached_split_ptr$->$name$_.Destroy();\n");
return;
}
format("$field$.Destroy();\n");
return;
}
// Explicitly calls ~InlinedStringField as its automatic call is disabled.
// Destructor has been implicitly skipped as a union, and even the
// message-owned arena is enabled, arena could still be missing for
// Arena::CreateMessage(nullptr).
GOOGLE_DCHECK(!ShouldSplit(descriptor_, options_));
format("$field$.~InlinedStringField();\n");
}
ArenaDtorNeeds StringFieldGenerator::NeedsArenaDestructor() const {
return inlined_ ? ArenaDtorNeeds::kOnDemand : ArenaDtorNeeds::kNone;
}
void StringFieldGenerator::GenerateArenaDestructorCode(
io::Printer* printer) const {
if (!inlined_) return;
Formatter format(printer, variables_);
// _this is the object being destructed (we are inside a static method here).
format(
"if (!_this->_internal_$name$_donated()) {\n"
" _this->$field$.~InlinedStringField();\n"
"}\n");
}
void StringFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (descriptor_->type() == FieldDescriptor::TYPE_STRING) {
GenerateUtf8CheckCodeForString(
descriptor_, options_, false,
"this->_internal_$name$().data(), "
"static_cast<int>(this->_internal_$name$().length()),\n",
format);
}
format(
"target = stream->Write$declared_type$MaybeAliased(\n"
" $number$, this->_internal_$name$(), target);\n");
}
void StringFieldGenerator::GenerateByteSize(io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"total_size += $tag_size$ +\n"
" ::$proto_ns$::internal::WireFormatLite::$declared_type$Size(\n"
" this->_internal_$name$());\n");
}
void StringFieldGenerator::GenerateConstexprAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (inlined_) {
format("/*decltype($field$)*/{nullptr, false}");
return;
}
if (descriptor_->default_value_string().empty()) {
format(
"/*decltype($field$)*/{&::_pbi::fixed_address_empty_string, "
"::_pbi::ConstantInitialized{}}");
} else {
format("/*decltype($field$)*/{nullptr, ::_pbi::ConstantInitialized{}}");
}
}
void StringFieldGenerator::GenerateAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
if (ShouldSplit(descriptor_, options_)) {
GOOGLE_CHECK(!inlined_);
format("decltype(Impl_::Split::$name$_){}");
return;
}
if (!inlined_) {
format("decltype($field$){}");
} else {
format("decltype($field$)(arena)");
}
}
void StringFieldGenerator::GenerateCopyAggregateInitializer(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("decltype($field$){}");
}
// ===================================================================
StringOneofFieldGenerator::StringOneofFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: StringFieldGenerator(descriptor, options) {
SetCommonOneofFieldVariables(descriptor, &variables_);
variables_["field_name"] = UnderscoresToCamelCase(descriptor->name(), true);
variables_["oneof_index"] =
StrCat(descriptor->containing_oneof()->index());
}
StringOneofFieldGenerator::~StringOneofFieldGenerator() {}
void StringOneofFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline const std::string& $classname$::$name$() const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$();\n"
"}\n"
"template <typename ArgT0, typename... ArgT>\n"
"inline void $classname$::set_$name$(ArgT0&& arg0, ArgT... args) {\n"
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $field$.InitDefault();\n"
" }\n"
" $field$.$setter$("
" static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline std::string* $classname$::mutable_$name$() {\n"
" std::string* _s = _internal_mutable_$name$();\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return _s;\n"
"}\n"
"inline const std::string& $classname$::_internal_$name$() const {\n"
" if (_internal_has_$name$()) {\n"
" return $field$.Get();\n"
" }\n"
" return $default_string$;\n"
"}\n"
"inline void $classname$::_internal_set_$name$(const std::string& "
"value) {\n"
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $field$.InitDefault();\n"
" }\n"
" $field$.Set(value, GetArenaForAllocation());\n"
"}\n");
format(
"inline std::string* $classname$::_internal_mutable_$name$() {\n"
" if (!_internal_has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $field$.InitDefault();\n"
" }\n"
" return $field$.Mutable($lazy_variable_args$"
" GetArenaForAllocation());\n"
"}\n"
"inline std::string* $classname$::$release_name$() {\n"
"$annotate_release$"
" // @@protoc_insertion_point(field_release:$full_name$)\n"
" if (_internal_has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" return $field$.Release();\n"
" } else {\n"
" return nullptr;\n"
" }\n"
"}\n"
"inline void $classname$::set_allocated_$name$(std::string* $name$) {\n"
" if (has_$oneof_name$()) {\n"
" clear_$oneof_name$();\n"
" }\n"
" if ($name$ != nullptr) {\n"
" set_has_$name$();\n"
" $field$.InitAllocated($name$, GetArenaForAllocation());\n"
" }\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
void StringOneofFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.Destroy();\n");
}
void StringOneofFieldGenerator::GenerateMessageClearingCode(
io::Printer* printer) const {
return GenerateClearingCode(printer);
}
void StringOneofFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
// Don't print any swapping code. Swapping the union will swap this field.
}
void StringOneofFieldGenerator::GenerateConstructorCode(
io::Printer* printer) const {
// Nothing required here.
}
// ===================================================================
RepeatedStringFieldGenerator::RepeatedStringFieldGenerator(
const FieldDescriptor* descriptor, const Options& options)
: FieldGenerator(descriptor, options) {
SetStringVariables(descriptor, &variables_, options);
}
RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {}
void RepeatedStringFieldGenerator::GeneratePrivateMembers(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("::$proto_ns$::RepeatedPtrField<std::string> $name$_;\n");
}
void RepeatedStringFieldGenerator::GenerateAccessorDeclarations(
io::Printer* printer) const {
Formatter format(printer, variables_);
// See comment above about unknown ctypes.
bool unknown_ctype = descriptor_->options().ctype() !=
EffectiveStringCType(descriptor_, options_);
if (unknown_ctype) {
format.Outdent();
format(
" private:\n"
" // Hidden due to unknown ctype option.\n");
format.Indent();
}
format(
"$deprecated_attr$const std::string& ${1$$name$$}$(int index) const;\n"
"$deprecated_attr$std::string* ${1$mutable_$name$$}$(int index);\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, const "
"std::string& value);\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, std::string&& "
"value);\n"
"$deprecated_attr$void ${1$set_$name$$}$(int index, const "
"char* value);\n",
descriptor_);
if (!options_.opensource_runtime) {
format(
"$deprecated_attr$void ${1$set_$name$$}$(int index, "
"StringPiece value);\n",
descriptor_);
}
format(
"$deprecated_attr$void ${1$set_$name$$}$("
"int index, const $pointer_type$* value, size_t size);\n"
"$deprecated_attr$std::string* ${1$add_$name$$}$();\n"
"$deprecated_attr$void ${1$add_$name$$}$(const std::string& value);\n"
"$deprecated_attr$void ${1$add_$name$$}$(std::string&& value);\n"
"$deprecated_attr$void ${1$add_$name$$}$(const char* value);\n",
descriptor_);
if (!options_.opensource_runtime) {
format(
"$deprecated_attr$void ${1$add_$name$$}$(StringPiece value);\n",
descriptor_);
}
format(
"$deprecated_attr$void ${1$add_$name$$}$(const $pointer_type$* "
"value, size_t size)"
";\n"
"$deprecated_attr$const ::$proto_ns$::RepeatedPtrField<std::string>& "
"${1$$name$$}$() "
"const;\n"
"$deprecated_attr$::$proto_ns$::RepeatedPtrField<std::string>* "
"${1$mutable_$name$$}$()"
";\n"
"private:\n"
"const std::string& ${1$_internal_$name$$}$(int index) const;\n"
"std::string* _internal_add_$name$();\n"
"public:\n",
descriptor_);
if (unknown_ctype) {
format.Outdent();
format(" public:\n");
format.Indent();
}
}
void RepeatedStringFieldGenerator::GenerateInlineAccessorDefinitions(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"inline std::string* $classname$::add_$name$() {\n"
" std::string* _s = _internal_add_$name$();\n"
"$annotate_add_mutable$"
" // @@protoc_insertion_point(field_add_mutable:$full_name$)\n"
" return _s;\n"
"}\n");
if (options_.safe_boundary_check) {
format(
"inline const std::string& $classname$::_internal_$name$(int index) "
"const {\n"
" return $field$.InternalCheckedGet(\n"
" index, ::$proto_ns$::internal::GetEmptyStringAlreadyInited());\n"
"}\n");
} else {
format(
"inline const std::string& $classname$::_internal_$name$(int index) "
"const {\n"
" return $field$.Get(index);\n"
"}\n");
}
format(
"inline const std::string& $classname$::$name$(int index) const {\n"
"$annotate_get$"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return _internal_$name$(index);\n"
"}\n"
"inline std::string* $classname$::mutable_$name$(int index) {\n"
"$annotate_mutable$"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $field$.Mutable(index);\n"
"}\n"
"inline void $classname$::set_$name$(int index, const std::string& "
"value) "
"{\n"
" $field$.Mutable(index)->assign(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::set_$name$(int index, std::string&& value) {\n"
" $field$.Mutable(index)->assign(std::move(value));\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set:$full_name$)\n"
"}\n"
"inline void $classname$::set_$name$(int index, const char* value) {\n"
" $null_check$"
" $field$.Mutable(index)->assign(value);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_char:$full_name$)\n"
"}\n");
if (!options_.opensource_runtime) {
format(
"inline void "
"$classname$::set_$name$(int index, StringPiece value) {\n"
" $field$.Mutable(index)->assign(value.data(), value.size());\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_string_piece:$full_name$)\n"
"}\n");
}
format(
"inline void "
"$classname$::set_$name$"
"(int index, const $pointer_type$* value, size_t size) {\n"
" $field$.Mutable(index)->assign(\n"
" reinterpret_cast<const char*>(value), size);\n"
"$annotate_set$"
" // @@protoc_insertion_point(field_set_pointer:$full_name$)\n"
"}\n"
"inline std::string* $classname$::_internal_add_$name$() {\n"
" return $field$.Add();\n"
"}\n"
"inline void $classname$::add_$name$(const std::string& value) {\n"
" $field$.Add()->assign(value);\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$(std::string&& value) {\n"
" $field$.Add(std::move(value));\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
"}\n"
"inline void $classname$::add_$name$(const char* value) {\n"
" $null_check$"
" $field$.Add()->assign(value);\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add_char:$full_name$)\n"
"}\n");
if (!options_.opensource_runtime) {
format(
"inline void $classname$::add_$name$(StringPiece value) {\n"
" $field$.Add()->assign(value.data(), value.size());\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add_string_piece:$full_name$)\n"
"}\n");
}
format(
"inline void "
"$classname$::add_$name$(const $pointer_type$* value, size_t size) {\n"
" $field$.Add()->assign(reinterpret_cast<const char*>(value), size);\n"
"$annotate_add$"
" // @@protoc_insertion_point(field_add_pointer:$full_name$)\n"
"}\n"
"inline const ::$proto_ns$::RepeatedPtrField<std::string>&\n"
"$classname$::$name$() const {\n"
"$annotate_list$"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $field$;\n"
"}\n"
"inline ::$proto_ns$::RepeatedPtrField<std::string>*\n"
"$classname$::mutable_$name$() {\n"
"$annotate_mutable_list$"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$field$;\n"
"}\n");
}
void RepeatedStringFieldGenerator::GenerateClearingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.Clear();\n");
}
void RepeatedStringFieldGenerator::GenerateMergingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("_this->$field$.MergeFrom(from.$field$);\n");
}
void RepeatedStringFieldGenerator::GenerateSwappingCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.InternalSwap(&other->$field$);\n");
}
void RepeatedStringFieldGenerator::GenerateDestructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
format("$field$.~RepeatedPtrField();\n");
}
void RepeatedStringFieldGenerator::GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"for (int i = 0, n = this->_internal_$name$_size(); i < n; i++) {\n"
" const auto& s = this->_internal_$name$(i);\n");
// format("for (const std::string& s : this->$name$()) {\n");
format.Indent();
if (descriptor_->type() == FieldDescriptor::TYPE_STRING) {
GenerateUtf8CheckCodeForString(descriptor_, options_, false,
"s.data(), static_cast<int>(s.length()),\n",
format);
}
format.Outdent();
format(
" target = stream->Write$declared_type$($number$, s, target);\n"
"}\n");
}
void RepeatedStringFieldGenerator::GenerateByteSize(
io::Printer* printer) const {
Formatter format(printer, variables_);
format(
"total_size += $tag_size$ *\n"
" ::$proto_ns$::internal::FromIntSize($field$.size());\n"
"for (int i = 0, n = $field$.size(); i < n; i++) {\n"
" total_size += "
"::$proto_ns$::internal::WireFormatLite::$declared_type$Size(\n"
" $field$.Get(i));\n"
"}\n");
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,136 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__
#include <map>
#include <string>
#include <google/protobuf/compiler/cpp/field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class StringFieldGenerator : public FieldGenerator {
public:
StringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~StringFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateStaticMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateNonInlineAccessorDefinitions(
io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMessageClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
void GenerateCreateSplitMessageCode(io::Printer* printer) const override;
void GenerateCopyConstructorCode(io::Printer* printer) const override;
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateArenaDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
void GenerateConstexprAggregateInitializer(
io::Printer* printer) const override;
void GenerateAggregateInitializer(io::Printer* printer) const override;
void GenerateCopyAggregateInitializer(io::Printer* printer) const override;
bool IsInlined() const override { return inlined_; }
ArenaDtorNeeds NeedsArenaDestructor() const override;
private:
bool inlined_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator);
};
class StringOneofFieldGenerator : public StringFieldGenerator {
public:
StringOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~StringOneofFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
// StringFieldGenerator, from which we inherit, overrides this so we need to
// override it as well.
void GenerateMessageClearingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator);
};
class RepeatedStringFieldGenerator : public FieldGenerator {
public:
RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~RepeatedStringFieldGenerator() override;
// implements FieldGenerator ---------------------------------------
void GeneratePrivateMembers(io::Printer* printer) const override;
void GenerateAccessorDeclarations(io::Printer* printer) const override;
void GenerateInlineAccessorDefinitions(io::Printer* printer) const override;
void GenerateClearingCode(io::Printer* printer) const override;
void GenerateMergingCode(io::Printer* printer) const override;
void GenerateSwappingCode(io::Printer* printer) const override;
void GenerateConstructorCode(io::Printer* printer) const override {}
void GenerateCopyConstructorCode(io::Printer* /*printer*/) const override {
GOOGLE_CHECK(!ShouldSplit(descriptor_, options_));
}
void GenerateDestructorCode(io::Printer* printer) const override;
void GenerateSerializeWithCachedSizesToArray(
io::Printer* printer) const override;
void GenerateByteSize(io::Printer* printer) const override;
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedStringFieldGenerator);
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__

View File

@@ -0,0 +1,184 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// This file tests that various identifiers work as field and type names even
// though the same identifiers are used internally by the C++ code generator.
// LINT: LEGACY_NAMES
syntax = "proto2";
// Some generic_services option(s) added automatically.
// See: http://go/proto2-generic-services-default
option cc_generic_services = true; // auto-added
// We don't put this in a package within proto2 because we need to make sure
// that the generated code doesn't depend on being in the proto2 namespace.
package protobuf_unittest;
// Test that fields can have names like "input" and "i" which are also used
// internally by the code generator for local variables.
message TestConflictingSymbolNames {
message BuildDescriptors {}
message TypeTraits {}
optional int32 input = 1;
optional int32 output = 2;
optional string length = 3;
repeated int32 i = 4;
repeated string new_element = 5 [ctype = STRING_PIECE];
optional int32 total_size = 6;
optional int32 tag = 7;
enum TestEnum { FOO = 0; }
message Data1 {
repeated int32 data = 1;
}
message Data2 {
repeated TestEnum data = 1;
}
message Data3 {
repeated string data = 1;
}
message Data4 {
repeated Data4 data = 1;
}
message Data5 {
repeated string data = 1 [ctype = STRING_PIECE];
}
message Data6 {
repeated string data = 1 [ctype = CORD];
}
optional int32 source = 8;
optional int32 value = 9;
optional int32 file = 10;
optional int32 from = 11;
optional int32 handle_uninterpreted = 12;
repeated int32 index = 13;
optional int32 controller = 14;
optional int32 already_here = 15;
optional uint32 uint32 = 16;
optional uint64 uint64 = 17;
optional string string = 18;
optional int32 memset = 19;
optional int32 int32 = 20;
optional int64 int64 = 21;
optional uint32 cached_size = 22;
optional uint32 extensions = 23;
optional uint32 bit = 24;
optional uint32 bits = 25;
optional uint32 offsets = 26;
optional uint32 reflection = 27;
message Cord {}
optional string some_cord = 28 [ctype = CORD];
message StringPiece {}
optional string some_string_piece = 29 [ctype = STRING_PIECE];
// Some keywords.
optional uint32 int = 30;
optional uint32 friend = 31;
optional uint32 class = 37;
optional uint32 typedecl = 39;
optional uint32 auto = 40;
// The generator used to #define a macro called "DO" inside the .cc file.
message DO {}
optional DO do = 32;
// Some template parameter names for extensions.
optional int32 field_type = 33;
optional bool is_packed = 34;
// test conflicting release_$name$. "length" and "do" field in this message
// must remain string or message fields to make the test valid.
optional string release_length = 35;
// A more extreme case, the field name "do" here is a keyword, which will be
// escaped to "do_" already. Test there is no conflict even with escaped field
// names.
optional DO release_do = 36;
// For clashing local variables in Serialize and ByteSize calculation.
optional string target = 38;
extensions 1000 to max; // NO_PROTO3
}
message TestConflictingSymbolNamesExtension { // NO_PROTO3
extend TestConflictingSymbolNames { // NO_PROTO3
repeated int32 repeated_int32_ext = 20423638 [packed = true]; // NO_PROTO3
} // NO_PROTO3
} // NO_PROTO3
message TestConflictingEnumNames { // NO_PROTO3
enum while { // NO_PROTO3
default = 0; // NO_PROTO3
and = 1; // NO_PROTO3
class = 2; // NO_PROTO3
int = 3; // NO_PROTO3
typedef = 4; // NO_PROTO3
XOR = 5; // NO_PROTO3
} // NO_PROTO3
optional while conflicting_enum = 1; // NO_PROTO3
} // NO_PROTO3
enum bool { // NO_PROTO3
default = 0; // NO_PROTO3
NOT_EQ = 1; // NO_PROTO3
volatile = 2; // NO_PROTO3
return = 3; // NO_PROTO3
} // NO_PROTO3
message DummyMessage {}
message NULL {
optional int32 int = 1;
}
extend TestConflictingSymbolNames { // NO_PROTO3
optional int32 void = 314253; // NO_PROTO3
} // NO_PROTO3
// Message names that could conflict.
message Shutdown {}
message TableStruct {}
service TestConflictingMethodNames {
rpc Closure(DummyMessage) returns (DummyMessage);
}

View File

@@ -0,0 +1,43 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Test that proto2 compiler can generate valid code when the enum value
// is INT_MAX. Note that this is a compile-only test and this proto is not
// referenced in any C++ code.
syntax = "proto2";
package protobuf_unittest;
message TestLargeEnumValue {
enum EnumWithLargeValue {
VALUE_1 = 1;
VALUE_MAX = 0x7fffffff;
}
}

View File

@@ -0,0 +1,133 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// To test the code generator, we actually use it to generate code for
// net/proto2/internal/unittest.proto, then test that. This means that we
// are actually testing the parser and other parts of the system at the same
// time, and that problems in the generator may show up as compile-time errors
// rather than unittest failures, which may be surprising. However, testing
// the output of the C++ generator directly would be very hard. We can't very
// well just check it against golden files since those files would have to be
// updated for any small change; such a test would be very brittle and probably
// not very helpful. What we really want to test is that the code compiles
// correctly and produces the interfaces we expect, which is why this test
// is written this way.
#include <google/protobuf/compiler/cpp/unittest.h>
#include <google/protobuf/unittest.pb.h>
#include <google/protobuf/unittest_embed_optimize_for.pb.h>
#include <google/protobuf/unittest_optimize_for.pb.h>
#include <google/protobuf/test_util.h>
#define MESSAGE_TEST_NAME MessageTest
#define GENERATED_DESCRIPTOR_TEST_NAME GeneratedDescriptorTest
#define GENERATED_MESSAGE_TEST_NAME GeneratedMessageTest
#define GENERATED_ENUM_TEST_NAME GeneratedEnumTest
#define GENERATED_SERVICE_TEST_NAME GeneratedServiceTest
#define HELPERS_TEST_NAME HelpersTest
#define DESCRIPTOR_INIT_TEST_NAME DescriptorInitializationTest
#define UNITTEST_PROTO_PATH "net/proto2/internal/unittest.proto"
#define UNITTEST ::protobuf_unittest
#define UNITTEST_IMPORT ::protobuf_unittest_import
// Must include after the above macros.
#include <google/protobuf/compiler/cpp/unittest.inc>
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// Can't use an anonymous namespace here due to brokenness of Tru64 compiler.
namespace cpp_unittest {
namespace protobuf_unittest = ::protobuf_unittest;
TEST(GENERATED_MESSAGE_TEST_NAME, TestConflictingSymbolNames) {
// test_bad_identifiers.proto successfully compiled, then it works. The
// following is just a token usage to insure that the code is, in fact,
// being compiled and linked.
protobuf_unittest::TestConflictingSymbolNames message;
message.set_uint32(1);
EXPECT_EQ(3, message.ByteSizeLong());
message.set_friend_(5);
EXPECT_EQ(5, message.friend_());
message.set_class_(6);
EXPECT_EQ(6, message.class_());
// Instantiate extension template functions to test conflicting template
// parameter names.
typedef protobuf_unittest::TestConflictingSymbolNamesExtension ExtensionMessage;
message.AddExtension(ExtensionMessage::repeated_int32_ext, 123);
EXPECT_EQ(123, message.GetExtension(ExtensionMessage::repeated_int32_ext, 0));
}
TEST(GENERATED_MESSAGE_TEST_NAME, TestConflictingEnumNames) {
protobuf_unittest::TestConflictingEnumNames message;
message.set_conflicting_enum(
protobuf_unittest::TestConflictingEnumNames_while_and_);
EXPECT_EQ(1, message.conflicting_enum());
message.set_conflicting_enum(
protobuf_unittest::TestConflictingEnumNames_while_XOR);
EXPECT_EQ(5, message.conflicting_enum());
protobuf_unittest::bool_ conflicting_enum;
conflicting_enum = protobuf_unittest::NOT_EQ;
EXPECT_EQ(1, conflicting_enum);
conflicting_enum = protobuf_unittest::return_;
EXPECT_EQ(3, conflicting_enum);
}
TEST(GENERATED_MESSAGE_TEST_NAME, TestConflictingMessageNames) {
protobuf_unittest::NULL_ message;
message.set_int_(123);
EXPECT_EQ(message.int_(), 123);
}
TEST(GENERATED_MESSAGE_TEST_NAME, TestConflictingExtension) {
protobuf_unittest::TestConflictingSymbolNames message;
message.SetExtension(protobuf_unittest::void_, 123);
EXPECT_EQ(123, message.GetExtension(protobuf_unittest::void_));
}
} // namespace cpp_unittest
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This header declares the namespace google::protobuf::protobuf_unittest in order to expose
// any problems with the generated class names. We use this header to ensure
// unittest.cc will declare the namespace prior to other includes, while obeying
// normal include ordering.
//
// When generating a class name of "foo.Bar" we must ensure we prefix the class
// name with "::", in case the namespace google::protobuf::foo exists. We intentionally
// trigger that case here by declaring google::protobuf::protobuf_unittest.
//
// See ClassName in helpers.h for more details.
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__
namespace google {
namespace protobuf {
namespace protobuf_unittest {}
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_UNITTEST_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This test insures that
// csharp/src/Google.Protobuf/Reflection/Descriptor.cs match exactly
// what would be generated by the protocol compiler. The file is not
// generated automatically at build time.
//
// If this test fails, run the script
// "generate_descriptor_proto.sh" and add the changed files under
// csharp/src/ to your changelist.
#include <map>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
namespace {
class MockErrorCollector : public MultiFileErrorCollector {
public:
MockErrorCollector() {}
~MockErrorCollector() {}
std::string text_;
// implements ErrorCollector ---------------------------------------
void AddError(const std::string& filename, int line, int column,
const std::string& message) {
strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n",
filename, line, column, message);
}
};
class MockGeneratorContext : public GeneratorContext {
public:
void ExpectFileMatches(const std::string& virtual_filename,
const std::string& physical_filename) {
auto it = files_.find(virtual_filename);
ASSERT_TRUE(it != files_.end())
<< "Generator failed to generate file: " << virtual_filename;
std::string expected_contents = *it->second;
std::string actual_contents;
GOOGLE_CHECK_OK(
File::GetContentsAsText(TestSourceDir() + "/" + physical_filename,
&actual_contents, true))
<< "Unable to get " << physical_filename;
EXPECT_TRUE(actual_contents == expected_contents)
<< physical_filename << " needs to be regenerated. Please run "
"generate_descriptor_proto.sh. Then add this file "
"to your CL.";
}
// implements GeneratorContext --------------------------------------
virtual io::ZeroCopyOutputStream* Open(const std::string& filename) {
auto& map_slot = files_[filename];
map_slot.reset(new std::string);
return new io::StringOutputStream(map_slot.get());
}
private:
std::map<std::string, std::unique_ptr<std::string>> files_;
};
class GenerateAndTest {
public:
GenerateAndTest() {}
void Run(const FileDescriptor* proto_file, std::string file1,
std::string file2) {
ASSERT_TRUE(proto_file != NULL) << TestSourceDir();
ASSERT_TRUE(generator_.Generate(proto_file, parameter_,
&context_, &error_));
context_.ExpectFileMatches(file1, file2);
}
void SetParameter(std::string parameter) {
parameter_ = parameter;
}
private:
Generator generator_;
MockGeneratorContext context_;
std::string error_;
std::string parameter_;
};
TEST(CsharpBootstrapTest, GeneratedCsharpDescriptorMatches) {
// Skip this whole test if the csharp directory doesn't exist (i.e., a C++11
// only distribution).
std::string descriptor_file_name =
"../csharp/src/Google.Protobuf/Reflection/Descriptor.cs";
if (!File::Exists(TestSourceDir() + "/" + descriptor_file_name)) {
return;
}
MockErrorCollector error_collector;
DiskSourceTree source_tree;
Importer importer(&source_tree, &error_collector);
GenerateAndTest generate_test;
generate_test.SetParameter("base_namespace=Google.Protobuf");
source_tree.MapPath("", TestSourceDir());
generate_test.Run(importer.Import("google/protobuf/descriptor.proto"),
"Reflection/Descriptor.cs",
"../csharp/src/Google.Protobuf/Reflection/Descriptor.cs");
generate_test.Run(importer.Import("google/protobuf/any.proto"),
"WellKnownTypes/Any.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Any.cs");
generate_test.Run(importer.Import("google/protobuf/api.proto"),
"WellKnownTypes/Api.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Api.cs");
generate_test.Run(importer.Import("google/protobuf/duration.proto"),
"WellKnownTypes/Duration.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Duration.cs");
generate_test.Run(importer.Import("google/protobuf/empty.proto"),
"WellKnownTypes/Empty.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Empty.cs");
generate_test.Run(importer.Import("google/protobuf/field_mask.proto"),
"WellKnownTypes/FieldMask.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/FieldMask.cs");
generate_test.Run(importer.Import("google/protobuf/source_context.proto"),
"WellKnownTypes/SourceContext.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/SourceContext.cs");
generate_test.Run(importer.Import("google/protobuf/struct.proto"),
"WellKnownTypes/Struct.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs");
generate_test.Run(importer.Import("google/protobuf/timestamp.proto"),
"WellKnownTypes/Timestamp.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Timestamp.cs");
generate_test.Run(importer.Import("google/protobuf/type.proto"),
"WellKnownTypes/Type.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Type.cs");
generate_test.Run(importer.Import("google/protobuf/wrappers.proto"),
"WellKnownTypes/Wrappers.cs",
"../csharp/src/Google.Protobuf/WellKnownTypes/Wrappers.cs");
generate_test.SetParameter("");
source_tree.MapPath("", TestSourceDir() + "/../conformance");
generate_test.Run(importer.Import("conformance.proto"),
"Conformance.cs",
"../csharp/src/Google.Protobuf.Conformance/Conformance.cs");
EXPECT_EQ("", error_collector.text_);
}
} // namespace
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,116 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
// Functions to create C# XML documentation comments.
// Currently this only includes documentation comments containing text specified as comments
// in the .proto file; documentation comments generated just from field/message/enum/proto names
// is inlined in the relevant code. If more control is required, that code can be moved here.
void WriteDocCommentBodyImpl(io::Printer* printer, SourceLocation location) {
std::string comments = location.leading_comments.empty() ?
location.trailing_comments : location.leading_comments;
if (comments.empty()) {
return;
}
// XML escaping... no need for apostrophes etc as the whole text is going to be a child
// node of a summary element, not part of an attribute.
comments = StringReplace(comments, "&", "&amp;", true);
comments = StringReplace(comments, "<", "&lt;", true);
std::vector<std::string> lines;
lines = Split(comments, "\n", false);
// TODO: We really should work out which part to put in the summary and which to put in the remarks...
// but that needs to be part of a bigger effort to understand the markdown better anyway.
printer->Print("/// <summary>\n");
bool last_was_empty = false;
// We squash multiple blank lines down to one, and remove any trailing blank lines. We need
// to preserve the blank lines themselves, as this is relevant in the markdown.
// Note that we can't remove leading or trailing whitespace as *that's* relevant in markdown too.
// (We don't skip "just whitespace" lines, either.)
for (std::vector<std::string>::iterator it = lines.begin();
it != lines.end(); ++it) {
std::string line = *it;
if (line.empty()) {
last_was_empty = true;
} else {
if (last_was_empty) {
printer->Print("///\n");
}
last_was_empty = false;
printer->Print("///$line$\n", "line", *it);
}
}
printer->Print("/// </summary>\n");
}
template <typename DescriptorType>
static void WriteDocCommentBody(
io::Printer* printer, const DescriptorType* descriptor) {
SourceLocation location;
if (descriptor->GetSourceLocation(&location)) {
WriteDocCommentBodyImpl(printer, location);
}
}
void WriteMessageDocComment(io::Printer* printer, const Descriptor* message) {
WriteDocCommentBody(printer, message);
}
void WritePropertyDocComment(io::Printer* printer, const FieldDescriptor* field) {
WriteDocCommentBody(printer, field);
}
void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enumDescriptor) {
WriteDocCommentBody(printer, enumDescriptor);
}
void WriteEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value) {
WriteDocCommentBody(printer, value);
}
void WriteMethodDocComment(io::Printer* printer, const MethodDescriptor* method) {
WriteDocCommentBody(printer, method);
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_DOC_COMMENT_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_DOC_COMMENT_H__
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
void WriteMessageDocComment(io::Printer* printer, const Descriptor* message);
void WritePropertyDocComment(io::Printer* printer, const FieldDescriptor* field);
void WriteEnumDocComment(io::Printer* printer, const EnumDescriptor* enumDescriptor);
void WriteEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value);
void WriteMethodDocComment(io::Printer* printer, const MethodDescriptor* method);
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_DOC_COMMENT_H__

View File

@@ -0,0 +1,99 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_enum.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, const Options* options) :
SourceGeneratorBase(options),
descriptor_(descriptor) {
}
EnumGenerator::~EnumGenerator() {
}
void EnumGenerator::Generate(io::Printer* printer) {
WriteEnumDocComment(printer, descriptor_);
printer->Print("$access_level$ enum $name$ {\n",
"access_level", class_access_level(),
"name", descriptor_->name());
printer->Indent();
std::set<std::string> used_names;
std::set<int> used_number;
for (int i = 0; i < descriptor_->value_count(); i++) {
WriteEnumValueDocComment(printer, descriptor_->value(i));
std::string original_name = descriptor_->value(i)->name();
std::string name =
GetEnumValueName(descriptor_->name(), descriptor_->value(i)->name());
// Make sure we don't get any duplicate names due to prefix removal.
while (!used_names.insert(name).second) {
// It's possible we'll end up giving this warning multiple times, but that's better than not at all.
GOOGLE_LOG(WARNING) << "Duplicate enum value " << name << " (originally " << original_name
<< ") in " << descriptor_->name() << "; adding underscore to distinguish";
name += "_";
}
int number = descriptor_->value(i)->number();
if (!used_number.insert(number).second) {
printer->Print("[pbr::OriginalName(\"$original_name$\", PreferredAlias = false)] $name$ = $number$,\n",
"original_name", original_name,
"name", name,
"number", StrCat(number));
} else {
printer->Print("[pbr::OriginalName(\"$original_name$\")] $name$ = $number$,\n",
"original_name", original_name,
"name", name,
"number", StrCat(number));
}
}
printer->Outdent();
printer->Print("}\n");
printer->Print("\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,66 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_source_generator_base.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class EnumGenerator : public SourceGeneratorBase {
public:
EnumGenerator(const EnumDescriptor* descriptor, const Options* options);
~EnumGenerator();
EnumGenerator(const EnumGenerator&) = delete;
EnumGenerator& operator=(const EnumGenerator&) = delete;
void Generate(io::Printer* printer);
private:
const EnumDescriptor* descriptor_;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__

View File

@@ -0,0 +1,135 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_enum_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
EnumFieldGenerator::EnumFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex, const Options *options)
: PrimitiveFieldGenerator(descriptor, presenceIndex, options) {
}
EnumFieldGenerator::~EnumFieldGenerator() {
}
void EnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(variables_,
"$property_name$ = ($type_name$) input.ReadEnum();\n");
}
void EnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(variables_,
"if ($has_property_check$) {\n"
" output.WriteRawTag($tag_bytes$);\n"
" output.WriteEnum((int) $property_name$);\n"
"}\n");
}
void EnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += $tag_size$ + pb::CodedOutputStream.ComputeEnumSize((int) $property_name$);\n"
"}\n");
}
void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) {
printer->Print(
variables_,
"pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x, $default_value$)");
}
void EnumFieldGenerator::GenerateExtensionCode(io::Printer* printer) {
WritePropertyDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"$access_level$ static readonly pb::Extension<$extended_type$, $type_name$> $property_name$ =\n"
" new pb::Extension<$extended_type$, $type_name$>($number$, ");
GenerateCodecCode(printer);
printer->Print(");\n");
}
EnumOneofFieldGenerator::EnumOneofFieldGenerator(
const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
: PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options) {
}
EnumOneofFieldGenerator::~EnumOneofFieldGenerator() {
}
void EnumOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(variables_, "$property_name$ = other.$property_name$;\n");
}
void EnumOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
// TODO(jonskeet): What about if we read the default value?
printer->Print(
variables_,
"$oneof_name$_ = input.ReadEnum();\n"
"$oneof_name$Case_ = $oneof_property_name$OneofCase.$oneof_case_name$;\n");
}
void EnumOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" output.WriteRawTag($tag_bytes$);\n"
" output.WriteEnum((int) $property_name$);\n"
"}\n");
}
void EnumOneofFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += $tag_size$ + pb::CodedOutputStream.ComputeEnumSize((int) $property_name$);\n"
"}\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,81 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class EnumFieldGenerator : public PrimitiveFieldGenerator {
public:
EnumFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~EnumFieldGenerator();
EnumFieldGenerator(const EnumFieldGenerator&) = delete;
EnumFieldGenerator& operator=(const EnumFieldGenerator&) = delete;
virtual void GenerateCodecCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
virtual void GenerateSerializationCode(io::Printer* printer) override;
virtual void GenerateSerializedSizeCode(io::Printer* printer) override;
virtual void GenerateExtensionCode(io::Printer* printer) override;
};
class EnumOneofFieldGenerator : public PrimitiveOneofFieldGenerator {
public:
EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~EnumOneofFieldGenerator();
EnumOneofFieldGenerator(const EnumOneofFieldGenerator&) = delete;
EnumOneofFieldGenerator& operator=(const EnumOneofFieldGenerator&) = delete;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
virtual void GenerateSerializationCode(io::Printer* printer) override;
virtual void GenerateSerializedSizeCode(io::Printer* printer) override;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__

View File

@@ -0,0 +1,464 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cmath>
#include <limits>
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
void FieldGeneratorBase::SetCommonFieldVariables(
std::map<std::string, std::string>* variables) {
// Note: this will be valid even though the tag emitted for packed and unpacked versions of
// repeated fields varies by wire format. The wire format is encoded in the bottom 3 bits, which
// never effects the tag size.
int tag_size = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
int part_tag_size = tag_size;
if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) {
part_tag_size /= 2;
}
uint tag = internal::WireFormat::MakeTag(descriptor_);
uint8_t tag_array[5];
io::CodedOutputStream::WriteTagToArray(tag, tag_array);
std::string tag_bytes = StrCat(tag_array[0]);
for (int i = 1; i < part_tag_size; i++) {
tag_bytes += ", " + StrCat(tag_array[i]);
}
(*variables)["tag"] = StrCat(tag);
(*variables)["tag_size"] = StrCat(tag_size);
(*variables)["tag_bytes"] = tag_bytes;
if (descriptor_->type() == FieldDescriptor::Type::TYPE_GROUP) {
tag = internal::WireFormatLite::MakeTag(
descriptor_->number(),
internal::WireFormatLite::WIRETYPE_END_GROUP);
io::CodedOutputStream::WriteTagToArray(tag, tag_array);
tag_bytes = StrCat(tag_array[0]);
for (int i = 1; i < part_tag_size; i++) {
tag_bytes += ", " + StrCat(tag_array[i]);
}
variables_["end_tag"] = StrCat(tag);
variables_["end_tag_bytes"] = tag_bytes;
}
(*variables)["access_level"] = "public";
(*variables)["property_name"] = property_name();
(*variables)["type_name"] = type_name();
(*variables)["extended_type"] = GetClassName(descriptor_->containing_type());
(*variables)["name"] = name();
(*variables)["descriptor_name"] = descriptor_->name();
(*variables)["default_value"] = default_value();
(*variables)["capitalized_type_name"] = capitalized_type_name();
(*variables)["number"] = number();
if (has_default_value() && !SupportsPresenceApi(descriptor_)) {
(*variables)["name_def_message"] =
(*variables)["name"] + "_ = " + (*variables)["default_value"];
} else {
(*variables)["name_def_message"] = (*variables)["name"] + "_";
}
if (SupportsPresenceApi(descriptor_)) {
(*variables)["has_property_check"] = "Has" + (*variables)["property_name"];
(*variables)["other_has_property_check"] = "other.Has" + (*variables)["property_name"];
(*variables)["has_not_property_check"] = "!" + (*variables)["has_property_check"];
(*variables)["other_has_not_property_check"] = "!" + (*variables)["other_has_property_check"];
if (presenceIndex_ != -1) {
std::string hasBitsNumber = StrCat(presenceIndex_ / 32);
std::string hasBitsMask = StrCat(1 << (presenceIndex_ % 32));
(*variables)["has_field_check"] = "(_hasBits" + hasBitsNumber + " & " + hasBitsMask + ") != 0";
(*variables)["set_has_field"] = "_hasBits" + hasBitsNumber + " |= " + hasBitsMask;
(*variables)["clear_has_field"] = "_hasBits" + hasBitsNumber + " &= ~" + hasBitsMask;
}
} else {
(*variables)["has_property_check"] =
(*variables)["property_name"] + " != " + (*variables)["default_value"];
(*variables)["other_has_property_check"] = "other." +
(*variables)["property_name"] + " != " + (*variables)["default_value"];
}
}
void FieldGeneratorBase::SetCommonOneofFieldVariables(
std::map<std::string, std::string>* variables) {
(*variables)["oneof_name"] = oneof_name();
if (SupportsPresenceApi(descriptor_)) {
(*variables)["has_property_check"] = "Has" + property_name();
} else {
(*variables)["has_property_check"] =
oneof_name() + "Case_ == " + oneof_property_name() +
"OneofCase." + oneof_case_name();
}
(*variables)["oneof_case_name"] = oneof_case_name();
(*variables)["oneof_property_name"] = oneof_property_name();
}
FieldGeneratorBase::FieldGeneratorBase(const FieldDescriptor* descriptor,
int presenceIndex, const Options* options)
: SourceGeneratorBase(options),
descriptor_(descriptor),
presenceIndex_(presenceIndex) {
SetCommonFieldVariables(&variables_);
}
FieldGeneratorBase::~FieldGeneratorBase() {
}
void FieldGeneratorBase::GenerateFreezingCode(io::Printer* printer) {
// No-op: only message fields and repeated fields need
// special handling for freezing, so default to not generating any code.
}
void FieldGeneratorBase::GenerateCodecCode(io::Printer* printer) {
// No-op: expect this to be overridden by appropriate types.
// Could fail if we get called here though...
}
void FieldGeneratorBase::GenerateExtensionCode(io::Printer* printer) {
// No-op: only message fields, enum fields, primitives,
// and repeated fields need this default is to not generate any code
}
void FieldGeneratorBase::GenerateParsingCode(io::Printer* printer, bool use_parse_context) {
// for some field types the value of "use_parse_context" doesn't matter,
// so we fallback to the default implementation.
GenerateParsingCode(printer);
}
void FieldGeneratorBase::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
// for some field types the value of "use_write_context" doesn't matter,
// so we fallback to the default implementation.
GenerateSerializationCode(printer);
}
void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
if (descriptor_->options().deprecated()) {
printer->Print("[global::System.ObsoleteAttribute]\n");
} else if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE &&
descriptor_->message_type()->options().deprecated()) {
printer->Print("[global::System.ObsoleteAttribute]\n");
}
}
void FieldGeneratorBase::AddPublicMemberAttributes(io::Printer* printer) {
AddDeprecatedFlag(printer);
WriteGeneratedCodeAttributes(printer);
}
std::string FieldGeneratorBase::oneof_case_name() {
return GetOneofCaseName(descriptor_);
}
std::string FieldGeneratorBase::oneof_property_name() {
return UnderscoresToCamelCase(descriptor_->containing_oneof()->name(), true);
}
std::string FieldGeneratorBase::oneof_name() {
return UnderscoresToCamelCase(descriptor_->containing_oneof()->name(), false);
}
std::string FieldGeneratorBase::property_name() {
return GetPropertyName(descriptor_);
}
std::string FieldGeneratorBase::name() {
return UnderscoresToCamelCase(GetFieldName(descriptor_), false);
}
std::string FieldGeneratorBase::type_name() {
return type_name(descriptor_);
}
std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
return GetClassName(descriptor->enum_type());
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
if (IsWrapperType(descriptor)) {
const FieldDescriptor* wrapped_field =
descriptor->message_type()->field(0);
std::string wrapped_field_type_name = type_name(wrapped_field);
// String and ByteString go to the same type; other wrapped types
// go to the nullable equivalent.
if (wrapped_field->type() == FieldDescriptor::TYPE_STRING ||
wrapped_field->type() == FieldDescriptor::TYPE_BYTES) {
return wrapped_field_type_name;
} else {
return wrapped_field_type_name + "?";
}
}
return GetClassName(descriptor->message_type());
case FieldDescriptor::TYPE_DOUBLE:
return "double";
case FieldDescriptor::TYPE_FLOAT:
return "float";
case FieldDescriptor::TYPE_INT64:
return "long";
case FieldDescriptor::TYPE_UINT64:
return "ulong";
case FieldDescriptor::TYPE_INT32:
return "int";
case FieldDescriptor::TYPE_FIXED64:
return "ulong";
case FieldDescriptor::TYPE_FIXED32:
return "uint";
case FieldDescriptor::TYPE_BOOL:
return "bool";
case FieldDescriptor::TYPE_STRING:
return "string";
case FieldDescriptor::TYPE_BYTES:
return "pb::ByteString";
case FieldDescriptor::TYPE_UINT32:
return "uint";
case FieldDescriptor::TYPE_SFIXED32:
return "int";
case FieldDescriptor::TYPE_SFIXED64:
return "long";
case FieldDescriptor::TYPE_SINT32:
return "int";
case FieldDescriptor::TYPE_SINT64:
return "long";
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return "";
}
}
bool FieldGeneratorBase::has_default_value() {
switch (descriptor_->type()) {
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
return true;
case FieldDescriptor::TYPE_DOUBLE:
return descriptor_->default_value_double() != 0.0;
case FieldDescriptor::TYPE_FLOAT:
return descriptor_->default_value_float() != 0.0;
case FieldDescriptor::TYPE_INT64:
return descriptor_->default_value_int64() != 0L;
case FieldDescriptor::TYPE_UINT64:
return descriptor_->default_value_uint64() != 0L;
case FieldDescriptor::TYPE_INT32:
return descriptor_->default_value_int32() != 0;
case FieldDescriptor::TYPE_FIXED64:
return descriptor_->default_value_uint64() != 0L;
case FieldDescriptor::TYPE_FIXED32:
return descriptor_->default_value_uint32() != 0;
case FieldDescriptor::TYPE_BOOL:
return descriptor_->default_value_bool();
case FieldDescriptor::TYPE_STRING:
return true;
case FieldDescriptor::TYPE_BYTES:
return true;
case FieldDescriptor::TYPE_UINT32:
return descriptor_->default_value_uint32() != 0;
case FieldDescriptor::TYPE_SFIXED32:
return descriptor_->default_value_int32() != 0;
case FieldDescriptor::TYPE_SFIXED64:
return descriptor_->default_value_int64() != 0L;
case FieldDescriptor::TYPE_SINT32:
return descriptor_->default_value_int32() != 0;
case FieldDescriptor::TYPE_SINT64:
return descriptor_->default_value_int64() != 0L;
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return true;
}
}
bool AllPrintableAscii(const std::string& text) {
for(int i = 0; i < text.size(); i++) {
if (text[i] < 0x20 || text[i] > 0x7e) {
return false;
}
}
return true;
}
std::string FieldGeneratorBase::GetStringDefaultValueInternal(const FieldDescriptor* descriptor) {
if (descriptor->default_value_string().empty())
return "\"\"";
else
return "global::System.Text.Encoding.UTF8.GetString(global::System."
"Convert.FromBase64String(\"" +
StringToBase64(descriptor->default_value_string()) + "\"), 0, " + StrCat(descriptor->default_value_string().length()) + ")";
}
std::string FieldGeneratorBase::GetBytesDefaultValueInternal(const FieldDescriptor* descriptor) {
if (descriptor->default_value_string().empty())
return "pb::ByteString.Empty";
else
return "pb::ByteString.FromBase64(\"" + StringToBase64(descriptor->default_value_string()) + "\")";
}
std::string FieldGeneratorBase::default_value() {
return default_value(descriptor_);
}
std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
return GetClassName(descriptor->default_value_enum()->type()) + "." +
GetEnumValueName(descriptor->default_value_enum()->type()->name(), descriptor->default_value_enum()->name());
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
if (IsWrapperType(descriptor)) {
const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0);
return default_value(wrapped_field);
} else {
return "null";
}
case FieldDescriptor::TYPE_DOUBLE: {
double value = descriptor->default_value_double();
if (value == std::numeric_limits<double>::infinity()) {
return "double.PositiveInfinity";
} else if (value == -std::numeric_limits<double>::infinity()) {
return "double.NegativeInfinity";
} else if (std::isnan(value)) {
return "double.NaN";
}
return StrCat(value) + "D";
}
case FieldDescriptor::TYPE_FLOAT: {
float value = descriptor->default_value_float();
if (value == std::numeric_limits<float>::infinity()) {
return "float.PositiveInfinity";
} else if (value == -std::numeric_limits<float>::infinity()) {
return "float.NegativeInfinity";
} else if (std::isnan(value)) {
return "float.NaN";
}
return StrCat(value) + "F";
}
case FieldDescriptor::TYPE_INT64:
return StrCat(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_UINT64:
return StrCat(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_INT32:
return StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_FIXED64:
return StrCat(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_FIXED32:
return StrCat(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_BOOL:
if (descriptor->default_value_bool()) {
return "true";
} else {
return "false";
}
case FieldDescriptor::TYPE_STRING:
return GetStringDefaultValueInternal(descriptor);
case FieldDescriptor::TYPE_BYTES:
return GetBytesDefaultValueInternal(descriptor);
case FieldDescriptor::TYPE_UINT32:
return StrCat(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_SFIXED32:
return StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SFIXED64:
return StrCat(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_SINT32:
return StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SINT64:
return StrCat(descriptor->default_value_int64()) + "L";
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return "";
}
}
std::string FieldGeneratorBase::number() {
return StrCat(descriptor_->number());
}
std::string FieldGeneratorBase::capitalized_type_name() {
switch (descriptor_->type()) {
case FieldDescriptor::TYPE_ENUM:
return "Enum";
case FieldDescriptor::TYPE_MESSAGE:
return "Message";
case FieldDescriptor::TYPE_GROUP:
return "Group";
case FieldDescriptor::TYPE_DOUBLE:
return "Double";
case FieldDescriptor::TYPE_FLOAT:
return "Float";
case FieldDescriptor::TYPE_INT64:
return "Int64";
case FieldDescriptor::TYPE_UINT64:
return "UInt64";
case FieldDescriptor::TYPE_INT32:
return "Int32";
case FieldDescriptor::TYPE_FIXED64:
return "Fixed64";
case FieldDescriptor::TYPE_FIXED32:
return "Fixed32";
case FieldDescriptor::TYPE_BOOL:
return "Bool";
case FieldDescriptor::TYPE_STRING:
return "String";
case FieldDescriptor::TYPE_BYTES:
return "Bytes";
case FieldDescriptor::TYPE_UINT32:
return "UInt32";
case FieldDescriptor::TYPE_SFIXED32:
return "SFixed32";
case FieldDescriptor::TYPE_SFIXED64:
return "SFixed64";
case FieldDescriptor::TYPE_SINT32:
return "SInt32";
case FieldDescriptor::TYPE_SINT64:
return "SInt64";
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return "";
}
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,112 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_BASE_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_BASE_H__
#include <string>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_source_generator_base.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class FieldGeneratorBase : public SourceGeneratorBase {
public:
FieldGeneratorBase(const FieldDescriptor* descriptor,
int presenceIndex,
const Options* options);
~FieldGeneratorBase();
FieldGeneratorBase(const FieldGeneratorBase&) = delete;
FieldGeneratorBase& operator=(const FieldGeneratorBase&) = delete;
virtual void GenerateCloningCode(io::Printer* printer) = 0;
virtual void GenerateFreezingCode(io::Printer* printer);
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateExtensionCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer) = 0;
virtual void GenerateMergingCode(io::Printer* printer) = 0;
virtual void GenerateParsingCode(io::Printer* printer) = 0;
virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context);
virtual void GenerateSerializationCode(io::Printer* printer) = 0;
virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context);
virtual void GenerateSerializedSizeCode(io::Printer* printer) = 0;
virtual void WriteHash(io::Printer* printer) = 0;
virtual void WriteEquals(io::Printer* printer) = 0;
// Currently unused, as we use reflection to generate JSON
virtual void WriteToString(io::Printer* printer) = 0;
protected:
const FieldDescriptor* descriptor_;
const int presenceIndex_;
std::map<std::string, std::string> variables_;
void AddDeprecatedFlag(io::Printer* printer);
void AddNullCheck(io::Printer* printer);
void AddNullCheck(io::Printer* printer, const std::string& name);
void AddPublicMemberAttributes(io::Printer* printer);
void SetCommonOneofFieldVariables(
std::map<std::string, std::string>* variables);
std::string oneof_property_name();
std::string oneof_case_name();
std::string oneof_name();
std::string property_name();
std::string name();
std::string type_name();
std::string type_name(const FieldDescriptor* descriptor);
bool has_default_value();
std::string default_value();
std::string default_value(const FieldDescriptor* descriptor);
std::string number();
std::string capitalized_type_name();
private:
void SetCommonFieldVariables(std::map<std::string, std::string>* variables);
std::string GetStringDefaultValueInternal(const FieldDescriptor* descriptor);
std::string GetBytesDefaultValueInternal(const FieldDescriptor* descriptor);
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_BASE_H__

View File

@@ -0,0 +1,112 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_reflection_class.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
Generator::Generator() {}
Generator::~Generator() {}
uint64_t Generator::GetSupportedFeatures() const {
return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL;
}
void GenerateFile(const FileDescriptor* file, io::Printer* printer,
const Options* options) {
ReflectionClassGenerator reflectionClassGenerator(file, options);
reflectionClassGenerator.Generate(printer);
}
bool Generator::Generate(const FileDescriptor* file,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const {
std::vector<std::pair<std::string, std::string> > options;
ParseGeneratorParameter(parameter, &options);
struct Options cli_options;
for (int i = 0; i < options.size(); i++) {
if (options[i].first == "file_extension") {
cli_options.file_extension = options[i].second;
} else if (options[i].first == "base_namespace") {
cli_options.base_namespace = options[i].second;
cli_options.base_namespace_specified = true;
} else if (options[i].first == "internal_access") {
cli_options.internal_access = true;
} else if (options[i].first == "serializable") {
cli_options.serializable = true;
} else {
*error = "Unknown generator option: " + options[i].first;
return false;
}
}
std::string filename_error = "";
std::string filename = GetOutputFile(file,
cli_options.file_extension,
cli_options.base_namespace_specified,
cli_options.base_namespace,
&filename_error);
if (filename.empty()) {
*error = filename_error;
return false;
}
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(filename));
io::Printer printer(output.get(), '$');
GenerateFile(file, &printer, &cli_options);
return true;
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,70 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Generates C# code for a given .proto file.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
// CodeGenerator implementation which generates a C# source file and
// header. If you create your own protocol compiler binary and you want
// it to support C# output, you can do so by registering an instance of this
// CodeGenerator with the CommandLineInterface in your main() function.
class PROTOC_EXPORT Generator : public CodeGenerator {
public:
Generator();
~Generator();
bool Generate(
const FileDescriptor* file,
const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__

View File

@@ -0,0 +1,81 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2014 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#include <google/protobuf/any.pb.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/printer.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
namespace {
TEST(CSharpEnumValue, PascalCasedPrefixStripping) {
EXPECT_EQ("Bar", GetEnumValueName("Foo", "BAR"));
EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "BAR_BAZ"));
EXPECT_EQ("Bar", GetEnumValueName("Foo", "FOO_BAR"));
EXPECT_EQ("Bar", GetEnumValueName("Foo", "FOO__BAR"));
EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "FOO_BAR_BAZ"));
EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "Foo_BarBaz"));
EXPECT_EQ("Bar", GetEnumValueName("FO_O", "FOO_BAR"));
EXPECT_EQ("Bar", GetEnumValueName("FOO", "F_O_O_BAR"));
EXPECT_EQ("Bar", GetEnumValueName("Foo", "BAR"));
EXPECT_EQ("BarBaz", GetEnumValueName("Foo", "BAR_BAZ"));
EXPECT_EQ("Foo", GetEnumValueName("Foo", "FOO"));
EXPECT_EQ("Foo", GetEnumValueName("Foo", "FOO___"));
// Identifiers can't start with digits
EXPECT_EQ("_2Bar", GetEnumValueName("Foo", "FOO_2_BAR"));
EXPECT_EQ("_2", GetEnumValueName("Foo", "FOO___2"));
}
TEST(DescriptorProtoHelpers, IsDescriptorProto) {
EXPECT_TRUE(IsDescriptorProto(DescriptorProto::descriptor()->file()));
EXPECT_FALSE(IsDescriptorProto(google::protobuf::Any::descriptor()->file()));
}
TEST(DescriptorProtoHelpers, IsDescriptorOptionMessage) {
EXPECT_TRUE(IsDescriptorOptionMessage(FileOptions::descriptor()));
EXPECT_FALSE(IsDescriptorOptionMessage(google::protobuf::Any::descriptor()));
EXPECT_FALSE(IsDescriptorOptionMessage(DescriptorProto::descriptor()));
}
} // namespace
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,599 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <algorithm>
#include <limits>
#include <vector>
#include <sstream>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_enum_field.h>
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
#include <google/protobuf/compiler/csharp/csharp_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h>
#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
CSharpType GetCSharpType(FieldDescriptor::Type type) {
switch (type) {
case FieldDescriptor::TYPE_INT32:
return CSHARPTYPE_INT32;
case FieldDescriptor::TYPE_INT64:
return CSHARPTYPE_INT64;
case FieldDescriptor::TYPE_UINT32:
return CSHARPTYPE_UINT32;
case FieldDescriptor::TYPE_UINT64:
return CSHARPTYPE_UINT32;
case FieldDescriptor::TYPE_SINT32:
return CSHARPTYPE_INT32;
case FieldDescriptor::TYPE_SINT64:
return CSHARPTYPE_INT64;
case FieldDescriptor::TYPE_FIXED32:
return CSHARPTYPE_UINT32;
case FieldDescriptor::TYPE_FIXED64:
return CSHARPTYPE_UINT64;
case FieldDescriptor::TYPE_SFIXED32:
return CSHARPTYPE_INT32;
case FieldDescriptor::TYPE_SFIXED64:
return CSHARPTYPE_INT64;
case FieldDescriptor::TYPE_FLOAT:
return CSHARPTYPE_FLOAT;
case FieldDescriptor::TYPE_DOUBLE:
return CSHARPTYPE_DOUBLE;
case FieldDescriptor::TYPE_BOOL:
return CSHARPTYPE_BOOL;
case FieldDescriptor::TYPE_ENUM:
return CSHARPTYPE_ENUM;
case FieldDescriptor::TYPE_STRING:
return CSHARPTYPE_STRING;
case FieldDescriptor::TYPE_BYTES:
return CSHARPTYPE_BYTESTRING;
case FieldDescriptor::TYPE_GROUP:
return CSHARPTYPE_MESSAGE;
case FieldDescriptor::TYPE_MESSAGE:
return CSHARPTYPE_MESSAGE;
// No default because we want the compiler to complain if any new
// types are added.
}
GOOGLE_LOG(FATAL)<< "Can't get here.";
return (CSharpType) -1;
}
std::string StripDotProto(const std::string& proto_file) {
int lastindex = proto_file.find_last_of(".");
return proto_file.substr(0, lastindex);
}
std::string GetFileNamespace(const FileDescriptor* descriptor) {
if (descriptor->options().has_csharp_namespace()) {
return descriptor->options().csharp_namespace();
}
return UnderscoresToCamelCase(descriptor->package(), true, true);
}
// Returns the Pascal-cased last part of the proto file. For example,
// input of "google/protobuf/foo_bar.proto" would result in "FooBar".
std::string GetFileNameBase(const FileDescriptor* descriptor) {
std::string proto_file = descriptor->name();
int lastslash = proto_file.find_last_of("/");
std::string base = proto_file.substr(lastslash + 1);
return UnderscoresToPascalCase(StripDotProto(base));
}
std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor) {
// TODO: Detect collisions with existing messages,
// and append an underscore if necessary.
return GetFileNameBase(descriptor) + "Reflection";
}
std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor) {
// TODO: Detect collisions with existing messages,
// and append an underscore if necessary.
return GetFileNameBase(descriptor) + "Extensions";
}
// TODO(jtattermusch): can we reuse a utility function?
std::string UnderscoresToCamelCase(const std::string& input,
bool cap_next_letter,
bool preserve_period) {
std::string result;
// Note: I distrust ctype.h due to locales.
for (int i = 0; i < input.size(); i++) {
if ('a' <= input[i] && input[i] <= 'z') {
if (cap_next_letter) {
result += input[i] + ('A' - 'a');
} else {
result += input[i];
}
cap_next_letter = false;
} else if ('A' <= input[i] && input[i] <= 'Z') {
if (i == 0 && !cap_next_letter) {
// Force first letter to lower-case unless explicitly told to
// capitalize it.
result += input[i] + ('a' - 'A');
} else {
// Capital letters after the first are left as-is.
result += input[i];
}
cap_next_letter = false;
} else if ('0' <= input[i] && input[i] <= '9') {
result += input[i];
cap_next_letter = true;
} else {
cap_next_letter = true;
if (input[i] == '.' && preserve_period) {
result += '.';
}
}
}
// Add a trailing "_" if the name should be altered.
if (input.size() > 0 && input[input.size() - 1] == '#') {
result += '_';
}
return result;
}
std::string UnderscoresToPascalCase(const std::string& input) {
return UnderscoresToCamelCase(input, true);
}
// Convert a string which is expected to be SHOUTY_CASE (but may not be *precisely* shouty)
// into a PascalCase string. Precise rules implemented:
// Previous input character Current character Case
// Any Non-alphanumeric Skipped
// None - first char of input Alphanumeric Upper
// Non-letter (e.g. _ or 1) Alphanumeric Upper
// Numeric Alphanumeric Upper
// Lower letter Alphanumeric Same as current
// Upper letter Alphanumeric Lower
std::string ShoutyToPascalCase(const std::string& input) {
std::string result;
// Simple way of implementing "always start with upper"
char previous = '_';
for (int i = 0; i < input.size(); i++) {
char current = input[i];
if (!ascii_isalnum(current)) {
previous = current;
continue;
}
if (!ascii_isalnum(previous)) {
result += ascii_toupper(current);
} else if (ascii_isdigit(previous)) {
result += ascii_toupper(current);
} else if (ascii_islower(previous)) {
result += current;
} else {
result += ascii_tolower(current);
}
previous = current;
}
return result;
}
// Attempt to remove a prefix from a value, ignoring casing and skipping underscores.
// (foo, foo_bar) => bar - underscore after prefix is skipped
// (FOO, foo_bar) => bar - casing is ignored
// (foo_bar, foobarbaz) => baz - underscore in prefix is ignored
// (foobar, foo_barbaz) => baz - underscore in value is ignored
// (foo, bar) => bar - prefix isn't matched; return original value
std::string TryRemovePrefix(const std::string& prefix, const std::string& value) {
// First normalize to a lower-case no-underscores prefix to match against
std::string prefix_to_match = "";
for (size_t i = 0; i < prefix.size(); i++) {
if (prefix[i] != '_') {
prefix_to_match += ascii_tolower(prefix[i]);
}
}
// This keeps track of how much of value we've consumed
size_t prefix_index, value_index;
for (prefix_index = 0, value_index = 0;
prefix_index < prefix_to_match.size() && value_index < value.size();
value_index++) {
// Skip over underscores in the value
if (value[value_index] == '_') {
continue;
}
if (ascii_tolower(value[value_index]) != prefix_to_match[prefix_index++]) {
// Failed to match the prefix - bail out early.
return value;
}
}
// If we didn't finish looking through the prefix, we can't strip it.
if (prefix_index < prefix_to_match.size()) {
return value;
}
// Step over any underscores after the prefix
while (value_index < value.size() && value[value_index] == '_') {
value_index++;
}
// If there's nothing left (e.g. it was a prefix with only underscores afterwards), don't strip.
if (value_index == value.size()) {
return value;
}
return value.substr(value_index);
}
// Format the enum value name in a pleasant way for C#:
// - Strip the enum name as a prefix if possible
// - Convert to PascalCase.
// For example, an enum called Color with a value of COLOR_BLUE should
// result in an enum value in C# called just Blue
std::string GetEnumValueName(const std::string& enum_name, const std::string& enum_value_name) {
std::string stripped = TryRemovePrefix(enum_name, enum_value_name);
std::string result = ShoutyToPascalCase(stripped);
// Just in case we have an enum name of FOO and a value of FOO_2... make sure the returned
// string is a valid identifier.
if (ascii_isdigit(result[0])) {
result = "_" + result;
}
return result;
}
uint GetGroupEndTag(const Descriptor* descriptor) {
const Descriptor* containing_type = descriptor->containing_type();
if (containing_type != NULL) {
const FieldDescriptor* field;
for (int i = 0; i < containing_type->field_count(); i++) {
field = containing_type->field(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(
field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
for (int i = 0; i < containing_type->extension_count(); i++) {
field = containing_type->extension(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(
field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
} else {
const FileDescriptor* containing_file = descriptor->file();
if (containing_file != NULL) {
const FieldDescriptor* field;
for (int i = 0; i < containing_file->extension_count(); i++) {
field = containing_file->extension(i);
if (field->type() == FieldDescriptor::Type::TYPE_GROUP &&
field->message_type() == descriptor) {
return internal::WireFormatLite::MakeTag(
field->number(), internal::WireFormatLite::WIRETYPE_END_GROUP);
}
}
}
}
return 0;
}
std::string ToCSharpName(const std::string& name, const FileDescriptor* file) {
std::string result = GetFileNamespace(file);
if (!result.empty()) {
result += '.';
}
std::string classname;
if (file->package().empty()) {
classname = name;
} else {
// Strip the proto package from full_name since we've replaced it with
// the C# namespace.
classname = name.substr(file->package().size() + 1);
}
result += StringReplace(classname, ".", ".Types.", true);
return "global::" + result;
}
std::string GetReflectionClassName(const FileDescriptor* descriptor) {
std::string result = GetFileNamespace(descriptor);
if (!result.empty()) {
result += '.';
}
result += GetReflectionClassUnqualifiedName(descriptor);
return "global::" + result;
}
std::string GetFullExtensionName(const FieldDescriptor* descriptor) {
if (descriptor->extension_scope()) {
return GetClassName(descriptor->extension_scope()) + ".Extensions." + GetPropertyName(descriptor);
}
else {
return GetExtensionClassUnqualifiedName(descriptor->file()) + "." + GetPropertyName(descriptor);
}
}
std::string GetClassName(const Descriptor* descriptor) {
return ToCSharpName(descriptor->full_name(), descriptor->file());
}
std::string GetClassName(const EnumDescriptor* descriptor) {
return ToCSharpName(descriptor->full_name(), descriptor->file());
}
// Groups are hacky: The name of the field is just the lower-cased name
// of the group type. In C#, though, we would like to retain the original
// capitalization of the type name.
std::string GetFieldName(const FieldDescriptor* descriptor) {
if (descriptor->type() == FieldDescriptor::TYPE_GROUP) {
return descriptor->message_type()->name();
} else {
return descriptor->name();
}
}
std::string GetFieldConstantName(const FieldDescriptor* field) {
return GetPropertyName(field) + "FieldNumber";
}
std::string GetPropertyName(const FieldDescriptor* descriptor) {
// TODO(jtattermusch): consider introducing csharp_property_name field option
std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
// Avoid either our own type name or reserved names. Note that not all names
// are reserved - a field called to_string, write_to etc would still cause a problem.
// There are various ways of ending up with naming collisions, but we try to avoid obvious
// ones.
if (property_name == descriptor->containing_type()->name()
|| property_name == "Types"
|| property_name == "Descriptor") {
property_name += "_";
}
return property_name;
}
std::string GetOneofCaseName(const FieldDescriptor* descriptor) {
// The name in a oneof case enum is the same as for the property, but as we always have a "None"
// value as well, we need to reserve that by appending an underscore.
std::string property_name = GetPropertyName(descriptor);
return property_name == "None" ? "None_" : property_name;
}
std::string GetOutputFile(const FileDescriptor* descriptor,
const std::string file_extension,
const bool generate_directories,
const std::string base_namespace,
std::string* error) {
std::string relative_filename = GetFileNameBase(descriptor) + file_extension;
if (!generate_directories) {
return relative_filename;
}
std::string ns = GetFileNamespace(descriptor);
std::string namespace_suffix = ns;
if (!base_namespace.empty()) {
// Check that the base_namespace is either equal to or a leading part of
// the file namespace. This isn't just a simple prefix; "Foo.B" shouldn't
// be regarded as a prefix of "Foo.Bar". The simplest option is to add "."
// to both.
std::string extended_ns = ns + ".";
if (extended_ns.find(base_namespace + ".") != 0) {
*error = "Namespace " + ns + " is not a prefix namespace of base namespace " + base_namespace;
return ""; // This will be ignored, because we've set an error.
}
namespace_suffix = ns.substr(base_namespace.length());
if (namespace_suffix.find(".") == 0) {
namespace_suffix = namespace_suffix.substr(1);
}
}
std::string namespace_dir = StringReplace(namespace_suffix, ".", "/", true);
if (!namespace_dir.empty()) {
namespace_dir += "/";
}
return namespace_dir + relative_filename;
}
// TODO: c&p from Java protoc plugin
// For encodings with fixed sizes, returns that size in bytes. Otherwise
// returns -1.
int GetFixedSize(FieldDescriptor::Type type) {
switch (type) {
case FieldDescriptor::TYPE_INT32 : return -1;
case FieldDescriptor::TYPE_INT64 : return -1;
case FieldDescriptor::TYPE_UINT32 : return -1;
case FieldDescriptor::TYPE_UINT64 : return -1;
case FieldDescriptor::TYPE_SINT32 : return -1;
case FieldDescriptor::TYPE_SINT64 : return -1;
case FieldDescriptor::TYPE_FIXED32 : return internal::WireFormatLite::kFixed32Size;
case FieldDescriptor::TYPE_FIXED64 : return internal::WireFormatLite::kFixed64Size;
case FieldDescriptor::TYPE_SFIXED32: return internal::WireFormatLite::kSFixed32Size;
case FieldDescriptor::TYPE_SFIXED64: return internal::WireFormatLite::kSFixed64Size;
case FieldDescriptor::TYPE_FLOAT : return internal::WireFormatLite::kFloatSize;
case FieldDescriptor::TYPE_DOUBLE : return internal::WireFormatLite::kDoubleSize;
case FieldDescriptor::TYPE_BOOL : return internal::WireFormatLite::kBoolSize;
case FieldDescriptor::TYPE_ENUM : return -1;
case FieldDescriptor::TYPE_STRING : return -1;
case FieldDescriptor::TYPE_BYTES : return -1;
case FieldDescriptor::TYPE_GROUP : return -1;
case FieldDescriptor::TYPE_MESSAGE : return -1;
// No default because we want the compiler to complain if any new
// types are added.
}
GOOGLE_LOG(FATAL) << "Can't get here.";
return -1;
}
static const char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
std::string StringToBase64(const std::string& input) {
std::string result;
size_t remaining = input.size();
const unsigned char *src = (const unsigned char*) input.c_str();
while (remaining > 2) {
result += base64_chars[src[0] >> 2];
result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
result += base64_chars[((src[1] & 0xf) << 2) | (src[2] >> 6)];
result += base64_chars[src[2] & 0x3f];
remaining -= 3;
src += 3;
}
switch (remaining) {
case 2:
result += base64_chars[src[0] >> 2];
result += base64_chars[((src[0] & 0x3) << 4) | (src[1] >> 4)];
result += base64_chars[(src[1] & 0xf) << 2];
result += '=';
src += 2;
break;
case 1:
result += base64_chars[src[0] >> 2];
result += base64_chars[((src[0] & 0x3) << 4)];
result += '=';
result += '=';
src += 1;
break;
}
return result;
}
std::string FileDescriptorToBase64(const FileDescriptor* descriptor) {
std::string fdp_bytes;
FileDescriptorProto fdp;
descriptor->CopyTo(&fdp);
fdp.SerializeToString(&fdp_bytes);
return StringToBase64(fdp_bytes);
}
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options* options) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_MESSAGE:
if (descriptor->is_repeated()) {
if (descriptor->is_map()) {
return new MapFieldGenerator(descriptor, presenceIndex, options);
} else {
return new RepeatedMessageFieldGenerator(descriptor, presenceIndex, options);
}
} else {
if (IsWrapperType(descriptor)) {
if (descriptor->real_containing_oneof()) {
return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new WrapperFieldGenerator(descriptor, presenceIndex, options);
}
} else {
if (descriptor->real_containing_oneof()) {
return new MessageOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new MessageFieldGenerator(descriptor, presenceIndex, options);
}
}
}
case FieldDescriptor::TYPE_ENUM:
if (descriptor->is_repeated()) {
return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options);
} else {
if (descriptor->real_containing_oneof()) {
return new EnumOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new EnumFieldGenerator(descriptor, presenceIndex, options);
}
}
default:
if (descriptor->is_repeated()) {
return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options);
} else {
if (descriptor->real_containing_oneof()) {
return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options);
} else {
return new PrimitiveFieldGenerator(descriptor, presenceIndex, options);
}
}
}
}
bool IsNullable(const FieldDescriptor* descriptor) {
if (descriptor->is_repeated()) {
return true;
}
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT:
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_BOOL:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
return false;
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES:
return true;
default:
GOOGLE_LOG(FATAL) << "Unknown field type.";
return true;
}
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,198 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__
#include <string>
#include <google/protobuf/port.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
struct Options;
class FieldGeneratorBase;
// TODO: start using this enum.
enum CSharpType {
CSHARPTYPE_INT32 = 1,
CSHARPTYPE_INT64 = 2,
CSHARPTYPE_UINT32 = 3,
CSHARPTYPE_UINT64 = 4,
CSHARPTYPE_FLOAT = 5,
CSHARPTYPE_DOUBLE = 6,
CSHARPTYPE_BOOL = 7,
CSHARPTYPE_STRING = 8,
CSHARPTYPE_BYTESTRING = 9,
CSHARPTYPE_MESSAGE = 10,
CSHARPTYPE_ENUM = 11,
MAX_CSHARPTYPE = 11
};
// Converts field type to corresponding C# type.
CSharpType GetCSharpType(FieldDescriptor::Type type);
std::string StripDotProto(const std::string& proto_file);
// Gets unqualified name of the reflection class
std::string GetReflectionClassUnqualifiedName(const FileDescriptor* descriptor);
// Gets unqualified name of the extension class
std::string GetExtensionClassUnqualifiedName(const FileDescriptor* descriptor);
std::string GetClassName(const EnumDescriptor* descriptor);
std::string GetFieldName(const FieldDescriptor* descriptor);
std::string GetFieldConstantName(const FieldDescriptor* field);
std::string GetPropertyName(const FieldDescriptor* descriptor);
std::string GetOneofCaseName(const FieldDescriptor* descriptor);
int GetFixedSize(FieldDescriptor::Type type);
std::string UnderscoresToCamelCase(const std::string& input,
bool cap_next_letter,
bool preserve_period);
inline std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter) {
return UnderscoresToCamelCase(input, cap_next_letter, false);
}
std::string UnderscoresToPascalCase(const std::string& input);
// Note that we wouldn't normally want to export this (we're not expecting
// it to be used outside libprotoc itself) but this exposes it for testing.
std::string PROTOC_EXPORT GetEnumValueName(const std::string& enum_name,
const std::string& enum_value_name);
// TODO(jtattermusch): perhaps we could move this to strutil
std::string StringToBase64(const std::string& input);
std::string FileDescriptorToBase64(const FileDescriptor* descriptor);
FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options* options);
std::string GetFullExtensionName(const FieldDescriptor* descriptor);
bool IsNullable(const FieldDescriptor* descriptor);
// Determines whether the given message is a map entry message,
// i.e. one implicitly created by protoc due to a map<key, value> field.
inline bool IsMapEntryMessage(const Descriptor* descriptor) {
return descriptor->options().map_entry();
}
// Checks if this descriptor is for a group and gets its end tag or 0 if it's not a group
uint GetGroupEndTag(const Descriptor* descriptor);
// Determines whether we're generating code for the proto representation of
// descriptors etc, for use in the runtime. This is the only type which is
// allowed to use proto2 syntax, and it generates internal classes.
inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
return descriptor->name() == "google/protobuf/descriptor.proto" ||
descriptor->name() == "net/proto2/proto/descriptor.proto";
}
// Determines whether the given message is an options message within descriptor.proto.
inline bool IsDescriptorOptionMessage(const Descriptor* descriptor) {
if (!IsDescriptorProto(descriptor->file())) {
return false;
}
const std::string name = descriptor->name();
return name == "FileOptions" ||
name == "MessageOptions" ||
name == "FieldOptions" ||
name == "OneofOptions" ||
name == "EnumOptions" ||
name == "EnumValueOptions" ||
name == "ServiceOptions" ||
name == "MethodOptions";
}
inline bool IsWrapperType(const FieldDescriptor* descriptor) {
return descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
}
inline bool IsProto2(const FileDescriptor* descriptor) {
return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2;
}
inline bool SupportsPresenceApi(const FieldDescriptor* descriptor) {
// Unlike most languages, we don't generate Has/Clear members for message
// types, because they can always be set to null in C#. They're not really
// needed for oneof fields in proto2 either, as everything can be done via
// oneof case, but we follow the convention from other languages. Proto3
// oneof fields never have Has/Clear members - but will also never have
// the explicit optional keyword either.
//
// None of the built-in helpers (descriptor->has_presence() etc) describe
// quite the behavior we want, so the rules are explicit below.
if (descriptor->is_repeated() ||
descriptor->type() == FieldDescriptor::TYPE_MESSAGE) {
return false;
}
// has_optional_keyword() has more complex rules for proto2, but that
// doesn't matter given the first part of this condition.
return IsProto2(descriptor->file()) || descriptor->has_optional_keyword();
}
inline bool RequiresPresenceBit(const FieldDescriptor* descriptor) {
return SupportsPresenceApi(descriptor) &&
!IsNullable(descriptor) &&
!descriptor->is_extension() &&
!descriptor->real_containing_oneof();
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__

View File

@@ -0,0 +1,152 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options* options)
: FieldGeneratorBase(descriptor, presenceIndex, options) {
}
MapFieldGenerator::~MapFieldGenerator() {
}
void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
const FieldDescriptor* key_descriptor =
descriptor_->message_type()->map_key();
const FieldDescriptor* value_descriptor =
descriptor_->message_type()->map_value();
variables_["key_type_name"] = type_name(key_descriptor);
variables_["value_type_name"] = type_name(value_descriptor);
std::unique_ptr<FieldGeneratorBase> key_generator(
CreateFieldGenerator(key_descriptor, 1, this->options()));
std::unique_ptr<FieldGeneratorBase> value_generator(
CreateFieldGenerator(value_descriptor, 2, this->options()));
printer->Print(
variables_,
"private static readonly pbc::MapField<$key_type_name$, $value_type_name$>.Codec _map_$name$_codec\n"
" = new pbc::MapField<$key_type_name$, $value_type_name$>.Codec(");
key_generator->GenerateCodecCode(printer);
printer->Print(", ");
value_generator->GenerateCodecCode(printer);
printer->Print(
variables_,
", $tag$);\n"
"private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\n");
WritePropertyDocComment(printer, descriptor_);
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
" get { return $name$_; }\n"
"}\n");
}
void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
"$name$_.Add(other.$name$_);\n");
}
void MapFieldGenerator::GenerateParsingCode(io::Printer* printer) {
GenerateParsingCode(printer, true);
}
void MapFieldGenerator::GenerateParsingCode(io::Printer* printer, bool use_parse_context) {
printer->Print(
variables_,
use_parse_context
? "$name$_.AddEntriesFrom(ref input, _map_$name$_codec);\n"
: "$name$_.AddEntriesFrom(input, _map_$name$_codec);\n");
}
void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
GenerateSerializationCode(printer, true);
}
void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer, bool use_write_context) {
printer->Print(
variables_,
use_write_context
? "$name$_.WriteTo(ref output, _map_$name$_codec);\n"
: "$name$_.WriteTo(output, _map_$name$_codec);\n");
}
void MapFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"size += $name$_.CalculateSize(_map_$name$_codec);\n");
}
void MapFieldGenerator::WriteHash(io::Printer* printer) {
printer->Print(
variables_,
"hash ^= $property_name$.GetHashCode();\n");
}
void MapFieldGenerator::WriteEquals(io::Printer* printer) {
printer->Print(
variables_,
"if (!$property_name$.Equals(other.$property_name$)) return false;\n");
}
void MapFieldGenerator::WriteToString(io::Printer* printer) {
// TODO: If we ever actually use ToString, we'll need to impleme this...
}
void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = other.$name$_.Clone();\n");
}
void MapFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,73 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class MapFieldGenerator : public FieldGeneratorBase {
public:
MapFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options* options);
~MapFieldGenerator();
MapFieldGenerator(const MapFieldGenerator&) = delete;
MapFieldGenerator& operator=(const MapFieldGenerator&) = delete;
virtual void GenerateCloningCode(io::Printer* printer) override;
virtual void GenerateFreezingCode(io::Printer* printer) override;
virtual void GenerateMembers(io::Printer* printer) override;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer, bool use_parse_context) override;
virtual void GenerateSerializationCode(io::Printer* printer) override;
virtual void GenerateSerializationCode(io::Printer* printer, bool use_write_context) override;
virtual void GenerateSerializedSizeCode(io::Printer* printer) override;
virtual void WriteHash(io::Printer* printer) override;
virtual void WriteEquals(io::Printer* printer) override;
virtual void WriteToString(io::Printer* printer) override;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__

View File

@@ -0,0 +1,779 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <algorithm>
#include <map>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_enum.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_message.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
bool CompareFieldNumbers(const FieldDescriptor* d1, const FieldDescriptor* d2) {
return d1->number() < d2->number();
}
MessageGenerator::MessageGenerator(const Descriptor* descriptor,
const Options* options)
: SourceGeneratorBase(options),
descriptor_(descriptor),
has_bit_field_count_(0),
end_tag_(GetGroupEndTag(descriptor)),
has_extension_ranges_(descriptor->extension_range_count() > 0) {
// fields by number
for (int i = 0; i < descriptor_->field_count(); i++) {
fields_by_number_.push_back(descriptor_->field(i));
}
std::sort(fields_by_number_.begin(), fields_by_number_.end(),
CompareFieldNumbers);
int presence_bit_count = 0;
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (RequiresPresenceBit(field)) {
presence_bit_count++;
if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0) {
has_bit_field_count_++;
}
}
}
}
MessageGenerator::~MessageGenerator() {
}
std::string MessageGenerator::class_name() {
return descriptor_->name();
}
std::string MessageGenerator::full_class_name() {
return GetClassName(descriptor_);
}
const std::vector<const FieldDescriptor*>& MessageGenerator::fields_by_number() {
return fields_by_number_;
}
void MessageGenerator::AddDeprecatedFlag(io::Printer* printer) {
if (descriptor_->options().deprecated()) {
printer->Print("[global::System.ObsoleteAttribute]\n");
}
}
void MessageGenerator::AddSerializableAttribute(io::Printer* printer) {
if (this->options()->serializable) {
printer->Print("[global::System.SerializableAttribute]\n");
}
}
void MessageGenerator::Generate(io::Printer* printer) {
std::map<std::string, std::string> vars;
vars["class_name"] = class_name();
vars["access_level"] = class_access_level();
WriteMessageDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
AddSerializableAttribute(printer);
printer->Print(
vars,
"$access_level$ sealed partial class $class_name$ : ");
if (has_extension_ranges_) {
printer->Print(vars, "pb::IExtendableMessage<$class_name$>\n");
}
else {
printer->Print(vars, "pb::IMessage<$class_name$>\n");
}
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
printer->Print(" , pb::IBufferMessage\n");
printer->Print("#endif\n");
printer->Print("{\n");
printer->Indent();
// All static fields and properties
printer->Print(
vars,
"private static readonly pb::MessageParser<$class_name$> _parser = new pb::MessageParser<$class_name$>(() => new $class_name$());\n");
printer->Print(
"private pb::UnknownFieldSet _unknownFields;\n");
if (has_extension_ranges_) {
if (IsDescriptorProto(descriptor_->file())) {
printer->Print(vars, "internal pb::ExtensionSet<$class_name$> _extensions;\n"); // CustomOptions compatibility
} else {
printer->Print(vars, "private pb::ExtensionSet<$class_name$> _extensions;\n");
}
// a read-only property for fast
// retrieval of the set in IsInitialized
printer->Print(vars,
"private pb::ExtensionSet<$class_name$> _Extensions { get { "
"return _extensions; } }\n");
}
for (int i = 0; i < has_bit_field_count_; i++) {
// don't use arrays since all arrays are heap allocated, saving allocations
// use ints instead of bytes since bytes lack bitwise operators, saving casts
printer->Print("private int _hasBits$i$;\n", "i", StrCat(i));
}
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public static pb::MessageParser<$class_name$> Parser { get { return _parser; } }\n\n");
// Access the message descriptor via the relevant file descriptor or containing message descriptor.
if (!descriptor_->containing_type()) {
vars["descriptor_accessor"] = GetReflectionClassName(descriptor_->file())
+ ".Descriptor.MessageTypes[" + StrCat(descriptor_->index()) + "]";
} else {
vars["descriptor_accessor"] = GetClassName(descriptor_->containing_type())
+ ".Descriptor.NestedTypes[" + StrCat(descriptor_->index()) + "]";
}
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public static pbr::MessageDescriptor Descriptor {\n"
" get { return $descriptor_accessor$; }\n"
"}\n"
"\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"pbr::MessageDescriptor pb::IMessage.Descriptor {\n"
" get { return Descriptor; }\n"
"}\n"
"\n");
// Parameterless constructor and partial OnConstruction method.
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public $class_name$() {\n"
" OnConstruction();\n"
"}\n\n"
"partial void OnConstruction();\n\n");
GenerateCloningCode(printer);
GenerateFreezingCode(printer);
// Fields/properties
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* fieldDescriptor = descriptor_->field(i);
// Rats: we lose the debug comment here :(
printer->Print(
"/// <summary>Field number for the \"$field_name$\" field.</summary>\n"
"public const int $field_constant_name$ = $index$;\n",
"field_name", fieldDescriptor->name(),
"field_constant_name", GetFieldConstantName(fieldDescriptor),
"index", StrCat(fieldDescriptor->number()));
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(fieldDescriptor));
generator->GenerateMembers(printer);
printer->Print("\n");
}
// oneof properties (for real oneofs, which come before synthetic ones)
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
vars["original_name"] = oneof->name();
printer->Print(
vars,
"private object $name$_;\n"
"/// <summary>Enum of possible cases for the \"$original_name$\" oneof.</summary>\n"
"public enum $property_name$OneofCase {\n");
printer->Indent();
printer->Print("None = 0,\n");
for (int j = 0; j < oneof->field_count(); j++) {
const FieldDescriptor* field = oneof->field(j);
printer->Print("$oneof_case_name$ = $index$,\n",
"oneof_case_name", GetOneofCaseName(field),
"index", StrCat(field->number()));
}
printer->Outdent();
printer->Print("}\n");
// TODO: Should we put the oneof .proto comments here?
// It's unclear exactly where they should go.
printer->Print(
vars,
"private $property_name$OneofCase $name$Case_ = $property_name$OneofCase.None;\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public $property_name$OneofCase $property_name$Case {\n"
" get { return $name$Case_; }\n"
"}\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public void Clear$property_name$() {\n"
" $name$Case_ = $property_name$OneofCase.None;\n"
" $name$_ = null;\n"
"}\n\n");
}
// Standard methods
GenerateFrameworkMethods(printer);
GenerateMessageSerializationMethods(printer);
GenerateMergingMethods(printer);
if (has_extension_ranges_) {
printer->Print(
vars,
"public TValue GetExtension<TValue>(pb::Extension<$class_name$, "
"TValue> extension) {\n"
" return pb::ExtensionSet.Get(ref _extensions, extension);\n"
"}\n"
"public pbc::RepeatedField<TValue> "
"GetExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
"extension) {\n"
" return pb::ExtensionSet.Get(ref _extensions, extension);\n"
"}\n"
"public pbc::RepeatedField<TValue> "
"GetOrInitializeExtension<TValue>(pb::RepeatedExtension<$class_name$, "
"TValue> extension) {\n"
" return pb::ExtensionSet.GetOrInitialize(ref _extensions, "
"extension);\n"
"}\n"
"public void SetExtension<TValue>(pb::Extension<$class_name$, TValue> "
"extension, TValue value) {\n"
" pb::ExtensionSet.Set(ref _extensions, extension, value);\n"
"}\n"
"public bool HasExtension<TValue>(pb::Extension<$class_name$, TValue> "
"extension) {\n"
" return pb::ExtensionSet.Has(ref _extensions, extension);\n"
"}\n"
"public void ClearExtension<TValue>(pb::Extension<$class_name$, "
"TValue> extension) {\n"
" pb::ExtensionSet.Clear(ref _extensions, extension);\n"
"}\n"
"public void "
"ClearExtension<TValue>(pb::RepeatedExtension<$class_name$, TValue> "
"extension) {\n"
" pb::ExtensionSet.Clear(ref _extensions, extension);\n"
"}\n\n");
}
// Nested messages and enums
if (HasNestedGeneratedTypes()) {
printer->Print(
vars,
"#region Nested types\n"
"/// <summary>Container for nested types declared in the $class_name$ message type.</summary>\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("public static partial class Types {\n");
printer->Indent();
for (int i = 0; i < descriptor_->enum_type_count(); i++) {
EnumGenerator enumGenerator(descriptor_->enum_type(i), this->options());
enumGenerator.Generate(printer);
}
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// Don't generate nested types for maps...
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
MessageGenerator messageGenerator(
descriptor_->nested_type(i), this->options());
messageGenerator.Generate(printer);
}
}
printer->Outdent();
printer->Print("}\n"
"#endregion\n"
"\n");
}
if (descriptor_->extension_count() > 0) {
printer->Print(
vars,
"#region Extensions\n"
"/// <summary>Container for extensions for other messages declared in the $class_name$ message type.</summary>\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("public static partial class Extensions {\n");
printer->Indent();
for (int i = 0; i < descriptor_->extension_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->extension(i)));
generator->GenerateExtensionCode(printer);
}
printer->Outdent();
printer->Print(
"}\n"
"#endregion\n"
"\n");
}
printer->Outdent();
printer->Print("}\n");
printer->Print("\n");
}
// Helper to work out whether we need to generate a class to hold nested types/enums.
// Only tricky because we don't want to generate map entry types.
bool MessageGenerator::HasNestedGeneratedTypes()
{
if (descriptor_->enum_type_count() > 0) {
return true;
}
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
if (!IsMapEntryMessage(descriptor_->nested_type(i))) {
return true;
}
}
return false;
}
void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
std::map<std::string, std::string> vars;
WriteGeneratedCodeAttributes(printer);
vars["class_name"] = class_name();
printer->Print(
vars,
"public $class_name$($class_name$ other) : this() {\n");
printer->Indent();
for (int i = 0; i < has_bit_field_count_; i++) {
printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", StrCat(i));
}
// Clone non-oneof fields first (treating optional proto3 fields as non-oneof)
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (field->real_containing_oneof()) {
continue;
}
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
generator->GenerateCloningCode(printer);
}
// Clone just the right field for each real oneof
for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) {
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
printer->Print(vars, "switch (other.$property_name$Case) {\n");
printer->Indent();
for (int j = 0; j < oneof->field_count(); j++) {
const FieldDescriptor* field = oneof->field(j);
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
vars["oneof_case_name"] = GetOneofCaseName(field);
printer->Print(
vars,
"case $property_name$OneofCase.$oneof_case_name$:\n");
printer->Indent();
generator->GenerateCloningCode(printer);
printer->Print("break;\n");
printer->Outdent();
}
printer->Outdent();
printer->Print("}\n\n");
}
// Clone unknown fields
printer->Print(
"_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);\n");
if (has_extension_ranges_) {
printer->Print(
"_extensions = pb::ExtensionSet.Clone(other._extensions);\n");
}
printer->Outdent();
printer->Print("}\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public $class_name$ Clone() {\n"
" return new $class_name$(this);\n"
"}\n\n");
}
void MessageGenerator::GenerateFreezingCode(io::Printer* printer) {
}
void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
std::map<std::string, std::string> vars;
vars["class_name"] = class_name();
// Equality
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public override bool Equals(object other) {\n"
" return Equals(other as $class_name$);\n"
"}\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public bool Equals($class_name$ other) {\n"
" if (ReferenceEquals(other, null)) {\n"
" return false;\n"
" }\n"
" if (ReferenceEquals(other, this)) {\n"
" return true;\n"
" }\n");
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->WriteEquals(printer);
}
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
printer->Print("if ($property_name$Case != other.$property_name$Case) return false;\n",
"property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true));
}
if (has_extension_ranges_) {
printer->Print(
"if (!Equals(_extensions, other._extensions)) {\n"
" return false;\n"
"}\n");
}
printer->Outdent();
printer->Print(
" return Equals(_unknownFields, other._unknownFields);\n"
"}\n\n");
// GetHashCode
// Start with a non-zero value to easily distinguish between null and "empty" messages.
WriteGeneratedCodeAttributes(printer);
printer->Print(
"public override int GetHashCode() {\n"
" int hash = 1;\n");
printer->Indent();
for (int i = 0; i < descriptor_->field_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->WriteHash(printer);
}
for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) {
printer->Print("hash ^= (int) $name$Case_;\n",
"name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false));
}
if (has_extension_ranges_) {
printer->Print(
"if (_extensions != null) {\n"
" hash ^= _extensions.GetHashCode();\n"
"}\n");
}
printer->Print(
"if (_unknownFields != null) {\n"
" hash ^= _unknownFields.GetHashCode();\n"
"}\n"
"return hash;\n");
printer->Outdent();
printer->Print("}\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
"public override string ToString() {\n"
" return pb::JsonFormatter.ToDiagnosticString(this);\n"
"}\n\n");
}
void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer) {
WriteGeneratedCodeAttributes(printer);
printer->Print(
"public void WriteTo(pb::CodedOutputStream output) {\n");
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
printer->Indent();
printer->Print("output.WriteRawMessage(this);\n");
printer->Outdent();
printer->Print("#else\n");
printer->Indent();
GenerateWriteToBody(printer, false);
printer->Outdent();
printer->Print("#endif\n");
printer->Print("}\n\n");
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {\n");
printer->Indent();
GenerateWriteToBody(printer, true);
printer->Outdent();
printer->Print("}\n");
printer->Print("#endif\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print(
"public int CalculateSize() {\n");
printer->Indent();
printer->Print("int size = 0;\n");
for (int i = 0; i < descriptor_->field_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->GenerateSerializedSizeCode(printer);
}
if (has_extension_ranges_) {
printer->Print(
"if (_extensions != null) {\n"
" size += _extensions.CalculateSize();\n"
"}\n");
}
printer->Print(
"if (_unknownFields != null) {\n"
" size += _unknownFields.CalculateSize();\n"
"}\n");
printer->Print("return size;\n");
printer->Outdent();
printer->Print("}\n\n");
}
void MessageGenerator::GenerateWriteToBody(io::Printer* printer, bool use_write_context) {
// Serialize all the fields
for (int i = 0; i < fields_by_number().size(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(fields_by_number()[i]));
generator->GenerateSerializationCode(printer, use_write_context);
}
if (has_extension_ranges_) {
// Serialize extensions
printer->Print(
use_write_context
? "if (_extensions != null) {\n"
" _extensions.WriteTo(ref output);\n"
"}\n"
: "if (_extensions != null) {\n"
" _extensions.WriteTo(output);\n"
"}\n");
}
// Serialize unknown fields
printer->Print(
use_write_context
? "if (_unknownFields != null) {\n"
" _unknownFields.WriteTo(ref output);\n"
"}\n"
: "if (_unknownFields != null) {\n"
" _unknownFields.WriteTo(output);\n"
"}\n");
// TODO(jonskeet): Memoize size of frozen messages?
}
void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
// Note: These are separate from GenerateMessageSerializationMethods()
// because they need to be generated even for messages that are optimized
// for code size.
std::map<std::string, std::string> vars;
vars["class_name"] = class_name();
WriteGeneratedCodeAttributes(printer);
printer->Print(
vars,
"public void MergeFrom($class_name$ other) {\n");
printer->Indent();
printer->Print(
"if (other == null) {\n"
" return;\n"
"}\n");
// Merge non-oneof fields, treating optional proto3 fields as normal fields
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
if (field->real_containing_oneof()) {
continue;
}
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
generator->GenerateMergingCode(printer);
}
// Merge oneof fields (for non-synthetic oneofs)
for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) {
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
vars["name"] = UnderscoresToCamelCase(oneof->name(), false);
vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true);
printer->Print(vars, "switch (other.$property_name$Case) {\n");
printer->Indent();
for (int j = 0; j < oneof->field_count(); j++) {
const FieldDescriptor* field = oneof->field(j);
vars["oneof_case_name"] = GetOneofCaseName(field);
printer->Print(
vars,
"case $property_name$OneofCase.$oneof_case_name$:\n");
printer->Indent();
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
generator->GenerateMergingCode(printer);
printer->Print("break;\n");
printer->Outdent();
}
printer->Outdent();
printer->Print("}\n\n");
}
// Merge extensions
if (has_extension_ranges_) {
printer->Print("pb::ExtensionSet.MergeFrom(ref _extensions, other._extensions);\n");
}
// Merge unknown fields.
printer->Print(
"_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);\n");
printer->Outdent();
printer->Print("}\n\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("public void MergeFrom(pb::CodedInputStream input) {\n");
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
printer->Indent();
printer->Print("input.ReadRawMessage(this);\n");
printer->Outdent();
printer->Print("#else\n");
printer->Indent();
GenerateMainParseLoop(printer, false);
printer->Outdent();
printer->Print("#endif\n");
printer->Print("}\n\n");
printer->Print("#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE\n");
WriteGeneratedCodeAttributes(printer);
printer->Print("void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {\n");
printer->Indent();
GenerateMainParseLoop(printer, true);
printer->Outdent();
printer->Print("}\n"); // method
printer->Print("#endif\n\n");
}
void MessageGenerator::GenerateMainParseLoop(io::Printer* printer, bool use_parse_context) {
std::map<std::string, std::string> vars;
vars["maybe_ref_input"] = use_parse_context ? "ref input" : "input";
printer->Print(
"uint tag;\n"
"while ((tag = input.ReadTag()) != 0) {\n"
" switch(tag) {\n");
printer->Indent();
printer->Indent();
if (end_tag_ != 0) {
printer->Print(
"case $end_tag$:\n"
" return;\n",
"end_tag", StrCat(end_tag_));
}
if (has_extension_ranges_) {
printer->Print(vars,
"default:\n"
" if (!pb::ExtensionSet.TryMergeFieldFrom(ref _extensions, $maybe_ref_input$)) {\n"
" _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, $maybe_ref_input$);\n"
" }\n"
" break;\n");
} else {
printer->Print(vars,
"default:\n"
" _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, $maybe_ref_input$);\n"
" break;\n");
}
for (int i = 0; i < fields_by_number().size(); i++) {
const FieldDescriptor* field = fields_by_number()[i];
internal::WireFormatLite::WireType wt =
internal::WireFormat::WireTypeForFieldType(field->type());
uint32_t tag = internal::WireFormatLite::MakeTag(field->number(), wt);
// Handle both packed and unpacked repeated fields with the same Read*Array call;
// the two generated cases are the packed and unpacked tags.
// TODO(jonskeet): Check that is_packable is equivalent to
// is_repeated && wt in { VARINT, FIXED32, FIXED64 }.
// It looks like it is...
if (field->is_packable()) {
printer->Print(
"case $packed_tag$:\n",
"packed_tag",
StrCat(
internal::WireFormatLite::MakeTag(
field->number(),
internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED)));
}
printer->Print("case $tag$: {\n", "tag", StrCat(tag));
printer->Indent();
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(field));
generator->GenerateParsingCode(printer, use_parse_context);
printer->Print("break;\n");
printer->Outdent();
printer->Print("}\n");
}
printer->Outdent();
printer->Print("}\n"); // switch
printer->Outdent();
printer->Print("}\n"); // while
}
// it's a waste of space to track presence for all values, so we only track them if they're not nullable
int MessageGenerator::GetPresenceIndex(const FieldDescriptor* descriptor) {
if (!RequiresPresenceBit(descriptor)) {
return -1;
}
int index = 0;
for (int i = 0; i < fields_by_number().size(); i++) {
const FieldDescriptor* field = fields_by_number()[i];
if (field == descriptor) {
return index;
}
if (RequiresPresenceBit(field)) {
index++;
}
}
GOOGLE_LOG(DFATAL)<< "Could not find presence index for field " << descriptor->name();
return -1;
}
FieldGeneratorBase* MessageGenerator::CreateFieldGeneratorInternal(
const FieldDescriptor* descriptor) {
return CreateFieldGenerator(descriptor, GetPresenceIndex(descriptor), this->options());
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,94 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__
#include <string>
#include <vector>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_source_generator_base.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class FieldGeneratorBase;
class MessageGenerator : public SourceGeneratorBase {
public:
MessageGenerator(const Descriptor* descriptor, const Options* options);
~MessageGenerator();
MessageGenerator(const MessageGenerator&) = delete;
MessageGenerator& operator=(const MessageGenerator&) = delete;
void GenerateCloningCode(io::Printer* printer);
void GenerateFreezingCode(io::Printer* printer);
void GenerateFrameworkMethods(io::Printer* printer);
void Generate(io::Printer* printer);
private:
const Descriptor* descriptor_;
std::vector<const FieldDescriptor*> fields_by_number_;
int has_bit_field_count_;
uint end_tag_;
bool has_extension_ranges_;
void GenerateMessageSerializationMethods(io::Printer* printer);
void GenerateWriteToBody(io::Printer* printer, bool use_write_context);
void GenerateMergingMethods(io::Printer* printer);
void GenerateMainParseLoop(io::Printer* printer, bool use_parse_context);
int GetPresenceIndex(const FieldDescriptor* descriptor);
FieldGeneratorBase* CreateFieldGeneratorInternal(
const FieldDescriptor* descriptor);
bool HasNestedGeneratedTypes();
void AddDeprecatedFlag(io::Printer* printer);
void AddSerializableAttribute(io::Printer* printer);
std::string class_name();
std::string full_class_name();
// field descriptors sorted by number
const std::vector<const FieldDescriptor*>& fields_by_number();
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__

View File

@@ -0,0 +1,293 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/wire_format_lite.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options)
: FieldGeneratorBase(descriptor, presenceIndex, options) {
if (!SupportsPresenceApi(descriptor_)) {
variables_["has_property_check"] = name() + "_ != null";
variables_["has_not_property_check"] = name() + "_ == null";
}
}
MessageFieldGenerator::~MessageFieldGenerator() {
}
void MessageFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
"private $type_name$ $name$_;\n");
WritePropertyDocComment(printer, descriptor_);
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" $name$_ = value;\n"
" }\n"
"}\n");
if (SupportsPresenceApi(descriptor_)) {
printer->Print(
variables_,
"/// <summary>Gets whether the $descriptor_name$ field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return $name$_ != null; }\n"
"}\n");
printer->Print(
variables_,
"/// <summary>Clears the value of the $descriptor_name$ field</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n"
" $name$_ = null;\n"
"}\n");
}
}
void MessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
"if (other.$has_property_check$) {\n"
" if ($has_not_property_check$) {\n"
" $property_name$ = new $type_name$();\n"
" }\n"
" $property_name$.MergeFrom(other.$property_name$);\n"
"}\n");
}
void MessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_not_property_check$) {\n"
" $property_name$ = new $type_name$();\n"
"}\n");
if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
printer->Print(variables_, "input.ReadMessage($property_name$);\n");
} else {
printer->Print(variables_, "input.ReadGroup($property_name$);\n");
}
}
void MessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" output.WriteRawTag($tag_bytes$);\n"
" output.WriteMessage($property_name$);\n"
"}\n");
} else {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" output.WriteRawTag($tag_bytes$);\n"
" output.WriteGroup($property_name$);\n"
" output.WriteRawTag($end_tag_bytes$);\n"
"}\n");
}
}
void MessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += $tag_size$ + pb::CodedOutputStream.ComputeMessageSize($property_name$);\n"
"}\n");
} else {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += $tag_size$ + pb::CodedOutputStream.ComputeGroupSize($property_name$);\n"
"}\n");
}
}
void MessageFieldGenerator::WriteHash(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) hash ^= $property_name$.GetHashCode();\n");
}
void MessageFieldGenerator::WriteEquals(io::Printer* printer) {
printer->Print(
variables_,
"if (!object.Equals($property_name$, other.$property_name$)) return false;\n");
}
void MessageFieldGenerator::WriteToString(io::Printer* printer) {
variables_["field_name"] = GetFieldName(descriptor_);
printer->Print(
variables_,
"PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");
}
void MessageFieldGenerator::GenerateExtensionCode(io::Printer* printer) {
WritePropertyDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"$access_level$ static readonly pb::Extension<$extended_type$, $type_name$> $property_name$ =\n"
" new pb::Extension<$extended_type$, $type_name$>($number$, ");
GenerateCodecCode(printer);
printer->Print(");\n");
}
void MessageFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = other.$has_property_check$ ? other.$name$_.Clone() : null;\n");
}
void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
}
void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
printer->Print(
variables_,
"pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)");
} else {
printer->Print(
variables_,
"pb::FieldCodec.ForGroup($tag$, $end_tag$, $type_name$.Parser)");
}
}
MessageOneofFieldGenerator::MessageOneofFieldGenerator(
const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options)
: MessageFieldGenerator(descriptor, presenceIndex, options) {
SetCommonOneofFieldVariables(&variables_);
}
MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {
}
void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
WritePropertyDocComment(printer, descriptor_);
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : null; }\n"
" set {\n"
" $oneof_name$_ = value;\n"
" $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$oneof_case_name$;\n"
" }\n"
"}\n");
if (SupportsPresenceApi(descriptor_)) {
printer->Print(
variables_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$oneof_case_name$; }\n"
"}\n");
printer->Print(
variables_,
"/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n"
" if ($has_property_check$) {\n"
" Clear$oneof_property_name$();\n"
" }\n"
"}\n");
}
}
void MessageOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(variables_,
"if ($property_name$ == null) {\n"
" $property_name$ = new $type_name$();\n"
"}\n"
"$property_name$.MergeFrom(other.$property_name$);\n");
}
void MessageOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
// TODO(jonskeet): We may be able to do better than this
printer->Print(
variables_,
"$type_name$ subBuilder = new $type_name$();\n"
"if ($has_property_check$) {\n"
" subBuilder.MergeFrom($property_name$);\n"
"}\n");
if (descriptor_->type() == FieldDescriptor::Type::TYPE_MESSAGE) {
printer->Print("input.ReadMessage(subBuilder);\n");
} else {
printer->Print("input.ReadGroup(subBuilder);\n");
}
printer->Print(variables_, "$property_name$ = subBuilder;\n");
}
void MessageOneofFieldGenerator::WriteToString(io::Printer* printer) {
printer->Print(
variables_,
"PrintField(\"$descriptor_name$\", $has_property_check$, $oneof_name$_, writer);\n");
}
void MessageOneofFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$property_name$ = other.$property_name$.Clone();\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,91 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class MessageFieldGenerator : public FieldGeneratorBase {
public:
MessageFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~MessageFieldGenerator();
MessageFieldGenerator(const MessageFieldGenerator&) = delete;
MessageFieldGenerator& operator=(const MessageFieldGenerator&) = delete;
virtual void GenerateCodecCode(io::Printer* printer) override;
virtual void GenerateCloningCode(io::Printer* printer) override;
virtual void GenerateFreezingCode(io::Printer* printer) override;
virtual void GenerateMembers(io::Printer* printer) override;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
virtual void GenerateSerializationCode(io::Printer* printer) override;
virtual void GenerateSerializedSizeCode(io::Printer* printer) override;
virtual void GenerateExtensionCode(io::Printer* printer) override;
virtual void WriteHash(io::Printer* printer) override;
virtual void WriteEquals(io::Printer* printer) override;
virtual void WriteToString(io::Printer* printer) override;
};
class MessageOneofFieldGenerator : public MessageFieldGenerator {
public:
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~MessageOneofFieldGenerator();
MessageOneofFieldGenerator(const MessageOneofFieldGenerator&) = delete;
MessageOneofFieldGenerator& operator=(const MessageOneofFieldGenerator&) =
delete;
virtual void GenerateCloningCode(io::Printer* printer) override;
virtual void GenerateMembers(io::Printer* printer) override;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void WriteToString(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__

View File

@@ -0,0 +1,109 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Provides a mechanism for mapping a descriptor to the
// fully-qualified name of the corresponding C# class.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_NAMES_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_NAMES_H__
#include <string>
#include <google/protobuf/port.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/port_def.inc>
namespace google {
namespace protobuf {
class Descriptor;
class EnumDescriptor;
class FileDescriptor;
class ServiceDescriptor;
namespace compiler {
namespace csharp {
// Requires:
// descriptor != NULL
//
// Returns:
// The namespace to use for given file descriptor.
std::string PROTOC_EXPORT GetFileNamespace(const FileDescriptor* descriptor);
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified C# class name.
std::string PROTOC_EXPORT GetClassName(const Descriptor* descriptor);
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified name of the C# class that provides
// access to the file descriptor. Proto compiler generates
// such class for each .proto file processed.
std::string PROTOC_EXPORT
GetReflectionClassName(const FileDescriptor* descriptor);
// Generates output file name for given file descriptor. If generate_directories
// is true, the output file will be put under directory corresponding to file's
// namespace. base_namespace can be used to strip some of the top level
// directories. E.g. for file with namespace "Bar.Foo" and base_namespace="Bar",
// the resulting file will be put under directory "Foo" (and not "Bar/Foo").
//
// Requires:
// descriptor != NULL
// error != NULL
//
// Returns:
// The file name to use as output file for given file descriptor. In case
// of failure, this function will return empty string and error parameter
// will contain the error message.
std::string PROTOC_EXPORT GetOutputFile(const FileDescriptor* descriptor,
const std::string file_extension,
const bool generate_directories,
const std::string base_namespace,
std::string* error);
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#include <google/protobuf/port_undef.inc>
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_NAMES_H__

View File

@@ -0,0 +1,81 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__
#include <string>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
// Generator options (used by csharp_generator.cc):
struct Options {
Options() :
file_extension(".cs"),
base_namespace(""),
base_namespace_specified(false),
internal_access(false),
serializable(false) {
}
// Extension of the generated file. Defaults to ".cs"
std::string file_extension;
// Base namespace to use to create directory hierarchy. Defaults to "".
// This option allows the simple creation of a conventional C# file layout,
// where directories are created relative to a project-specific base
// namespace. For example, in a project with a base namespace of PetShop, a
// proto of user.proto with a C# namespace of PetShop.Model.Shared would
// generate Model/Shared/User.cs underneath the specified --csharp_out
// directory.
//
// If no base namespace is specified, all files are generated in the
// --csharp_out directory, with no subdirectories created automatically.
std::string base_namespace;
// Whether the base namespace has been explicitly specified by the user.
// This is required as the base namespace can be explicitly set to the empty
// string, meaning "create a full directory hierarchy, starting from the first
// segment of the namespace."
bool base_namespace_specified;
// Whether the generated classes should have accessibility level of "internal".
// Defaults to false that generates "public" classes.
bool internal_access;
// Whether the generated classes should have a global::System.Serializable attribute added
// Defaults to false
bool serializable;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_OPTIONS_H__

View File

@@ -0,0 +1,349 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_doc_comment.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
PrimitiveFieldGenerator::PrimitiveFieldGenerator(
const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
: FieldGeneratorBase(descriptor, presenceIndex, options) {
// TODO(jonskeet): Make this cleaner...
is_value_type = descriptor->type() != FieldDescriptor::TYPE_STRING
&& descriptor->type() != FieldDescriptor::TYPE_BYTES;
if (!is_value_type && !SupportsPresenceApi(descriptor_)) {
variables_["has_property_check"] = variables_["property_name"] + ".Length != 0";
variables_["other_has_property_check"] = "other." + variables_["property_name"] + ".Length != 0";
}
}
PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {
}
void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
// Note: in multiple places, this code assumes that all fields
// that support presence are either nullable, or use a presence field bit.
// Fields which are oneof members are not generated here; they're generated in PrimitiveOneofFieldGenerator below.
// Extensions are not generated here either.
// Proto2 allows different default values to be specified. These are retained
// via static fields. They don't particularly need to be, but we don't need
// to change that. In Proto3 the default value we don't generate these
// fields, just using the literal instead.
if (IsProto2(descriptor_->file())) {
// Note: "private readonly static" isn't as idiomatic as
// "private static readonly", but changing this now would create a lot of
// churn in generated code with near-to-zero benefit.
printer->Print(
variables_,
"private readonly static $type_name$ $property_name$DefaultValue = $default_value$;\n\n");
variables_["default_value_access"] =
variables_["property_name"] + "DefaultValue";
} else {
variables_["default_value_access"] = variables_["default_value"];
}
// Declare the field itself.
printer->Print(
variables_,
"private $type_name$ $name_def_message$;\n");
WritePropertyDocComment(printer, descriptor_);
AddPublicMemberAttributes(printer);
// Most of the work is done in the property:
// Declare the property itself (the same for all options)
printer->Print(variables_, "$access_level$ $type_name$ $property_name$ {\n");
// Specify the "getter", which may need to check for a presence field.
if (SupportsPresenceApi(descriptor_)) {
if (IsNullable(descriptor_)) {
printer->Print(
variables_,
" get { return $name$_ ?? $default_value_access$; }\n");
} else {
printer->Print(
variables_,
// Note: it's possible that this could be rewritten as a
// conditional ?: expression, but there's no significant benefit
// to changing it.
" get { if ($has_field_check$) { return $name$_; } else { return $default_value_access$; } }\n");
}
} else {
printer->Print(
variables_,
" get { return $name$_; }\n");
}
// Specify the "setter", which may need to set a field bit as well as the
// value.
printer->Print(" set {\n");
if (presenceIndex_ != -1) {
printer->Print(
variables_,
" $set_has_field$;\n");
}
if (is_value_type) {
printer->Print(
variables_,
" $name$_ = value;\n");
} else {
printer->Print(
variables_,
" $name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
}
printer->Print(
" }\n"
"}\n");
// The "HasFoo" property, where required.
if (SupportsPresenceApi(descriptor_)) {
printer->Print(variables_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return ");
if (IsNullable(descriptor_)) {
printer->Print(
variables_,
"$name$_ != null; }\n}\n");
} else {
printer->Print(
variables_,
"$has_field_check$; }\n}\n");
}
}
// The "ClearFoo" method, where required.
if (SupportsPresenceApi(descriptor_)) {
printer->Print(variables_,
"/// <summary>Clears the value of the \"$descriptor_name$\" field</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n");
if (IsNullable(descriptor_)) {
printer->Print(variables_, " $name$_ = null;\n");
} else {
printer->Print(variables_, " $clear_has_field$;\n");
}
printer->Print("}\n");
}
}
void PrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($other_has_property_check$) {\n"
" $property_name$ = other.$property_name$;\n"
"}\n");
}
void PrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer) {
// Note: invoke the property setter rather than writing straight to the field,
// so that we can normalize "null to empty" for strings and bytes.
printer->Print(
variables_,
"$property_name$ = input.Read$capitalized_type_name$();\n");
}
void PrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" output.WriteRawTag($tag_bytes$);\n"
" output.Write$capitalized_type_name$($property_name$);\n"
"}\n");
}
void PrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n");
printer->Indent();
int fixedSize = GetFixedSize(descriptor_->type());
if (fixedSize == -1) {
printer->Print(
variables_,
"size += $tag_size$ + pb::CodedOutputStream.Compute$capitalized_type_name$Size($property_name$);\n");
} else {
printer->Print(
"size += $tag_size$ + $fixed_size$;\n",
"fixed_size", StrCat(fixedSize),
"tag_size", variables_["tag_size"]);
}
printer->Outdent();
printer->Print("}\n");
}
void PrimitiveFieldGenerator::WriteHash(io::Printer* printer) {
const char *text = "if ($has_property_check$) hash ^= $property_name$.GetHashCode();\n";
if (descriptor_->type() == FieldDescriptor::TYPE_FLOAT) {
text = "if ($has_property_check$) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode($property_name$);\n";
} else if (descriptor_->type() == FieldDescriptor::TYPE_DOUBLE) {
text = "if ($has_property_check$) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode($property_name$);\n";
}
printer->Print(variables_, text);
}
void PrimitiveFieldGenerator::WriteEquals(io::Printer* printer) {
const char *text = "if ($property_name$ != other.$property_name$) return false;\n";
if (descriptor_->type() == FieldDescriptor::TYPE_FLOAT) {
text = "if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals($property_name$, other.$property_name$)) return false;\n";
} else if (descriptor_->type() == FieldDescriptor::TYPE_DOUBLE) {
text = "if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals($property_name$, other.$property_name$)) return false;\n";
}
printer->Print(variables_, text);
}
void PrimitiveFieldGenerator::WriteToString(io::Printer* printer) {
printer->Print(
variables_,
"PrintField(\"$descriptor_name$\", $has_property_check$, $property_name$, writer);\n");
}
void PrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$name$_ = other.$name$_;\n");
}
void PrimitiveFieldGenerator::GenerateCodecCode(io::Printer* printer) {
printer->Print(
variables_,
"pb::FieldCodec.For$capitalized_type_name$($tag$, $default_value$)");
}
void PrimitiveFieldGenerator::GenerateExtensionCode(io::Printer* printer) {
WritePropertyDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"$access_level$ static readonly pb::Extension<$extended_type$, $type_name$> $property_name$ =\n"
" new pb::Extension<$extended_type$, $type_name$>($number$, ");
GenerateCodecCode(printer);
printer->Print(");\n");
}
PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
const FieldDescriptor* descriptor, int presenceIndex, const Options *options)
: PrimitiveFieldGenerator(descriptor, presenceIndex, options) {
SetCommonOneofFieldVariables(&variables_);
}
PrimitiveOneofFieldGenerator::~PrimitiveOneofFieldGenerator() {
}
void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
WritePropertyDocComment(printer, descriptor_);
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : $default_value$; }\n"
" set {\n");
if (is_value_type) {
printer->Print(
variables_,
" $oneof_name$_ = value;\n");
} else {
printer->Print(
variables_,
" $oneof_name$_ = pb::ProtoPreconditions.CheckNotNull(value, \"value\");\n");
}
printer->Print(
variables_,
" $oneof_name$Case_ = $oneof_property_name$OneofCase.$oneof_case_name$;\n"
" }\n"
"}\n");
if (SupportsPresenceApi(descriptor_)) {
printer->Print(
variables_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ bool Has$property_name$ {\n"
" get { return $oneof_name$Case_ == $oneof_property_name$OneofCase.$oneof_case_name$; }\n"
"}\n");
printer->Print(
variables_,
"/// <summary> Clears the value of the oneof if it's currently set to \"$descriptor_name$\" </summary>\n");
AddPublicMemberAttributes(printer);
printer->Print(
variables_,
"$access_level$ void Clear$property_name$() {\n"
" if ($has_property_check$) {\n"
" Clear$oneof_property_name$();\n"
" }\n"
"}\n");
}
}
void PrimitiveOneofFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(variables_, "$property_name$ = other.$property_name$;\n");
}
void PrimitiveOneofFieldGenerator::WriteToString(io::Printer* printer) {
printer->Print(variables_,
"PrintField(\"$descriptor_name$\", $has_property_check$, $oneof_name$_, writer);\n");
}
void PrimitiveOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"$property_name$ = input.Read$capitalized_type_name$();\n");
}
void PrimitiveOneofFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$property_name$ = other.$property_name$;\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,95 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
struct Options;
class PrimitiveFieldGenerator : public FieldGeneratorBase {
public:
PrimitiveFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~PrimitiveFieldGenerator();
PrimitiveFieldGenerator(const PrimitiveFieldGenerator&) = delete;
PrimitiveFieldGenerator& operator=(const PrimitiveFieldGenerator&) = delete;
virtual void GenerateCodecCode(io::Printer* printer) override;
virtual void GenerateCloningCode(io::Printer* printer) override;
virtual void GenerateMembers(io::Printer* printer) override;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
virtual void GenerateSerializationCode(io::Printer* printer) override;
virtual void GenerateSerializedSizeCode(io::Printer* printer) override;
virtual void GenerateExtensionCode(io::Printer* printer) override;
virtual void WriteHash(io::Printer* printer) override;
virtual void WriteEquals(io::Printer* printer) override;
virtual void WriteToString(io::Printer* printer) override;
protected:
bool is_value_type;
};
class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
public:
PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
int presenceIndex,
const Options *options);
~PrimitiveOneofFieldGenerator();
PrimitiveOneofFieldGenerator(const PrimitiveOneofFieldGenerator&) = delete;
PrimitiveOneofFieldGenerator& operator=(const PrimitiveOneofFieldGenerator&) =
delete;
virtual void GenerateCloningCode(io::Printer* printer) override;
virtual void GenerateMembers(io::Printer* printer) override;
virtual void GenerateMergingCode(io::Printer* printer) override;
virtual void WriteToString(io::Printer* printer) override;
virtual void GenerateParsingCode(io::Printer* printer) override;
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__

View File

@@ -0,0 +1,330 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_enum.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
#include <google/protobuf/compiler/csharp/csharp_message.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_reflection_class.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
ReflectionClassGenerator::ReflectionClassGenerator(const FileDescriptor* file,
const Options* options)
: SourceGeneratorBase(options),
file_(file) {
namespace_ = GetFileNamespace(file);
reflectionClassname_ = GetReflectionClassUnqualifiedName(file);
extensionClassname_ = GetExtensionClassUnqualifiedName(file);
}
ReflectionClassGenerator::~ReflectionClassGenerator() {
}
void ReflectionClassGenerator::Generate(io::Printer* printer) {
WriteIntroduction(printer);
WriteDescriptor(printer);
// Close the class declaration.
printer->Outdent();
printer->Print("}\n");
if (file_->extension_count() > 0) {
printer->Print(
"/// <summary>Holder for extension identifiers generated from the top "
"level of $file_name$</summary>\n"
"$access_level$ static partial class $class_name$ {\n",
"access_level", class_access_level(), "class_name", extensionClassname_,
"file_name", file_->name());
printer->Indent();
for (int i = 0; i < file_->extension_count(); i++) {
std::unique_ptr<FieldGeneratorBase> generator(
CreateFieldGenerator(file_->extension(i), -1, this->options()));
generator->GenerateExtensionCode(printer);
}
printer->Outdent();
printer->Print(
"}\n"
"\n");
}
// write children: Enums
if (file_->enum_type_count() > 0) {
printer->Print("#region Enums\n");
for (int i = 0; i < file_->enum_type_count(); i++) {
EnumGenerator enumGenerator(file_->enum_type(i), this->options());
enumGenerator.Generate(printer);
}
printer->Print("#endregion\n");
printer->Print("\n");
}
// write children: Messages
if (file_->message_type_count() > 0) {
printer->Print("#region Messages\n");
for (int i = 0; i < file_->message_type_count(); i++) {
MessageGenerator messageGenerator(file_->message_type(i), this->options());
messageGenerator.Generate(printer);
}
printer->Print("#endregion\n");
printer->Print("\n");
}
// TODO(jtattermusch): add insertion point for services.
if (!namespace_.empty()) {
printer->Outdent();
printer->Print("}\n");
}
printer->Print("\n");
printer->Print("#endregion Designer generated code\n");
}
void ReflectionClassGenerator::WriteIntroduction(io::Printer* printer) {
printer->Print(
"// <auto-generated>\n"
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
"// source: $file_name$\n"
"// </auto-generated>\n"
"#pragma warning disable 1591, 0612, 3021, 8981\n"
"#region Designer generated code\n"
"\n"
"using pb = global::Google.Protobuf;\n"
"using pbc = global::Google.Protobuf.Collections;\n"
"using pbr = global::Google.Protobuf.Reflection;\n"
"using scg = global::System.Collections.Generic;\n",
"file_name", file_->name());
if (!namespace_.empty()) {
printer->Print("namespace $namespace$ {\n", "namespace", namespace_);
printer->Indent();
printer->Print("\n");
}
printer->Print(
"/// <summary>Holder for reflection information generated from $file_name$</summary>\n"
"$access_level$ static partial class $reflection_class_name$ {\n"
"\n",
"file_name", file_->name(),
"access_level", class_access_level(),
"reflection_class_name", reflectionClassname_);
printer->Indent();
}
void ReflectionClassGenerator::WriteDescriptor(io::Printer* printer) {
printer->Print(
"#region Descriptor\n"
"/// <summary>File descriptor for $file_name$</summary>\n"
"public static pbr::FileDescriptor Descriptor {\n"
" get { return descriptor; }\n"
"}\n"
"private static pbr::FileDescriptor descriptor;\n"
"\n"
"static $reflection_class_name$() {\n",
"file_name", file_->name(),
"reflection_class_name", reflectionClassname_);
printer->Indent();
printer->Print(
"byte[] descriptorData = global::System.Convert.FromBase64String(\n");
printer->Indent();
printer->Indent();
printer->Print("string.Concat(\n");
printer->Indent();
// TODO(jonskeet): Consider a C#-escaping format here instead of just Base64.
std::string base64 = FileDescriptorToBase64(file_);
while (base64.size() > 60) {
printer->Print("\"$base64$\",\n", "base64", base64.substr(0, 60));
base64 = base64.substr(60);
}
printer->Print("\"$base64$\"));\n", "base64", base64);
printer->Outdent();
printer->Outdent();
printer->Outdent();
// -----------------------------------------------------------------
// Invoke InternalBuildGeneratedFileFrom() to build the file.
printer->Print(
"descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,\n");
printer->Print(" new pbr::FileDescriptor[] { ");
for (int i = 0; i < file_->dependency_count(); i++) {
printer->Print(
"$full_reflection_class_name$.Descriptor, ",
"full_reflection_class_name",
GetReflectionClassName(file_->dependency(i)));
}
printer->Print("},\n"
" new pbr::GeneratedClrTypeInfo(");
// Specify all the generated code information, recursively.
if (file_->enum_type_count() > 0) {
printer->Print("new[] {");
for (int i = 0; i < file_->enum_type_count(); i++) {
printer->Print("typeof($type_name$), ", "type_name", GetClassName(file_->enum_type(i)));
}
printer->Print("}, ");
}
else {
printer->Print("null, ");
}
if (file_->extension_count() > 0) {
std::vector<std::string> extensions;
for (int i = 0; i < file_->extension_count(); i++) {
extensions.push_back(GetFullExtensionName(file_->extension(i)));
}
printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", Join(extensions, ", "));
}
else {
printer->Print("null, ");
}
if (file_->message_type_count() > 0) {
printer->Print("new pbr::GeneratedClrTypeInfo[] {\n");
printer->Indent();
printer->Indent();
printer->Indent();
for (int i = 0; i < file_->message_type_count(); i++) {
WriteGeneratedCodeInfo(file_->message_type(i), printer, i == file_->message_type_count() - 1);
}
printer->Outdent();
printer->Print("\n}));\n");
printer->Outdent();
printer->Outdent();
}
else {
printer->Print("null));\n");
}
printer->Outdent();
printer->Print("}\n");
printer->Print("#endregion\n\n");
}
// Write out the generated code for a particular message. This consists of the CLR type, property names
// corresponding to fields, names corresponding to oneofs, nested enums, and nested types. Each array part
// can be specified as null if it would be empty, to make the generated code somewhat simpler to read.
// We write a line break at the end of each generated code info, so that in the final file we'll see all
// the types, pre-ordered depth first, one per line. The indentation will be slightly unusual,
// in that it will look like a single array when it's actually constructing a tree, but it'll be easy to
// read even with multiple levels of nesting.
// The "last" parameter indicates whether this message descriptor is the last one being printed in this immediate
// context. It governs whether or not a trailing comma and newline is written after the constructor, effectively
// just controlling the formatting in the generated code.
void ReflectionClassGenerator::WriteGeneratedCodeInfo(const Descriptor* descriptor, io::Printer* printer, bool last) {
if (IsMapEntryMessage(descriptor)) {
printer->Print("null, ");
return;
}
// Generated message type
printer->Print("new pbr::GeneratedClrTypeInfo(typeof($type_name$), $type_name$.Parser, ", "type_name", GetClassName(descriptor));
// Fields
if (descriptor->field_count() > 0) {
std::vector<std::string> fields;
fields.reserve(descriptor->field_count());
for (int i = 0; i < descriptor->field_count(); i++) {
fields.push_back(GetPropertyName(descriptor->field(i)));
}
printer->Print("new[]{ \"$fields$\" }, ", "fields", Join(fields, "\", \""));
}
else {
printer->Print("null, ");
}
// Oneofs
if (descriptor->oneof_decl_count() > 0) {
std::vector<std::string> oneofs;
oneofs.reserve(descriptor->oneof_decl_count());
for (int i = 0; i < descriptor->oneof_decl_count(); i++) {
oneofs.push_back(UnderscoresToCamelCase(descriptor->oneof_decl(i)->name(), true));
}
printer->Print("new[]{ \"$oneofs$\" }, ", "oneofs", Join(oneofs, "\", \""));
}
else {
printer->Print("null, ");
}
// Nested enums
if (descriptor->enum_type_count() > 0) {
std::vector<std::string> enums;
enums.reserve(descriptor->enum_type_count());
for (int i = 0; i < descriptor->enum_type_count(); i++) {
enums.push_back(GetClassName(descriptor->enum_type(i)));
}
printer->Print("new[]{ typeof($enums$) }, ", "enums", Join(enums, "), typeof("));
}
else {
printer->Print("null, ");
}
// Extensions
if (descriptor->extension_count() > 0) {
std::vector<std::string> extensions;
for (int i = 0; i < descriptor->extension_count(); i++) {
extensions.push_back(GetFullExtensionName(descriptor->extension(i)));
}
printer->Print("new pb::Extension[] { $extensions$ }, ", "extensions", Join(extensions, ", "));
}
else {
printer->Print("null, ");
}
// Nested types
if (descriptor->nested_type_count() > 0) {
// Need to specify array type explicitly here, as all elements may be null.
printer->Print("new pbr::GeneratedClrTypeInfo[] { ");
for (int i = 0; i < descriptor->nested_type_count(); i++) {
WriteGeneratedCodeInfo(descriptor->nested_type(i), printer, i == descriptor->nested_type_count() - 1);
}
printer->Print("}");
}
else {
printer->Print("null");
}
printer->Print(last ? ")" : "),\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,75 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_REFLECTION_CLASS_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_REFLECTION_CLASS_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_source_generator_base.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/printer.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class ReflectionClassGenerator : public SourceGeneratorBase {
public:
ReflectionClassGenerator(const FileDescriptor* file, const Options* options);
~ReflectionClassGenerator();
ReflectionClassGenerator(const ReflectionClassGenerator&) = delete;
ReflectionClassGenerator& operator=(const ReflectionClassGenerator&) = delete;
void Generate(io::Printer* printer);
private:
const FileDescriptor* file_;
std::string namespace_;
std::string reflectionClassname_;
std::string extensionClassname_;
void WriteIntroduction(io::Printer* printer);
void WriteDescriptor(io::Printer* printer);
void WriteGeneratedCodeInfo(const Descriptor* descriptor,
io::Printer* printer,
bool last);
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_REFLECTION_CLASS_H__

Some files were not shown because too many files have changed in this diff Show More