Merge commit '36bca61764984ff5395653cf8377ec5daa71b709' as 'libs/protobuf'

This commit is contained in:
Henry Winkel
2022-10-22 14:46:58 +02:00
2186 changed files with 838730 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
################################################################################
# Protocol Buffers: C++ Runtime
################################################################################
# Most rules are under google/protobuf. This package exists for convenience.
load("@rules_pkg//:mappings.bzl", "pkg_filegroup", "pkg_files", "strip_prefix")
load("@upb//cmake:build_defs.bzl", "staleness_test")
load("//conformance:defs.bzl", "conformance_test")
pkg_files(
name = "dist_files",
srcs = glob(["**"]),
strip_prefix = strip_prefix.from_root(""),
visibility = ["//src:__pkg__"],
)
pkg_filegroup(
name = "all_dist_files",
srcs = [
":dist_files",
"//src/google/protobuf:dist_files",
"//src/google/protobuf/compiler:dist_files",
"//src/google/protobuf/compiler/cpp:dist_files",
"//src/google/protobuf/compiler/csharp:dist_files",
"//src/google/protobuf/compiler/java:dist_files",
"//src/google/protobuf/compiler/objectivec:dist_files",
"//src/google/protobuf/compiler/php:dist_files",
"//src/google/protobuf/compiler/python:dist_files",
"//src/google/protobuf/compiler/ruby:dist_files",
"//src/google/protobuf/io:dist_files",
"//src/google/protobuf/stubs:dist_files",
"//src/google/protobuf/testing:dist_files",
"//src/google/protobuf/util:dist_files",
],
visibility = ["//pkg:__pkg__"],
)
conformance_test(
name = "conformance_test",
failure_list = "//conformance:failure_list_cpp.txt",
testee = "//conformance:conformance_cpp",
text_format_failure_list = "//conformance:text_format_failure_list_cpp.txt",
)
# Copy the generated file_lists.cmake into a place where the staleness test
# below can use it.
genrule(
name = "copy_cmake_lists",
srcs = ["//pkg:gen_src_file_lists"],
outs = ["cmake_copy/file_lists.cmake"],
cmd = "cp $< $@",
visibility = ["//visibility:private"],
)
staleness_test(
name = "cmake_lists_staleness_test",
outs = ["file_lists.cmake"],
generated_pattern = "cmake_copy/%s",
# Only run this test if it is explicitly specified on the command line (not
# via //src:all or ...). This file will be automatically updated in a
# GitHub action, so developers should not worry about failures from this
# test.
tags = ["manual"],
)

135
libs/protobuf/src/README.md Normal file
View File

@@ -0,0 +1,135 @@
Protocol Buffers - Google's data interchange format
===================================================
Copyright 2008 Google Inc.
https://developers.google.com/protocol-buffers/
CMake Installation
-----------------------
To compile or install protobuf from source using CMake, see
[cmake/README.md](../cmake/README.md).
C++ Protobuf - Unix
-----------------------
To build protobuf from source, the following tools are needed:
* bazel
* git
* g++
On Ubuntu/Debian, for example, you can install them with:
sudo apt-get install g++ git bazel
On other platforms, please use the corresponding package managing tool to
install them before proceeding. See https://bazel.build/install for further
instructions on installing Bazel, or to build from source using CMake, see
[cmake/README.md](../cmake/README.md).
To get the source, download the release .tar.gz or .zip package in the
release page:
https://github.com/protocolbuffers/protobuf/releases/latest
For example: if you only need C++, download `protobuf-cpp-[VERSION].tar.gz`; if
you need C++ and Java, download `protobuf-java-[VERSION].tar.gz` (every package
contains C++ source already); if you need C++ and multiple other languages,
download `protobuf-all-[VERSION].tar.gz`.
You can also get the source by "git clone" our git repository. Make sure you
have also cloned the submodules and generated the configure script (skip this
if you are using a release .tar.gz or .zip package):
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive
To build the C++ Protocol Buffer runtime and the Protocol Buffer compiler
(protoc) execute the following:
bazel build :protoc :protobuf
The compiler can then be installed, for example on Linux:
cp bazel-bin/protoc /usr/local/bin
For more usage information on Bazel, please refer to http://bazel.build.
**Compiling dependent packages**
To compile a package that uses Protocol Buffers, you need to setup a Bazel
WORKSPACE that's hooked up to the protobuf repository and loads its
dependencies. For an example, see [WORKSPACE](../examples/WORKSPACE).
**Note for Mac users**
For a Mac system, Unix tools are not available by default. You will first need
to install Xcode from the Mac AppStore and then run the following command from
a terminal:
sudo xcode-select --install
To install Unix tools, you can install "port" following the instructions at
https://www.macports.org . This will reside in /opt/local/bin/port for most
Mac installations.
sudo /opt/local/bin/port install bazel
Alternative for Homebrew users:
brew install bazel
Then follow the Unix instructions above.
C++ Protobuf - Windows
--------------------------
If you only need the protoc binary, you can download it from the release
page:
https://github.com/protocolbuffers/protobuf/releases/latest
In the downloads section, download the zip file protoc-$VERSION-win32.zip.
It contains the protoc binary as well as public proto files of protobuf
library.
Protobuf and its dependencies can be installed directly by using `vcpkg`:
>vcpkg install protobuf protobuf:x64-windows
If zlib support is desired, you'll also need to install the zlib feature:
>vcpkg install protobuf[zlib] protobuf[zlib]:x64-windows
See https://github.com/Microsoft/vcpkg for more information.
To build from source using Microsoft Visual C++, see [cmake/README.md](../cmake/README.md).
To build from source using Cygwin or MinGW, follow the Unix installation
instructions, above.
Binary Compatibility Warning
----------------------------
Due to the nature of C++, it is unlikely that any two versions of the
Protocol Buffers C++ runtime libraries will have compatible ABIs.
That is, if you linked an executable against an older version of
libprotobuf, it is unlikely to work with a newer version without
re-compiling. This problem, when it occurs, will normally be detected
immediately on startup of your app. Still, you may want to consider
using static linkage. You can configure this in your `cc_binary` Bazel rules
by specifying:
linkstatic=True
Usage
-----
The complete documentation for Protocol Buffers is available via the
web at:
https://developers.google.com/protocol-buffers/

View File

