parent
4008d229aa
commit
ce560630bf
91 changed files with 1819 additions and 1098 deletions
@ -0,0 +1,26 @@ |
||||
#!/bin/bash |
||||
|
||||
set -e |
||||
|
||||
# go to the repo root |
||||
cd $(dirname $0)/../../../.. |
||||
|
||||
# running dockcross image without any arguments generates a wrapper |
||||
# scripts that can be used to run commands under the dockcross image |
||||
# easily. |
||||
# See https://github.com/dockcross/dockcross#usage for details |
||||
docker run --rm -it dockcross/manylinux2014-aarch64 >dockcross-manylinux2014-aarch64.sh |
||||
chmod +x dockcross-manylinux2014-aarch64.sh |
||||
|
||||
# the wrapper script has CRLF line endings and bash doesn't like that |
||||
# so we change CRLF line endings into LF. |
||||
sed -i 's/\r//g' dockcross-manylinux2014-aarch64.sh |
||||
|
||||
# The dockcross wrapper script runs arbitrary commands under the selected dockcross |
||||
# image with the following properties which make its use very convenient: |
||||
# * the current working directory is mounted under /work so the container can easily |
||||
# access the current workspace |
||||
# * the processes in the container run under the same UID and GID as the host process so unlike |
||||
# vanilla "docker run" invocations, the workspace doesn't get polluted with files |
||||
# owned by root. |
||||
./dockcross-manylinux2014-aarch64.sh "$@" |
@ -0,0 +1,34 @@ |
||||
#!/bin/bash |
||||
# |
||||
# Builds protobuf C++ with aarch64 crosscompiler and runs a basic set of tests under an emulator. |
||||
# NOTE: This script is expected to run under the dockcross/linux-arm64 docker image. |
||||
|
||||
set -ex |
||||
|
||||
PYTHON="/opt/python/cp38-cp38/bin/python" |
||||
|
||||
./autogen.sh |
||||
CXXFLAGS="-fPIC -g -O2" ./configure |
||||
make -j8 |
||||
|
||||
pushd python |
||||
|
||||
# TODO: currently this step relies on qemu being registered with binfmt_misc so that |
||||
# aarch64 binaries are automatically run with an emulator. This works well once |
||||
# "sudo apt install qemu-user-static binfmt-support" is installed on the host machine. |
||||
${PYTHON} setup.py build_py |
||||
|
||||
# when crosscompiling for aarch64, --plat-name needs to be set explicitly |
||||
# to end up with correctly named wheel file |
||||
# the value should be manylinuxABC_ARCH and dockcross docker image |
||||
# conveniently provides the value in the AUDITWHEEL_PLAT env |
||||
plat_name_flag="--plat-name=$AUDITWHEEL_PLAT" |
||||
|
||||
# override the value of EXT_SUFFIX to make sure the crosscompiled .so files in the wheel have the correct filename suffix |
||||
export PROTOCOL_BUFFERS_OVERRIDE_EXT_SUFFIX="$(${PYTHON} -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX").replace("-x86_64-linux-gnu.so", "-aarch64-linux-gnu.so"))')" |
||||
|
||||
# Build the python extension inplace to be able to python unittests later |
||||
${PYTHON} setup.py build_ext --cpp_implementation --compile_static_extension --inplace |
||||
|
||||
# Build the binary wheel (to check it with auditwheel) |
||||
${PYTHON} setup.py bdist_wheel --cpp_implementation --compile_static_extension $plat_name_flag |
@ -0,0 +1,28 @@ |
||||
#!/bin/bash |
||||
|
||||
set -ex |
||||
|
||||
# go to the repo root |
||||
cd $(dirname $0)/../../.. |
||||
cd python |
||||
|
||||
PYTHON="/opt/python/cp38-cp38/bin/python" |
||||
${PYTHON} -m pip install --user six pytest auditwheel |
||||
|
||||
# check that we are really using aarch64 python |
||||
(${PYTHON} -c 'import sysconfig; print(sysconfig.get_platform())' | grep -q "linux-aarch64") || (echo "Wrong python platform, needs to be aarch64 python."; exit 1) |
||||
|
||||
# step 1: run all python unittests |
||||
# we've built the python extension previously with --inplace option |
||||
# so we can just discover all the unittests and run them directly under |
||||
# the python/ directory. |
||||
LD_LIBRARY_PATH=../src/.libs PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp ${PYTHON} -m pytest google/protobuf |
||||
|
||||
# step 2: run auditwheel show to check that the wheel is manylinux2014 compatible. |
||||
# auditwheel needs to run on wheel's target platform (or under an emulator) |
||||
${PYTHON} -m auditwheel show dist/protobuf-*-manylinux2014_aarch64.whl |
||||
|
||||
# step 3: smoketest that the wheel can be installed and run a smokecheck |
||||
${PYTHON} -m pip install dist/protobuf-*-manylinux2014_aarch64.whl |
||||
# when python cpp extension is on, simply importing a message type will trigger loading the cpp extension |
||||
PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp ${PYTHON} -c 'import google.protobuf.timestamp_pb2; print("Successfully loaded the python cpp extension!")' |
@ -0,0 +1,19 @@ |
||||
#!/bin/bash |
||||
|
||||
set -e |
||||
|
||||
# go to the repo root |
||||
cd $(dirname $0)/../../.. |
||||
|
||||
# crosscompile python extension and the binary wheel under dockcross/manylinux2014-aarch64 image |
||||
kokoro/linux/aarch64/dockcross_helpers/run_dockcross_manylinux2014_aarch64.sh kokoro/linux/aarch64/python_crosscompile_aarch64.sh |
||||
|
||||
# once crosscompilation is done, use an actual aarch64 docker image (with a real aarch64 python) to run all the tests under an emulator |
||||
# * mount the protobuf root as /work to be able to access the crosscompiled files |
||||
# * intentionally use a different image than manylinux2014 so that we don't build and test on the same linux distribution |
||||
# (manylinux_2_24 is debian-based while manylinux2014 is centos-based) |
||||
# * to avoid running the process inside docker as root (which can pollute the workspace with files owned by root), we force |
||||
# running under current user's UID and GID. To be able to do that, we need to provide a home directory for the user |
||||
# otherwise the UID would be homeless under the docker container and pip install wouldn't work. For simplicity, |
||||
# we just run map the user's home to a throwaway temporary directory |
||||
docker run -it --rm --user "$(id -u):$(id -g)" -e "HOME=/home/fake-user" -v "$(mktemp -d):/home/fake-user" -v "$(pwd)":/work -w /work quay.io/pypa/manylinux_2_24_aarch64 kokoro/linux/aarch64/python_run_tests_with_qemu_aarch64.sh |
@ -0,0 +1,580 @@ |
||||
// 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/cpp_parse_function_generator.h> |
||||
|
||||
#include <google/protobuf/wire_format.h> |
||||
#include <google/protobuf/wire_format_lite.h> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace compiler { |
||||
namespace cpp { |
||||
|
||||
namespace { |
||||
using google::protobuf::internal::WireFormat; |
||||
using google::protobuf::internal::WireFormatLite; |
||||
|
||||
std::vector<const FieldDescriptor*> GetOrderedFields( |
||||
const Descriptor* descriptor, const Options& options) { |
||||
std::vector<const FieldDescriptor*> ordered_fields; |
||||
for (auto field : FieldRange(descriptor)) { |
||||
if (!IsFieldStripped(field, options)) { |
||||
ordered_fields.push_back(field); |
||||
} |
||||
} |
||||
std::sort(ordered_fields.begin(), ordered_fields.end(), |
||||
[](const FieldDescriptor* a, const FieldDescriptor* b) { |
||||
return a->number() < b->number(); |
||||
}); |
||||
return ordered_fields; |
||||
} |
||||
|
||||
bool HasInternalAccessors(const FieldOptions::CType ctype) { |
||||
return ctype == FieldOptions::STRING || ctype == FieldOptions::CORD; |
||||
} |
||||
|
||||
class ParseFunctionGenerator { |
||||
public: |
||||
ParseFunctionGenerator(const Descriptor* descriptor, int num_hasbits, |
||||
const Options& options, |
||||
MessageSCCAnalyzer* scc_analyzer, io::Printer* printer) |
||||
: descriptor_(descriptor), |
||||
scc_analyzer_(scc_analyzer), |
||||
options_(options), |
||||
format_(printer), |
||||
num_hasbits_(num_hasbits) { |
||||
format_.Set("classname", ClassName(descriptor)); |
||||
format_.Set("p_ns", "::" + ProtobufNamespace(options_)); |
||||
format_.Set("pi_ns", |
||||
StrCat("::", ProtobufNamespace(options_), "::internal")); |
||||
format_.Set("GOOGLE_PROTOBUF", MacroPrefix(options_)); |
||||
std::map<std::string, std::string> vars; |
||||
SetCommonVars(options_, &vars); |
||||
SetUnknkownFieldsVariable(descriptor, options_, &vars); |
||||
format_.AddMap(vars); |
||||
} |
||||
|
||||
void GenerateLoopingParseFunction() { |
||||
format_( |
||||
"const char* $classname$::_InternalParse(const char* ptr, " |
||||
"$pi_ns$::ParseContext* ctx) {\n" |
||||
"#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure\n"); |
||||
format_.Indent(); |
||||
int hasbits_size = 0; |
||||
if (num_hasbits_ > 0) { |
||||
hasbits_size = (num_hasbits_ + 31) / 32; |
||||
} |
||||
// For now only optimize small hasbits.
|
||||
if (hasbits_size != 1) hasbits_size = 0; |
||||
if (hasbits_size) { |
||||
format_("_Internal::HasBits has_bits{};\n"); |
||||
format_.Set("has_bits", "has_bits"); |
||||
} else { |
||||
format_.Set("has_bits", "_has_bits_"); |
||||
} |
||||
format_.Set("continue", "continue"); |
||||
format_("while (!ctx->Done(&ptr)) {\n"); |
||||
format_.Indent(); |
||||
|
||||
GenerateParseIterationBody(descriptor_, |
||||
GetOrderedFields(descriptor_, options_)); |
||||
|
||||
format_.Outdent(); |
||||
format_("} // while\n"); |
||||
|
||||
format_.Outdent(); |
||||
format_("success:\n"); |
||||
if (hasbits_size) format_(" _has_bits_.Or(has_bits);\n"); |
||||
|
||||
format_( |
||||
" return ptr;\n" |
||||
"failure:\n" |
||||
" ptr = nullptr;\n" |
||||
" goto success;\n" |
||||
"#undef CHK_\n" |
||||
"}\n"); |
||||
} |
||||
|
||||
private: |
||||
const Descriptor* descriptor_; |
||||
MessageSCCAnalyzer* scc_analyzer_; |
||||
const Options& options_; |
||||
Formatter format_; |
||||
int num_hasbits_; |
||||
|
||||
void GenerateArenaString(const FieldDescriptor* field) { |
||||
if (HasHasbit(field)) { |
||||
format_("_Internal::set_has_$1$(&$has_bits$);\n", FieldName(field)); |
||||
} |
||||
std::string default_string = |
||||
field->default_value_string().empty() |
||||
? "::" + ProtobufNamespace(options_) + |
||||
"::internal::GetEmptyStringAlreadyInited()" |
||||
: QualifiedClassName(field->containing_type(), options_) + |
||||
"::" + MakeDefaultName(field) + ".get()"; |
||||
format_( |
||||
"if (arena != nullptr) {\n" |
||||
" ptr = ctx->ReadArenaString(ptr, &$1$_, arena);\n" |
||||
"} else {\n" |
||||
" ptr = " |
||||
"$pi_ns$::InlineGreedyStringParser($1$_.MutableNoArenaNoDefault(&$2$" |
||||
"), ptr, ctx);" |
||||
"\n}\n" |
||||
"const std::string* str = &$1$_.Get(); (void)str;\n", |
||||
FieldName(field), default_string); |
||||
} |
||||
|
||||
void GenerateStrings(const FieldDescriptor* field, bool check_utf8) { |
||||
FieldOptions::CType ctype = FieldOptions::STRING; |
||||
if (!options_.opensource_runtime) { |
||||
// Open source doesn't support other ctypes;
|
||||
ctype = field->options().ctype(); |
||||
} |
||||
if (!field->is_repeated() && !options_.opensource_runtime && |
||||
GetOptimizeFor(field->file(), options_) != FileOptions::LITE_RUNTIME && |
||||
// For now only use arena string for strings with empty defaults.
|
||||
field->default_value_string().empty() && |
||||
!field->real_containing_oneof() && ctype == FieldOptions::STRING) { |
||||
GenerateArenaString(field); |
||||
} else { |
||||
std::string name; |
||||
switch (ctype) { |
||||
case FieldOptions::STRING: |
||||
name = "GreedyStringParser"; |
||||
break; |
||||
case FieldOptions::CORD: |
||||
name = "CordParser"; |
||||
break; |
||||
case FieldOptions::STRING_PIECE: |
||||
name = "StringPieceParser"; |
||||
break; |
||||
} |
||||
format_( |
||||
"auto str = $1$$2$_$3$();\n" |
||||
"ptr = $pi_ns$::Inline$4$(str, ptr, ctx);\n", |
||||
HasInternalAccessors(ctype) ? "_internal_" : "", |
||||
field->is_repeated() && !field->is_packable() ? "add" : "mutable", |
||||
FieldName(field), name); |
||||
} |
||||
if (!check_utf8) return; // return if this is a bytes field
|
||||
auto level = GetUtf8CheckMode(field, options_); |
||||
switch (level) { |
||||
case NONE: |
||||
return; |
||||
case VERIFY: |
||||
format_("#ifndef NDEBUG\n"); |
||||
break; |
||||
case STRICT: |
||||
format_("CHK_("); |
||||
break; |
||||
} |
||||
std::string field_name; |
||||
field_name = "nullptr"; |
||||
if (HasDescriptorMethods(field->file(), options_)) { |
||||
field_name = StrCat("\"", field->full_name(), "\""); |
||||
} |
||||
format_("$pi_ns$::VerifyUTF8(str, $1$)", field_name); |
||||
switch (level) { |
||||
case NONE: |
||||
return; |
||||
case VERIFY: |
||||
format_( |
||||
";\n" |
||||
"#endif // !NDEBUG\n"); |
||||
break; |
||||
case STRICT: |
||||
format_(");\n"); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void GenerateLengthDelim(const FieldDescriptor* field) { |
||||
if (field->is_packable()) { |
||||
std::string enum_validator; |
||||
if (field->type() == FieldDescriptor::TYPE_ENUM && |
||||
!HasPreservingUnknownEnumSemantics(field)) { |
||||
enum_validator = |
||||
StrCat(", ", QualifiedClassName(field->enum_type(), options_), |
||||
"_IsValid, &_internal_metadata_, ", field->number()); |
||||
format_( |
||||
"ptr = " |
||||
"$pi_ns$::Packed$1$Parser<$unknown_fields_type$>(_internal_mutable_" |
||||
"$2$(), ptr, " |
||||
"ctx$3$);\n", |
||||
DeclaredTypeMethodName(field->type()), FieldName(field), |
||||
enum_validator); |
||||
} else { |
||||
format_( |
||||
"ptr = $pi_ns$::Packed$1$Parser(_internal_mutable_$2$(), ptr, " |
||||
"ctx$3$);\n", |
||||
DeclaredTypeMethodName(field->type()), FieldName(field), |
||||
enum_validator); |
||||
} |
||||
} else { |
||||
auto field_type = field->type(); |
||||
switch (field_type) { |
||||
case FieldDescriptor::TYPE_STRING: |
||||
GenerateStrings(field, true /* utf8 */); |
||||
break; |
||||
case FieldDescriptor::TYPE_BYTES: |
||||
GenerateStrings(field, false /* utf8 */); |
||||
break; |
||||
case FieldDescriptor::TYPE_MESSAGE: { |
||||
if (field->is_map()) { |
||||
const FieldDescriptor* val = |
||||
field->message_type()->FindFieldByName("value"); |
||||
GOOGLE_CHECK(val); |
||||
if (val->type() == FieldDescriptor::TYPE_ENUM && |
||||
!HasPreservingUnknownEnumSemantics(field)) { |
||||
format_( |
||||
"auto object = " |
||||
"::$proto_ns$::internal::InitEnumParseWrapper<$unknown_" |
||||
"fields_type$>(" |
||||
"&$1$_, $2$_IsValid, $3$, &_internal_metadata_);\n" |
||||
"ptr = ctx->ParseMessage(&object, ptr);\n", |
||||
FieldName(field), QualifiedClassName(val->enum_type()), |
||||
field->number()); |
||||
} else { |
||||
format_("ptr = ctx->ParseMessage(&$1$_, ptr);\n", |
||||
FieldName(field)); |
||||
} |
||||
} else if (IsLazy(field, options_)) { |
||||
if (field->real_containing_oneof()) { |
||||
format_( |
||||
"if (!_internal_has_$1$()) {\n" |
||||
" clear_$2$();\n" |
||||
" $2$_.$1$_ = ::$proto_ns$::Arena::CreateMessage<\n" |
||||
" $pi_ns$::LazyField>(GetArena());\n" |
||||
" set_has_$1$();\n" |
||||
"}\n" |
||||
"ptr = ctx->ParseMessage($2$_.$1$_, ptr);\n", |
||||
FieldName(field), field->containing_oneof()->name()); |
||||
} else if (HasHasbit(field)) { |
||||
format_( |
||||
"_Internal::set_has_$1$(&$has_bits$);\n" |
||||
"ptr = ctx->ParseMessage(&$1$_, ptr);\n", |
||||
FieldName(field)); |
||||
} else { |
||||
format_("ptr = ctx->ParseMessage(&$1$_, ptr);\n", |
||||
FieldName(field)); |
||||
} |
||||
} else if (IsImplicitWeakField(field, options_, scc_analyzer_)) { |
||||
if (!field->is_repeated()) { |
||||
format_( |
||||
"ptr = ctx->ParseMessage(_Internal::mutable_$1$(this), " |
||||
"ptr);\n", |
||||
FieldName(field)); |
||||
} else { |
||||
format_( |
||||
"ptr = ctx->ParseMessage($1$_.AddWeak(reinterpret_cast<const " |
||||
"::$proto_ns$::MessageLite*>($2$::_$3$_default_instance_ptr_)" |
||||
"), ptr);\n", |
||||
FieldName(field), Namespace(field->message_type(), options_), |
||||
ClassName(field->message_type())); |
||||
} |
||||
} else if (IsWeak(field, options_)) { |
||||
format_( |
||||
"{\n" |
||||
" auto* default_ = &reinterpret_cast<const Message&>($1$);\n" |
||||
" ptr = ctx->ParseMessage(_weak_field_map_.MutableMessage($2$," |
||||
" default_), ptr);\n" |
||||
"}\n", |
||||
QualifiedDefaultInstanceName(field->message_type(), options_), |
||||
field->number()); |
||||
} else { |
||||
format_("ptr = ctx->ParseMessage(_internal_$1$_$2$(), ptr);\n", |
||||
field->is_repeated() ? "add" : "mutable", FieldName(field)); |
||||
} |
||||
break; |
||||
} |
||||
default: |
||||
GOOGLE_LOG(FATAL) << "Illegal combination for length delimited wiretype " |
||||
<< " filed type is " << field->type(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Convert a 1 or 2 byte varint into the equivalent value upon a direct load.
|
||||
static uint32_t SmallVarintValue(uint32_t x) { |
||||
GOOGLE_DCHECK(x < 128 * 128); |
||||
if (x >= 128) x += (x & 0xFF80) + 128; |
||||
return x; |
||||
} |
||||
|
||||
static bool ShouldRepeat(const FieldDescriptor* descriptor, |
||||
internal::WireFormatLite::WireType wiretype) { |
||||
constexpr int kMaxTwoByteFieldNumber = 16 * 128; |
||||
return descriptor->number() < kMaxTwoByteFieldNumber && |
||||
descriptor->is_repeated() && |
||||
(!descriptor->is_packable() || |
||||
wiretype != internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED); |
||||
} |
||||
|
||||
void GenerateFieldBody(internal::WireFormatLite::WireType wiretype, |
||||
const FieldDescriptor* field) { |
||||
uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype); |
||||
switch (wiretype) { |
||||
case WireFormatLite::WIRETYPE_VARINT: { |
||||
std::string type = PrimitiveTypeName(options_, field->cpp_type()); |
||||
std::string prefix = field->is_repeated() ? "add" : "set"; |
||||
if (field->type() == FieldDescriptor::TYPE_ENUM) { |
||||
format_( |
||||
"$uint64$ val = $pi_ns$::ReadVarint64(&ptr);\n" |
||||
"CHK_(ptr);\n"); |
||||
if (!HasPreservingUnknownEnumSemantics(field)) { |
||||
format_("if (PROTOBUF_PREDICT_TRUE($1$_IsValid(val))) {\n", |
||||
QualifiedClassName(field->enum_type(), options_)); |
||||
format_.Indent(); |
||||
} |
||||
format_("_internal_$1$_$2$(static_cast<$3$>(val));\n", prefix, |
||||
FieldName(field), |
||||
QualifiedClassName(field->enum_type(), options_)); |
||||
if (!HasPreservingUnknownEnumSemantics(field)) { |
||||
format_.Outdent(); |
||||
format_( |
||||
"} else {\n" |
||||
" $pi_ns$::WriteVarint($1$, val, mutable_unknown_fields());\n" |
||||
"}\n", |
||||
field->number()); |
||||
} |
||||
} else { |
||||
std::string size = (field->type() == FieldDescriptor::TYPE_SINT32 || |
||||
field->type() == FieldDescriptor::TYPE_UINT32) |
||||
? "32" |
||||
: "64"; |
||||
std::string zigzag; |
||||
if ((field->type() == FieldDescriptor::TYPE_SINT32 || |
||||
field->type() == FieldDescriptor::TYPE_SINT64)) { |
||||
zigzag = "ZigZag"; |
||||
} |
||||
if (field->is_repeated() || field->real_containing_oneof()) { |
||||
std::string prefix = field->is_repeated() ? "add" : "set"; |
||||
format_( |
||||
"_internal_$1$_$2$($pi_ns$::ReadVarint$3$$4$(&ptr));\n" |
||||
"CHK_(ptr);\n", |
||||
prefix, FieldName(field), zigzag, size); |
||||
} else { |
||||
if (HasHasbit(field)) { |
||||
format_("_Internal::set_has_$1$(&$has_bits$);\n", |
||||
FieldName(field)); |
||||
} |
||||
format_( |
||||
"$1$_ = $pi_ns$::ReadVarint$2$$3$(&ptr);\n" |
||||
"CHK_(ptr);\n", |
||||
FieldName(field), zigzag, size); |
||||
} |
||||
} |
||||
break; |
||||
} |
||||
case WireFormatLite::WIRETYPE_FIXED32: |
||||
case WireFormatLite::WIRETYPE_FIXED64: { |
||||
std::string type = PrimitiveTypeName(options_, field->cpp_type()); |
||||
if (field->is_repeated() || field->real_containing_oneof()) { |
||||
std::string prefix = field->is_repeated() ? "add" : "set"; |
||||
format_( |
||||
"_internal_$1$_$2$($pi_ns$::UnalignedLoad<$3$>(ptr));\n" |
||||
"ptr += sizeof($3$);\n", |
||||
prefix, FieldName(field), type); |
||||
} else { |
||||
if (HasHasbit(field)) { |
||||
format_("_Internal::set_has_$1$(&$has_bits$);\n", FieldName(field)); |
||||
} |
||||
format_( |
||||
"$1$_ = $pi_ns$::UnalignedLoad<$2$>(ptr);\n" |
||||
"ptr += sizeof($2$);\n", |
||||
FieldName(field), type); |
||||
} |
||||
break; |
||||
} |
||||
case WireFormatLite::WIRETYPE_LENGTH_DELIMITED: { |
||||
GenerateLengthDelim(field); |
||||
format_("CHK_(ptr);\n"); |
||||
break; |
||||
} |
||||
case WireFormatLite::WIRETYPE_START_GROUP: { |
||||
format_( |
||||
"ptr = ctx->ParseGroup(_internal_$1$_$2$(), ptr, $3$);\n" |
||||
"CHK_(ptr);\n", |
||||
field->is_repeated() ? "add" : "mutable", FieldName(field), tag); |
||||
break; |
||||
} |
||||
case WireFormatLite::WIRETYPE_END_GROUP: { |
||||
GOOGLE_LOG(FATAL) << "Can't have end group field\n"; |
||||
break; |
||||
} |
||||
} // switch (wire_type)
|
||||
} |
||||
|
||||
// Returns the tag for this field and in case of repeated packable fields,
|
||||
// sets a fallback tag in fallback_tag_ptr.
|
||||
static uint32_t ExpectedTag(const FieldDescriptor* field, |
||||
uint32_t* fallback_tag_ptr) { |
||||
uint32_t expected_tag; |
||||
if (field->is_packable()) { |
||||
auto expected_wiretype = WireFormat::WireTypeForFieldType(field->type()); |
||||
expected_tag = |
||||
WireFormatLite::MakeTag(field->number(), expected_wiretype); |
||||
GOOGLE_CHECK(expected_wiretype != WireFormatLite::WIRETYPE_LENGTH_DELIMITED); |
||||
auto fallback_wiretype = WireFormatLite::WIRETYPE_LENGTH_DELIMITED; |
||||
uint32_t fallback_tag = |
||||
WireFormatLite::MakeTag(field->number(), fallback_wiretype); |
||||
|
||||
if (field->is_packed()) std::swap(expected_tag, fallback_tag); |
||||
*fallback_tag_ptr = fallback_tag; |
||||
} else { |
||||
auto expected_wiretype = WireFormat::WireTypeForField(field); |
||||
expected_tag = |
||||
WireFormatLite::MakeTag(field->number(), expected_wiretype); |
||||
} |
||||
return expected_tag; |
||||
} |
||||
|
||||
void GenerateParseIterationBody( |
||||
const Descriptor* descriptor, |
||||
const std::vector<const FieldDescriptor*>& ordered_fields) { |
||||
format_( |
||||
"$uint32$ tag;\n" |
||||
"ptr = $pi_ns$::ReadTag(ptr, &tag);\n"); |
||||
if (!ordered_fields.empty()) format_("switch (tag >> 3) {\n"); |
||||
|
||||
format_.Indent(); |
||||
|
||||
for (const auto* field : ordered_fields) { |
||||
PrintFieldComment(format_, field); |
||||
format_("case $1$:\n", field->number()); |
||||
format_.Indent(); |
||||
uint32_t fallback_tag = 0; |
||||
uint32_t expected_tag = ExpectedTag(field, &fallback_tag); |
||||
format_( |
||||
"if (PROTOBUF_PREDICT_TRUE(static_cast<$uint8$>(tag) == $1$)) {\n", |
||||
expected_tag & 0xFF); |
||||
format_.Indent(); |
||||
auto wiretype = WireFormatLite::GetTagWireType(expected_tag); |
||||
uint32_t tag = WireFormatLite::MakeTag(field->number(), wiretype); |
||||
int tag_size = io::CodedOutputStream::VarintSize32(tag); |
||||
bool is_repeat = ShouldRepeat(field, wiretype); |
||||
if (is_repeat) { |
||||
format_( |
||||
"ptr -= $1$;\n" |
||||
"do {\n" |
||||
" ptr += $1$;\n", |
||||
tag_size); |
||||
format_.Indent(); |
||||
} |
||||
GenerateFieldBody(wiretype, field); |
||||
if (is_repeat) { |
||||
format_.Outdent(); |
||||
format_( |
||||
" if (!ctx->DataAvailable(ptr)) break;\n" |
||||
"} while ($pi_ns$::ExpectTag<$1$>(ptr));\n", |
||||
tag); |
||||
} |
||||
format_.Outdent(); |
||||
if (fallback_tag) { |
||||
format_("} else if (static_cast<$uint8$>(tag) == $1$) {\n", |
||||
fallback_tag & 0xFF); |
||||
format_.Indent(); |
||||
GenerateFieldBody(WireFormatLite::GetTagWireType(fallback_tag), field); |
||||
format_.Outdent(); |
||||
} |
||||
format_.Outdent(); |
||||
format_( |
||||
" } else goto handle_unusual;\n" |
||||
" $continue$;\n"); |
||||
} // for loop over ordered fields
|
||||
|
||||
// Default case
|
||||
if (!ordered_fields.empty()) format_("default: {\n"); |
||||
if (!ordered_fields.empty()) format_("handle_unusual:\n"); |
||||
format_( |
||||
" if ((tag == 0) || ((tag & 7) == 4)) {\n" |
||||
" CHK_(ptr);\n" |
||||
" ctx->SetLastTag(tag);\n" |
||||
" goto success;\n" |
||||
" }\n"); |
||||
if (IsMapEntryMessage(descriptor)) { |
||||
format_(" $continue$;\n"); |
||||
} else { |
||||
if (descriptor->extension_range_count() > 0) { |
||||
format_("if ("); |
||||
for (int i = 0; i < descriptor->extension_range_count(); i++) { |
||||
const Descriptor::ExtensionRange* range = |
||||
descriptor->extension_range(i); |
||||
if (i > 0) format_(" ||\n "); |
||||
|
||||
uint32_t start_tag = WireFormatLite::MakeTag( |
||||
range->start, static_cast<WireFormatLite::WireType>(0)); |
||||
uint32_t end_tag = WireFormatLite::MakeTag( |
||||
range->end, static_cast<WireFormatLite::WireType>(0)); |
||||
|
||||
if (range->end > FieldDescriptor::kMaxNumber) { |
||||
format_("($1$u <= tag)", start_tag); |
||||
} else { |
||||
format_("($1$u <= tag && tag < $2$u)", start_tag, end_tag); |
||||
} |
||||
} |
||||
format_(") {\n"); |
||||
format_( |
||||
" ptr = _extensions_.ParseField(tag, ptr,\n" |
||||
" internal_default_instance(), &_internal_metadata_, ctx);\n" |
||||
" CHK_(ptr != nullptr);\n" |
||||
" $continue$;\n" |
||||
"}\n"); |
||||
} |
||||
format_( |
||||
" ptr = UnknownFieldParse(tag,\n" |
||||
" _internal_metadata_.mutable_unknown_fields<$unknown_" |
||||
"fields_type$>(),\n" |
||||
" ptr, ctx);\n" |
||||
" CHK_(ptr != nullptr);\n" |
||||
" $continue$;\n"); |
||||
} |
||||
if (!ordered_fields.empty()) format_("}\n"); // default case
|
||||
format_.Outdent(); |
||||
if (!ordered_fields.empty()) format_("} // switch\n"); |
||||
} |
||||
}; |
||||
|
||||
} // namespace
|
||||
|
||||
void GenerateParseFunction(const Descriptor* descriptor, int num_hasbits, |
||||
const Options& options, |
||||
MessageSCCAnalyzer* scc_analyzer, |
||||
io::Printer* printer) { |
||||
ParseFunctionGenerator generator(descriptor, num_hasbits, options, |
||||
scc_analyzer, printer); |
||||
generator.GenerateLoopingParseFunction(); |
||||
} |
||||
|
||||
} // namespace cpp
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
@ -0,0 +1,54 @@ |
||||
// 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 <google/protobuf/compiler/cpp/cpp_helpers.h> |
||||
#include <google/protobuf/compiler/cpp/cpp_options.h> |
||||
#include <google/protobuf/io/printer.h> |
||||
#include <google/protobuf/descriptor.h> |
||||
|
||||
namespace google { |
||||
namespace protobuf { |
||||
namespace compiler { |
||||
namespace cpp { |
||||
|
||||
void GenerateParseFunction(const Descriptor* descriptor, int num_hasbits, |
||||
const Options& options, |
||||
MessageSCCAnalyzer* scc_analyzer, |
||||
io::Printer* printer); |
||||
|
||||
} // namespace cpp
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PARSE_FUNCTION_GENERATOR_H__
|
Loading…
Reference in new issue