@@ -0,0 +1,732 @@
# Auto-generated by //pkg:gen_src_file_lists_cmake
#
# This file contains lists of sources based on Bazel rules. It should
# be included from a hand-written CMake file that defines targets.
#
# Changes to this file will be overwritten based on Bazel definitions.
if(${CMAKE_VERSION} VERSION_GREATER 3.10 OR ${CMAKE_VERSION} VERSION_EQUAL 3.10)
include_guard()
endif()
# //pkg:protobuf
set(libprotobuf_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/any.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/api.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/duration.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/empty.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/field_mask.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/source_context.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/struct.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/timestamp.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/type.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/wrappers.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/any.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/any_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_align.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor_database.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/dynamic_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set_heavy.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_enum_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_bases.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_reflection.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_full.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_gen.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/implicit_weak_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/inlined_string_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/coded_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/gzip_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/io_win32.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/printer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/strtod.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/tokenizer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_sink.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/lexer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/message_path.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/parser.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/unparser.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/untyped_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/writer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/zero_copy_buffered_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/json/json.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/message_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/parse_context.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/service.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/bytestream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/structurally_valid.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/strutil.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/text_format.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/unknown_field_set.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/delimited_message_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_comparator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_mask_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/message_differencer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/time_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/type_resolver_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_lite.cc
)
# //pkg:protobuf
set(libprotobuf_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/any.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/api.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/duration.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/empty.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/field_mask.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/source_context.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/struct.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/timestamp.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/type.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/wrappers.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/any.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_align.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_allocation_policy.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_cleanup.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser.h
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.h
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor_database.h
${protobuf_SOURCE_DIR}/src/google/protobuf/dynamic_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/endian.h
${protobuf_SOURCE_DIR}/src/google/protobuf/explicitly_constructed.h
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set.h
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set_inl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/field_access_listener.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_enum_reflection.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_enum_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_bases.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_reflection.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_decl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_gen.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/has_bits.h
${protobuf_SOURCE_DIR}/src/google/protobuf/implicit_weak_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/inlined_string_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/coded_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/gzip_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/io_win32.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/printer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/strtod.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/tokenizer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_sink.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/descriptor_traits.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/lexer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/message_path.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/parser.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/parser_traits.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/unparser.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/unparser_traits.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/untyped_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/writer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/internal/zero_copy_buffered_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/json/json.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_entry.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_entry_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field_inl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_type_handler.h
${protobuf_SOURCE_DIR}/src/google/protobuf/message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/message_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/metadata.h
${protobuf_SOURCE_DIR}/src/google/protobuf/metadata_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/parse_context.h
${protobuf_SOURCE_DIR}/src/google/protobuf/port.h
${protobuf_SOURCE_DIR}/src/google/protobuf/port_def.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/port_undef.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection.h
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_internal.h
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops.h
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/service.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/bytestream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/callback.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/logging.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/mathutil.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/platform_macros.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/port.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/status_macros.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/strutil.h
${protobuf_SOURCE_DIR}/src/google/protobuf/text_format.h
${protobuf_SOURCE_DIR}/src/google/protobuf/unknown_field_set.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/delimited_message_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_comparator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_mask_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/json_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/message_differencer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/time_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/type_resolver.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/type_resolver_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format.h
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_lite.h
)
# //pkg:protobuf_lite
set(libprotobuf_lite_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/any_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_align.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_enum_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/implicit_weak_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/inlined_string_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/coded_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/io_win32.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/message_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/parse_context.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/bytestream.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/structurally_valid.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/strutil.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_lite.cc
)
# //pkg:protobuf_lite
set(libprotobuf_lite_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/any.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_align.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_allocation_policy.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_cleanup.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_config.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring.h
${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler.h
${protobuf_SOURCE_DIR}/src/google/protobuf/endian.h
${protobuf_SOURCE_DIR}/src/google/protobuf/explicitly_constructed.h
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set.h
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set_inl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_enum_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_decl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/has_bits.h
${protobuf_SOURCE_DIR}/src/google/protobuf/implicit_weak_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/inlined_string_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/coded_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/io_win32.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_impl_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_entry_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_type_handler.h
${protobuf_SOURCE_DIR}/src/google/protobuf/message_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/metadata_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/parse_context.h
${protobuf_SOURCE_DIR}/src/google/protobuf/port.h
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_ptr_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/bytestream.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/callback.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/logging.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/mathutil.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/platform_macros.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/port.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/status_macros.h
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/strutil.h
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_lite.h
)
# //pkg:protoc
set(libprotoc_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/code_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/extension.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/file.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/helpers.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/padding_optimizer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/parse_function_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/primitive_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/service.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/string_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_field_base.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_helpers.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_reflection_class.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/names.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/context.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_field_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/extension.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/extension_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/file.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/generator_factory.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/helpers.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/kotlin_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/map_field_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_builder.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_builder_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_field_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_serialization.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/name_resolver.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/names.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/service.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/shared_code_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field_lite.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/enum.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/extension.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/file.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/helpers.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/import_writer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/line_consumer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/names.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/oneof.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/primitive_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/text_format_decode_data.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/names.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/php_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/helpers.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/subprocess.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/zip_writer.cc
)
# //pkg:protoc
set(libprotoc_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/code_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/extension.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/file.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_layout_helper.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/names.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/options.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/padding_optimizer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/parse_function_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/primitive_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/service.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/string_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_doc_comment.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_field_base.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_options.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_primitive_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_reflection_class.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/names.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/context.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/enum_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/extension.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/extension_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/file.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/generator_factory.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/kotlin_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/map_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_builder.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_builder_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_serialization.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/name_resolver.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/names.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/options.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/primitive_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/service.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/shared_code_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/string_field_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/enum.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/extension.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/file.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/import_writer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/line_consumer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/names.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/nsobject_methods.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/oneof.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/options.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/primitive_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/text_format_decode_data.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/names.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/php_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/scc.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/subprocess.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/zip_writer.h
)
# //src/google/protobuf:well_known_type_protos
set(wkt_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/any.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/api.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/duration.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/empty.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/field_mask.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/source_context.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/struct.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/timestamp.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/type.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/wrappers.proto
)
# //src/google/protobuf:descriptor_proto
set(descriptor_proto_proto_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.proto
)
# //src/google/protobuf:descriptor_proto
set(descriptor_proto_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.proto.pb.cc
)
# //src/google/protobuf:descriptor_proto
set(descriptor_proto_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.proto.pb.h
)
# //src/google/protobuf:descriptor_proto
set(descriptor_proto_files
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor_proto-descriptor-set.proto.bin
)
# //src/google/protobuf/compiler:plugin_proto
set(plugin_proto_proto_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.proto
)
# //src/google/protobuf/compiler:plugin_proto
set(plugin_proto_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.proto.pb.cc
)
# //src/google/protobuf/compiler:plugin_proto
set(plugin_proto_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.proto.pb.h
)
# //src/google/protobuf/compiler:plugin_proto
set(plugin_proto_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin_proto-descriptor-set.proto.bin
)
# //pkg:common_test
set(common_test_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/mock_code_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/testing/file.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/testing/googletest.cc
)
# //pkg:common_test
set(common_test_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/mock_code_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/testing/file.h
${protobuf_SOURCE_DIR}/src/google/protobuf/testing/googletest.h
)
# //pkg:lite_test_util
set(lite_test_util_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_test_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_lite_test_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util_lite.cc
)
# //pkg:lite_test_util
set(lite_test_util_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_lite_test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_test_util_impl.h
${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_lite_unittest.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util_lite.h
)
# //pkg:test_util
set(test_util_srcs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/annotation_test_util.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_tester.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util.cc
)
# //pkg:test_util
set(test_util_hdrs
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/annotation_test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/unittest.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/unittest.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_test.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/map_test_util.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/message_unittest.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_tester.h
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util.h
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util.inc
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util2.h
${protobuf_SOURCE_DIR}/src/google/protobuf/test_util_lite.h
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_unittest.inc
)
# //src/google/protobuf:full_test_srcs
set(protobuf_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/any_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_align_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arena_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenastring_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/arenaz_sampler_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor_database_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/drop_unknown_fields_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/dynamic_message_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/extension_set_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_reflection_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/generated_message_tctable_lite_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/inlined_string_field_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_field_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/map_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/message_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/no_field_presence_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/preserve_unknown_enum_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_arena_lite_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_arena_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/proto3_lite_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/reflection_ops_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field_reflection_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/repeated_field_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/text_format_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/unknown_field_set_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/well_known_types_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/wire_format_unittest.cc
)
# //src/google/protobuf:test_proto_srcs
set(protobuf_test_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/any_test.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/map_proto2_unittest.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/map_unittest.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_arena.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_custom_options.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_drop_unknown_fields.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_embed_optimize_for.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_empty.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_enormous_descriptor.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_import.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_import_public.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_lazy_dependencies.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_lazy_dependencies_custom_option.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_lazy_dependencies_enum.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_lite_imports_nonlite.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_mset.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_mset_wire_format.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_no_field_presence.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_no_generic_services.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_optimize_for.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_preserve_unknown_enum.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_preserve_unknown_enum2.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_proto3.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_proto3_arena.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_proto3_arena_lite.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_proto3_lite.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_proto3_optional.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_well_known_types.proto
)
# //src/google/protobuf:lite_test_srcs
set(protobuf_lite_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/lite_arena_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/lite_unittest.cc
)
# //src/google/protobuf:lite_test_proto_srcs
set(protobuf_lite_test_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/map_lite_unittest.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_import_lite.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_import_public_lite.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/unittest_lite.proto
)
# //src/google/protobuf/compiler:test_srcs
set(compiler_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/command_line_interface_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/bootstrap_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/message_size_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/metadata_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/move_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/plugin_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_bootstrap_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_serialization_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/plugin_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/line_consumer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/names_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/text_format_decode_data_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/plugin_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
)
# //src/google/protobuf/compiler:test_proto_srcs
set(compiler_test_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/test_bad_identifiers.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/cpp/test_large_enum_value.proto
)
# //src/google/protobuf/compiler:test_plugin_srcs
set(test_plugin_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/test_plugin.cc
)
# //src/google/protobuf/io:test_srcs
set(io_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/io/coded_stream_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/io_win32_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/printer_death_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/printer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/tokenizer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_sink_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/io/zero_copy_stream_unittest.cc
)
# //src/google/protobuf/util:test_srcs
set(util_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/util/delimited_message_util_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_comparator_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/field_mask_util_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/message_differencer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/time_util_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/type_resolver_util_test.cc
)
# //src/google/protobuf/util:test_proto_srcs
set(util_test_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/util/json_format.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/json_format_proto3.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/message_differencer_unittest.proto
)
# //src/google/protobuf/stubs:test_srcs
set(stubs_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/bytestream_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/common_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/structurally_valid_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/stubs/strutil_unittest.cc
)

File diff suppressed because it is too large Load Diff

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,
absl::string_view 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/port.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(absl::string_view message_name,
absl::string_view 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) {}
AnyMetadata(const AnyMetadata&) = delete;
AnyMetadata& operator=(const AnyMetadata&) = delete;
// 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,
absl::string_view type_url_prefix) {
return InternalPackFrom(arena, message, type_url_prefix,
T::FullMessageName());
}
bool PackFrom(Arena* arena, const Message& message,
absl::string_view 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,
absl::string_view type_url_prefix,
absl::string_view type_name);
bool InternalUnpackTo(absl::string_view type_name,
MessageLite* message) const;
bool InternalIs(absl::string_view type_name) const;
UrlType* type_url_;
ValueType* value_;
};
// 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(absl::string_view 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(absl::string_view 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,395 @@
// 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)
// Must be included last.
#include "google/protobuf/port_def.inc"
PROTOBUF_PRAGMA_INIT_SEG
namespace _pb = ::PROTOBUF_NAMESPACE_ID;
namespace _pbi = ::PROTOBUF_NAMESPACE_ID::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 const ::_pb::EnumDescriptor**
file_level_enum_descriptors_google_2fprotobuf_2fany_2eproto = nullptr;
static constexpr const ::_pb::ServiceDescriptor**
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_
~0u, // no _split_
~0u, // no sizeof(Split)
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 ::absl::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,
};
// This function exists to be marked as weak.
// It can significantly speed up compilation by breaking up LLVM's SCC
// in the .pb.cc translation units. Large translation units see a
// reduction of more than 35% of walltime for optimized builds. Without
// the weak attribute all the messages in the file, including all the
// vtables and everything they use become part of the same SCC through
// a cycle like:
// GetMetadata -> descriptor table -> default instances ->
// vtables -> GetMetadata
// By adding a weak function here we break the connection from the
// individual vtables back into the descriptor table.
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(
::absl::string_view 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,389 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/any.proto
#ifndef GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto_2epb_2eh
#define GOOGLE_PROTOBUF_INCLUDED_google_2fprotobuf_2fany_2eproto_2epb_2eh
#include <limits>
#include <string>
#include <type_traits>
#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 // PROTOBUF_VERSION
#if 3021008 < 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 // PROTOBUF_MIN_PROTOC_VERSION
#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)
// Must be included last.
#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_;
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,
::absl::string_view 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,
::absl::string_view 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(::absl::string_view 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 ::absl::string_view 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) {
_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) {
_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_2fprotobuf_2fany_2eproto_2epb_2eh

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.
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,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.
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.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(absl::string_view message_name,
absl::string_view type_url_prefix) {
if (!type_url_prefix.empty() &&
type_url_prefix[type_url_prefix.size() - 1] == '/') {
return absl::StrCat(type_url_prefix, message_name);
} else {
return absl::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,
absl::string_view type_url_prefix,
absl::string_view type_name) {
type_url_->Set(GetTypeUrl(type_name, type_url_prefix), arena);
return message.SerializeToString(value_->Mutable(arena));
}
bool AnyMetadata::InternalUnpackTo(absl::string_view type_name,
MessageLite* message) const {
if (!InternalIs(type_name)) {
return false;
}
return message->ParseFromString(value_->Get());
}
bool AnyMetadata::InternalIs(absl::string_view type_name) const {
absl::string_view type_url = type_url_->Get();
return type_url.size() >= type_name.size() + 1 &&
type_url[type_url.size() - type_name.size() - 1] == '/' &&
absl::EndsWith(type_url, type_name);
}
bool ParseAnyTypeUrl(absl::string_view 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(absl::string_view 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,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.
#include "google/protobuf/any_test.pb.h"
#include "google/protobuf/unittest.pb.h"
#include <gtest/gtest.h>
#include "absl/strings/str_cat.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,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.
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,901 @@
// 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 "absl/synchronization/mutex.h"
#include "google/protobuf/arena_allocation_policy.h"
#include "google/protobuf/arena_impl.h"
#include "google/protobuf/arenaz_sampler.h"
#include "google/protobuf/port.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 {
namespace {
#if defined(__GNUC__) && __GNUC__ >= 5
// kSentryArenaBlock is used for arenas which can be referenced pre-main. So,
// constexpr is required.
constexpr ArenaBlock kSentryArenaBlock;
ArenaBlock* SentryArenaBlock() {
// const_cast<> is okay as kSentryArenaBlock will never be mutated.
return const_cast<ArenaBlock*>(&kSentryArenaBlock);
}
#else
// TODO(b/248322260) Remove this once we're not using GCC 4.9 for tests.
// There is a compiler bug in this version that causes the above constexpr to
// fail. This version is no longer in our support window, but we use it in
// some of our aarch64 docker images.
ArenaBlock* SentryArenaBlock() {
static const ArenaBlock kSentryArenaBlock;
// const_cast<> is okay as kSentryArenaBlock will never be mutated.
return const_cast<ArenaBlock*>(&kSentryArenaBlock);
}
#endif
} // namespace
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_;
};
// It is guaranteed that this is constructed in `b`. IOW, this is not the first
// arena and `b` cannot be sentry.
SerialArena::SerialArena(ArenaBlock* b, ThreadSafeArena& parent)
: ptr_{b->Pointer(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize)},
limit_{b->Limit()},
head_{b},
space_allocated_{b->size},
parent_{parent} {
GOOGLE_DCHECK(!b->IsSentry());
}
// It is guaranteed that this is the first SerialArena. Use sentry block.
SerialArena::SerialArena(ThreadSafeArena& parent)
: head_{SentryArenaBlock()}, parent_{parent} {}
// It is guaranteed that this is the first SerialArena but `b` may be user
// provided or newly allocated to store AllocationPolicy.
SerialArena::SerialArena(FirstSerialArena, ArenaBlock* b,
ThreadSafeArena& parent)
: head_{b}, space_allocated_{b->size}, parent_{parent} {
if (b->IsSentry()) return;
set_ptr(b->Pointer(kBlockHeaderSize));
limit_ = b->Limit();
}
void SerialArena::Init(ArenaBlock* b, size_t offset) {
set_ptr(b->Pointer(offset));
limit_ = b->Limit();
head_.store(b, std::memory_order_relaxed);
space_used_.store(0, std::memory_order_relaxed);
space_allocated_.store(b->size, std::memory_order_relaxed);
cached_block_length_ = 0;
cached_blocks_ = nullptr;
}
SerialArena* SerialArena::New(Memory mem, ThreadSafeArena& parent) {
GOOGLE_DCHECK_LE(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize, mem.size);
ThreadSafeArenaStats::RecordAllocateStats(parent.arena_stats_.MutableStats(),
/*used=*/0, /*allocated=*/mem.size,
/*wasted=*/0);
auto b = new (mem.ptr) ArenaBlock{nullptr, mem.size};
return new (b->Pointer(kBlockHeaderSize)) SerialArena(b, parent);
}
template <typename Deallocator>
SerialArena::Memory SerialArena::Free(Deallocator deallocator) {
ArenaBlock* 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
void* SerialArena::AllocateAlignedFallback(size_t n) {
AllocateNewBlock(n);
return AllocateFromExisting(n);
}
PROTOBUF_NOINLINE
void* SerialArena::AllocateAlignedWithCleanupFallback(
size_t n, size_t align, void (*destructor)(void*)) {
size_t required = AlignUpTo(n, align) + cleanup::Size(destructor);
AllocateNewBlock(required);
return AllocateFromExistingWithCleanupFallback(n, align, destructor);
}
PROTOBUF_NOINLINE
void SerialArena::AddCleanupFallback(void* elem, void (*destructor)(void*)) {
size_t required = cleanup::Size(destructor);
AllocateNewBlock(required);
AddCleanupFromExisting(elem, destructor);
}
void SerialArena::AllocateNewBlock(size_t n) {
size_t used = 0;
size_t wasted = 0;
ArenaBlock* old_head = head();
if (!old_head->IsSentry()) {
// Sync limit to block
old_head->cleanup_nodes = limit_;
// Record how much used in this block.
used = static_cast<size_t>(ptr() - old_head->Pointer(kBlockHeaderSize));
wasted = old_head->size - used;
space_used_.store(space_used_.load(std::memory_order_relaxed) + used,
std::memory_order_relaxed);
}
// 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(parent_.AllocPolicy(), old_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.
space_allocated_.store(
space_allocated_.load(std::memory_order_relaxed) + mem.size,
std::memory_order_relaxed);
ThreadSafeArenaStats::RecordAllocateStats(parent_.arena_stats_.MutableStats(),
/*used=*/used,
/*allocated=*/mem.size, wasted);
auto* new_head = new (mem.ptr) ArenaBlock{old_head, mem.size};
set_ptr(new_head->Pointer(kBlockHeaderSize));
limit_ = new_head->Limit();
// Previous writes must take effect before writing new head.
head_.store(new_head, std::memory_order_release);
#ifdef ADDRESS_SANITIZER
ASAN_POISON_MEMORY_REGION(ptr(), limit_ - ptr());
#endif // ADDRESS_SANITIZER
}
uint64_t SerialArena::SpaceUsed() const {
// Note: the calculation below technically causes a race with
// AllocateNewBlock when called from another thread (which happens in
// ThreadSafeArena::SpaceUsed). However, worst-case space_used_ will have
// stale data and the calculation will incorrectly assume 100%
// usage of the *current* block.
// TODO(mkruskal) Consider eliminating this race in exchange for a possible
// performance hit on ARM (see cl/455186837).
const ArenaBlock* h = head_.load(std::memory_order_acquire);
if (h->IsSentry()) return 0;
const uint64_t current_block_size = h->size;
uint64_t current_space_used = std::min(
static_cast<uint64_t>(
ptr() - const_cast<ArenaBlock*>(h)->Pointer(kBlockHeaderSize)),
current_block_size);
return current_space_used + space_used_.load(std::memory_order_relaxed);
}
void SerialArena::CleanupList() {
ArenaBlock* b = head();
if (b->IsSentry()) return;
b->cleanup_nodes = limit_;
do {
char* limit = b->Limit();
char* it = reinterpret_cast<char*>(b->cleanup_nodes);
GOOGLE_DCHECK(!b->IsSentry() || it == limit);
if (it < limit) {
// A prefetch distance of 8 here was chosen arbitrarily. It makes the
// pending nodes fill a cacheline which seemed nice.
constexpr int kPrefetchDist = 8;
cleanup::Tag pending_type[kPrefetchDist];
char* pending_node[kPrefetchDist];
int pos = 0;
for (; pos < kPrefetchDist && it < limit; ++pos) {
pending_type[pos] = cleanup::Type(it);
pending_node[pos] = it;
it += cleanup::Size(pending_type[pos]);
}
if (pos < kPrefetchDist) {
for (int i = 0; i < pos; ++i) {
cleanup::DestroyNode(pending_type[i], pending_node[i]);
}
} else {
pos = 0;
while (it < limit) {
cleanup::PrefetchNode(it);
cleanup::DestroyNode(pending_type[pos], pending_node[pos]);
pending_type[pos] = cleanup::Type(it);
pending_node[pos] = it;
it += cleanup::Size(pending_type[pos]);
pos = (pos + 1) % kPrefetchDist;
}
for (int i = pos; i < pos + kPrefetchDist; ++i) {
cleanup::DestroyNode(pending_type[i % kPrefetchDist],
pending_node[i % kPrefetchDist]);
}
}
}
b = b->next;
} while (b);
}
// Stores arrays of void* and SerialArena* instead of linked list of
// SerialArena* to speed up traversing all SerialArena. The cost of walk is non
// trivial when there are many nodes. Separately storing "ids" minimizes cache
// footprints and more efficient when looking for matching arena.
//
// Uses absl::container_internal::Layout to emulate the following:
//
// struct SerialArenaChunk {
// struct SerialArenaChunkHeader {
// SerialArenaChunk* next_chunk;
// uint32_t capacity;
// std::atomic<uint32_t> size;
// } header;
// std::atomic<void*> ids[];
// std::atomic<SerialArena*> arenas[];
// };
//
// where the size of "ids" and "arenas" is determined at runtime; hence the use
// of Layout.
struct SerialArenaChunkHeader {
constexpr SerialArenaChunkHeader(uint32_t capacity, uint32_t size)
: next_chunk(nullptr), capacity(capacity), size(size) {}
ThreadSafeArena::SerialArenaChunk* next_chunk;
uint32_t capacity;
std::atomic<uint32_t> size;
};
class ThreadSafeArena::SerialArenaChunk {
public:
SerialArenaChunk(uint32_t capacity, void* me, SerialArena* serial) {
new (&header()) SerialArenaChunkHeader{capacity, 1};
new (&id(0)) std::atomic<void*>{me};
for (uint32_t i = 1; i < capacity; ++i) {
new (&id(i)) std::atomic<void*>{nullptr};
}
new (&arena(0)) std::atomic<SerialArena*>{serial};
for (uint32_t i = 1; i < capacity; ++i) {
new (&arena(i)) std::atomic<void*>{nullptr};
}
}
bool IsSentry() const { return capacity() == 0; }
// next_chunk
const SerialArenaChunk* next_chunk() const { return header().next_chunk; }
SerialArenaChunk* next_chunk() { return header().next_chunk; }
void set_next(SerialArenaChunk* next_chunk) {
header().next_chunk = next_chunk;
}
// capacity
uint32_t capacity() const { return header().capacity; }
void set_capacity(uint32_t capacity) { header().capacity = capacity; }
// ids: returns up to size().
absl::Span<const std::atomic<void*>> ids() const {
return Layout(capacity()).Slice<kIds>(ptr()).first(safe_size());
}
absl::Span<std::atomic<void*>> ids() {
return Layout(capacity()).Slice<kIds>(ptr()).first(safe_size());
}
std::atomic<void*>& id(uint32_t i) {
GOOGLE_DCHECK_LT(i, capacity());
return Layout(capacity()).Pointer<kIds>(ptr())[i];
}
// arenas: returns up to size().
absl::Span<const std::atomic<SerialArena*>> arenas() const {
return Layout(capacity()).Slice<kArenas>(ptr()).first(safe_size());
}
absl::Span<std::atomic<SerialArena*>> arenas() {
return Layout(capacity()).Slice<kArenas>(ptr()).first(safe_size());
}
const std::atomic<SerialArena*>& arena(uint32_t i) const {
GOOGLE_DCHECK_LT(i, capacity());
return Layout(capacity()).Pointer<kArenas>(ptr())[i];
}
std::atomic<SerialArena*>& arena(uint32_t i) {
GOOGLE_DCHECK_LT(i, capacity());
return Layout(capacity()).Pointer<kArenas>(ptr())[i];
}
// Tries to insert {id, serial} to head chunk. Returns false if the head is
// already full.
//
// Note that the updating "size", "id", "arena" is individually atomic but
// those are not protected by a mutex. This is acceptable because concurrent
// lookups from SpaceUsed or SpaceAllocated accept inaccuracy due to race. On
// other paths, either race is not possible (GetSerialArenaFallback) or must
// be prevented by users (CleanupList, Free).
bool insert(void* me, SerialArena* serial) {
uint32_t idx = size().fetch_add(1, std::memory_order_relaxed);
// Bail out if this chunk is full.
if (idx >= capacity()) {
// Write old value back to avoid potential overflow.
size().store(capacity(), std::memory_order_relaxed);
return false;
}
id(idx).store(me, std::memory_order_relaxed);
arena(idx).store(serial, std::memory_order_release);
return true;
}
constexpr static size_t AllocSize(size_t n) { return Layout(n).AllocSize(); }
private:
constexpr static int kHeader = 0;
constexpr static int kIds = 1;
constexpr static int kArenas = 2;
using layout_type = absl::container_internal::Layout<
SerialArenaChunkHeader, std::atomic<void*>, std::atomic<SerialArena*>>;
const char* ptr() const { return reinterpret_cast<const char*>(this); }
char* ptr() { return reinterpret_cast<char*>(this); }
SerialArenaChunkHeader& header() {
return *layout_type::Partial().Pointer<kHeader>(ptr());
}
const SerialArenaChunkHeader& header() const {
return *layout_type::Partial().Pointer<kHeader>(ptr());
}
std::atomic<uint32_t>& size() { return header().size; }
const std::atomic<uint32_t>& size() const { return header().size; }
// Returns the size capped by the capacity as fetch_add may result in a size
// greater than capacity.
uint32_t safe_size() const {
return std::min(capacity(), size().load(std::memory_order_relaxed));
}
constexpr static layout_type Layout(size_t n) {
return layout_type(
/*header*/ 1,
/*ids*/ n,
/*arenas*/ n);
}
};
constexpr SerialArenaChunkHeader kSentryArenaChunk = {0, 0};
ThreadSafeArena::SerialArenaChunk* ThreadSafeArena::SentrySerialArenaChunk() {
// const_cast is okay because the sentry chunk is never mutated. Also,
// reinterpret_cast is acceptable here as it should be identical to
// SerialArenaChunk with zero payload. This is a necessary trick to
// constexpr initialize kSentryArenaChunk.
return reinterpret_cast<SerialArenaChunk*>(
const_cast<SerialArenaChunkHeader*>(&kSentryArenaChunk));
}
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
ThreadSafeArena::ThreadSafeArena() : first_arena_(*this) { Init(); }
// Constructor solely used by message-owned arena.
ThreadSafeArena::ThreadSafeArena(internal::MessageOwned)
: tag_and_id_(kMessageOwnedArena), first_arena_(*this) {
Init();
}
ThreadSafeArena::ThreadSafeArena(char* mem, size_t size)
: first_arena_(FirstSerialArena{}, FirstBlock(mem, size), *this) {
Init();
}
ThreadSafeArena::ThreadSafeArena(void* mem, size_t size,
const AllocationPolicy& policy)
: first_arena_(FirstSerialArena{}, FirstBlock(mem, size, policy), *this) {
InitializeWithPolicy(policy);
}
ArenaBlock* ThreadSafeArena::FirstBlock(void* buf, size_t size) {
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(buf) & 7, 0u);
if (buf == nullptr || size <= kBlockHeaderSize) {
return SentryArenaBlock();
}
// Record user-owned block.
alloc_policy_.set_is_user_owned_initial_block(true);
return new (buf) ArenaBlock{nullptr, size};
}
ArenaBlock* ThreadSafeArena::FirstBlock(void* buf, size_t size,
const AllocationPolicy& policy) {
if (policy.IsDefault()) return FirstBlock(buf, size);
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(buf) & 7, 0u);
SerialArena::Memory mem;
if (buf == nullptr || size < kBlockHeaderSize + kAllocPolicySize) {
mem = AllocateMemory(&policy, 0, kAllocPolicySize);
} else {
mem = {buf, size};
// Record user-owned block.
alloc_policy_.set_is_user_owned_initial_block(true);
}
return new (mem.ptr) ArenaBlock{nullptr, mem.size};
}
void ThreadSafeArena::InitializeWithPolicy(const AllocationPolicy& policy) {
Init();
if (policy.IsDefault()) return;
#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
// We ensured enough space so this cannot fail.
void* p;
if (!first_arena_.MaybeAllocateAligned(kAllocPolicySize, &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_
}
uint64_t ThreadSafeArena::GetNextLifeCycleId() {
ThreadCache& tc = thread_cache();
uint64_t 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)) {
// 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, std::memory_order_relaxed) *
kInc;
}
tc.next_lifecycle_id = id + kDelta;
return id;
}
// We assume that #threads / arena is bimodal; i.e. majority small ones are
// single threaded but some big ones are highly concurrent. To balance between
// memory overhead and minimum pointer chasing, we start with few entries and
// exponentially (4x) grow with a limit (255 entries). Note that parameters are
// picked for x64 architectures as hint and the actual size is calculated by
// Layout.
ThreadSafeArena::SerialArenaChunk* ThreadSafeArena::NewSerialArenaChunk(
uint32_t prev_capacity, void* id, SerialArena* serial) {
constexpr size_t kMaxBytes = 4096; // Can hold up to 255 entries.
constexpr size_t kGrowthFactor = 4;
constexpr size_t kHeaderSize = SerialArenaChunk::AllocSize(0);
constexpr size_t kEntrySize = SerialArenaChunk::AllocSize(1) - kHeaderSize;
// On x64 arch: {4, 16, 64, 256, 256, ...} * 16.
size_t prev_bytes = SerialArenaChunk::AllocSize(prev_capacity);
size_t next_bytes = std::min(kMaxBytes, prev_bytes * kGrowthFactor);
uint32_t next_capacity =
static_cast<uint32_t>(next_bytes - kHeaderSize) / kEntrySize;
// Growth based on bytes needs to be adjusted by AllocSize.
next_bytes = SerialArenaChunk::AllocSize(next_capacity);
void* mem;
mem = ::operator new(next_bytes);
return new (mem) SerialArenaChunk{next_capacity, id, serial};
}
// Tries to reserve an entry by atomic fetch_add. If the head chunk is already
// full (size >= capacity), acquires the mutex and adds a new head.
void ThreadSafeArena::AddSerialArena(void* id, SerialArena* serial) {
SerialArenaChunk* head = head_.load(std::memory_order_acquire);
// Fast path without acquiring mutex.
if (!head->IsSentry() && head->insert(id, serial)) {
return;
}
// Slow path with acquiring mutex.
absl::MutexLock lock(&mutex_);
// Refetch and if someone else installed a new head, try allocating on that!
SerialArenaChunk* new_head = head_.load(std::memory_order_acquire);
if (new_head != head) {
if (new_head->insert(id, serial)) return;
// Update head to link to the latest one.
head = new_head;
}
new_head = NewSerialArenaChunk(head->capacity(), id, serial);
new_head->set_next(head);
// Use "std::memory_order_release" to make sure prior stores are visible after
// this one.
head_.store(new_head, std::memory_order_release);
}
void ThreadSafeArena::Init() {
const bool message_owned = IsMessageOwned();
if (!message_owned) {
// Message-owned arenas bypass thread cache and do not need life cycle ID.
tag_and_id_ = GetNextLifeCycleId();
} else {
GOOGLE_DCHECK_EQ(tag_and_id_, kMessageOwnedArena);
}
arena_stats_ = Sample();
head_.store(SentrySerialArenaChunk(), std::memory_order_relaxed);
GOOGLE_DCHECK_EQ(message_owned, IsMessageOwned());
first_owner_ = &thread_cache();
// Record allocation for the first block that was either user-provided or
// newly allocated.
ThreadSafeArenaStats::RecordAllocateStats(
arena_stats_.MutableStats(),
/*used=*/0,
/*allocated=*/first_arena_.SpaceAllocated(),
/*wasted=*/0);
CacheSerialArena(&first_arena_);
}
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);
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 if (mem.size > 0) {
GetDeallocator(alloc_policy_.get(), &space_allocated)(mem);
}
}
SerialArena::Memory ThreadSafeArena::Free(size_t* space_allocated) {
auto deallocator = GetDeallocator(alloc_policy_.get(), space_allocated);
WalkSerialArenaChunk([deallocator](SerialArenaChunk* chunk) {
absl::Span<std::atomic<SerialArena*>> span = chunk->arenas();
// Walks arenas backward to handle the first serial arena the last. Freeing
// in reverse-order to the order in which objects were created may not be
// necessary to Free and we should revisit this. (b/247560530)
for (auto it = span.rbegin(); it != span.rend(); ++it) {
SerialArena* serial = it->load(std::memory_order_relaxed);
GOOGLE_DCHECK_NE(serial, nullptr);
// Always frees the first block of "serial" as it cannot be user-provided.
SerialArena::Memory mem = serial->Free(deallocator);
GOOGLE_DCHECK_NE(mem.ptr, nullptr);
deallocator(mem);
}
// Delete the chunk as we're done with it.
internal::SizedDelete(chunk,
SerialArenaChunk::AllocSize(chunk->capacity()));
});
// The first block of the first arena is special and let the caller handle it.
return first_arena_.Free(deallocator);
}
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 first one. Whether it is user-provided or
// allocated, always reuse the first block for the first arena.
size_t space_allocated = 0;
auto mem = Free(&space_allocated);
space_allocated += mem.size;
// Reset the first arena with the first block. This avoids redundant
// free / allocation and re-allocating for AllocationPolicy. Adjust offset if
// we need to preserve alloc_policy_.
if (alloc_policy_.is_user_owned_initial_block() ||
alloc_policy_.get() != nullptr) {
size_t offset = alloc_policy_.get() == nullptr
? kBlockHeaderSize
: kBlockHeaderSize + kAllocPolicySize;
first_arena_.Init(new (mem.ptr) ArenaBlock{nullptr, mem.size}, offset);
} else {
first_arena_.Init(SentryArenaBlock(), 0);
}
// Since the first block and potential alloc_policy on the first block is
// preserved, this can be initialized by Init().
Init();
return space_allocated;
}
void* ThreadSafeArena::AllocateAlignedWithCleanup(size_t n, size_t align,
void (*destructor)(void*)) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
return arena->AllocateAlignedWithCleanup(n, align, destructor);
} else {
return AllocateAlignedWithCleanupFallback(n, align, destructor);
}
}
void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) {
SerialArena* arena;
if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) {
arena = GetSerialArenaFallback(kMaxCleanupNodeSize);
}
arena->AddCleanup(elem, cleanup);
}
PROTOBUF_NOINLINE
void* ThreadSafeArena::AllocateAlignedWithCleanupFallback(
size_t n, size_t align, void (*destructor)(void*)) {
return GetSerialArenaFallback(n + kMaxCleanupNodeSize)
->AllocateAlignedWithCleanup(n, align, destructor);
}
template <typename Functor>
void ThreadSafeArena::WalkConstSerialArenaChunk(Functor fn) const {
const SerialArenaChunk* chunk = head_.load(std::memory_order_acquire);
for (; !chunk->IsSentry(); chunk = chunk->next_chunk()) {
fn(chunk);
}
}
template <typename Functor>
void ThreadSafeArena::WalkSerialArenaChunk(Functor fn) {
// By omitting an Acquire barrier we help the sanitizer that any user code
// that doesn't properly synchronize Reset() or the destructor will throw a
// TSAN warning.
SerialArenaChunk* chunk = head_.load(std::memory_order_relaxed);
while (!chunk->IsSentry()) {
// Cache next chunk in case this chunk is destroyed.
SerialArenaChunk* next_chunk = chunk->next_chunk();
fn(chunk);
chunk = next_chunk;
}
}
template <typename Functor>
void ThreadSafeArena::PerConstSerialArenaInChunk(Functor fn) const {
WalkConstSerialArenaChunk([&fn](const SerialArenaChunk* chunk) {
for (const auto& each : chunk->arenas()) {
const SerialArena* serial = each.load(std::memory_order_acquire);
// It is possible that newly added SerialArena is not updated although
// size was. This is acceptable for SpaceAllocated and SpaceUsed.
if (serial == nullptr) continue;
fn(serial);
}
});
}
uint64_t ThreadSafeArena::SpaceAllocated() const {
uint64_t space_allocated = first_arena_.SpaceAllocated();
PerConstSerialArenaInChunk([&space_allocated](const SerialArena* serial) {
space_allocated += serial->SpaceAllocated();
});
return space_allocated;
}
uint64_t ThreadSafeArena::SpaceUsed() const {
// First arena is inlined to ThreadSafeArena and the first block's overhead is
// smaller than others that contain SerialArena.
uint64_t space_used = first_arena_.SpaceUsed();
PerConstSerialArenaInChunk([&space_used](const SerialArena* serial) {
// SerialArena on chunks directly allocated from the block and needs to be
// subtracted from SpaceUsed.
space_used += serial->SpaceUsed() - kSerialArenaSize;
});
return space_used - (alloc_policy_.get() ? sizeof(AllocationPolicy) : 0);
}
template <AllocationClient alloc_client>
PROTOBUF_NOINLINE void* ThreadSafeArena::AllocateAlignedFallback(size_t n) {
return GetSerialArenaFallback(n)->AllocateAligned<alloc_client>(n);
}
template void* ThreadSafeArena::AllocateAlignedFallback<
AllocationClient::kDefault>(size_t);
template void*
ThreadSafeArena::AllocateAlignedFallback<AllocationClient::kArray>(size_t);
void ThreadSafeArena::CleanupList() {
WalkSerialArenaChunk([](SerialArenaChunk* chunk) {
absl::Span<std::atomic<SerialArena*>> span = chunk->arenas();
// Walks arenas backward to handle the first serial arena the last.
// Destroying in reverse-order to the construction is often assumed by users
// and required not to break inter-object dependencies. (b/247560530)
for (auto it = span.rbegin(); it != span.rend(); ++it) {
SerialArena* serial = it->load(std::memory_order_relaxed);
GOOGLE_DCHECK_NE(serial, nullptr);
serial->CleanupList();
}
});
// First arena must be cleaned up last. (b/247560530)
first_arena_.CleanupList();
}
PROTOBUF_NOINLINE
SerialArena* ThreadSafeArena::GetSerialArenaFallback(size_t n) {
void* const id = &thread_cache();
if (id == first_owner_) {
CacheSerialArena(&first_arena_);
return &first_arena_;
}
// Search matching SerialArena.
SerialArena* serial = nullptr;
WalkConstSerialArenaChunk([&serial, id](const SerialArenaChunk* chunk) {
absl::Span<const std::atomic<void*>> ids = chunk->ids();
for (uint32_t i = 0; i < ids.size(); ++i) {
if (ids[i].load(std::memory_order_relaxed) == id) {
serial = chunk->arena(i).load(std::memory_order_relaxed);
GOOGLE_DCHECK_NE(serial, nullptr);
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. It must be
// big enough to host SerialArena and the pending request.
serial = SerialArena::New(
AllocateMemory(alloc_policy_.get(), 0, n + kSerialArenaSize), *this);
AddSerialArena(id, serial);
}
CacheSerialArena(serial);
return serial;
}
} // namespace internal
void* Arena::Allocate(size_t n) { return impl_.AllocateAligned(n); }
void* Arena::AllocateForArray(size_t n) {
return impl_.AllocateAligned<internal::AllocationClient::kArray>(n);
}
void* Arena::AllocateAlignedWithCleanup(size_t n, size_t align,
void (*destructor)(void*)) {
return impl_.AllocateAlignedWithCleanup(n, align, destructor);
}
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"

View File

@@ -0,0 +1,773 @@
// 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_config.h"
#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
class TcParser; // defined in generated_message_tctable_impl.h
template <typename Type>
class GenericTypeHandler; // defined in repeated_field.h
template <bool destructor_skippable, typename T>
struct ObjectDestructor {
constexpr static void (*destructor)(void*) =
&internal::cleanup::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 nullptr 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::GetDefaultArenaMaxBlockSize()),
initial_block(nullptr),
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;
return res;
}
friend void arena_metrics::EnableArenaMetrics(ArenaOptions*);
friend class Arena;
friend class ArenaOptionsTestFriend;
};
// 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() {}
// API to create proto2 message objects on the arena. If the arena passed in
// is nullptr, 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)...);
}
// API to delete any objects not on an arena. This can be used to safely
// clean up messages or repeated fields without knowing whether or not they're
// owned by an arena. The pointer passed to this function should not be used
// again.
template <typename T>
PROTOBUF_ALWAYS_INLINE static void Destroy(T* obj) {
if (InternalGetOwningArena(obj) == nullptr) delete obj;
}
// Allocates memory with the specific size and alignment.
void* AllocateAligned(size_t size, size_t align = 8) {
if (align <= 8) {
return Allocate(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(Allocate(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 == nullptr) {
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. This is a best-effort
// estimate and may inaccurately calculate space used by other threads
// executing concurrently with the call to this method. These inaccuracies
// are due to race conditions, and are bounded but unpredictable. Stale data
// can lead to underestimates of the space used, and race conditions can lead
// to overestimates (up to the current block size).
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 != nullptr) {
impl_.AddCleanup(object, &internal::cleanup::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 nullptr 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:
// A SFINAE friendly trait that probes for `U` but always evalues to
// `Arena*`.
template <typename U>
using EnableIfArena =
typename std::enable_if<std::is_same<Arena*, U>::value, Arena*>::type;
// Rather than use SFINAE that must fully cover the space of options in a
// mutually exclusive fashion, we use implicit conversions to base classes
// to force an explicit ranking for our preferences. The lowest ranked
// version that compiles will be accepted.
struct Rank2 {};
struct Rank1 : Rank2 {};
struct Rank0 : Rank1 {};
static Arena* GetOwningArena(const T* p) {
return GetOwningArena(Rank0{}, p);
}
template <typename U>
static auto GetOwningArena(Rank0, const U* p)
-> EnableIfArena<decltype(p->GetOwningArena())> {
return p->GetOwningArena();
}
template <typename U>
static Arena* GetOwningArena(Rank1, const U* p) {
return nullptr;
}
static void InternalSwap(T* a, T* b) { a->InternalSwap(b); }
static Arena* GetArenaForAllocation(const T* p) {
return GetArenaForAllocation(Rank0{}, p);
}
static Arena* GetArena(const T* p) {
// Rather than replicate probing for `GetArena` with fallback to nullptr,
// we borrow the implementation of `GetArenaForAllocation` but skip
// `Rank0` which probes for `GetArenaForAllocation`.
return GetArenaForAllocation(Rank1{}, p);
}
template <typename U>
static auto GetArenaForAllocation(Rank0, const U* p)
-> EnableIfArena<decltype(p->GetArenaForAllocation())> {
return p->GetArenaForAllocation();
}
template <typename U>
static auto GetArenaForAllocation(Rank1, const U* p)
-> EnableIfArena<decltype(p->GetArena())> {
return p->GetArena();
}
template <typename U>
static Arena* GetArenaForAllocation(Rank2, const U* p) {
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... 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);
}
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>::GetArenaForAllocation(p);
}
// 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_;
// 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 == nullptr) {
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 == nullptr) {
// Generated arena constructor T(Arena*) is protected. Call via
// InternalHelper.
return InternalHelper<T>::New();
} else {
return arena->DoCreateMessage<T>();
}
}
PROTOBUF_NDEBUG_INLINE void* AllocateInternal(size_t size, size_t align,
void (*destructor)(void*)) {
// Monitor allocation if needed.
if (destructor == nullptr) {
return AllocateAligned(size, align);
} else {
return AllocateAlignedWithCleanup(size, align, destructor);
}
}
// 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*>(AllocateAlignedForArray(n, alignof(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),
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))
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))
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 != nullptr) {
impl_.AddCleanup(object, &internal::arena_delete_object<MessageLite>);
}
}
template <typename T>
PROTOBUF_ALWAYS_INLINE void OwnInternal(T* object, std::false_type) {
if (object != nullptr) {
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>
PROTOBUF_ALWAYS_INLINE static Arena* GetArenaInternal(const T* value) {
return InternalHelper<T>::GetArena(value);
}
void* AllocateAlignedForArray(size_t n, size_t align) {
if (align <= 8) {
return AllocateForArray(internal::AlignUpTo8(n));
} 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(AllocateForArray(n + align - 8), align);
}
}
void* Allocate(size_t n);
void* AllocateForArray(size_t n);
void* AllocateAlignedWithCleanup(size_t n, size_t align,
void (*destructor)(void*));
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 internal::TcParser; // 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;
};
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_ARENA_H__

View File

@@ -0,0 +1,45 @@
// 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_align.h"
#include <cstddef>
#include <cstdint>
namespace google {
namespace protobuf {
namespace internal {
// There are still compilers (open source) requiring a definition for constexpr.
constexpr size_t ArenaAlignDefault::align; // NOLINT
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,155 @@
// 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 provides alignment utilities for use in arenas.
//
// `ArenaAlign` contains a single `align` data member and provides
// the below functions which operate on the given alignment.
//
// Ceil(size_t n) - rounds `n` up to the nearest `align` boundary.
// Floor(size_t n) - rounds `n` down to the nearest `align` boundary.
// Ceil(T* P) - rounds `p` up to the nearest `align` boundary.
// IsAligned(size_t n) - returns true if `n` is aligned to `align`
// IsAligned(T* p) - returns true if `p` is aligned to `align`
// CheckAligned(T* p) - returns `p`. Checks alignment of `p` in debug.
//
// Additionally there is an optimized `CeilDefaultAligned(T*)` method which is
// equivalent to `Ceil(ArenaAlignDefault().CheckAlign(p))` but more efficiently
// implemented as a 'check only' for ArenaAlignDefault.
//
// These classes allow for generic arena logic using 'alignment policies'.
//
// For example:
//
// template <Align>
// void* NaiveAlloc(size_t n, Align align) {
// align.CheckAligned(n);
// uint8_t* ptr = align.CeilDefaultAligned(ptr_);
// ptr_ += n;
// return ptr;
// }
//
// void CallSites() {
// void *p1 = NaiveAlloc(n, ArenaAlignDefault());
// void *p2 = NaiveAlloc(n, ArenaAlignAs(32));
// }
//
#ifndef GOOGLE_PROTOBUF_ARENA_ALIGN_H__
#define GOOGLE_PROTOBUF_ARENA_ALIGN_H__
#include <cstddef>
#include <cstdint>
#include "google/protobuf/stubs/logging.h"
#include "google/protobuf/stubs/common.h"
#include "absl/numeric/bits.h"
namespace google {
namespace protobuf {
namespace internal {
struct ArenaAlignDefault {
static constexpr size_t align = 8; // NOLINT
static constexpr bool IsAligned(size_t n) { return (n & (align - 1)) == 0; }
template <typename T>
static bool IsAligned(T* ptr) {
return (reinterpret_cast<uintptr_t>(ptr) & (align - 1)) == 0;
}
static constexpr size_t Ceil(size_t n) { return (n + align - 1) & -align; }
static constexpr size_t Floor(size_t n) { return (n & ~(align - 1)); }
template <typename T>
T* Ceil(T* ptr) const {
uintptr_t intptr = reinterpret_cast<uintptr_t>(ptr);
return reinterpret_cast<T*>((intptr + align - 1) & -align);
}
template <typename T>
T* CeilDefaultAligned(T* ptr) const {
return ArenaAlignDefault().CheckAligned(ptr);
}
// Address sanitizer enabled alignment check
template <typename T>
static T* CheckAligned(T* ptr) {
GOOGLE_DCHECK(IsAligned(ptr)) << static_cast<void*>(ptr);
return ptr;
}
};
struct ArenaAlign {
static constexpr bool IsDefault() { return false; };
size_t align;
constexpr bool IsAligned(size_t n) const { return (n & (align - 1)) == 0; }
template <typename T>
bool IsAligned(T* ptr) const {
return (reinterpret_cast<uintptr_t>(ptr) & (align - 1)) == 0;
}
constexpr size_t Ceil(size_t n) const { return (n + align - 1) & -align; }
constexpr size_t Floor(size_t n) const { return (n & ~(align - 1)); }
template <typename T>
T* Ceil(T* ptr) const {
uintptr_t intptr = reinterpret_cast<uintptr_t>(ptr);
return reinterpret_cast<T*>((intptr + align - 1) & -align);
}
template <typename T>
T* CeilDefaultAligned(T* ptr) const {
return Ceil(ArenaAlignDefault().CheckAligned(ptr));
}
// Address sanitizer enabled alignment check
template <typename T>
T* CheckAligned(T* ptr) const {
GOOGLE_DCHECK(IsAligned(ptr)) << static_cast<void*>(ptr);
return ptr;
}
};
inline ArenaAlign ArenaAlignAs(size_t align) {
// align must be a non zero power of 2 >= 8
GOOGLE_DCHECK_NE(align, 0);
GOOGLE_DCHECK(absl::has_single_bit(align)) << "Invalid alignment " << align;
return ArenaAlign{align};
}
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_ARENA_ALIGN_H__

View File

@@ -0,0 +1,215 @@
// 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_align.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace internal {
namespace {
using ::testing::Eq;
TEST(ArenaAlignDefault, Align) {
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.align, Eq(8));
}
TEST(ArenaAlignDefault, Floor) {
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.Floor(0), Eq(0));
EXPECT_THAT(align_default.Floor(1), Eq(0));
EXPECT_THAT(align_default.Floor(7), Eq(0));
EXPECT_THAT(align_default.Floor(8), Eq(8));
EXPECT_THAT(align_default.Floor(9), Eq(8));
EXPECT_THAT(align_default.Floor(15), Eq(8));
EXPECT_THAT(align_default.Floor(16), Eq(16));
}
TEST(ArenaAlignDefault, Ceil) {
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.Ceil(0), Eq(0));
EXPECT_THAT(align_default.Ceil(1), Eq(8));
EXPECT_THAT(align_default.Ceil(7), Eq(8));
EXPECT_THAT(align_default.Ceil(8), Eq(8));
EXPECT_THAT(align_default.Ceil(9), Eq(16));
EXPECT_THAT(align_default.Ceil(15), Eq(16));
EXPECT_THAT(align_default.Ceil(16), Eq(16));
}
TEST(ArenaAlignDefault, CeilPtr) {
alignas(8) char p[17] = {0};
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.Ceil(p + 0), Eq(p + 0));
EXPECT_THAT(align_default.Ceil(p + 1), Eq(p + 8));
EXPECT_THAT(align_default.Ceil(p + 7), Eq(p + 8));
EXPECT_THAT(align_default.Ceil(p + 8), Eq(p + 8));
EXPECT_THAT(align_default.Ceil(p + 9), Eq(p + 16));
EXPECT_THAT(align_default.Ceil(p + 15), Eq(p + 16));
EXPECT_THAT(align_default.Ceil(p + 16), Eq(p + 16));
}
TEST(ArenaAlignDefault, CheckAligned) {
alignas(8) char p[17] = {0};
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.CheckAligned(p + 0), Eq(p + 0));
EXPECT_THAT(align_default.CheckAligned(p + 8), Eq(p + 8));
EXPECT_THAT(align_default.CheckAligned(p + 16), Eq(p + 16));
#ifdef PROTOBUF_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 1), ".*");
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 7), ".*");
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 9), ".*");
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 15), ".*");
EXPECT_DEBUG_DEATH(align_default.CheckAligned(p + 17), ".*");
#endif // PROTOBUF_HAS_DEATH_TEST
}
TEST(ArenaAlignDefault, CeilDefaultAligned) {
alignas(8) char p[17] = {0};
auto align_default = ArenaAlignDefault();
EXPECT_THAT(align_default.CeilDefaultAligned(p + 0), Eq(p + 0));
EXPECT_THAT(align_default.CeilDefaultAligned(p + 8), Eq(p + 8));
EXPECT_THAT(align_default.CeilDefaultAligned(p + 16), Eq(p + 16));
#ifdef PROTOBUF_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 1), ".*");
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 7), ".*");
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 9), ".*");
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 15), ".*");
EXPECT_DEBUG_DEATH(align_default.CeilDefaultAligned(p + 17), ".*");
#endif // PROTOBUF_HAS_DEATH_TEST
}
TEST(ArenaAlignDefault, IsAligned) {
auto align_default = ArenaAlignDefault();
EXPECT_TRUE(align_default.IsAligned(0));
EXPECT_FALSE(align_default.IsAligned(1));
EXPECT_FALSE(align_default.IsAligned(7));
EXPECT_TRUE(align_default.IsAligned(8));
EXPECT_FALSE(align_default.IsAligned(9));
EXPECT_FALSE(align_default.IsAligned(15));
EXPECT_TRUE(align_default.IsAligned(16));
}
TEST(ArenaAlign, Align) {
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.align, Eq(64));
}
TEST(ArenaAlign, Floor) {
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.Floor(0), Eq(0));
EXPECT_THAT(align_64.Floor(1), Eq(0));
EXPECT_THAT(align_64.Floor(63), Eq(0));
EXPECT_THAT(align_64.Floor(64), Eq(64));
EXPECT_THAT(align_64.Floor(65), Eq(64));
EXPECT_THAT(align_64.Floor(127), Eq(64));
EXPECT_THAT(align_64.Floor(128), Eq(128));
}
TEST(ArenaAlign, Ceil) {
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.Ceil(0), Eq(0));
EXPECT_THAT(align_64.Ceil(1), Eq(64));
EXPECT_THAT(align_64.Ceil(63), Eq(64));
EXPECT_THAT(align_64.Ceil(64), Eq(64));
EXPECT_THAT(align_64.Ceil(65), Eq(128));
EXPECT_THAT(align_64.Ceil(127), Eq(128));
EXPECT_THAT(align_64.Ceil(128), Eq(128));
}
TEST(ArenaAlign, CeilPtr) {
alignas(64) char p[129] = {0};
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.Ceil(p + 0), Eq(p));
EXPECT_THAT(align_64.Ceil(p + 1), Eq(p + 64));
EXPECT_THAT(align_64.Ceil(p + 63), Eq(p + 64));
EXPECT_THAT(align_64.Ceil(p + 64), Eq(p + 64));
EXPECT_THAT(align_64.Ceil(p + 65), Eq(p + 128));
EXPECT_THAT(align_64.Ceil(p + 127), Eq(p + 128));
EXPECT_THAT(align_64.Ceil(p + 128), Eq(p + 128));
}
TEST(ArenaAlign, CheckAligned) {
alignas(128) char p[129] = {0};
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.CheckAligned(p + 0), Eq(p));
EXPECT_THAT(align_64.CheckAligned(p + 64), Eq(p + 64));
EXPECT_THAT(align_64.CheckAligned(p + 128), Eq(p + 128));
#ifdef PROTOBUF_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 1), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 7), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 8), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 56), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 63), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 65), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 72), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 120), ".*");
EXPECT_DEBUG_DEATH(align_64.CheckAligned(p + 129), ".*");
#endif // PROTOBUF_HAS_DEATH_TEST
}
TEST(ArenaAlign, CeilDefaultAligned) {
alignas(128) char p[129] = {0};
auto align_64 = ArenaAlignAs(64);
EXPECT_THAT(align_64.CeilDefaultAligned(p + 0), Eq(p));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 8), Eq(p + 64));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 56), Eq(p + 64));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 64), Eq(p + 64));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 72), Eq(p + 128));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 120), Eq(p + 128));
EXPECT_THAT(align_64.CeilDefaultAligned(p + 128), Eq(p + 128));
#ifdef PROTOBUF_HAS_DEATH_TEST
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 1), ".*");
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 7), ".*");
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 63), ".*");
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 65), ".*");
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 127), ".*");
EXPECT_DEBUG_DEATH(align_64.CeilDefaultAligned(p + 129), ".*");
#endif // PROTOBUF_HAS_DEATH_TEST
}
TEST(ArenaAlign, IsAligned) {
auto align_64 = ArenaAlignAs(64);
EXPECT_TRUE(align_64.IsAligned(0));
EXPECT_FALSE(align_64.IsAligned(1));
EXPECT_FALSE(align_64.IsAligned(63));
EXPECT_TRUE(align_64.IsAligned(64));
EXPECT_FALSE(align_64.IsAligned(65));
EXPECT_FALSE(align_64.IsAligned(127));
EXPECT_TRUE(align_64.IsAligned(128));
}
} // namespace
} // namespace internal
} // 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.
#ifndef GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__
#define GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__
#include <cstddef>
#include <cstdint>
#include "google/protobuf/arena_config.h"
namespace google {
namespace protobuf {
namespace internal {
// `AllocationPolicy` defines `Arena` allocation policies. Applications can
// customize the initial and maximum sizes for arena allocation, as well as set
// custom allocation and deallocation functions. `AllocationPolicy` is for
// protocol buffer internal use only, and typically created from a user facing
// public configuration class such as `ArenaOptions`.
struct AllocationPolicy {
static constexpr size_t kDefaultStartBlockSize = 256;
size_t start_block_size = kDefaultStartBlockSize;
size_t max_block_size = GetDefaultArenaMaxBlockSize();
void* (*block_alloc)(size_t) = nullptr;
void (*block_dealloc)(void*, size_t) = nullptr;
bool IsDefault() const {
return start_block_size == kDefaultStartBlockSize &&
max_block_size == GetDefaultArenaMaxBlockSize() &&
block_alloc == nullptr && block_dealloc == 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);
}
uintptr_t get_raw() const { return policy_; }
private:
enum : uintptr_t {
kUserOwnedInitialBlock = 1,
};
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_;
};
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_ARENA_ALLOCATION_POLICY_H__

View File

@@ -0,0 +1,189 @@
// 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_CLEANUP_H__
#define GOOGLE_PROTOBUF_ARENA_CLEANUP_H__
#include <cstddef>
#include <cstdint>
#include <string>
#include "google/protobuf/stubs/logging.h"
#include "google/protobuf/stubs/common.h"
#include "absl/base/attributes.h"
namespace google {
namespace protobuf {
namespace internal {
namespace cleanup {
// Helper function invoking the destructor of `object`
template <typename T>
void arena_destruct_object(void* object) {
reinterpret_cast<T*>(object)->~T();
}
// Tag defines the type of cleanup / cleanup object. This tag is stored in the
// lowest 2 bits of the `elem` value identifying the type of node. All node
// types must start with a `uintptr_t` that stores `Tag` in its low two bits.
enum class Tag : uintptr_t {
kDynamic = 0, // DynamicNode
kString = 1, // StringNode (std::string)
};
// DynamicNode contains the object (`elem`) that needs to be
// destroyed, and the function to destroy it (`destructor`)
// elem must be aligned at minimum on a 4 byte boundary.
struct DynamicNode {
uintptr_t elem;
void (*destructor)(void*);
};
// StringNode contains a `std::string` object (`elem`) that needs to be
// destroyed. The lowest 2 bits of `elem` contain the non-zero kString tag.
struct StringNode {
uintptr_t elem;
};
// EnableSpecializedTags() return true if the alignment of tagged objects
// such as std::string allow us to poke tags in the 2 LSB bits.
inline constexpr bool EnableSpecializedTags() {
// For now we require 2 bits
return alignof(std::string) >= 8;
}
// Adds a cleanup entry identified by `tag` at memory location `pos`.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void CreateNode(Tag tag, void* pos,
const void* elem_raw,
void (*destructor)(void*)) {
auto elem = reinterpret_cast<uintptr_t>(elem_raw);
if (EnableSpecializedTags()) {
GOOGLE_DCHECK_EQ(elem & 3, 0ULL); // Must be aligned
switch (tag) {
case Tag::kString: {
StringNode n = {elem | static_cast<uintptr_t>(Tag::kString)};
memcpy(pos, &n, sizeof(n));
return;
}
default:
break;
}
}
DynamicNode n = {elem, destructor};
memcpy(pos, &n, sizeof(n));
}
// Optimization: performs a prefetch on `elem_address`.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void PrefetchNode(
const void* elem_address) {
(void)elem_address;
}
// Destroys the node idenitfied by `tag` stored at memory location `pos`.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE void DestroyNode(Tag tag, const void* pos) {
if (EnableSpecializedTags()) {
switch (tag) {
case Tag::kString: {
StringNode n;
memcpy(&n, pos, sizeof(n));
auto* s = reinterpret_cast<std::string*>(n.elem & ~0x7ULL);
// Some compilers don't like fully qualified explicit dtor calls,
// so use an alias to avoid having to type `::`.
using string_type = std::string;
s->~string_type();
return;
}
default:
break;
}
}
DynamicNode n;
memcpy(&n, pos, sizeof(n));
n.destructor(reinterpret_cast<void*>(n.elem));
}
// Returns the `tag` identifying the type of object for `destructor` or
// kDynamic if `destructor` does not identify a well know object type.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE Tag Type(void (*destructor)(void*)) {
if (EnableSpecializedTags()) {
if (destructor == &arena_destruct_object<std::string>) {
return Tag::kString;
}
}
return Tag::kDynamic;
}
// Returns the `tag` identifying the type of object stored at memory location
// `elem`, which represents the first uintptr_t value in the node.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE Tag Type(void* raw) {
if (!EnableSpecializedTags()) return Tag::kDynamic;
uintptr_t elem;
memcpy(&elem, raw, sizeof(elem));
switch (static_cast<Tag>(elem & 0x7ULL)) {
case Tag::kDynamic:
return Tag::kDynamic;
case Tag::kString:
return Tag::kString;
default:
GOOGLE_LOG(FATAL) << "Corrupted cleanup tag: " << (elem & 0x7ULL);
return Tag::kDynamic;
}
}
// Returns the required size in bytes off the node type identified by `tag`.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE size_t Size(Tag tag) {
if (!EnableSpecializedTags()) return sizeof(DynamicNode);
switch (tag) {
case Tag::kDynamic:
return sizeof(DynamicNode);
case Tag::kString:
return sizeof(StringNode);
default:
GOOGLE_LOG(FATAL) << "Corrupted cleanup tag: " << static_cast<int>(tag);
return sizeof(DynamicNode);
}
}
// Returns the required size in bytes off the node type for `destructor`.
inline ABSL_ATTRIBUTE_ALWAYS_INLINE size_t Size(void (*destructor)(void*)) {
return destructor == nullptr ? 0 : Size(Type(destructor));
}
} // namespace cleanup
} // namespace internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_ARENA_CLEANUP_H__

View File

@@ -0,0 +1,53 @@
// 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_config.h"
#include <atomic>
#include <cstddef>
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
PROTOBUF_CONSTINIT const size_t kDefaultDefaultArenaMaxBlockSize = 8 << 10;
namespace arena_config_internal {
std::atomic<size_t> default_arena_max_block_size{
kDefaultDefaultArenaMaxBlockSize};
} // namespace arena_config_internal
} // namespace internal
} // 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_ARENA_CONFIG_H__
#define GOOGLE_PROTOBUF_ARENA_CONFIG_H__
#include <atomic>
#include <cstddef>
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
namespace arena_config_internal {
// We use an atomic here only for correctness so that we can read/write
// concurrently. We don't have memory order requirements so we use relaxed
// memory ordering.
PROTOBUF_EXPORT extern std::atomic<size_t> default_arena_max_block_size;
} // namespace arena_config_internal
// The default value to use for DefaultArenaMaxBlockSize when
// SetDefaultArenaMaxBlockSize hasn't been called.
PROTOBUF_EXPORT extern const size_t kDefaultDefaultArenaMaxBlockSize;
// The default value to use for arena max block size when no value is provided
// in ArenaOptions.
inline size_t GetDefaultArenaMaxBlockSize() {
return arena_config_internal::default_arena_max_block_size.load(
std::memory_order_relaxed);
}
inline void SetDefaultArenaMaxBlockSize(size_t default_arena_max_block_size) {
return arena_config_internal::default_arena_max_block_size.store(
default_arena_max_block_size, std::memory_order_relaxed);
}
} // namespace internal
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_ARENA_CONFIG_H__

View File

@@ -0,0 +1,694 @@
// 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 <string>
#include <type_traits>
#include <typeinfo>
#include "google/protobuf/stubs/common.h"
#include "google/protobuf/stubs/logging.h"
#include "absl/numeric/bits.h"
#include "absl/synchronization/mutex.h"
#include "google/protobuf/arena_allocation_policy.h"
#include "google/protobuf/arena_cleanup.h"
#include "google/protobuf/arena_config.h"
#include "google/protobuf/arenaz_sampler.h"
#include "google/protobuf/port.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 PROTOBUF_ALWAYS_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);
}
inline PROTOBUF_ALWAYS_INLINE constexpr size_t AlignUpTo(size_t n, size_t a) {
// 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 a <= 8 ? AlignUpTo8(n) : n + a - 8;
}
inline PROTOBUF_ALWAYS_INLINE void* AlignTo(void* p, size_t a) {
if (a <= 8) {
return p;
} else {
auto u = reinterpret_cast<uintptr_t>(p);
return reinterpret_cast<void*>((u + a - 1) & (~a + 1));
}
}
// Arena blocks are variable length malloc-ed objects. The following structure
// describes the common header for all blocks.
struct ArenaBlock {
// For the sentry block with zero-size where ptr_, limit_, cleanup_nodes all
// point to "this".
constexpr ArenaBlock()
: next(nullptr), cleanup_nodes(this), size(0) {}
ArenaBlock(ArenaBlock* next, size_t size)
: next(next), cleanup_nodes(nullptr), size(size) {
GOOGLE_DCHECK_GT(size, sizeof(ArenaBlock));
}
char* Pointer(size_t n) {
GOOGLE_DCHECK_LE(n, size);
return reinterpret_cast<char*>(this) + n;
}
char* Limit() { return Pointer(size & static_cast<size_t>(-8)); }
bool IsSentry() const { return size == 0; }
ArenaBlock* const next;
void* cleanup_nodes;
const size_t size;
// data follows
};
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_;
};
enum class AllocationClient { kDefault, kArray };
class ThreadSafeArena;
// Tag type used to invoke the constructor of the first SerialArena.
struct FirstSerialArena {
explicit FirstSerialArena() = default;
};
// 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 delegates 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;
};
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 = absl::bit_width(size - 1) - 4;
if (index >= cached_block_length_) return nullptr;
auto& cached_head = cached_blocks_[index];
if (cached_head == nullptr) return nullptr;
void* ret = cached_head;
PROTOBUF_UNPOISON_MEMORY_REGION(ret, size);
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) {
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);
}
return AllocateFromExisting(n);
}
private:
void* AllocateFromExisting(size_t n) {
PROTOBUF_UNPOISON_MEMORY_REGION(ptr(), n);
void* ret = ptr();
set_ptr(static_cast<char*>(ret) + n);
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 {
PROTOBUF_ASSUME(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 = absl::bit_width(size) - 5;
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);
// We need to unpoison this memory before filling it in case it has been
// poisoned by another santizer client.
PROTOBUF_UNPOISON_MEMORY_REGION(
new_list + cached_block_length_,
(new_size - cached_block_length_) * sizeof(CachedBlock*));
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;
PROTOBUF_POISON_MEMORY_REGION(p, size);
}
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;
}
// If there is enough space in the current block, allocate space for one `T`
// object and register for destruction. The object has not been constructed
// and the memory returned is uninitialized.
template <typename T>
PROTOBUF_ALWAYS_INLINE void* MaybeAllocateWithCleanup() {
GOOGLE_DCHECK_GE(limit_, ptr());
static_assert(!std::is_trivially_destructible<T>::value,
"This function is only for non-trivial types.");
constexpr int aligned_size = AlignUpTo8(sizeof(T));
constexpr auto destructor = cleanup::arena_destruct_object<T>;
size_t required = aligned_size + cleanup::Size(destructor);
if (PROTOBUF_PREDICT_FALSE(!HasSpace(required))) {
return nullptr;
}
void* ptr = AllocateFromExistingWithCleanupFallback(aligned_size,
alignof(T), destructor);
PROTOBUF_ASSUME(ptr != nullptr);
return ptr;
}
PROTOBUF_ALWAYS_INLINE
void* AllocateAlignedWithCleanup(size_t n, size_t align,
void (*destructor)(void*)) {
size_t required = AlignUpTo(n, align) + cleanup::Size(destructor);
if (PROTOBUF_PREDICT_FALSE(!HasSpace(required))) {
return AllocateAlignedWithCleanupFallback(n, align, destructor);
}
return AllocateFromExistingWithCleanupFallback(n, align, destructor);
}
PROTOBUF_ALWAYS_INLINE
void AddCleanup(void* elem, void (*destructor)(void*)) {
size_t required = cleanup::Size(destructor);
if (PROTOBUF_PREDICT_FALSE(!HasSpace(required))) {
return AddCleanupFallback(elem, destructor);
}
AddCleanupFromExisting(elem, destructor);
}
private:
void* AllocateFromExistingWithCleanupFallback(size_t n, size_t align,
void (*destructor)(void*)) {
n = AlignUpTo(n, align);
PROTOBUF_UNPOISON_MEMORY_REGION(ptr(), n);
void* ret = internal::AlignTo(ptr(), align);
set_ptr(ptr() + n);
GOOGLE_DCHECK_GE(limit_, ptr());
AddCleanupFromExisting(ret, destructor);
return ret;
}
PROTOBUF_ALWAYS_INLINE
void AddCleanupFromExisting(void* elem, void (*destructor)(void*)) {
cleanup::Tag tag = cleanup::Type(destructor);
size_t n = cleanup::Size(tag);
PROTOBUF_UNPOISON_MEMORY_REGION(limit_ - n, n);
limit_ -= n;
GOOGLE_DCHECK_GE(limit_, ptr());
cleanup::CreateNode(tag, limit_, elem, destructor);
}
private:
friend class ThreadSafeArena;
// Creates a new SerialArena inside mem using the remaining memory as for
// future allocations.
// The `parent` arena must outlive the serial arena, which is guaranteed
// because the parent manages the lifetime of the serial arenas.
static SerialArena* New(SerialArena::Memory mem, ThreadSafeArena& parent);
// Free SerialArena returning the memory passed in to New
template <typename Deallocator>
Memory Free(Deallocator deallocator);
// Members are declared here to track sizeof(SerialArena) and hotness
// centrally. They are (roughly) laid out in descending order of hotness.
// 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.
std::atomic<char*> ptr_{nullptr};
// Limiting address up to which memory can be allocated from the head block.
char* limit_ = nullptr;
std::atomic<ArenaBlock*> head_{nullptr}; // Head of linked list of blocks.
std::atomic<size_t> space_used_{0}; // Necessary for metrics.
std::atomic<size_t> space_allocated_{0};
ThreadSafeArena& parent_;
// 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;
// Helper getters/setters to handle relaxed operations on atomic variables.
ArenaBlock* head() { return head_.load(std::memory_order_relaxed); }
const ArenaBlock* head() const {
return head_.load(std::memory_order_relaxed);
}
char* ptr() { return ptr_.load(std::memory_order_relaxed); }
const char* ptr() const { return ptr_.load(std::memory_order_relaxed); }
void set_ptr(char* ptr) { return ptr_.store(ptr, std::memory_order_relaxed); }
// Constructor is private as only New() should be used.
inline SerialArena(ArenaBlock* b, ThreadSafeArena& parent);
// Constructors to handle the first SerialArena.
inline explicit SerialArena(ThreadSafeArena& parent);
inline SerialArena(FirstSerialArena, ArenaBlock* b, ThreadSafeArena& parent);
void* AllocateAlignedFallback(size_t n);
void* AllocateAlignedWithCleanupFallback(size_t n, size_t align,
void (*destructor)(void*));
void AddCleanupFallback(void* elem, void (*destructor)(void*));
inline void AllocateNewBlock(size_t n);
inline void Init(ArenaBlock* b, size_t offset);
public:
static constexpr size_t kBlockHeaderSize = AlignUpTo8(sizeof(ArenaBlock));
};
// 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();
// Constructor solely used by message-owned arena.
explicit ThreadSafeArena(internal::MessageOwned);
ThreadSafeArena(char* mem, size_t size);
explicit ThreadSafeArena(void* mem, size_t size,
const AllocationPolicy& policy);
// All protos have pointers back to the arena hence Arena must have
// pointer stability.
ThreadSafeArena(const ThreadSafeArena&) = delete;
ThreadSafeArena& operator=(const ThreadSafeArena&) = delete;
ThreadSafeArena(ThreadSafeArena&&) = delete;
ThreadSafeArena& operator=(ThreadSafeArena&&) = delete;
// 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) {
SerialArena* arena;
if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) {
return arena->AllocateAligned<alloc_client>(n);
} else {
return AllocateAlignedFallback<alloc_client>(n);
}
}
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(GetSerialArenaFast(&arena))) {
return arena->MaybeAllocateAligned(n, out);
}
return false;
}
void* AllocateAlignedWithCleanup(size_t n, size_t align,
void (*destructor)(void*));
// 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:
friend class ArenaBenchmark;
friend class TcParser;
friend class SerialArena;
friend struct SerialArenaChunkHeader;
static uint64_t GetNextLifeCycleId();
class SerialArenaChunk;
// Returns a new SerialArenaChunk that has {id, serial} at slot 0. It may
// grow based on "prev_num_slots".
static SerialArenaChunk* NewSerialArenaChunk(uint32_t prev_capacity, void* id,
SerialArena* serial);
static SerialArenaChunk* SentrySerialArenaChunk();
// Returns the first ArenaBlock* for the first SerialArena. If users provide
// one, use it if it's acceptable. Otherwise returns a sentry block.
ArenaBlock* FirstBlock(void* buf, size_t size);
// Same as the above but returns a valid block if "policy" is not default.
ArenaBlock* FirstBlock(void* buf, size_t size,
const AllocationPolicy& policy);
// Adds SerialArena to the chunked list. May create a new chunk.
void AddSerialArena(void* id, SerialArena* serial);
// Members are declared here to track sizeof(ThreadSafeArena) and hotness
// centrally.
// Unique for each arena. Changes on Reset().
uint64_t tag_and_id_ = 0;
TaggedAllocationPolicyPtr alloc_policy_; // Tagged pointer to AllocPolicy.
ThreadSafeArenaStatsHandle arena_stats_;
// Adding a new chunk to head_ must be protected by mutex_.
absl::Mutex mutex_;
// Pointer to a linked list of SerialArenaChunk.
std::atomic<SerialArenaChunk*> head_{nullptr};
void* first_owner_;
// Must be declared after alloc_policy_; otherwise, it may lose info on
// user-provided initial block.
SerialArena first_arena_;
// The LSB of tag_and_id_ indicates if the arena is message-owned.
enum : uint64_t { kMessageOwnedArena = 1 };
static_assert(std::is_trivially_destructible<SerialArena>{},
"SerialArena needs to be trivially destructible.");
const AllocationPolicy* AllocPolicy() const { return alloc_policy_.get(); }
void InitializeWithPolicy(const AllocationPolicy& policy);
void* AllocateAlignedWithCleanupFallback(size_t n, size_t align,
void (*destructor)(void*));
void Init();
// Delete or Destruct all objects owned by the arena.
void CleanupList();
inline void CacheSerialArena(SerialArena* serial) {
if (!IsMessageOwned()) {
thread_cache().last_serial_arena = serial;
thread_cache().last_lifecycle_id_seen = tag_and_id_;
}
}
PROTOBUF_NDEBUG_INLINE bool GetSerialArenaFast(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;
}
// Finds SerialArena or creates one if not found. When creating a new one,
// create a big enough block to accommodate n bytes.
SerialArena* GetSerialArenaFallback(size_t n);
template <AllocationClient alloc_client = AllocationClient::kDefault>
void* AllocateAlignedFallback(size_t n);
// Executes callback function over SerialArenaChunk. Passes const
// SerialArenaChunk*.
template <typename Functor>
void WalkConstSerialArenaChunk(Functor fn) const;
// Executes callback function over SerialArenaChunk.
template <typename Functor>
void WalkSerialArenaChunk(Functor fn);
// Executes callback function over SerialArena in chunked list in reverse
// chronological order. Passes const SerialArena*.
template <typename Functor>
void PerConstSerialArenaInChunk(Functor fn) const;
// 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 {
constexpr CacheAlignedLifecycleIdGenerator() : id{0} {}
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
public:
// kBlockHeaderSize is sizeof(ArenaBlock), 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 constexpr size_t kAllocPolicySize =
AlignUpTo8(sizeof(AllocationPolicy));
static constexpr size_t kMaxCleanupNodeSize = 16;
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,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.
#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 "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "google/protobuf/message_lite.h"
#include "google/protobuf/parse_context.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 absl::Mutex mu{absl::kConstInit};
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) || !defined(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(absl::string_view value) {
TaggedStringPtr res;
res.SetAllocated(new std::string(value.data(), value.length()));
return res;
}
#ifndef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL
// Creates an arena allocated std::string value.
TaggedStringPtr CreateArenaString(Arena& arena, absl::string_view 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(absl::string_view 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 {
#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
if (arena == nullptr) {
auto* old = tagged_ptr_.GetIfAllocated();
tagged_ptr_ = CreateString(value);
delete old;
} else {
auto* old = UnsafeMutablePointer();
tagged_ptr_ = CreateArenaString(*arena, value);
old->assign("garbagedata");
}
#else // PROTOBUF_FORCE_COPY_DEFAULT_STRING
UnsafeMutablePointer()->assign(value.data(), value.length());
#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
}
}
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,481 @@
// 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 "absl/strings/string_view.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(absl::string_view 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(absl::string_view 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(absl::string_view{s}, arena);
}
inline void ArenaStringPtr::Set(const char* s, size_t n, Arena* arena) {
Set(absl::string_view{s, n}, arena);
}
inline void ArenaStringPtr::SetBytes(absl::string_view 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(absl::string_view{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 "absl/strings/string_view.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,246 @@
// 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>
#include <utility>
// 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_CONSTINIT std::atomic<ThreadSafeArenazConfigListener>
g_arenaz_config_listener{nullptr};
PROTOBUF_THREAD_LOCAL absl::profiling_internal::ExponentialBiased
g_exponential_biased_generator;
void TriggerThreadSafeArenazConfigListener() {
auto* listener = g_arenaz_config_listener.load(std::memory_order_acquire);
if (listener != nullptr) listener();
}
} // namespace
PROTOBUF_THREAD_LOCAL SamplingState global_sampling_state = {
/*next_sample=*/0, /*sample_stride=*/0};
ThreadSafeArenaStats::ThreadSafeArenaStats() { PrepareForSampling(0); }
ThreadSafeArenaStats::~ThreadSafeArenaStats() = default;
void ThreadSafeArenaStats::BlockStats::PrepareForSampling() {
num_allocations.store(0, std::memory_order_relaxed);
bytes_allocated.store(0, std::memory_order_relaxed);
bytes_used.store(0, std::memory_order_relaxed);
bytes_wasted.store(0, std::memory_order_relaxed);
}
void ThreadSafeArenaStats::PrepareForSampling(int64_t stride) {
for (auto& blockstats : block_histogram) blockstats.PrepareForSampling();
max_block_size.store(0, std::memory_order_relaxed);
thread_ids.store(0, std::memory_order_relaxed);
weight = stride;
// 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);
}
size_t ThreadSafeArenaStats::FindBin(size_t bytes) {
if (bytes <= kMaxSizeForBinZero) return 0;
if (bytes <= kMaxSizeForPenultimateBin) {
// absl::bit_width() returns one plus the base-2 logarithm of x, with any
// fractional part discarded.
return absl::bit_width(absl::bit_ceil(bytes)) - kLogMaxSizeForBinZero - 1;
}
return kBlockHistogramBins - 1;
}
std::pair<size_t, size_t> ThreadSafeArenaStats::MinMaxBlockSizeForBin(
size_t bin) {
ABSL_ASSERT(bin < kBlockHistogramBins);
if (bin == 0) return {1, kMaxSizeForBinZero};
if (bin < kBlockHistogramBins - 1) {
return {(1 << (kLogMaxSizeForBinZero + bin - 1)) + 1,
1 << (kLogMaxSizeForBinZero + bin)};
}
return {kMaxSizeForPenultimateBin + 1, std::numeric_limits<size_t>::max()};
}
void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t used,
size_t allocated, size_t wasted) {
// Update the allocated bytes for the current block.
ThreadSafeArenaStats::BlockStats& curr =
info->block_histogram[ThreadSafeArenaStats::FindBin(allocated)];
curr.bytes_allocated.fetch_add(allocated, std::memory_order_relaxed);
curr.num_allocations.fetch_add(1, std::memory_order_relaxed);
// Update the used and wasted bytes for the previous block.
ThreadSafeArenaStats::BlockStats& prev =
info->block_histogram[ThreadSafeArenaStats::FindBin(used + wasted)];
prev.bytes_used.fetch_add(used, std::memory_order_relaxed);
prev.bytes_wasted.fetch_add(wasted, std::memory_order_relaxed);
if (info->max_block_size.load(std::memory_order_relaxed) < allocated) {
info->max_block_size.store(allocated, std::memory_order_relaxed);
}
const uint64_t tid = 1ULL << (GetCachedTID() % 63);
info->thread_ids.fetch_or(tid, std::memory_order_relaxed);
}
ThreadSafeArenaStats* SampleSlow(SamplingState& sampling_state) {
bool first = sampling_state.next_sample < 0;
const int64_t next_stride = 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_stride >= 1);
sampling_state.next_sample = next_stride;
const int64_t old_stride =
absl::exchange(sampling_state.sample_stride, next_stride);
// 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(--sampling_state.next_sample > 0)) return nullptr;
return SampleSlow(sampling_state);
}
return GlobalThreadSafeArenazSampler().Register(old_stride);
}
void SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener l) {
g_arenaz_config_listener.store(l, std::memory_order_release);
}
bool IsThreadSafeArenazEnabled() {
return g_arenaz_enabled.load(std::memory_order_acquire);
}
void SetThreadSafeArenazEnabled(bool enabled) {
SetThreadSafeArenazEnabledInternal(enabled);
TriggerThreadSafeArenazConfigListener();
}
void SetThreadSafeArenazEnabledInternal(bool enabled) {
g_arenaz_enabled.store(enabled, std::memory_order_release);
}
void SetThreadSafeArenazSampleParameter(int32_t rate) {
SetThreadSafeArenazSampleParameterInternal(rate);
TriggerThreadSafeArenazConfigListener();
}
void SetThreadSafeArenazSampleParameterInternal(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)
}
}
int32_t ThreadSafeArenazSampleParameter() {
return g_arenaz_sample_parameter.load(std::memory_order_relaxed);
}
void SetThreadSafeArenazMaxSamples(int32_t max) {
SetThreadSafeArenazMaxSamplesInternal(max);
TriggerThreadSafeArenazConfigListener();
}
void SetThreadSafeArenazMaxSamplesInternal(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)
}
}
size_t ThreadSafeArenazMaxSamples() {
return GlobalThreadSafeArenazSampler().GetMaxSamples();
}
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {
if (next_sample >= 0) {
global_sampling_state.next_sample = next_sample;
global_sampling_state.sample_stride = 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 SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener) {}
void SetThreadSafeArenazEnabled(bool enabled) {}
void SetThreadSafeArenazEnabledInternal(bool enabled) {}
bool IsThreadSafeArenazEnabled() { return false; }
void SetThreadSafeArenazSampleParameter(int32_t rate) {}
void SetThreadSafeArenazSampleParameterInternal(int32_t rate) {}
int32_t ThreadSafeArenazSampleParameter() { return 0; }
void SetThreadSafeArenazMaxSamples(int32_t max) {}
void SetThreadSafeArenazMaxSamplesInternal(int32_t max) {}
size_t ThreadSafeArenazMaxSamples() { return 0; }
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,261 @@
// 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 <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <utility>
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace internal {
#if defined(PROTOBUF_ARENAZ_SAMPLE)
struct ThreadSafeArenaStats;
void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t used,
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. The
// 'stride' parameter is the number of ThreadSafeArenas that were instantiated
// between this sample and the previous one.
void PrepareForSampling(int64_t stride)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(init_mu);
// These fields are mutated by the various Record* APIs and need to be
// thread-safe.
struct BlockStats {
std::atomic<int> num_allocations;
std::atomic<size_t> bytes_allocated;
std::atomic<size_t> bytes_used;
std::atomic<size_t> bytes_wasted;
void PrepareForSampling();
};
// block_histogram is a kBlockHistogramBins sized histogram. The zeroth bin
// stores info about blocks of size \in [1, 1 << kLogMaxSizeForBinZero]. Bin
// i, where i > 0, stores info for blocks of size \in (max_size_bin (i-1),
// 1 << (kLogMaxSizeForBinZero + i)]. The final bin stores info about blocks
// of size \in [kMaxSizeForPenultimateBin + 1,
// std::numeric_limits<size_t>::max()].
static constexpr size_t kBlockHistogramBins = 15;
static constexpr size_t kLogMaxSizeForBinZero = 7;
static constexpr size_t kMaxSizeForBinZero = (1 << kLogMaxSizeForBinZero);
static constexpr size_t kMaxSizeForPenultimateBin =
1 << (kLogMaxSizeForBinZero + kBlockHistogramBins - 2);
std::array<BlockStats, kBlockHistogramBins> block_histogram;
// Records the largest block allocated for the arena.
std::atomic<size_t> max_block_size;
// Bit `i` is set to 1 indicates that a thread with `tid % 63 = i` accessed
// the underlying arena. We use `% 63` as a rudimentary hash to ensure some
// bit mixing for thread-ids; `% 64` would only grab the low bits and might
// create sampling artifacts.
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 used,
size_t allocated, size_t wasted) {
if (PROTOBUF_PREDICT_TRUE(info == nullptr)) return;
RecordAllocateSlow(info, used, allocated, wasted);
}
// Returns the bin for the provided size.
static size_t FindBin(size_t bytes);
// Returns the min and max bytes that can be stored in the histogram for
// blocks in the provided bin.
static std::pair<size_t, size_t> MinMaxBlockSizeForBin(size_t bin);
};
struct SamplingState {
// Number of ThreadSafeArenas that should be instantiated before the next
// ThreadSafeArena is sampled. This variable is decremented with each
// instantiation.
int64_t next_sample;
// When we make a sampling decision, we record that distance between from the
// previous sample so we can weight each sample. 'distance' here is the
// number of instantiations of ThreadSafeArena.
int64_t sample_stride;
};
ThreadSafeArenaStats* SampleSlow(SamplingState& sampling_state);
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;
}
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 SamplingState global_sampling_state;
// Returns an RAII sampling handle that manages registration and unregistation
// with the global sampler.
inline ThreadSafeArenaStatsHandle Sample() {
if (PROTOBUF_PREDICT_TRUE(--global_sampling_state.next_sample > 0)) {
return ThreadSafeArenaStatsHandle(nullptr);
}
return ThreadSafeArenaStatsHandle(SampleSlow(global_sampling_state));
}
#else
using SamplingState = int64_t;
struct ThreadSafeArenaStats {
static void RecordAllocateStats(ThreadSafeArenaStats*, size_t /*requested*/,
size_t /*allocated*/, size_t /*wasted*/) {}
};
ThreadSafeArenaStats* SampleSlow(SamplingState& 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();
using ThreadSafeArenazConfigListener = void (*)();
void SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener l);
// Enables or disables sampling for thread safe arenas.
void SetThreadSafeArenazEnabled(bool enabled);
void SetThreadSafeArenazEnabledInternal(bool enabled);
// Returns true if sampling is on, false otherwise.
bool IsThreadSafeArenazEnabled();
// Sets the rate at which thread safe arena will be sampled.
void SetThreadSafeArenazSampleParameter(int32_t rate);
void SetThreadSafeArenazSampleParameterInternal(int32_t rate);
// Returns the rate at which thread safe arena will be sampled.
int32_t ThreadSafeArenazSampleParameter();
// Sets a soft max for the number of samples that will be kept.
void SetThreadSafeArenazMaxSamples(int32_t max);
void SetThreadSafeArenazMaxSamplesInternal(int32_t max);
// Returns the max number of samples that will be kept.
size_t ThreadSafeArenazMaxSamples();
// 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,595 @@
// 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 <limits>
#include <memory>
#include <random>
#include <utility>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.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) {
for (const auto& block_stats : info.block_histogram) {
size_t bytes_allocated =
block_stats.bytes_allocated.load(std::memory_order_acquire);
if (bytes_allocated != 0) {
res.push_back(bytes_allocated);
}
}
});
return res;
}
ThreadSafeArenaStats* Register(ThreadSafeArenazSampler* s, size_t size,
int64_t stride) {
auto* info = s->Register(stride);
assert(info != nullptr);
info->block_histogram[0].bytes_allocated.store(size,
std::memory_order_relaxed);
return info;
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
namespace {
#if defined(PROTOBUF_ARENAZ_SAMPLE)
TEST(ThreadSafeArenaStatsTest, PrepareForSampling) {
ThreadSafeArenaStats info;
constexpr int64_t kTestStride = 107;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling(kTestStride);
for (const auto& block_stats : info.block_histogram) {
EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0);
}
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0);
EXPECT_EQ(info.weight, kTestStride);
for (auto& block_stats : info.block_histogram) {
block_stats.num_allocations.store(1, std::memory_order_relaxed);
block_stats.bytes_used.store(1, std::memory_order_relaxed);
block_stats.bytes_allocated.store(1, std::memory_order_relaxed);
block_stats.bytes_wasted.store(1, std::memory_order_relaxed);
}
info.max_block_size.store(1, std::memory_order_relaxed);
info.PrepareForSampling(2 * kTestStride);
for (auto& block_stats : info.block_histogram) {
EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0);
EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0);
}
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0);
EXPECT_EQ(info.weight, 2 * kTestStride);
}
TEST(ThreadSafeArenaStatsTest, FindBin) {
size_t current_bin = 0;
size_t bytes = 1;
while (current_bin < ThreadSafeArenaStats::kBlockHistogramBins - 1) {
size_t next_bin = ThreadSafeArenaStats::FindBin(bytes);
if (next_bin != current_bin) {
// Test the bins increase linearly.
EXPECT_EQ(next_bin, current_bin + 1);
// Test the bins change only at values of the form 2^k + 1.
EXPECT_EQ(absl::popcount(bytes - 1), 1);
current_bin = next_bin;
}
++bytes;
}
}
TEST(ThreadSafeArenaStatsTest, MinMaxBlockSizeForBin) {
std::pair<size_t, size_t> current_limits =
ThreadSafeArenaStats::MinMaxBlockSizeForBin(0);
EXPECT_EQ(current_limits.first, 1);
EXPECT_LT(current_limits.first, current_limits.second);
for (size_t i = 1; i < ThreadSafeArenaStats::kBlockHistogramBins; ++i) {
std::pair<size_t, size_t> next_limits =
ThreadSafeArenaStats::MinMaxBlockSizeForBin(i);
EXPECT_LT(next_limits.first, next_limits.second);
// Test the limits do not have gaps.
EXPECT_EQ(next_limits.first, current_limits.second + 1);
if (i != ThreadSafeArenaStats::kBlockHistogramBins - 1) {
EXPECT_EQ(next_limits.second, 2 * current_limits.second);
}
current_limits = next_limits;
}
// Test the limits cover the entire range possible.
EXPECT_EQ(current_limits.second, std::numeric_limits<size_t>::max());
}
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlow) {
ThreadSafeArenaStats info;
constexpr int64_t kTestStride = 458;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling(kTestStride);
RecordAllocateSlow(&info, /*requested=*/0, /*allocated=*/128, /*wasted=*/0);
EXPECT_EQ(
info.block_histogram[0].num_allocations.load(std::memory_order_relaxed),
1);
EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed),
0);
EXPECT_EQ(
info.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed),
128);
EXPECT_EQ(
info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 0);
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 128);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28);
EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed),
100);
EXPECT_EQ(
info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 28);
EXPECT_EQ(
info.block_histogram[1].num_allocations.load(std::memory_order_relaxed),
1);
EXPECT_EQ(
info.block_histogram[1].bytes_allocated.load(std::memory_order_relaxed),
256);
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256);
}
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlowMaxBlockSizeTest) {
ThreadSafeArenaStats info;
constexpr int64_t kTestStride = 458;
absl::MutexLock l(&info.init_mu);
info.PrepareForSampling(kTestStride);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0);
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 128);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28);
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128,
/*wasted=*/28);
EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256);
}
TEST(ThreadSafeArenazSamplerTest, SamplingCorrectness) {
SetThreadSafeArenazEnabled(true);
for (int p = 0; p <= 15; ++p) {
SetThreadSafeArenazSampleParameter(1 << p);
SetThreadSafeArenazGlobalNextSample(1 << p);
const int kTrials = 1000 << p;
std::vector<ThreadSafeArenaStatsHandle> hv;
for (int i = 0; i < kTrials; ++i) {
ThreadSafeArenaStatsHandle h = Sample();
if (h.MutableStats() != nullptr) hv.push_back(std::move(h));
}
// Ideally samples << p should be very close to kTrials. But we keep a
// factor of two guard band.
EXPECT_GE(hv.size() << p, kTrials / 2);
EXPECT_LE(hv.size() << p, 2 * kTrials);
}
}
TEST(ThreadSafeArenazSamplerTest, SmallSampleParameter) {
SetThreadSafeArenazEnabled(true);
SetThreadSafeArenazSampleParameter(100);
constexpr int64_t kTestStride = 0;
for (int i = 0; i < 1000; ++i) {
SamplingState sampling_state = {kTestStride, kTestStride};
ThreadSafeArenaStats* sample = SampleSlow(sampling_state);
EXPECT_GT(sampling_state.next_sample, 0);
EXPECT_NE(sample, nullptr);
UnsampleSlow(sample);
}
}
TEST(ThreadSafeArenazSamplerTest, LargeSampleParameter) {
SetThreadSafeArenazEnabled(true);
SetThreadSafeArenazSampleParameter(std::numeric_limits<int32_t>::max());
constexpr int64_t kTestStride = 0;
for (int i = 0; i < 1000; ++i) {
SamplingState sampling_state = {kTestStride, kTestStride};
ThreadSafeArenaStats* sample = SampleSlow(sampling_state);
EXPECT_GT(sampling_state.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();
constexpr int64_t kTestStride = 17;
ThreadSafeArenaStatsHandle h(sampler.Register(kTestStride));
auto* info = ThreadSafeArenaStatsHandlePeer::GetInfo(&h);
info->block_histogram[0].bytes_allocated.store(0x12345678,
std::memory_order_relaxed);
bool found = false;
sampler.Iterate([&](const ThreadSafeArenaStats& h) {
if (&h == info) {
EXPECT_EQ(
h.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed),
0x12345678);
EXPECT_EQ(h.weight, kTestStride);
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.block_histogram[0].bytes_allocated.load(
std::memory_order_relaxed) == 0x12345678) {
found = true;
}
}
});
EXPECT_FALSE(found);
}
TEST(ThreadSafeArenazSamplerTest, Registration) {
ThreadSafeArenazSampler sampler;
constexpr int64_t kTestStride = 100;
auto* info1 = Register(&sampler, 1, kTestStride);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1));
auto* info2 = Register(&sampler, 2, kTestStride);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2));
info1->block_histogram[0].bytes_allocated.store(3, std::memory_order_relaxed);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(3, 2));
sampler.Unregister(info1);
sampler.Unregister(info2);
}
TEST(ThreadSafeArenazSamplerTest, Unregistration) {
ThreadSafeArenazSampler sampler;
std::vector<ThreadSafeArenaStats*> infos;
constexpr int64_t kTestStride = 200;
for (size_t i = 0; i < 3; ++i) {
infos.push_back(Register(&sampler, i + 1, kTestStride));
}
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2, 3));
sampler.Unregister(infos[1]);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3));
infos.push_back(Register(&sampler, 3, kTestStride));
infos.push_back(Register(&sampler, 4, kTestStride));
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 3, 4));
sampler.Unregister(infos[3]);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 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) {
const int64_t sampling_stride = 11 + i % 3;
pool.Schedule([&sampler, &stop, sampling_stride]() {
std::random_device rd;
std::mt19937 gen(rd());
std::vector<ThreadSafeArenaStats*> infoz;
while (!stop.HasBeenNotified()) {
if (infoz.empty()) {
infoz.push_back(sampler.Register(sampling_stride));
}
switch (std::uniform_int_distribution<>(0, 1)(gen)) {
case 0: {
infoz.push_back(sampler.Register(sampling_stride));
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();
EXPECT_EQ(info->weight, sampling_stride);
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;
constexpr int64_t kTestStride = 203;
auto* info1 = Register(&sampler, 1, kTestStride);
auto* info2 = Register(&sampler, 2, kTestStride);
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);
}
TEST(ThreadSafeArenazSamplerTest, InitialBlockReportsZeroUsedAndWasted) {
SetThreadSafeArenazEnabled(true);
// Setting 1 as the parameter value means one in every two arenas would be
// sampled, on average.
int32_t oldparam = ThreadSafeArenazSampleParameter();
SetThreadSafeArenazSampleParameter(1);
SetThreadSafeArenazGlobalNextSample(0);
constexpr int kSize = 571;
int count_found_allocation = 0;
auto& sampler = GlobalThreadSafeArenazSampler();
for (int i = 0; i < 10; ++i) {
char block[kSize];
google::protobuf::Arena arena(/*initial_block=*/block, /*initial_block_size=*/kSize);
sampler.Iterate([&](const ThreadSafeArenaStats& h) {
const auto& histbin =
h.block_histogram[ThreadSafeArenaStats::FindBin(kSize)];
if (histbin.bytes_allocated.load(std::memory_order_relaxed) == kSize) {
count_found_allocation++;
EXPECT_EQ(histbin.bytes_used, 0);
EXPECT_EQ(histbin.bytes_wasted, 0);
}
});
}
EXPECT_GT(count_found_allocation, 0);
SetThreadSafeArenazSampleParameter(oldparam);
}
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,
absl::string_view 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.
int32_t oldparam = ThreadSafeArenazSampleParameter();
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, absl::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);
SetThreadSafeArenazSampleParameter(oldparam);
}
class SampleFirstArenaThread : public Thread {
protected:
void Run() override {
google::protobuf::Arena arena;
google::protobuf::ArenaSafeUniquePtr<
protobuf_test_messages::proto2::TestAllTypesProto2>
message = google::protobuf::MakeArenaSafeUnique<
protobuf_test_messages::proto2::TestAllTypesProto2>(&arena);
GOOGLE_CHECK(message != nullptr);
arena_created_.Notify();
samples_counted_.WaitForNotification();
}
public:
explicit SampleFirstArenaThread(const thread::Options& options)
: Thread(options, "SampleFirstArenaThread") {}
absl::Notification arena_created_;
absl::Notification samples_counted_;
};
// Test that the first arena created on a thread may and may not be chosen for
// sampling.
TEST(ThreadSafeArenazSamplerTest, SampleFirstArena) {
SetThreadSafeArenazEnabled(true);
auto& sampler = GlobalThreadSafeArenazSampler();
enum class SampleResult {
kSampled,
kUnsampled,
kSpoiled,
};
auto count_samples = [&]() {
int count = 0;
sampler.Iterate([&](const ThreadSafeArenaStats& h) { ++count; });
return count;
};
auto run_sample_experiment = [&]() {
int before = count_samples();
thread::Options options;
options.set_joinable(true);
SampleFirstArenaThread t(options);
t.Start();
t.arena_created_.WaitForNotification();
int during = count_samples();
t.samples_counted_.Notify();
t.Join();
int after = count_samples();
// If we didn't get back where we were, some other thread may have
// created an arena and produced an invalid experiment run.
if (before != after) return SampleResult::kSpoiled;
switch (during - before) {
case 1:
return SampleResult::kSampled;
case 0:
return SampleResult::kUnsampled;
default:
return SampleResult::kSpoiled;
}
};
constexpr int kTrials = 10000;
bool sampled = false;
bool unsampled = false;
for (int i = 0; i < kTrials; ++i) {
switch (run_sample_experiment()) {
case SampleResult::kSampled:
sampled = true;
break;
case SampleResult::kUnsampled:
unsampled = true;
break;
default:
break;
}
// This is the success criteria for the entire test. At some point
// we sampled the first arena and at some point we did not.
if (sampled && unsampled) return;
}
EXPECT_TRUE(sampled);
EXPECT_TRUE(unsampled);
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
} // namespace
} // namespace internal
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,375 @@
################################################################################
# Protocol Buffers Compiler
################################################################################
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load(
"@rules_pkg//:mappings.bzl",
"pkg_attributes",
"pkg_files",
"strip_prefix",
)
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//build_defs:arch_tests.bzl", "aarch64_test", "x86_64_test")
load("//build_defs:cpp_opts.bzl", "COPTS", "LINK_OPTS", "PROTOC_LINK_OPTS")
proto_library(
name = "plugin_proto",
srcs = ["plugin.proto"],
visibility = [
"//:__pkg__",
"//pkg:__pkg__",
],
deps = ["//:descriptor_proto"],
)
cc_library(
name = "importer",
srcs = [
"importer.cc",
"parser.cc",
],
hdrs = [
"importer.h",
"parser.h",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler",
visibility = ["//visibility:public"],
deps = [
"//src/google/protobuf:protobuf_nowkt",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "code_generator",
srcs = [
"code_generator.cc",
"plugin.cc",
"plugin.pb.cc",
],
hdrs = [
"code_generator.h",
"plugin.h",
"plugin.pb.h",
"scc.h",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler",
visibility = ["//visibility:public"],
deps = [
"//src/google/protobuf:protobuf_nowkt",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "command_line_interface",
srcs = [
"command_line_interface.cc",
"subprocess.cc",
"zip_writer.cc",
],
hdrs = [
"command_line_interface.h",
"subprocess.h",
"zip_writer.h",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler",
visibility = ["//visibility:public"],
deps = [
":code_generator",
":importer",
"//src/google/protobuf:protobuf_nowkt",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
cc_library(
name = "protoc_lib_nowkt",
srcs = [
"main.cc",
],
copts = COPTS,
deps = [
":code_generator",
":command_line_interface",
":importer",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler/cpp",
"//src/google/protobuf/compiler/csharp",
"//src/google/protobuf/compiler/java",
"//src/google/protobuf/compiler/objectivec",
"//src/google/protobuf/compiler/php",
"//src/google/protobuf/compiler/python",
"//src/google/protobuf/compiler/ruby",
],
)
cc_binary(
name = "protoc_nowkt",
copts = COPTS,
linkopts = LINK_OPTS + PROTOC_LINK_OPTS,
visibility = [
"//src/google/protobuf:__pkg__",
],
deps = [":protoc_lib_nowkt"],
)
cc_library(
name = "protoc_lib",
copts = COPTS,
visibility = [
"//:__pkg__",
"//pkg:__pkg__",
],
deps = [
":protoc_lib_nowkt",
"//:protobuf",
],
)
# Note: this is an alias for now. In the future, this rule will become the
# cc_binary for protoc, and //:protoc will become an alias.
alias(
name = "protoc",
actual = "//:protoc",
visibility = ["//visibility:public"],
)
# Test that the protoc binary is built for the correct architecture.
aarch64_test(
name = "protoc_aarch64_test",
bazel_binaries = ["//:protoc"],
)
x86_64_test(
name = "protoc_x86_64_test",
bazel_binaries = ["//:protoc"],
)
################################################################################
# Tests and support libraries
################################################################################
cc_library(
name = "annotation_test_util",
testonly = 1,
srcs = ["annotation_test_util.cc"],
hdrs = ["annotation_test_util.h"],
copts = COPTS,
strip_include_prefix = "/src",
visibility = ["//visibility:public"],
deps = [
":code_generator",
":command_line_interface",
"//:protobuf",
"//src/google/protobuf/io",
"//src/google/protobuf/testing",
],
)
################################################################################
# Tests
################################################################################
filegroup(
name = "plugin_proto_srcs",
testonly = 1,
srcs = [
"plugin.pb.cc",
"plugin.pb.h",
"plugin.proto",
],
visibility = [
"//src/google/protobuf/compiler/cpp:__pkg__",
],
)
exports_files(
srcs = ["plugin.proto"],
visibility = ["//:__pkg__"],
)
cc_library(
name = "mock_code_generator",
testonly = 1,
srcs = ["mock_code_generator.cc"],
hdrs = ["mock_code_generator.h"],
copts = COPTS,
strip_include_prefix = "/src",
visibility = ["//pkg:__pkg__"],
deps = [
":code_generator",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
],
)
cc_binary(
name = "test_plugin",
testonly = 1,
srcs = ["test_plugin.cc"],
copts = COPTS,
deps = [
":code_generator",
":mock_code_generator",
],
)
cc_test(
name = "command_line_interface_unittest",
srcs = ["command_line_interface_unittest.cc"],
copts = COPTS + select({
"//build_defs:config_msvc": [],
"//conditions:default": [
"-Wno-deprecated",
"-Wno-deprecated-declarations",
],
}) + [
# Note: This only works on Windows with symlinks and runfiles enabled.
"-DGOOGLE_PROTOBUF_TEST_PLUGIN_PATH=\\\"$(rootpath :test_plugin)\\\"",
],
data = [
":test_plugin",
"//:test_proto_srcs",
"//src/google/protobuf:testdata",
],
deps = [
":code_generator",
":command_line_interface",
":mock_code_generator",
"//:protobuf",
"//src/google/protobuf/compiler/cpp:names",
"//src/google/protobuf:cc_test_protos",
"//src/google/protobuf:test_util2",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "importer_unittest",
srcs = ["importer_unittest.cc"],
copts = COPTS,
deps = [
":importer",
"//:protobuf",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "parser_unittest",
srcs = ["parser_unittest.cc"],
copts = COPTS + select({
"//build_defs:config_msvc": [],
"//conditions:default": [
"-Wno-deprecated",
"-Wno-deprecated-declarations",
],
}),
deps = [
":importer",
"//:protobuf",
"//src/google/protobuf:cc_test_protos",
"//src/google/protobuf:test_util2",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
################################################################################
# Generates protoc release artifacts.
################################################################################
genrule(
name = "protoc_readme",
outs = ["readme.txt"],
cmd = """
echo "Protocol Buffers - Google's data interchange format
Copyright 2008 Google Inc.
https://developers.google.com/protocol-buffers/
This package contains a precompiled binary version of the protocol buffer
compiler (protoc). This binary is intended for users who want to use Protocol
Buffers in languages other than C++ but do not want to compile protoc
themselves. To install, simply place this binary somewhere in your PATH.
If you intend to use the included well known types then don't forget to
copy the contents of the 'include' directory somewhere as well, for example
into '/usr/local/include/'.
Please refer to our official github site for more installation instructions:
https://github.com/protocolbuffers/protobuf" > $@
""",
visibility = ["//:__pkg__"],
)
pkg_files(
name = "compiler_plugin_protos_files",
srcs = ["plugin.proto"],
prefix = "include/google/protobuf/compiler",
visibility = ["//pkg:__pkg__"],
)
pkg_files(
name = "protoc_files",
srcs = [":protoc"],
attributes = pkg_attributes(mode = "0555"),
prefix = "bin/",
visibility = ["//:__pkg__"],
)
################################################################################
# Distribution packaging
################################################################################
pkg_files(
name = "dist_files",
srcs = glob(["**/*"]),
strip_prefix = strip_prefix.from_root(""),
visibility = ["//src:__pkg__"],
)
filegroup(
name = "test_srcs",
srcs = glob([
"*_test.cc",
"*unittest.cc",
]) + [
"//src/google/protobuf/compiler/cpp:test_srcs",
"//src/google/protobuf/compiler/csharp:test_srcs",
"//src/google/protobuf/compiler/java:test_srcs",
"//src/google/protobuf/compiler/objectivec:test_srcs",
"//src/google/protobuf/compiler/php:test_srcs",
"//src/google/protobuf/compiler/python:test_srcs",
"//src/google/protobuf/compiler/ruby:test_srcs",
],
visibility = ["//pkg:__pkg__"],
)
filegroup(
name = "test_plugin_srcs",
srcs = ["test_plugin.cc"],
visibility = ["//pkg:__pkg__"],
)
filegroup(
name = "test_proto_srcs",
srcs = [
"//src/google/protobuf/compiler/cpp:test_proto_srcs",
],
visibility = ["//pkg:__pkg__"],
)

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,138 @@
// 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 "absl/strings/str_split.h"
#include "absl/strings/strip.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 = absl::StrSplit(text, ",", absl::SkipEmpty());
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 (absl::EndsWith(filename, ".protodevel")) {
return std::string(absl::StripSuffix(filename, ".protodevel"));
} else {
return std::string(absl::StripSuffix(filename, ".proto"));
}
}
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,206 @@
// 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/port.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:
CodeGenerator() {}
CodeGenerator(const CodeGenerator&) = delete;
CodeGenerator& operator=(const CodeGenerator&) = delete;
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; }
};
// 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:
GeneratorContext() {
}
GeneratorContext(const GeneratorContext&) = delete;
GeneratorContext& operator=(const GeneratorContext&) = delete;
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;
};
// 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,470 @@
// 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 <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "google/protobuf/port.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(const CommandLineInterface&) = delete;
CommandLineInterface& operator=(const CommandLineInterface&) = delete;
~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;
};
const GeneratorInfo* FindGeneratorByFlag(const std::string& name) const;
const GeneratorInfo* FindGeneratorByOption(const std::string& option) const;
using GeneratorMap = std::map<std::string, GeneratorInfo>;
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;
};
} // 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,260 @@
################################################################################
# Protocol Buffers Compiler - C++ code generator
################################################################################
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_proto_library", "cc_test")
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//build_defs:cpp_opts.bzl", "COPTS")
cc_library(
name = "names",
hdrs = ["names.h"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/cpp",
visibility = ["//visibility:public"],
deps = [
":names_internal",
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf:protobuf_nowkt",
],
)
cc_library(
name = "names_internal",
hdrs = [
"helpers.h",
"names.h",
"options.h",
],
srcs = [
"helpers.cc",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler/cpp",
visibility = ["//pkg:__pkg__"],
deps = [
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf:protobuf_nowkt",
],
)
cc_library(
name = "cpp",
srcs = [
"enum.cc",
"enum_field.cc",
"extension.cc",
"field.cc",
"file.cc",
"generator.cc",
"map_field.cc",
"message.cc",
"message_field.cc",
"padding_optimizer.cc",
"parse_function_generator.cc",
"primitive_field.cc",
"service.cc",
"string_field.cc",
],
hdrs = [
"enum.h",
"enum_field.h",
"extension.h",
"field.h",
"file.h",
"generator.h",
"map_field.h",
"message.h",
"message_field.h",
"message_layout_helper.h",
"padding_optimizer.h",
"parse_function_generator.h",
"primitive_field.h",
"service.h",
"string_field.h",
],
copts = COPTS,
include_prefix = "google/protobuf/compiler/cpp",
visibility = [
"//pkg:__pkg__",
"//src/google/protobuf/compiler:__pkg__",
],
deps = [
":names_internal",
":names",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/container:layout",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
],
)
proto_library(
name = "test_bad_identifiers_proto",
testonly = 1,
srcs = ["test_bad_identifiers.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_bad_identifiers_cc_proto",
testonly = 1,
deps = [":test_bad_identifiers_proto"],
)
proto_library(
name = "test_large_enum_value_proto",
testonly = 1,
srcs = ["test_large_enum_value.proto"],
strip_import_prefix = "/src",
)
cc_proto_library(
name = "test_large_enum_value_cc_proto",
testonly = 1,
deps = [":test_large_enum_value_proto"],
)
cc_library(
name = "unittest_lib",
hdrs = [
"unittest.h",
"unittest.inc",
],
strip_include_prefix = "/src",
visibility = ["//pkg:__pkg__"],
)
cc_test(
name = "unittest",
srcs = ["unittest.cc"],
copts = COPTS,
data = [
"//:test_proto_srcs",
"//src/google/protobuf:testdata",
],
deps = [
":cpp",
":test_bad_identifiers_cc_proto",
":unittest_lib",
"//:protobuf",
"//src/google/protobuf:cc_test_protos",
"//src/google/protobuf:test_util",
"//src/google/protobuf:test_util2",
"//src/google/protobuf/compiler:importer",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "bootstrap_unittest",
srcs = ["bootstrap_unittest.cc"],
data = [
"//:well_known_type_protos",
"//src/google/protobuf:descriptor_cc_srcs",
"//src/google/protobuf:descriptor_proto_srcs",
"//src/google/protobuf:testdata",
"//src/google/protobuf/compiler:plugin_proto_srcs",
],
deps = [
":cpp",
"//:protobuf",
"//src/google/protobuf:test_util2",
"//src/google/protobuf/compiler:importer",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "message_size_unittest",
srcs = ["message_size_unittest.cc"],
deps = [
"//:protobuf",
"//src/google/protobuf:cc_test_protos",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "metadata_test",
srcs = ["metadata_test.cc"],
deps = [
":cpp",
"//:protobuf",
"//src/google/protobuf/compiler:annotation_test_util",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "move_unittest",
srcs = ["move_unittest.cc"],
copts = COPTS,
deps = [
"//:protobuf",
"//src/google/protobuf:cc_test_protos",
"//src/google/protobuf:test_util",
"//src/google/protobuf/stubs:lite",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "plugin_unittest",
srcs = ["plugin_unittest.cc"],
deps = [
":cpp",
"//:protobuf",
"//src/google/protobuf/compiler:command_line_interface",
"//src/google/protobuf/io",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
################################################################################
# Distribution packaging
################################################################################
pkg_files(
name = "dist_files",
srcs = glob(["**/*"]),
strip_prefix = strip_prefix.from_root(""),
visibility = ["//src:__pkg__"],
)
filegroup(
name = "test_srcs",
srcs = glob([
"*_test.cc",
"*unittest.cc",
]),
visibility = ["//src/google/protobuf/compiler:__pkg__"],
)
filegroup(
name = "test_proto_srcs",
srcs = [
"test_bad_identifiers.proto",
"test_large_enum_value.proto",
],
visibility = ["//src/google/protobuf/compiler:__pkg__"],
)

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.
//
// 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 <string>
#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/testing/googletest.h"
#include <gtest/gtest.h>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/substitute.h"
#include "google/protobuf/compiler/cpp/helpers.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
std::string FindWithDefault(
const absl::flat_hash_map<std::string, std::string>& m,
const std::string& k, const std::string& v) {
auto it = m.find(k);
if (it == m.end()) return v;
return it->second;
}
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 {
absl::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;
#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.
absl::flat_hash_map<std::string, std::string> vpath_map;
absl::flat_hash_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,36 @@
// 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_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,574 @@
// 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 <algorithm>
#include <cstdint>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/descriptor.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/names.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
absl::flat_hash_map<std::string, std::string> EnumVars(
const EnumDescriptor* enum_, const Options& options,
const EnumValueDescriptor* min, const EnumValueDescriptor* max) {
auto classname = ClassName(enum_, false);
return {
{"Enum", enum_->name()},
{"Enum_", ResolveKeyword(enum_->name())},
{"Msg_Enum", classname},
{"::Msg_Enum", QualifiedClassName(enum_, options)},
{"Msg_Enum_",
enum_->containing_type() == nullptr ? "" : absl::StrCat(classname, "_")},
{"kMin", absl::StrCat(min->number())},
{"kMax", absl::StrCat(max->number())},
};
}
// The ARRAYSIZE constant is the max enum value plus 1. If the max enum value
// is kint32max, ARRAYSIZE will overflow. In such cases we should omit the
// generation of the 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();
}
} // namespace
EnumGenerator::ValueLimits EnumGenerator::ValueLimits::FromEnum(
const EnumDescriptor* descriptor) {
const EnumValueDescriptor* min_desc = descriptor->value(0);
const EnumValueDescriptor* max_desc = descriptor->value(0);
for (int i = 1; i < descriptor->value_count(); ++i) {
if (descriptor->value(i)->number() < min_desc->number()) {
min_desc = descriptor->value(i);
}
if (descriptor->value(i)->number() > max_desc->number()) {
max_desc = descriptor->value(i);
}
}
return EnumGenerator::ValueLimits{min_desc, max_desc};
}
EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
const Options& options)
: enum_(descriptor),
options_(options),
generate_array_size_(ShouldGenerateArraySize(descriptor)),
has_reflection_(HasDescriptorMethods(enum_->file(), options_)),
limits_(ValueLimits::FromEnum(enum_)) {
// The conditions here for what is "sparse" are not rigorously
// chosen.
size_t values_range = static_cast<size_t>(limits_.max->number()) -
static_cast<size_t>(limits_.min->number());
size_t total_values = static_cast<size_t>(enum_->value_count());
should_cache_ = has_reflection_ &&
(values_range < 16u || values_range < total_values * 2u);
}
void EnumGenerator::GenerateDefinition(io::Printer* p) {
auto v1 = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
auto v2 = p->WithVars({
{"Msg_Enum_Enum_MIN",
absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(), "_MIN"), enum_},
{"Msg_Enum_Enum_MAX",
absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(), "_MAX"), enum_},
});
p->Emit(
{
{"values",
[&] {
for (int i = 0; i < enum_->value_count(); ++i) {
const auto* value = enum_->value(i);
p->Emit(
{
{
"Msg_Enum_VALUE",
absl::StrCat(p->LookupVar("Msg_Enum_"),
EnumValueName(value)),
value,
},
{"kNumber", Int32ToString(value->number())},
{"DEPRECATED", value->options().deprecated()
? "PROTOBUF_DEPRECATED_ENUM"
: ""},
},
R"cc(
$Msg_Enum_VALUE$$ DEPRECATED$ = $kNumber$,
)cc");
}
}},
// Only emit annotations for the $Msg_Enum$ used in the `enum`
// definition.
{"Msg_Enum_annotated", p->LookupVar("Msg_Enum"), enum_},
{"open_enum_sentinels",
[&] {
if (enum_->is_closed()) {
return;
}
// For open enum semantics: generate min and max sentinel values
// equal to INT32_MIN and INT32_MAX
p->Emit({{"Msg_Enum_Msg_Enum_",
absl::StrCat(p->LookupVar("Msg_Enum"), "_",
p->LookupVar("Msg_Enum_"))}},
R"cc(
$Msg_Enum_Msg_Enum_$INT_MIN_SENTINEL_DO_NOT_USE_ =
std::numeric_limits<int32_t>::min(),
$Msg_Enum_Msg_Enum_$INT_MAX_SENTINEL_DO_NOT_USE_ =
std::numeric_limits<int32_t>::max(),
)cc");
}},
},
R"cc(
enum $Msg_Enum_annotated$ : int {
$values$,
$open_enum_sentinels$,
};
$dllexport_decl $bool $Msg_Enum$_IsValid(int value);
constexpr $Msg_Enum$ $Msg_Enum_Enum_MIN$ = static_cast<$Msg_Enum$>($kMin$);
constexpr $Msg_Enum$ $Msg_Enum_Enum_MAX$ = static_cast<$Msg_Enum$>($kMax$);
)cc");
if (generate_array_size_) {
p->Emit(
{{"Msg_Enum_Enum_ARRAYSIZE",
absl::StrCat(p->LookupVar("Msg_Enum_"), enum_->name(), "_ARRAYSIZE"),
enum_}},
R"cc(
constexpr int $Msg_Enum_Enum_ARRAYSIZE$ = $kMax$ + 1;
)cc");
}
if (has_reflection_) {
p->Emit(R"cc(
$dllexport_decl $const ::$proto_ns$::EnumDescriptor*
$Msg_Enum$_descriptor();
)cc");
} else {
p->Emit(R"cc(
const std::string& $Msg_Enum$_Name($Msg_Enum$ value);
)cc");
}
// There are three possible implementations of $Enum$_Name() and
// $Msg_Enum$_Parse(), depending on whether we are using a dense enum name
// cache or not, and whether or not we have reflection. Very little code is
// shared between the three, so it is split into three Emit() calls.
// Can't use WithVars here, since callbacks can only be passed to Emit()
// directly. Because this includes $Enum$, it must be a callback.
auto write_assert = [&] {
p->Emit(R"cc(
static_assert(std::is_same<T, $Msg_Enum$>::value ||
std::is_integral<T>::value,
"Incorrect type passed to $Enum$_Name().");
)cc");
};
if (should_cache_ || !has_reflection_) {
p->Emit({{"static_assert", write_assert}}, R"cc(
template <typename T>
const std::string& $Msg_Enum$_Name(T value) {
$static_assert$;
return $Msg_Enum$_Name(static_cast<$Msg_Enum$>(value));
}
)cc");
if (should_cache_) {
// Using the NameOfEnum routine can be slow, so we create a small
// cache of pointers to the std::string objects that reflection
// stores internally. This cache is a simple contiguous array of
// pointers, so if the enum values are sparse, it's not worth it.
p->Emit(R"cc(
template <>
inline const std::string& $Msg_Enum$_Name($Msg_Enum$ value) {
return ::$proto_ns$::internal::NameOfDenseEnum<$Msg_Enum$_descriptor,
$kMin$, $kMax$>(
static_cast<int>(value));
}
)cc");
} else {
p->Emit(R"cc(
const std::string& $Msg_Enum$_Name($Msg_Enum$ value);
)cc");
}
} else {
p->Emit({{"static_assert", write_assert}}, R"cc(
template <typename T>
const std::string& $Msg_Enum$_Name(T value) {
$static_assert$;
return ::$proto_ns$::internal::NameOfEnum($Msg_Enum$_descriptor(), value);
}
)cc");
}
if (has_reflection_) {
p->Emit(R"cc(
inline bool $Msg_Enum$_Parse(absl::string_view name, $Msg_Enum$* value) {
return ::$proto_ns$::internal::ParseNamedEnum<$Msg_Enum$>(
$Msg_Enum$_descriptor(), name, value);
}
)cc");
} else {
p->Emit(R"cc(
bool $Msg_Enum$_Parse(absl::string_view name, $Msg_Enum$* value);
)cc");
}
}
void EnumGenerator::GenerateGetEnumDescriptorSpecializations(io::Printer* p) {
auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
p->Emit(R"cc(
template <>
struct is_proto_enum<$::Msg_Enum$> : std::true_type {};
)cc");
if (!has_reflection_) {
return;
}
p->Emit(R"cc(
template <>
inline const EnumDescriptor* GetEnumDescriptor<$::Msg_Enum$>() {
return $::Msg_Enum$_descriptor();
}
)cc");
}
void EnumGenerator::GenerateSymbolImports(io::Printer* p) const {
auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
p->Emit(R"cc(
using $Enum_$ = $Msg_Enum$;
)cc");
for (int j = 0; j < enum_->value_count(); ++j) {
const auto* value = enum_->value(j);
p->Emit(
{
{"VALUE", EnumValueName(enum_->value(j)), value},
{"DEPRECATED",
value->options().deprecated() ? "PROTOBUF_DEPRECATED_ENUM" : ""},
},
R"cc(
$DEPRECATED $static constexpr $Enum_$ $VALUE$ = $Msg_Enum$_$VALUE$;
)cc");
}
p->Emit(
{
{"Enum_MIN", absl::StrCat(enum_->name(), "_MIN"), enum_},
{"Enum_MAX", absl::StrCat(enum_->name(), "_MAX"), enum_},
},
R"cc(
static inline bool $Enum$_IsValid(int value) {
return $Msg_Enum$_IsValid(value);
}
static constexpr $Enum_$ $Enum_MIN$ = $Msg_Enum$_$Enum$_MIN;
static constexpr $Enum_$ $Enum_MAX$ = $Msg_Enum$_$Enum$_MAX;
)cc");
if (generate_array_size_) {
p->Emit(
{{"Enum_ARRAYSIZE", absl::StrCat(enum_->name(), "_ARRAYSIZE"), enum_}},
R"cc(
static constexpr int $Enum_ARRAYSIZE$ = $Msg_Enum$_$Enum$_ARRAYSIZE;
)cc");
}
if (has_reflection_) {
p->Emit(R"cc(
static inline const ::$proto_ns$::EnumDescriptor* $Enum$_descriptor() {
return $Msg_Enum$_descriptor();
}
)cc");
}
p->Emit(R"cc(
template <typename T>
static inline const std::string& $Enum$_Name(T value) {
return $Msg_Enum$_Name(value);
}
static inline bool $Enum$_Parse(absl::string_view name, $Enum_$* value) {
return $Msg_Enum$_Parse(name, value);
}
)cc");
}
void EnumGenerator::GenerateMethods(int idx, io::Printer* p) {
auto v = p->WithVars(EnumVars(enum_, options_, limits_.min, limits_.max));
if (has_reflection_) {
p->Emit({{"idx", idx}}, R"cc(
const ::$proto_ns$::EnumDescriptor* $Msg_Enum$_descriptor() {
::$proto_ns$::internal::AssignDescriptors(&$desc_table$);
return $file_level_enum_descriptors$[$idx$];
}
)cc");
}
p->Emit({{"cases",
[&] {
// 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::vector<int> numbers;
numbers.reserve(enum_->value_count());
for (int i = 0; i < enum_->value_count(); ++i) {
numbers.push_back(enum_->value(i)->number());
}
// Sort and deduplicate `numbers`.
absl::c_sort(numbers);
numbers.erase(std::unique(numbers.begin(), numbers.end()),
numbers.end());
for (int n : numbers) {
p->Emit({{"n", n}}, R"cc(
case $n$:
)cc");
}
}}},
R"(
bool $Msg_Enum$_IsValid(int value) {
switch (value) {
$cases$;
return true;
default:
return false;
}
}
)");
if (!has_reflection_) {
// 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.
absl::btree_map<std::string, int> name_to_number;
absl::flat_hash_map<int, std::string> number_to_canonical_name;
for (int i = 0; i < enum_->value_count(); ++i) {
const auto* value = enum_->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());
}
// Build the offset table for the strings table.
struct Offset {
int number;
size_t index, byte_offset, len;
};
std::vector<Offset> offsets;
size_t index = 0;
size_t offset = 0;
for (const auto& e : name_to_number) {
offsets.push_back(Offset{e.second, index, offset, e.first.size()});
++index;
offset += e.first.size();
}
absl::c_sort(offsets, [](const auto& a, const auto& b) {
return a.byte_offset < b.byte_offset;
});
std::vector<Offset> offsets_by_number = offsets;
absl::c_sort(offsets_by_number, [](const auto& a, const auto& b) {
return a.number < b.number;
});
offsets_by_number.erase(
std::unique(
offsets_by_number.begin(), offsets_by_number.end(),
[](const auto& a, const auto& b) { return a.number == b.number; }),
offsets_by_number.end());
p->Emit(
{
{"num_unique", number_to_canonical_name.size()},
{"num_declared", enum_->value_count()},
{"names",
// 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.
[&] {
for (const auto& e : name_to_number) {
p->Emit({{"name", e.first}}, R"cc(
"$name$"
)cc");
}
}},
{"entries",
[&] {
for (const auto& offset : offsets) {
p->Emit({{"number", offset.number},
{"offset", offset.byte_offset},
{"len", offset.len}},
R"cc(
{{&$Msg_Enum$_names[$offset$], $len$}, $number$},
)cc");
}
}},
{"entries_by_number",
[&] {
for (const auto& offset : offsets_by_number) {
p->Emit({{"number", offset.number},
{"index", offset.index},
{"name", number_to_canonical_name[offset.number]}},
R"cc(
$index$, // $number$ -> $name$
)cc");
}
}},
},
R"cc(
static ::$proto_ns$::internal::ExplicitlyConstructed<std::string>
$Msg_Enum$_strings[$num_unique$] = {};
static const char $Msg_Enum$_names[] = {
$names$,
};
static const ::$proto_ns$::internal::EnumEntry $Msg_Enum$_entries[] =
{
$entries$,
};
static const int $Msg_Enum$_entries_by_number[] = {
$entries_by_number$,
};
const std::string& $Msg_Enum$_Name($Msg_Enum$ value) {
static const bool kDummy =
::$proto_ns$::internal::InitializeEnumStrings(
$Msg_Enum$_entries, $Msg_Enum$_entries_by_number,
$num_unique$, $Msg_Enum$_strings);
(void)kDummy;
int idx = ::$proto_ns$::internal::LookUpEnumName(
$Msg_Enum$_entries, $Msg_Enum$_entries_by_number, $num_unique$,
value);
return idx == -1 ? ::$proto_ns$::internal::GetEmptyString()
: $Msg_Enum$_strings[idx].get();
}
bool $Msg_Enum$_Parse(absl::string_view name, $Msg_Enum$* value) {
int int_value;
bool success = ::$proto_ns$::internal::LookUpEnumValue(
$Msg_Enum$_entries, $num_declared$, name, &int_value);
if (success) {
*value = static_cast<$Msg_Enum$>(int_value);
}
return success;
}
)cc");
}
if (enum_->containing_type() != nullptr) {
// 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.
p->Emit(
{
{"Msg_", ClassName(enum_->containing_type(), false)},
{"constexpr_storage",
[&] {
for (int i = 0; i < enum_->value_count(); i++) {
p->Emit({{"VALUE", EnumValueName(enum_->value(i))}},
R"cc(
constexpr $Msg_Enum$ $Msg_$::$VALUE$;
)cc");
}
}},
{"array_size",
[&] {
if (generate_array_size_) {
p->Emit(R"cc(
constexpr int $Msg_$::$Enum$_ARRAYSIZE;
)cc");
}
}},
},
R"(
#if (__cplusplus < 201703) && \
(!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
$constexpr_storage$;
constexpr $Msg_Enum$ $Msg_$::$Enum$_MIN;
constexpr $Msg_Enum$ $Msg_$::$Enum$_MAX;
$array_size$;
#endif // (__cplusplus < 201703) &&
// (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
)");
}
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,105 @@
// 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"
#include "google/protobuf/io/printer.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class EnumGenerator {
public:
EnumGenerator(const EnumDescriptor* descriptor, const Options& options);
EnumGenerator(const EnumGenerator&) = delete;
EnumGenerator& operator=(const EnumGenerator&) = delete;
~EnumGenerator() = default;
// 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* p);
// Generate specialization of GetEnumDescriptor<MyEnum>().
// Precondition: in ::google::protobuf namespace.
void GenerateGetEnumDescriptorSpecializations(io::Printer* p);
// 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* p) 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* p);
private:
friend class FileGenerator;
struct ValueLimits {
const EnumValueDescriptor* min;
const EnumValueDescriptor* max;
static ValueLimits FromEnum(const EnumDescriptor* descriptor);
};
const EnumDescriptor* enum_;
Options options_;
bool generate_array_size_;
bool should_cache_;
bool has_reflection_;
ValueLimits limits_;
};
} // 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/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 (!internal::cpp::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 (!internal::cpp::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 ::$proto_ns$::internal::CachedSize "
"$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 (!internal::cpp::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 (!internal::cpp::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$.Get();\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$.Set(cached_size);\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,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_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(const EnumFieldGenerator&) = delete;
EnumFieldGenerator& operator=(const EnumFieldGenerator&) = delete;
~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;
};
class EnumOneofFieldGenerator : public EnumFieldGenerator {
public:
EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
EnumOneofFieldGenerator(const EnumOneofFieldGenerator&) = delete;
EnumOneofFieldGenerator& operator=(const EnumOneofFieldGenerator&) = delete;
~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;
};
class RepeatedEnumFieldGenerator : public FieldGenerator {
public:
RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
~RepeatedEnumFieldGenerator() override;
RepeatedEnumFieldGenerator(const RepeatedEnumFieldGenerator&) = delete;
RepeatedEnumFieldGenerator& operator=(const RepeatedEnumFieldGenerator&) =
delete;
// 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;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__

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.
// 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 "absl/strings/str_cat.h"
#include "absl/strings/str_replace.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;
}
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"] =
absl::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"] = absl::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
? absl::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 =
absl::StrReplaceAll(variables_["scoped_name"], {{"::", "_"}}) +
"_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/compiler/cpp/options.h"
#include "google/protobuf/port.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(const ExtensionGenerator&) = delete;
ExtensionGenerator& operator=(const ExtensionGenerator&) = delete;
~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_;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__

View File

@@ -0,0 +1,448 @@
// 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 "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/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,
absl::string_view annotation_name,
absl::string_view substitute_template_prefix,
absl::string_view prepared_template,
int field_index, absl::string_view 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)[absl::StrCat("annotate_", annotation_name)] = absl::Substitute(
absl::StrCat(substitute_template_prefix, prepared_template, ");\n"),
field_index, access_type);
}
std::string GenerateTemplateForOneofString(const FieldDescriptor* descriptor,
absl::string_view proto_ns,
absl::string_view 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 absl::Substitute(absl::StrCat("_internal_has_", field_name, "() ? ",
field_pointer, ": nullptr"),
field_member);
}
if (descriptor->options().ctype() == google::protobuf::FieldOptions::STRING_PIECE) {
return absl::Substitute(absl::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 absl::Substitute(
absl::StrCat("_internal_has_", field_name, "() ? ", field_pointer, " : ",
default_value_pointer),
field_member, MakeDefaultFieldName(descriptor));
}
std::string GenerateTemplateForSingleString(const FieldDescriptor* descriptor,
absl::string_view field_member) {
if (descriptor->default_value_string().empty()) {
return absl::StrCat("&", field_member);
}
if (descriptor->options().ctype() == google::protobuf::FieldOptions::STRING) {
return absl::Substitute(
"$0.IsDefault() ? &$1.get() : $0.UnsafeGetPointer()", field_member,
MakeDefaultFieldName(descriptor));
}
return absl::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 < ABSL_ARRAYSIZE(kAccessorsAnnotations); ++i) {
(*variables)[kAccessorsAnnotations[i]] = "";
}
if (options.annotate_accessor) {
for (size_t i = 0; i < ABSL_ARRAYSIZE(kAccessorsAnnotations); ++i) {
(*variables)[kAccessorsAnnotations[i]] = absl::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 substitute_template_prefix =
absl::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 = absl::Substitute("&$0.Get(index)", field_member);
prepared_add_template =
absl::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, ProtobufNamespace(options), field_member);
} else {
prepared_template =
GenerateTemplateForSingleString(descriptor, field_member);
}
} else {
prepared_template = absl::StrCat("&", field_member);
}
if (descriptor->is_repeated() && !descriptor->is_map() &&
descriptor->type() != FieldDescriptor::TYPE_MESSAGE &&
descriptor->type() != FieldDescriptor::TYPE_GROUP) {
prepared_flat_template = absl::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);
}
absl::flat_hash_map<std::string, std::string> FieldVars(
const FieldDescriptor* desc, const Options& opts) {
bool split = ShouldSplit(desc, opts);
absl::flat_hash_map<std::string, std::string> vars = {
{"ns", Namespace(desc, opts)},
{"name", FieldName(desc)},
{"index", absl::StrCat(desc->index())},
{"number", absl::StrCat(desc->number())},
{"classname", ClassName(FieldScope(desc), false)},
{"declared_type", DeclaredTypeMethodName(desc->type())},
{"field", FieldMemberName(desc, split)},
{"tag_size",
absl::StrCat(WireFormat::TagSize(desc->number(), desc->type()))},
{"deprecated_attr", DeprecatedAttribute(opts, desc)},
{"set_hasbit", ""},
{"clear_hasbit", ""},
{"maybe_prepare_split_message",
split ? "PrepareSplitMessageForWrite();" : ""},
// 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.
{"{", ""},
{"}", ""},
};
// TODO(b/245791219): Refactor AddAccessorAnnotations to avoid this
// workaround.
std::map<std::string, std::string> workaround = {
{"field", vars["field"]},
{"tracker", "Impl_::_tracker_"},
};
AddAccessorAnnotations(desc, opts, &workaround);
for (auto& pair : workaround) {
vars.emplace(pair);
}
return vars;
}
void SetCommonFieldVariables(const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables,
const Options& options) {
SetCommonMessageDataVariables(descriptor->containing_type(), variables);
for (auto& pair : FieldVars(descriptor, options)) {
variables->emplace(pair);
}
}
absl::flat_hash_map<std::string, std::string> OneofFieldVars(
const FieldDescriptor* descriptor) {
if (descriptor->containing_oneof() == nullptr) {
return {};
}
return {{"oneof_name", descriptor->containing_oneof()->name()}};
}
void SetCommonOneofFieldVariables(
const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables) {
for (auto& pair : OneofFieldVars(descriptor)) {
variables->emplace(pair);
}
}
void FieldGenerator::SetHasBitIndex(int32_t has_bit_index) {
if (!internal::cpp::HasHasbit(descriptor_)) {
GOOGLE_CHECK_EQ(has_bit_index, -1);
return;
}
variables_["set_hasbit"] = absl::StrCat(
variables_["has_bits"], "[", has_bit_index / 32, "] |= 0x",
absl::Hex(1u << (has_bit_index % 32), absl::kZeroPad8), "u;");
variables_["clear_hasbit"] = absl::StrCat(
variables_["has_bits"], "[", has_bit_index / 32, "] &= ~0x",
absl::Hex(1u << (has_bit_index % 32), absl::kZeroPad8), "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"] = absl::StrCat(
"(", variables_["inlined_string_donated_array"], "[",
inlined_string_index / 32, "] & 0x",
absl::Hex(1u << (inlined_string_index % 32), absl::kZeroPad8),
"u) != 0;");
variables_["donating_states_word"] =
absl::StrCat(variables_["inlined_string_donated_array"], "[",
inlined_string_index / 32, "]");
variables_["mask_for_undonate"] = absl::StrCat(
"~0x", absl::Hex(1u << (inlined_string_index % 32), absl::kZeroPad8),
"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");
}
}
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,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.
// 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 {
absl::flat_hash_map<std::string, std::string> FieldVars(
const FieldDescriptor* desc, const Options& opts);
absl::flat_hash_map<std::string, std::string> OneofFieldVars(
const FieldDescriptor* descriptor);
// 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) {}
FieldGenerator(const FieldGenerator&) = delete;
FieldGenerator& operator=(const FieldGenerator&) = delete;
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 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_;
};
// Convenience class which constructs FieldGenerators for a Descriptor.
class FieldGeneratorMap {
public:
FieldGeneratorMap(const Descriptor* descriptor, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
FieldGeneratorMap(const FieldGeneratorMap&) = delete;
FieldGeneratorMap& operator=(const FieldGeneratorMap&) = delete;
~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);
};
} // 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,213 @@
// 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 <functional>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "google/protobuf/stubs/common.h"
#include "google/protobuf/compiler/scc.h"
#include "absl/container/flat_hash_set.h"
#include "google/protobuf/compiler/cpp/enum.h"
#include "google/protobuf/compiler/cpp/extension.h"
#include "google/protobuf/compiler/cpp/field.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/message.h"
#include "google/protobuf/compiler/cpp/options.h"
#include "google/protobuf/compiler/cpp/service.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/port.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class FileGenerator {
public:
FileGenerator(const FileDescriptor* file, const Options& options);
FileGenerator(const FileGenerator&) = delete;
FileGenerator& operator=(const FileGenerator&) = delete;
~FileGenerator() = default;
// 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* p, absl::string_view 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* p, absl::string_view info_path);
void GenerateSource(io::Printer* p);
// 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* p);
// Generates the source file for one extension.
void GenerateSourceForExtension(int idx, io::Printer* p);
// Generates a source file containing everything except messages and
// extensions.
void GenerateGlobalSource(io::Printer* p);
private:
// Generates a file, setting up the necessary accoutrements that start and
// end the file, calling `cb` in between.
//
// This includes header guards and file-global variables.
void GenerateFile(io::Printer* p, GeneratedFileType file_type,
std::function<void()> cb);
// Shared code between the two header generators.
void GenerateSharedHeaderCode(io::Printer* p);
// Internal type used by GenerateForwardDeclarations (defined in file.cc).
class ForwardDeclarations;
struct CrossFileReferences;
void IncludeFile(absl::string_view google3_name, io::Printer* p) {
DoIncludeFile(google3_name, false, p);
}
void IncludeFileAndExport(absl::string_view google3_name, io::Printer* p) {
DoIncludeFile(google3_name, true, p);
}
void DoIncludeFile(absl::string_view google3_name, bool do_export,
io::Printer* p);
std::string CreateHeaderInclude(absl::string_view 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* p);
void GenerateSourceIncludes(io::Printer* p);
void GenerateSourcePrelude(io::Printer* p);
void GenerateSourceDefaultInstance(int idx, io::Printer* p);
void GenerateInitForSCC(const SCC* scc, const CrossFileReferences& refs,
io::Printer* p);
void GenerateReflectionInitializationCode(io::Printer* p);
// For other imports, generates their forward-declarations.
void GenerateForwardDeclarations(io::Printer* p);
// Generates top or bottom of a header file.
void GenerateTopHeaderGuard(io::Printer* p, GeneratedFileType file_type);
void GenerateBottomHeaderGuard(io::Printer* p, GeneratedFileType file_type);
// Generates #include directives.
void GenerateLibraryIncludes(io::Printer* p);
void GenerateDependencyIncludes(io::Printer* p);
// 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* p, absl::string_view info_path);
// Generates a couple of different pieces before definitions:
void GenerateGlobalStateFunctionDeclarations(io::Printer* p);
// Generates types for classes.
void GenerateMessageDefinitions(io::Printer* p);
void GenerateEnumDefinitions(io::Printer* p);
// Generates generic service definitions.
void GenerateServiceDefinitions(io::Printer* p);
// Generates extension identifiers.
void GenerateExtensionIdentifiers(io::Printer* p);
// Generates inline function definitions.
void GenerateInlineFunctionDefinitions(io::Printer* p);
void GenerateProto2NamespaceEnumSpecializations(io::Printer* p);
// 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* p);
bool IsDepWeak(const FileDescriptor* dep) const {
if (weak_deps_.count(dep) != 0) {
GOOGLE_CHECK(!options_.opensource_runtime);
return true;
}
return false;
}
absl::flat_hash_set<const FileDescriptor*> weak_deps_;
const FileDescriptor* file_;
Options options_;
MessageSCCAnalyzer scc_analyzer_;
// This member is unused and should be deleted once all old-style variable
// maps are gone.
// TODO(b/245791219)
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_;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__

View File

@@ -0,0 +1,348 @@
// 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 "absl/strings/str_cat.h"
#include "absl/strings/str_replace.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 {
namespace {
std::string NumberedCcFileName(absl::string_view basename, int number) {
return absl::StrCat(basename, ".out/", number, ".cc");
}
absl::flat_hash_map<std::string, std::string> CommonVars(
const Options& options) {
bool is_oss = options.opensource_runtime;
return {
{"proto_ns", ProtobufNamespace(options)},
{"string", "std::string"},
{"int8", "int8_t"},
{"int32", "int32_t"},
{"int64", "int64_t"},
{"uint8", "uint8_t"},
{"uint32", "uint32_t"},
{"uint64", "uint64_t"},
{"hrule_thick", kThickSeparator},
{"hrule_thin", kThinSeparator},
// Warning: there is some clever naming/splitting here to avoid extract
// script rewrites. The names of these variables must not be things that
// the extract script will rewrite. That's why we use "CHK" (for example)
// instead of "GOOGLE_CHECK".
//
// These values are things the extract script would rewrite if we did not
// split them. It might not strictly matter since we don't generate
// google3 code in open-source. But it's good to prevent surprising
// things from happening.
{"GOOGLE_PROTOBUF", is_oss ? "GOOGLE_PROTOBUF"
: "GOOGLE3_PROTOBU"
"F"},
{"CHK", is_oss ? "GOOGLE_CHECK"
: "CHEC"
"K"},
{"DCHK", is_oss ? "GOOGLE_DCHECK"
: "DCHEC"
"K"},
};
}
} // 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 (const auto& option : options) {
const auto& key = option.first;
const auto& value = option.second;
if (key == "dllexport_decl") {
file_options.dllexport_decl = value;
} else if (key == "safe_boundary_check") {
file_options.safe_boundary_check = true;
} else if (key == "annotate_headers") {
file_options.annotate_headers = true;
} else if (key == "annotation_pragma_name") {
file_options.annotation_pragma_name = value;
} else if (key == "annotation_guard_name") {
file_options.annotation_guard_name = value;
} else if (key == "speed") {
file_options.enforce_mode = EnforceOptimizeMode::kSpeed;
} else if (key == "code_size") {
file_options.enforce_mode = EnforceOptimizeMode::kCodeSize;
} else if (key == "lite") {
file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
} else if (key == "lite_implicit_weak_fields") {
file_options.enforce_mode = EnforceOptimizeMode::kLiteRuntime;
file_options.lite_implicit_weak_fields = true;
if (!value.empty()) {
file_options.num_cc_files = strto32(value.c_str(), nullptr, 10);
}
} else if (key == "proto_h") {
file_options.proto_h = true;
} else if (key == "proto_static_reflection_h") {
} else if (key == "annotate_accessor") {
file_options.annotate_accessor = true;
} else if (key == "inject_field_listener_events") {
file_options.field_listener_options.inject_field_listener_events = true;
} else if (key == "forbidden_field_listener_events") {
std::size_t pos = 0;
do {
std::size_t next_pos = value.find_first_of("+", pos);
if (next_pos == std::string::npos) {
next_pos = value.size();
}
if (next_pos > pos)
file_options.field_listener_options.forbidden_field_listener_events
.insert(value.substr(pos, next_pos - pos));
pos = next_pos + 1;
} while (pos < value.size());
} else if (key == "unverified_lazy_message_sets") {
file_options.unverified_lazy_message_sets = true;
} else if (key == "message_owned_arena_trial") {
file_options.message_owned_arena_trial = true;
} else if (key == "force_eagerly_verified_lazy") {
file_options.force_eagerly_verified_lazy = true;
} else if (key == "experimental_tail_call_table_mode") {
if (value == "never") {
file_options.tctable_mode = Options::kTCTableNever;
} else if (value == "guarded") {
file_options.tctable_mode = Options::kTCTableGuarded;
} else if (value == "always") {
file_options.tctable_mode = Options::kTCTableAlways;
} else {
*error =
"Unknown value for experimental_tail_call_table_mode: " + value;
return false;
}
} else {
*error = "Unknown generator option: " + key;
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());
auto generate_reserved_static_reflection_header = [&basename,
&generator_context]() {
auto output = absl::WrapUnique(generator_context->Open(
absl::StrCat(basename, ".proto.static_reflection.h")));
io::Printer(output.get()).Emit(R"cc(
// Reserved for future use.
)cc");
};
// Suppress maybe unused warning.
(void)generate_reserved_static_reflection_header;
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) {
auto output = absl::WrapUnique(
generator_context->Open(absl::StrCat(basename, ".proto.h")));
GeneratedCodeInfo annotations;
io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
&annotations);
io::Printer::Options options;
if (file_options.annotate_headers) {
options.annotation_collector = &annotation_collector;
}
io::Printer p(output.get(), options);
auto v = p.WithVars(CommonVars(file_options));
std::string info_path = absl::StrCat(basename, ".proto.h.meta");
file_generator.GenerateProtoHeader(
&p, file_options.annotate_headers ? info_path : "");
if (file_options.annotate_headers) {
auto info_output = absl::WrapUnique(generator_context->Open(info_path));
annotations.SerializeToZeroCopyStream(info_output.get());
}
}
{
auto output = absl::WrapUnique(
generator_context->Open(absl::StrCat(basename, ".pb.h")));
GeneratedCodeInfo annotations;
io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
&annotations);
io::Printer::Options options;
if (file_options.annotate_headers) {
options.annotation_collector = &annotation_collector;
}
io::Printer p(output.get(), options);
auto v = p.WithVars(CommonVars(file_options));
std::string info_path = absl::StrCat(basename, ".pb.h.meta");
file_generator.GeneratePBHeader(
&p, file_options.annotate_headers ? info_path : "");
if (file_options.annotate_headers) {
auto info_output = absl::WrapUnique(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
auto output = absl::WrapUnique(
generator_context->Open(absl::StrCat(basename, ".pb.cc")));
io::Printer p(output.get());
auto v = p.WithVars(CommonVars(file_options));
file_generator.GenerateGlobalSource(&p);
}
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) {
auto output = absl::WrapUnique(generator_context->Open(
NumberedCcFileName(basename, cc_file_number++)));
io::Printer p(output.get());
auto v = p.WithVars(CommonVars(file_options));
file_generator.GenerateSourceForMessage(i, &p);
}
for (int i = 0; i < file_generator.NumExtensions(); ++i) {
auto output = absl::WrapUnique(generator_context->Open(
NumberedCcFileName(basename, cc_file_number++)));
io::Printer p(output.get());
auto v = p.WithVars(CommonVars(file_options));
file_generator.GenerateSourceForExtension(i, &p);
}
// Create empty placeholder files if necessary to match the expected number
// of files.
while (cc_file_number < num_cc_files) {
(void)absl::WrapUnique(generator_context->Open(
NumberedCcFileName(basename, cc_file_number++)));
}
} else {
auto output = absl::WrapUnique(
generator_context->Open(absl::StrCat(basename, ".pb.cc")));
io::Printer p(output.get());
auto v = p.WithVars(CommonVars(file_options));
file_generator.GenerateSource(&p);
}
return true;
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,105 @@
// 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 <utility>
#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() = default;
CppGenerator(const CppGenerator&) = delete;
CppGenerator& operator=(const CppGenerator&) = delete;
~CppGenerator() override = default;
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(std::string base) {
runtime_include_base_ = std::move(base);
}
bool Generate(const FileDescriptor* file, const std::string& parameter,
GeneratorContext* generator_context,
std::string* error) const override;
uint64_t GetSupportedFeatures() const override {
return FEATURE_PROTO3_OPTIONAL;
}
private:
bool opensource_runtime_ = PROTO2_IS_OSS;
std::string runtime_include_base_;
};
} // 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,333 @@
// 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 "absl/strings/ascii.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/stubs/strutil.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
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_" + absl::AsciiStrToUpper(DeclaredTypeMethodName(key->type()));
(*variables)["val_wire_type"] =
"TYPE_" + absl::AsciiStrToUpper(DeclaredTypeMethodName(val->type()));
(*variables)["map_classname"] = ClassName(descriptor->message_type(), false);
(*variables)["number"] = absl::StrCat(descriptor->number());
(*variables)["tag"] = absl::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(const MapFieldGenerator&) = delete;
MapFieldGenerator& operator=(const MapFieldGenerator&) = delete;
~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_;
};
} // 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,226 @@
// 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 <limits>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "google/protobuf/compiler/cpp/enum.h"
#include "google/protobuf/compiler/cpp/extension.h"
#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"
#include "google/protobuf/io/printer.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class MessageGenerator {
public:
MessageGenerator(const Descriptor* descriptor,
const std::map<std::string, std::string>& ignored,
int index_in_file_messages, const Options& options,
MessageSCCAnalyzer* scc_analyzer);
MessageGenerator(const MessageGenerator&) = delete;
MessageGenerator& operator=(const MessageGenerator&) = delete;
~MessageGenerator() = default;
// 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* p);
// Generate definitions of inline methods (placed at the end of the header
// file).
void GenerateInlineMethods(io::Printer* p);
// Generate all non-inline methods for this class.
void GenerateClassMethods(io::Printer* p);
// Generate source file code that should go outside any namespace.
void GenerateSourceInProto2Namespace(io::Printer* p);
void GenerateInitDefaultSplitInstance(io::Printer* p);
// Generate the constexpr constructor for constant initialization of the
// default instance.
void GenerateConstexprConstructor(io::Printer* p);
void GenerateSchema(io::Printer* p, int offset, int has_offset);
// 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* p);
const Descriptor* descriptor() const { return descriptor_; }
private:
// Generate declarations and definitions of accessors for fields.
void GenerateFieldAccessorDeclarations(io::Printer* p);
void GenerateFieldAccessorDefinitions(io::Printer* p);
// Generate constructors and destructor.
void GenerateStructors(io::Printer* p);
// 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* p);
// Generate the shared destructor code.
void GenerateSharedDestructorCode(io::Printer* p);
// Generate the arena-specific destructor code.
void GenerateArenaDestructorCode(io::Printer* p);
// Generate standard Message methods.
void GenerateClear(io::Printer* p);
void GenerateOneofClear(io::Printer* p);
void GenerateVerify(io::Printer* p);
void GenerateSerializeWithCachedSizes(io::Printer* p);
void GenerateSerializeWithCachedSizesToArray(io::Printer* p);
void GenerateSerializeWithCachedSizesBody(io::Printer* p);
void GenerateSerializeWithCachedSizesBodyShuffled(io::Printer* p);
void GenerateByteSize(io::Printer* p);
void GenerateMergeFrom(io::Printer* p);
void GenerateClassSpecificMergeImpl(io::Printer* p);
void GenerateCopyFrom(io::Printer* p);
void GenerateSwap(io::Printer* p);
void GenerateIsInitialized(io::Printer* p);
// 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* p, 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* p, const std::vector<const FieldDescriptor*>& fields);
void GenerateSerializeOneExtensionRange(
io::Printer* p, const Descriptor::ExtensionRange* range);
// Generates has_foo() functions and variables for singular field has-bits.
void GenerateSingularFieldHasBits(const FieldDescriptor* field,
io::Printer* p);
// Generates has_foo() functions and variables for oneof field has-bits.
void GenerateOneofHasBits(io::Printer* p);
// Generates has_foo_bar() functions for oneof members.
void GenerateOneofMemberHasBits(const FieldDescriptor* field, io::Printer* p);
// Generates the clear_foo() method for a field.
void GenerateFieldClear(const FieldDescriptor* field, bool is_inline,
io::Printer* p);
// Generates the body of the message's copy constructor.
void GenerateCopyConstructorBody(io::Printer* p) 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* field) const;
int HasByteIndex(const FieldDescriptor* field) const;
int HasWordIndex(const FieldDescriptor* field) const;
std::vector<uint32_t> RequiredFieldsBitMask() const;
const Descriptor* descriptor_;
int index_in_file_messages_;
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_ = 0;
// 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_ = 0;
std::vector<const EnumGenerator*> enum_generators_;
std::vector<const ExtensionGenerator*> extension_generators_;
int num_required_fields_ = 0;
int num_weak_fields_ = 0;
std::unique_ptr<MessageLayoutHelper> message_layout_helper_;
std::unique_ptr<ParseFunctionGenerator> parse_function_generator_;
MessageSCCAnalyzer* scc_analyzer_;
std::map<std::string, std::string> variables_;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__

View File

@@ -0,0 +1,965 @@
// 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 ? (" ::" + ProtobufNamespace(options) +
"::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");
}
auto nonempty = [this](const char* fn) {
auto var_it = variables_.find(fn);
return var_it != variables_.end() && !var_it->second.empty();
};
if (nonempty("set_hasbit") || nonempty("clear_hasbit")) {
format(
" if ($name$) {\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n");
}
format(
"$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 (internal::cpp::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 (!internal::cpp::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 (!internal::cpp::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(const MessageFieldGenerator&) = delete;
MessageFieldGenerator& operator=(const MessageFieldGenerator&) = delete;
~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_;
};
class MessageOneofFieldGenerator : public MessageFieldGenerator {
public:
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
MessageOneofFieldGenerator(const MessageOneofFieldGenerator&) = delete;
MessageOneofFieldGenerator& operator=(const MessageOneofFieldGenerator&) =
delete;
~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;
};
class RepeatedMessageFieldGenerator : public FieldGenerator {
public:
RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options,
MessageSCCAnalyzer* scc_analyzer);
RepeatedMessageFieldGenerator(const RepeatedMessageFieldGenerator&) = delete;
RepeatedMessageFieldGenerator& operator=(
const RepeatedMessageFieldGenerator&) = delete;
~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_;
};
} // 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 cached_size; // 4 bytes
MockRepeatedPtrField data; // 24 bytes
// + 4 bytes padding
};
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,103 @@
// 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;
class SplitMap;
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;
const SplitMap* split_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 profile_driven_inline_string = true;
bool message_owned_arena_trial = false;
bool force_split = false;
bool profile_driven_split = true;
#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,253 @@
// 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
static void OptimizeLayoutHelper(std::vector<const FieldDescriptor*>* fields,
const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
if (fields->empty()) return;
// 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());
}
}
}
// 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.
//
// If there are split fields in `fields`, they will be placed at the end. The
// order within split fields follows the same rule, aka classify and order by
// "family".
void PaddingOptimizer::OptimizeLayout(
std::vector<const FieldDescriptor*>* fields, const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
std::vector<const FieldDescriptor*> normal;
std::vector<const FieldDescriptor*> split;
for (const auto* field : *fields) {
if (ShouldSplit(field, options)) {
split.push_back(field);
} else {
normal.push_back(field);
}
}
OptimizeLayoutHelper(&normal, options, scc_analyzer);
OptimizeLayoutHelper(&split, options, scc_analyzer);
fields->clear();
fields->insert(fields->end(), normal.begin(), normal.end());
fields->insert(fields->end(), split.begin(), split.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,141 @@
// 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"
#include "google/protobuf/generated_message_tctable_gen.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
// 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:
class GeneratedOptionProvider;
// 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);
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<internal::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,540 @@
// 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 "absl/strings/str_cat.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"] = absl::StrCat(internal::WireFormat::MakeTag(descriptor));
int fixed_size = FixedSize(descriptor->type());
if (fixed_size != -1) {
(*variables)["fixed_size"] = absl::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 ::$proto_ns$::internal::CachedSize "
"$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$.Get();\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$.Set(cached_size);\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(const PrimitiveFieldGenerator&) = delete;
PrimitiveFieldGenerator& operator=(const PrimitiveFieldGenerator&) = delete;
~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;
};
class PrimitiveOneofFieldGenerator : public PrimitiveFieldGenerator {
public:
PrimitiveOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
PrimitiveOneofFieldGenerator(const PrimitiveOneofFieldGenerator&) = delete;
PrimitiveOneofFieldGenerator& operator=(const PrimitiveOneofFieldGenerator&) =
delete;
~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;
};
class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
public:
RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
RepeatedPrimitiveFieldGenerator(const RepeatedPrimitiveFieldGenerator&) =
delete;
RepeatedPrimitiveFieldGenerator& operator=(
const RepeatedPrimitiveFieldGenerator&) = delete;
~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;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__

View File

@@ -0,0 +1,315 @@
// 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 <map>
#include <string>
#include "absl/strings/str_cat.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/io/printer.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
void ServiceGenerator::GenerateDeclarations(io::Printer* printer) {
auto vars = printer->WithVars(&vars_);
printer->Emit(
{
{"virts", [&] { GenerateMethodSignatures(kVirtual, printer); }},
{"impls", [&] { GenerateMethodSignatures(kNonVirtual, printer); }},
},
R"cc(
class $classname$_Stub;
class $dllexport_decl $$classname$ : public ::$proto_ns$::Service {
protected:
$classname$() = default;
public:
using Stub = $classname$_Stub;
$classname$(const $classname$&) = delete;
$classname$& operator=(const $classname$&) = delete;
virtual ~$classname$() = default;
static const ::$proto_ns$::ServiceDescriptor* descriptor();
$virts$;
// implements Service ----------------------------------------------
const ::$proto_ns$::ServiceDescriptor* GetDescriptor() override;
void CallMethod(const ::$proto_ns$::MethodDescriptor* method,
::$proto_ns$::RpcController* controller,
const ::$proto_ns$::Message* request,
::$proto_ns$::Message* response,
::google::protobuf::Closure* done) override;
const ::$proto_ns$::Message& GetRequestPrototype(
const ::$proto_ns$::MethodDescriptor* method) const override;
const ::$proto_ns$::Message& GetResponsePrototype(
const ::$proto_ns$::MethodDescriptor* method) const override;
};
class $dllexport_decl $$classname$_Stub : public $classname$ {
public:
$classname$_Stub(::$proto_ns$::RpcChannel* channel);
$classname$_Stub(::$proto_ns$::RpcChannel* channel,
::$proto_ns$::Service::ChannelOwnership ownership);
$classname$_Stub(const $classname$_Stub&) = delete;
$classname$_Stub& operator=(const $classname$_Stub&) = delete;
~$classname$_Stub() override;
inline ::$proto_ns$::RpcChannel* channel() { return channel_; }
// implements $classname$ ------------------------------------------
$impls$;
private:
::$proto_ns$::RpcChannel* channel_;
bool owns_channel_;
};
)cc");
}
void ServiceGenerator::GenerateMethodSignatures(VirtualOrNot virtual_or_not,
io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); ++i) {
const MethodDescriptor* method = descriptor_->method(i);
printer->Emit(
{
{"name", method->name()},
{"input", QualifiedClassName(method->input_type(), *options_)},
{"output", QualifiedClassName(method->output_type(), *options_)},
{"virtual", virtual_or_not == kVirtual ? "virtual" : ""},
{"override", virtual_or_not != kVirtual ? "override" : ""},
},
// No cc, clang-format does not format this string well due to the
// $ override$ substitution.
R"(
$virtual $void $name$(::$proto_ns$::RpcController* controller,
const $input$* request,
$output$* response,
::google::protobuf::Closure* done)$ override$;
)");
}
}
// ===================================================================
void ServiceGenerator::GenerateImplementation(io::Printer* printer) {
auto vars = printer->WithVars(&vars_);
printer->Emit(
{
{"index", index_in_metadata_},
{"no_impl_methods", [&] { GenerateNotImplementedMethods(printer); }},
{"call_method", [&] { GenerateCallMethod(printer); }},
{"get_request", [&] { GenerateGetPrototype(kRequest, printer); }},
{"get_response", [&] { GenerateGetPrototype(kResponse, printer); }},
{"stub_methods", [&] { GenerateStubMethods(printer); }},
},
R"cc(
const ::$proto_ns$::ServiceDescriptor* $classname$::descriptor() {
::$proto_ns$::internal::AssignDescriptors(&$desc_table$);
return $file_level_service_descriptors$[$index$];
}
const ::$proto_ns$::ServiceDescriptor* $classname$::GetDescriptor() {
return descriptor();
}
$no_impl_methods$;
$call_method$;
$get_request$;
$get_response$;
$classname$_Stub::$classname$_Stub(::$proto_ns$::RpcChannel* channel)
: channel_(channel), owns_channel_(false) {}
$classname$_Stub::$classname$_Stub(
::$proto_ns$::RpcChannel* channel,
::$proto_ns$::Service::ChannelOwnership ownership)
: channel_(channel),
owns_channel_(ownership ==
::$proto_ns$::Service::STUB_OWNS_CHANNEL) {}
$classname$_Stub::~$classname$_Stub() {
if (owns_channel_) delete channel_;
}
$stub_methods$;
)cc");
}
void ServiceGenerator::GenerateNotImplementedMethods(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); ++i) {
const MethodDescriptor* method = descriptor_->method(i);
printer->Emit(
{
{"name", method->name()},
{"input", QualifiedClassName(method->input_type(), *options_)},
{"output", QualifiedClassName(method->output_type(), *options_)},
},
R"cc(
void $classname$::$name$(::$proto_ns$::RpcController* controller,
const $input$*, $output$*, ::google::protobuf::Closure* done) {
controller->SetFailed("Method $name$() not implemented.");
done->Run();
}
)cc");
}
}
void ServiceGenerator::GenerateCallMethod(io::Printer* printer) {
printer->Emit(
{
{"index", absl::StrCat(index_in_metadata_)},
{"cases", [&] { GenerateCallMethodCases(printer); }},
},
R"cc(
void $classname$::CallMethod(
const ::$proto_ns$::MethodDescriptor* method,
::$proto_ns$::RpcController* controller,
const ::$proto_ns$::Message* request,
::$proto_ns$::Message* response, ::google::protobuf::Closure* done) {
GOOGLE_DCHECK_EQ(method->service(), $file_level_service_descriptors$[$index$]);
switch (method->index()) {
$cases$;
default:
GOOGLE_LOG(FATAL) << "Bad method index; this should never happen.";
break;
}
}
)cc");
}
void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
io::Printer* printer) {
printer->Emit(
{
{"which", which == kRequest ? "Request" : "Response"},
{"which_type", which == kRequest ? "input" : "output"},
{"cases",
[&] {
for (int i = 0; i < descriptor_->method_count(); ++i) {
const MethodDescriptor* method = descriptor_->method(i);
const Descriptor* type = which == kRequest
? method->input_type()
: method->output_type();
printer->Emit(
{
{"index", absl::StrCat(i)},
{"type", QualifiedClassName(type, *options_)},
},
R"cc(
case $index$:
return $type$::default_instance();
)cc");
}
}},
},
R"cc(
const ::$proto_ns$::Message& $classname$::Get$which$Prototype(
const ::$proto_ns$::MethodDescriptor* method) const {
GOOGLE_DCHECK_EQ(method->service(), descriptor());
switch (method->index()) {
$cases$;
default:
GOOGLE_LOG(FATAL) << "Bad method index; this should never happen.";
return *::$proto_ns$::MessageFactory::generated_factory()
->GetPrototype(method->$which_type$_type());
}
}
)cc");
}
void ServiceGenerator::GenerateCallMethodCases(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); ++i) {
const MethodDescriptor* method = descriptor_->method(i);
printer->Emit(
{
{"name", method->name()},
{"input", QualifiedClassName(method->input_type(), *options_)},
{"output", QualifiedClassName(method->output_type(), *options_)},
{"index", absl::StrCat(i)},
},
R"cc(
case $index$:
$name$(controller,
::$proto_ns$::internal::DownCast<const $input$*>(request),
::$proto_ns$::internal::DownCast<$output$*>(response), done);
break;
)cc");
}
}
void ServiceGenerator::GenerateStubMethods(io::Printer* printer) {
for (int i = 0; i < descriptor_->method_count(); ++i) {
const MethodDescriptor* method = descriptor_->method(i);
printer->Emit(
{
{"name", method->name()},
{"input", QualifiedClassName(method->input_type(), *options_)},
{"output", QualifiedClassName(method->output_type(), *options_)},
{"index", absl::StrCat(i)},
},
R"cc(
void $classname$_Stub::$name$(::$proto_ns$::RpcController* controller,
const $input$* request,
$output$* response, ::google::protobuf::Closure* done) {
channel_->CallMethod(descriptor()->method($index$), controller,
request, response, done);
}
)cc");
}
}
} // namespace cpp
} // 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.
// 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"
#include "google/protobuf/io/printer.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
class ServiceGenerator {
public:
// See generator.cc for the meaning of dllexport_decl.
ServiceGenerator(const ServiceDescriptor* descriptor,
const std::map<std::string, std::string>& vars,
const Options& options)
: descriptor_(descriptor), options_(&options), vars_(vars) {
vars_["classname"] = descriptor_->name();
vars_["full_name"] = descriptor_->full_name();
}
ServiceGenerator(const ServiceGenerator&) = delete;
ServiceGenerator& operator=(const ServiceGenerator&) = delete;
ServiceGenerator(ServiceGenerator&&) = delete;
ServiceGenerator& operator=(ServiceGenerator&&) = delete;
~ServiceGenerator() = default;
// Generate the class definitions for the service's interface and the
// stub implementation.
void GenerateDeclarations(io::Printer* printer);
// Generate implementations of everything declared by
// GenerateDeclarations().
void GenerateImplementation(io::Printer* printer);
private:
enum RequestOrResponse { kRequest, kResponse };
enum VirtualOrNot { kVirtual, kNonVirtual };
// Prints signatures for all methods in the
void GenerateMethodSignatures(VirtualOrNot virtual_or_not,
io::Printer* printer);
// 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 cases in CallMethod().
void GenerateCallMethodCases(io::Printer* printer);
// Generate the stub's implementations of the service methods.
void GenerateStubMethods(io::Printer* printer);
const ServiceDescriptor* descriptor_;
const Options* options_;
std::map<std::string, std::string> vars_;
int index_in_metadata_;
friend class FileGenerator;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__

View File

@@ -0,0 +1,944 @@
// 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 "absl/strings/str_cat.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/stubs/strutil.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 = "::" + ProtobufNamespace(options) + "::internal::";
const std::string kArenaStringPtr = kNS + "ArenaStringPtr";
(*variables)["default"] = DefaultValue(options, descriptor);
(*variables)["default_length"] =
absl::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"] =
absl::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";
// 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"] = "::absl::string_view";
}
}
} // 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 (internal::cpp::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"
" $field$.Set(\"\", GetArenaForAllocation());\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$");
auto nonempty = [this](const char* fn) {
auto var_it = variables_.find(fn);
return var_it != variables_.end() && !var_it->second.empty();
};
if (nonempty("set_hasbit") || nonempty("clear_hasbit")) {
format(
" 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 = internal::cpp::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::GenerateCopyConstructorCode(
io::Printer* printer) const {
Formatter format(printer, variables_);
GenerateConstructorCode(printer);
if (inlined_) {
format("new (&_this->$field$) ::_pbi::InlinedStringField();\n");
}
if (internal::cpp::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;
}
format(
"/*decltype($field$)*/{&::_pbi::fixed_address_empty_string, "
"::_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"] =
absl::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, "
"absl::string_view 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$$}$(absl::string_view 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"
" $DCHK$(value != nullptr);"
" $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, absl::string_view 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"
" $DCHK$(value != nullptr);"
" $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$(absl::string_view 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(const StringFieldGenerator&) = delete;
StringFieldGenerator& operator=(const StringFieldGenerator&) = delete;
~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 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_;
};
class StringOneofFieldGenerator : public StringFieldGenerator {
public:
StringOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
StringOneofFieldGenerator(const StringOneofFieldGenerator&) = delete;
StringOneofFieldGenerator& operator=(const StringOneofFieldGenerator&) =
delete;
~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;
};
class RepeatedStringFieldGenerator : public FieldGenerator {
public:
RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options);
RepeatedStringFieldGenerator(const RepeatedStringFieldGenerator&) = delete;
RepeatedStringFieldGenerator& operator=(const RepeatedStringFieldGenerator&) =
delete;
~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;
};
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__

View File

@@ -0,0 +1,186 @@
// 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,133 @@
################################################################################
# Protocol Buffers Compiler - C# code generator
################################################################################
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
load("//build_defs:cpp_opts.bzl", "COPTS")
cc_library(
name = "names",
hdrs = ["names.h"],
srcs = ["names.cc"],
copts = COPTS,
include_prefix = "google/protobuf/compiler/csharp",
visibility = ["//visibility:public"],
deps = [
"//src/google/protobuf:protobuf_nowkt",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "csharp",
srcs = [
"csharp_doc_comment.cc",
"csharp_enum.cc",
"csharp_enum_field.cc",
"csharp_field_base.cc",
"csharp_generator.cc",
"csharp_helpers.cc",
"csharp_map_field.cc",
"csharp_message.cc",
"csharp_message_field.cc",
"csharp_primitive_field.cc",
"csharp_reflection_class.cc",
"csharp_repeated_enum_field.cc",
"csharp_repeated_message_field.cc",
"csharp_repeated_primitive_field.cc",
"csharp_source_generator_base.cc",
"csharp_wrapper_field.cc",
],
hdrs = [
"csharp_doc_comment.h",
"csharp_enum.h",
"csharp_enum_field.h",
"csharp_field_base.h",
"csharp_generator.h",
"csharp_helpers.h",
"csharp_map_field.h",
"csharp_message.h",
"csharp_message_field.h",
"csharp_options.h",
"csharp_primitive_field.h",
"csharp_reflection_class.h",
"csharp_repeated_enum_field.h",
"csharp_repeated_message_field.h",
"csharp_repeated_primitive_field.h",
"csharp_source_generator_base.h",
"csharp_wrapper_field.h",
],
copts = COPTS + select({
"//build_defs:config_msvc": [],
"//conditions:default": ["-Wno-overloaded-virtual"],
}),
include_prefix = "google/protobuf/compiler/csharp",
visibility = [
"//pkg:__pkg__",
"//src/google/protobuf/compiler:__pkg__",
],
deps = [
":names",
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
],
)
cc_test(
name = "bootstrap_unittest",
srcs = ["csharp_bootstrap_unittest.cc"],
data = [
"//:well_known_type_protos",
"//conformance:all_files",
"//conformance:conformance_proto",
"//csharp:wkt_cs_srcs",
"//src/google/protobuf:descriptor_proto_srcs",
"//src/google/protobuf:testdata",
],
deps = [
":csharp",
"//:protobuf",
"//src/google/protobuf/compiler:importer",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "generator_unittest",
srcs = ["csharp_generator_unittest.cc"],
deps = [
":csharp",
"//:protobuf",
"//src/google/protobuf/compiler:command_line_interface",
"//src/google/protobuf/io",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
################################################################################
# Distribution packaging
################################################################################
pkg_files(
name = "dist_files",
srcs = glob(["**/*"]),
strip_prefix = strip_prefix.from_root(""),
visibility = ["//src:__pkg__"],
)
filegroup(
name = "test_srcs",
srcs = glob([
"*_test.cc",
"*unittest.cc",
]),
visibility = ["//src/google/protobuf/compiler:__pkg__"],
)

View File

@@ -0,0 +1,188 @@
// 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/testing/file.h"
#include "google/protobuf/testing/googletest.h"
#include <gtest/gtest.h>
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/strings/substitute.h"
#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"
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) {
absl::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");
EXPECT_EQ("", error_collector.text_);
}
} // namespace
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@@ -0,0 +1,117 @@
// 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 "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.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 = absl::StrReplaceAll(comments, {{"&", "&amp;"}, {"<", "&lt;"}});
std::vector<std::string> lines;
lines = absl::StrSplit(comments, "\n", absl::AllowEmpty());
// 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,104 @@
// 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/csharp/csharp_enum.h"
#include <sstream>
#include "google/protobuf/compiler/code_generator.h"
#include "absl/strings/str_cat.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/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.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_);
if (descriptor_->options().deprecated()) {
printer->Print("[global::System.ObsoleteAttribute]\n");
}
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));
if (descriptor_->value(i)->options().deprecated()) {
printer->Print("[global::System.ObsoleteAttribute]\n");
}
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", absl::StrCat(number));
} else {
printer->Print("[pbr::OriginalName(\"$original_name$\")] $name$ = $number$,\n",
"original_name", original_name,
"name", name,
"number", absl::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 "google/protobuf/compiler/csharp/csharp_enum_field.h"
#include <sstream>
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/descriptor.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/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/io/zero_copy_stream.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,467 @@
// 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/csharp/csharp_field_base.h"
#include <cmath>
#include <limits>
#include <sstream>
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/wire_format.h"
#include "google/protobuf/compiler/csharp/csharp_helpers.h"
#include "google/protobuf/compiler/csharp/names.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/printer.h"
// Must be last.
#include "google/protobuf/port_def.inc"
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 = absl::StrCat(tag_array[0]);
for (int i = 1; i < part_tag_size; i++) {
tag_bytes += ", " + absl::StrCat(tag_array[i]);
}
(*variables)["tag"] = absl::StrCat(tag);
(*variables)["tag_size"] = absl::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 = absl::StrCat(tag_array[0]);
for (int i = 1; i < part_tag_size; i++) {
tag_bytes += ", " + absl::StrCat(tag_array[i]);
}
variables_["end_tag"] = absl::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 = absl::StrCat(presenceIndex_ / 32);
std::string hasBitsMask = absl::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, " + absl::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 absl::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 absl::StrCat(value) + "F";
}
case FieldDescriptor::TYPE_INT64:
return absl::StrCat(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_UINT64:
return absl::StrCat(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_INT32:
return absl::StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_FIXED64:
return absl::StrCat(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_FIXED32:
return absl::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 absl::StrCat(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_SFIXED32:
return absl::StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SFIXED64:
return absl::StrCat(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_SINT32:
return absl::StrCat(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SINT64:
return absl::StrCat(descriptor->default_value_int64()) + "L";
default:
GOOGLE_LOG(FATAL)<< "Unknown field type.";
return "";
}
}
std::string FieldGeneratorBase::number() {
return absl::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
#include "google/protobuf/port_undef.inc"

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.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_BASE_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_BASE_H__
#include <string>
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/stubs/strutil.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.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,111 @@
// 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/csharp/csharp_generator.h"
#include <sstream>
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/compiler/csharp/csharp_helpers.h"
#include "google/protobuf/compiler/csharp/csharp_options.h"
#include "google/protobuf/compiler/csharp/csharp_reflection_class.h"
#include "google/protobuf/compiler/csharp/names.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/io/zero_copy_stream.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,96 @@
// 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 <gtest/gtest.h>
#include "google/protobuf/compiler/csharp/csharp_helpers.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/io/zero_copy_stream.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()));
}
TEST(CSharpIdentifiers, UnderscoresToCamelCase) {
EXPECT_EQ("FooBar", UnderscoresToCamelCase("Foo_Bar", true));
EXPECT_EQ("fooBar", UnderscoresToCamelCase("FooBar", false));
EXPECT_EQ("foo123", UnderscoresToCamelCase("foo_123", false));
// remove leading underscores
EXPECT_EQ("Foo123", UnderscoresToCamelCase("_Foo_123", true));
// this one has slight unexpected output as it capitalises the first
// letter after consuming the underscores, but this was the existing
// behaviour so I have not changed it
EXPECT_EQ("FooBar", UnderscoresToCamelCase("___fooBar", false));
// leave a leading underscore for identifiers that would otherwise
// be invalid because they would start with a digit
EXPECT_EQ("_123Foo", UnderscoresToCamelCase("_123_foo", true));
EXPECT_EQ("_123Foo", UnderscoresToCamelCase("___123_foo", true));
}
} // namespace
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google

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