Sync from Piper @mkruskal/protobuf-absl

PROTOBUF_SYNC_PIPER
pull/10729/head
Mike Kruskal 2 years ago
commit 32bea52ee6
  1. 6
      BUILD.bazel
  2. 23
      CHANGES.txt
  3. 5
      Protobuf-C++.podspec
  4. 2
      Protobuf.podspec
  5. 14
      WORKSPACE
  6. 4
      build_defs/cpp_opts.bzl
  7. 637
      conformance/binary_json_conformance_suite.cc
  8. 23
      conformance/binary_json_conformance_suite.h
  9. 16
      conformance/conformance.proto
  10. 14
      conformance/conformance_objc.m
  11. 65
      conformance/conformance_test.cc
  12. 16
      conformance/conformance_test.h
  13. 57
      conformance/conformance_test_runner.cc
  14. 3
      conformance/failure_list_php_c.txt
  15. 3
      conformance/failure_list_ruby.txt
  16. 607
      conformance/text_format_conformance_suite.cc
  17. 16
      conformance/text_format_conformance_suite.h
  18. 2
      csharp/Google.Protobuf.Tools.nuspec
  19. 2
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  20. 135
      docs/cmake_protobuf_generate.md
  21. 6
      java/README.md
  22. 2
      java/bom/pom.xml
  23. 2
      java/core/pom.xml
  24. 12
      java/core/src/main/java/com/google/protobuf/FieldSet.java
  25. 14
      java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
  26. 2
      java/internal/testing.bzl
  27. 2
      java/kotlin-lite/pom.xml
  28. 2
      java/kotlin/pom.xml
  29. 2
      java/lite.md
  30. 2
      java/lite/pom.xml
  31. 2
      java/pom.xml
  32. 42
      java/protoc/README.md
  33. 2
      java/util/pom.xml
  34. 5
      java/util/src/main/java/com/google/protobuf/util/Values.java
  35. 2
      kokoro/linux/aarch64/python_run_tests_with_qemu_aarch64.sh
  36. 53
      kokoro/linux/bazel.sh
  37. 17
      kokoro/linux/bazel/common.cfg
  38. 18
      kokoro/linux/staleness/release.cfg
  39. 2
      kokoro/macos/prepare_build_macos_rc
  40. 2
      kokoro/release/protoc/build-protoc.sh
  41. 2
      kokoro/windows/bazel/build.bat
  42. 2
      objectivec/DevTools/check_version_stamps.sh
  43. 3
      objectivec/GPBAny.pbobjc.h
  44. 5
      objectivec/GPBAny.pbobjc.m
  45. 3
      objectivec/GPBApi.pbobjc.h
  46. 5
      objectivec/GPBApi.pbobjc.m
  47. 4
      objectivec/GPBDescriptor.m
  48. 3
      objectivec/GPBDuration.pbobjc.h
  49. 5
      objectivec/GPBDuration.pbobjc.m
  50. 3
      objectivec/GPBEmpty.pbobjc.h
  51. 5
      objectivec/GPBEmpty.pbobjc.m
  52. 3
      objectivec/GPBFieldMask.pbobjc.h
  53. 5
      objectivec/GPBFieldMask.pbobjc.m
  54. 3
      objectivec/GPBSourceContext.pbobjc.h
  55. 5
      objectivec/GPBSourceContext.pbobjc.m
  56. 3
      objectivec/GPBStruct.pbobjc.h
  57. 5
      objectivec/GPBStruct.pbobjc.m
  58. 3
      objectivec/GPBTimestamp.pbobjc.h
  59. 5
      objectivec/GPBTimestamp.pbobjc.m
  60. 3
      objectivec/GPBType.pbobjc.h
  61. 5
      objectivec/GPBType.pbobjc.m
  62. 3
      objectivec/GPBWrappers.pbobjc.h
  63. 5
      objectivec/GPBWrappers.pbobjc.m
  64. 11
      objectivec/Tests/GPBMessageTests.m
  65. 24
      php/BUILD.bazel
  66. 42
      php/ext/google/protobuf/message.c
  67. 24
      php/ext/google/protobuf/package.xml
  68. 20020
      php/ext/google/protobuf/php-upb.c
  69. 7645
      php/ext/google/protobuf/php-upb.h
  70. 2
      php/ext/google/protobuf/protobuf.h
  71. 9
      php/src/Google/Protobuf/Internal/Message.php
  72. 19
      php/tests/EncodeDecodeTest.php
  73. 4
      php/tests/proto/test.proto
  74. 19
      pkg/BUILD.bazel
  75. 1
      pkg/build_systems.bzl
  76. 10
      protobuf_deps.bzl
  77. 6
      protobuf_version.bzl
  78. 11
      python/BUILD.bazel
  79. 2
      python/google/protobuf/__init__.py
  80. 14
      python/google/protobuf/internal/descriptor_pool_test.py
  81. 215
      python/google/protobuf/internal/numpy_test.py
  82. 24
      ruby/BUILD.bazel
  83. 64
      ruby/ext/google/protobuf_c/message.c
  84. 19381
      ruby/ext/google/protobuf_c/ruby-upb.c
  85. 7233
      ruby/ext/google/protobuf_c/ruby-upb.h
  86. 2
      ruby/google-protobuf.gemspec
  87. 4
      ruby/pom.xml
  88. 23
      src/BUILD.bazel
  89. 120
      src/file_lists.cmake
  90. 80
      src/google/protobuf/BUILD.bazel
  91. 2
      src/google/protobuf/any.pb.h
  92. 2
      src/google/protobuf/api.pb.h
  93. 152
      src/google/protobuf/arena.cc
  94. 13
      src/google/protobuf/arena_align.cc
  95. 155
      src/google/protobuf/arena_align.h
  96. 215
      src/google/protobuf/arena_align_test.cc
  97. 126
      src/google/protobuf/arena_allocation_policy.h
  98. 189
      src/google/protobuf/arena_cleanup.h
  99. 281
      src/google/protobuf/arena_impl.h
  100. 1
      src/google/protobuf/compiler/command_line_interface.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -217,6 +217,12 @@ alias(
visibility = ["//visibility:public"],
)
alias(
name = "json",
actual = "//src/google/protobuf/json",
visibility = ["//visibility:public"],
)
################################################################################
# Java support
################################################################################

@ -41,14 +41,6 @@
* More thoroughly annotate public generated code in Java lite protocol buffers.
* Fixed Bug in proto3 java lite repeated enum fields. Failed to call copyOnWrite before modifying previously built message. Causes modification to already "built" messages that should be immutable.
* Fix Java reflection serialization of empty packed fields.
* Refactoring java full runtime to reuse sub-message builders and prepare to
migrate parsing logic from parse constructor to builder.
* Move proto wireformat parsing functionality from the private "parsing
constructor" to the Builder class.
* Change the Lite runtime to prefer merging from the wireformat into mutable
messages rather than building up a new immutable object before merging. This
way results in fewer allocations and copy operations.
* Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
Python
* Changes ordering of printed fields in .pyi files from lexicographic to the same ordering found in the proto descriptor.
@ -58,6 +50,21 @@
Compiler
* Print full path name of source .proto file on error
2022-09-29 version 21.7 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
Java
* Refactoring java full runtime to reuse sub-message builders and prepare to
migrate parsing logic from parse constructor to builder.
* Move proto wireformat parsing functionality from the private "parsing
constructor" to the Builder class.
* Change the Lite runtime to prefer merging from the wireformat into mutable
messages rather than building up a new immutable object before merging. This
way results in fewer allocations and copy operations.
* Make message-type extensions merge from wire-format instead of building up
instances and merging afterwards. This has much better performance.
* Fix TextFormat parser to build up recurring (but supposedly not repeated)
sub-messages directly from text rather than building a new sub-message and
merging the fully formed message into the existing field.
2022-09-13 version 21.6 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
C++

@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Protobuf-C++'
s.version = '3.21.6'
s.version = '3.21.7'
s.summary = 'Protocol Buffers v3 runtime library for C++.'
s.homepage = 'https://github.com/google/protobuf'
s.license = 'BSD-3-Clause'
@ -13,8 +13,7 @@ Pod::Spec.new do |s|
s.source_files = 'src/google/protobuf/*.{h,cc,inc}',
'src/google/protobuf/stubs/*.{h,cc}',
'src/google/protobuf/io/*.{h,cc}',
'src/google/protobuf/util/*.{h,cc}',
'src/google/protobuf/util/internal/*.{h,cc}'
'src/google/protobuf/util/*.{h,cc}'
# Excluding all the tests in the directories above
s.exclude_files = 'src/google/**/*_test.{h,cc,inc}',

@ -5,7 +5,7 @@
# dependent projects use the :git notation to refer to the library.
Pod::Spec.new do |s|
s.name = 'Protobuf'
s.version = '3.21.6'
s.version = '3.21.7'
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
s.homepage = 'https://github.com/protocolbuffers/protobuf'
s.license = 'BSD-3-Clause'

@ -16,6 +16,13 @@ http_archive(
],
)
http_archive(
name = "com_googlesource_code_re2",
sha256 = "906d0df8ff48f8d3a00a808827f009a840190f404559f649cb8e4d7143255ef9",
strip_prefix = "re2-a276a8c738735a0fe45a6ee590fe2df69bcf4502",
urls = ["https://github.com/google/re2/archive/a276a8c738735a0fe45a6ee590fe2df69bcf4502.zip"], # 2022-04-08
)
# Bazel platform rules.
http_archive(
name = "platforms",
@ -48,6 +55,13 @@ pinned_maven_install()
# For `cc_proto_blacklist_test` and `build_test`.
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
load("@rules_python//python:pip.bzl", "pip_install")
pip_install(
name="pip_deps",
requirements = "//python:requirements.txt"
)
bazel_skylib_workspace()
load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")

@ -21,7 +21,9 @@ COPTS = select({
"-Wno-sign-compare",
"-Werror",
],
})
}) + [
"-std=c++14", # Protobuf requires C++14.
]
# Android and MSVC builds do not need to link in a separate pthread library.
LINK_OPTS = select({

@ -37,6 +37,7 @@
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "third_party/jsoncpp/json.h"
#include "conformance/conformance.pb.h"
#include "conformance_test.h"
#include "google/protobuf/test_messages_proto2.pb.h"
#include "google/protobuf/test_messages_proto3.pb.h"
@ -60,6 +61,10 @@ namespace {
static const char kTypeUrlPrefix[] = "type.googleapis.com";
// The number of repetitions to use for performance tests.
// Corresponds approx to 500KB wireformat bytes.
static const size_t kPerformanceRepeatCount = 50000;
static string GetTypeUrl(const Descriptor* message) {
return string(kTypeUrlPrefix) + "/" + message->full_name();
}
@ -477,6 +482,25 @@ void BinaryAndJsonConformanceSuite::RunValidJsonTest(
const string& test_name, ConformanceLevel level, const string& input_json,
const string& equivalent_text_format) {
TestAllTypesProto3 prototype;
RunValidJsonTestWithMessage(test_name, level, input_json,
equivalent_text_format, prototype);
}
void BinaryAndJsonConformanceSuite::RunValidJsonTest(
const string& test_name, ConformanceLevel level, const string& input_json,
const string& equivalent_text_format, bool is_proto3) {
if (is_proto3) {
RunValidJsonTest(test_name, level, input_json, equivalent_text_format);
} else {
TestAllTypesProto2 prototype;
RunValidJsonTestWithMessage(test_name, level, input_json,
equivalent_text_format, prototype);
}
}
void BinaryAndJsonConformanceSuite::RunValidJsonTestWithMessage(
const string& test_name, ConformanceLevel level, const string& input_json,
const string& equivalent_text_format, const Message& prototype) {
ConformanceRequestSetting setting1(
level, conformance::JSON, conformance::PROTOBUF,
conformance::JSON_TEST,
@ -550,6 +574,27 @@ void BinaryAndJsonConformanceSuite::RunValidBinaryProtobufTest(
RunValidBinaryInputTest(setting, expected_protobuf, true);
}
void BinaryAndJsonConformanceSuite::RunBinaryPerformanceMergeMessageWithField(
const string& test_name, const string& field_proto, bool is_proto3) {
string message_tag = tag(27, WireFormatLite::WIRETYPE_LENGTH_DELIMITED);
string message_proto = cat(message_tag, delim(field_proto));
string proto;
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
proto.append(message_proto);
}
string multiple_repeated_field_proto;
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
multiple_repeated_field_proto.append(field_proto);
}
string expected_proto =
cat(message_tag, delim(multiple_repeated_field_proto));
RunValidBinaryProtobufTest(test_name, RECOMMENDED, proto, expected_proto,
is_proto3);
}
void BinaryAndJsonConformanceSuite::RunValidProtobufTestWithMessage(
const string& test_name, ConformanceLevel level, const Message *input,
const string& equivalent_text_format, bool is_proto3) {
@ -1311,6 +1356,60 @@ void BinaryAndJsonConformanceSuite::TestUnknownMessage(
message.SerializeAsString(), is_proto3);
}
void BinaryAndJsonConformanceSuite::
TestBinaryPerformanceForAlternatingUnknownFields() {
string unknown_field_1 =
cat(tag(UNKNOWN_FIELD, WireFormatLite::WIRETYPE_VARINT), varint(1234));
string unknown_field_2 = cat(
tag(UNKNOWN_FIELD + 1, WireFormatLite::WIRETYPE_VARINT), varint(5678));
for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) {
string proto;
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
proto.append(unknown_field_1);
proto.append(unknown_field_2);
}
RunValidBinaryProtobufTest(
"TestBinaryPerformanceForAlternatingUnknownFields", RECOMMENDED, proto,
is_proto3);
}
}
void BinaryAndJsonConformanceSuite::
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::Type type) {
const string type_name =
UpperCase(string(".") + FieldDescriptor::TypeName(type));
for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) {
int field_number =
GetFieldForType(type, true, is_proto3, Packed::kFalse)->number();
string rep_field_proto = cat(
tag(field_number, WireFormatLite::WireTypeForFieldType(
static_cast<WireFormatLite::FieldType>(type))),
GetNonDefaultValue(type));
RunBinaryPerformanceMergeMessageWithField(
"TestBinaryPerformanceMergeMessageWithRepeatedFieldForType" + type_name,
rep_field_proto, is_proto3);
}
}
void BinaryAndJsonConformanceSuite::
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::Type type) {
const string type_name =
UpperCase(string(".") + FieldDescriptor::TypeName(type));
string unknown_field_proto =
cat(tag(UNKNOWN_FIELD, WireFormatLite::WireTypeForFieldType(
static_cast<WireFormatLite::FieldType>(type))),
GetNonDefaultValue(type));
for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) {
RunBinaryPerformanceMergeMessageWithField(
"TestBinaryPerformanceMergeMessageWithUnknownFieldForType" + type_name,
unknown_field_proto, is_proto3);
}
}
void BinaryAndJsonConformanceSuite::RunSuiteImpl() {
// Hack to get the list of test failures based on whether
// GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER is enabled or not.
@ -1330,232 +1429,324 @@ void BinaryAndJsonConformanceSuite::RunSuiteImpl() {
kTypeUrlPrefix, DescriptorPool::generated_pool()));
type_url_ = GetTypeUrl(TestAllTypesProto3::descriptor());
for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) {
if (i == FieldDescriptor::TYPE_GROUP) continue;
TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i));
}
if (!performance_) {
for (int i = 1; i <= FieldDescriptor::MAX_TYPE; i++) {
if (i == FieldDescriptor::TYPE_GROUP) continue;
TestPrematureEOFForType(static_cast<FieldDescriptor::Type>(i));
}
TestIllegalTags();
int64 kInt64Min = -9223372036854775808ULL;
int64 kInt64Max = 9223372036854775807ULL;
uint64 kUint64Max = 18446744073709551615ULL;
int32 kInt32Max = 2147483647;
int32 kInt32Min = -2147483648;
uint32 kUint32Max = 4294967295UL;
TestValidDataForType(
FieldDescriptor::TYPE_DOUBLE,
{
{dbl(0), dbl(0)},
{dbl(0.1), dbl(0.1)},
{dbl(1.7976931348623157e+308), dbl(1.7976931348623157e+308)},
{dbl(2.22507385850720138309e-308), dbl(2.22507385850720138309e-308)},
});
TestValidDataForType(
FieldDescriptor::TYPE_FLOAT,
{
{flt(0), flt(0)},
{flt(0.1), flt(0.1)},
{flt(1.00000075e-36), flt(1.00000075e-36)},
{flt(3.402823e+38), flt(3.402823e+38)}, // 3.40282347e+38
{flt(1.17549435e-38f), flt(1.17549435e-38)},
});
TestValidDataForType(FieldDescriptor::TYPE_INT64,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{varint(kInt64Max), varint(kInt64Max)},
{varint(kInt64Min), varint(kInt64Min)},
});
TestValidDataForType(FieldDescriptor::TYPE_UINT64,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{varint(kUint64Max), varint(kUint64Max)},
});
TestValidDataForType(FieldDescriptor::TYPE_INT32,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{longvarint(12345, 2), varint(12345)},
{longvarint(12345, 7), varint(12345)},
{varint(kInt32Max), varint(kInt32Max)},
{varint(kInt32Min), varint(kInt32Min)},
{varint(1LL << 33), varint(0)},
{varint((1LL << 33) - 1), varint(-1)},
{varint(kInt64Max), varint(-1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForType(
FieldDescriptor::TYPE_UINT32,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{longvarint(12345, 2), varint(12345)},
{longvarint(12345, 7), varint(12345)},
{varint(kUint32Max), varint(kUint32Max)}, // UINT32_MAX
{varint(1LL << 33), varint(0)},
{varint((1LL << 33) + 1), varint(1)},
{varint((1LL << 33) - 1), varint((1LL << 32) - 1)},
{varint(kInt64Max), varint((1LL << 32) - 1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_FIXED64,
{
{u64(0), u64(0)},
{u64(12345), u64(12345)},
{u64(kUint64Max), u64(kUint64Max)},
});
TestValidDataForType(FieldDescriptor::TYPE_FIXED32,
{
{u32(0), u32(0)},
{u32(12345), u32(12345)},
{u32(kUint32Max), u32(kUint32Max)}, // UINT32_MAX
});
TestValidDataForType(FieldDescriptor::TYPE_SFIXED64,
{
{u64(0), u64(0)},
{u64(12345), u64(12345)},
{u64(kInt64Max), u64(kInt64Max)},
{u64(kInt64Min), u64(kInt64Min)},
});
TestValidDataForType(FieldDescriptor::TYPE_SFIXED32,
{
{u32(0), u32(0)},
{u32(12345), u32(12345)},
{u32(kInt32Max), u32(kInt32Max)},
{u32(kInt32Min), u32(kInt32Min)},
});
// Bools should be serialized as 0 for false and 1 for true. Parsers should
// also interpret any nonzero value as true.
TestValidDataForType(FieldDescriptor::TYPE_BOOL,
{
{varint(0), varint(0)},
{varint(1), varint(1)},
{varint(-1), varint(1)},
{varint(12345678), varint(1)},
{varint(1LL << 33), varint(1)},
{varint(kInt64Max), varint(1)},
{varint(kInt64Min), varint(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_SINT32,
{
{zz32(0), zz32(0)},
{zz32(12345), zz32(12345)},
{zz32(kInt32Max), zz32(kInt32Max)},
{zz32(kInt32Min), zz32(kInt32Min)},
{zz64(kInt32Max + 2LL), zz32(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_SINT64,
{
{zz64(0), zz64(0)},
{zz64(12345), zz64(12345)},
{zz64(kInt64Max), zz64(kInt64Max)},
{zz64(kInt64Min), zz64(kInt64Min)},
});
TestValidDataForType(
FieldDescriptor::TYPE_STRING,
{
{delim(""), delim("")},
{delim("Hello world!"), delim("Hello world!")},
{delim("\'\"\?\\\a\b\f\n\r\t\v"),
delim("\'\"\?\\\a\b\f\n\r\t\v")}, // escape
{delim("谷歌"), delim("谷歌")}, // Google in Chinese
{delim("\u8C37\u6B4C"), delim("谷歌")}, // unicode escape
{delim("\u8c37\u6b4c"), delim("谷歌")}, // lowercase unicode
{delim("\xF0\x9F\x98\x81"), delim("\xF0\x9F\x98\x81")}, // emoji: 😁
});
TestValidDataForType(FieldDescriptor::TYPE_BYTES,
{
{delim(""), delim("")},
{delim("Hello world!"), delim("Hello world!")},
{delim("\x01\x02"), delim("\x01\x02")},
{delim("\xfb"), delim("\xfb")},
});
TestValidDataForType(FieldDescriptor::TYPE_ENUM,
{
{varint(0), varint(0)},
{varint(1), varint(1)},
{varint(2), varint(2)},
{varint(-1), varint(-1)},
{varint(kInt64Max), varint(-1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForRepeatedScalarMessage();
TestValidDataForType(
FieldDescriptor::TYPE_MESSAGE,
{
{delim(""), delim("")},
{delim(cat(tag(1, WireFormatLite::WIRETYPE_VARINT), varint(1234))),
delim(cat(tag(1, WireFormatLite::WIRETYPE_VARINT), varint(1234)))},
});
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_INT32);
TestValidDataForMapType(FieldDescriptor::TYPE_INT64,
FieldDescriptor::TYPE_INT64);
TestValidDataForMapType(FieldDescriptor::TYPE_UINT32,
FieldDescriptor::TYPE_UINT32);
TestValidDataForMapType(FieldDescriptor::TYPE_UINT64,
FieldDescriptor::TYPE_UINT64);
TestValidDataForMapType(FieldDescriptor::TYPE_SINT32,
FieldDescriptor::TYPE_SINT32);
TestValidDataForMapType(FieldDescriptor::TYPE_SINT64,
FieldDescriptor::TYPE_SINT64);
TestValidDataForMapType(FieldDescriptor::TYPE_FIXED32,
FieldDescriptor::TYPE_FIXED32);
TestValidDataForMapType(FieldDescriptor::TYPE_FIXED64,
FieldDescriptor::TYPE_FIXED64);
TestValidDataForMapType(FieldDescriptor::TYPE_SFIXED32,
FieldDescriptor::TYPE_SFIXED32);
TestValidDataForMapType(FieldDescriptor::TYPE_SFIXED64,
FieldDescriptor::TYPE_SFIXED64);
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_FLOAT);
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_DOUBLE);
TestValidDataForMapType(FieldDescriptor::TYPE_BOOL,
FieldDescriptor::TYPE_BOOL);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_STRING);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_BYTES);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_ENUM);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_MESSAGE);
// Additional test to check overwriting message value map.
TestOverwriteMessageValueMap();
TestValidDataForOneofType(FieldDescriptor::TYPE_UINT32);
TestValidDataForOneofType(FieldDescriptor::TYPE_BOOL);
TestValidDataForOneofType(FieldDescriptor::TYPE_UINT64);
TestValidDataForOneofType(FieldDescriptor::TYPE_FLOAT);
TestValidDataForOneofType(FieldDescriptor::TYPE_DOUBLE);
TestValidDataForOneofType(FieldDescriptor::TYPE_STRING);
TestValidDataForOneofType(FieldDescriptor::TYPE_BYTES);
TestValidDataForOneofType(FieldDescriptor::TYPE_ENUM);
TestValidDataForOneofType(FieldDescriptor::TYPE_MESSAGE);
// Additional test to check merging oneof message.
TestMergeOneofMessage();
// TODO(haberman):
// TestValidDataForType(FieldDescriptor::TYPE_GROUP
// Unknown fields.
{
TestAllTypesProto3 messageProto3;
TestAllTypesProto2 messageProto2;
// TODO(yilunchong): update this behavior when unknown field's behavior
// changed in open source. Also delete
// Required.Proto3.ProtobufInput.UnknownVarint.ProtobufOutput
// from failure list of python_cpp python java
TestUnknownMessage(messageProto3, true);
TestUnknownMessage(messageProto2, false);
TestIllegalTags();
int64 kInt64Min = -9223372036854775808ULL;
int64 kInt64Max = 9223372036854775807ULL;
uint64 kUint64Max = 18446744073709551615ULL;
int32 kInt32Max = 2147483647;
int32 kInt32Min = -2147483648;
uint32 kUint32Max = 4294967295UL;
TestValidDataForType(
FieldDescriptor::TYPE_DOUBLE,
{
{dbl(0), dbl(0)},
{dbl(0.1), dbl(0.1)},
{dbl(1.7976931348623157e+308), dbl(1.7976931348623157e+308)},
{dbl(2.22507385850720138309e-308),
dbl(2.22507385850720138309e-308)},
});
TestValidDataForType(
FieldDescriptor::TYPE_FLOAT,
{
{flt(0), flt(0)},
{flt(0.1), flt(0.1)},
{flt(1.00000075e-36), flt(1.00000075e-36)},
{flt(3.402823e+38), flt(3.402823e+38)}, // 3.40282347e+38
{flt(1.17549435e-38f), flt(1.17549435e-38)},
});
TestValidDataForType(FieldDescriptor::TYPE_INT64,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{varint(kInt64Max), varint(kInt64Max)},
{varint(kInt64Min), varint(kInt64Min)},
});
TestValidDataForType(FieldDescriptor::TYPE_UINT64,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{varint(kUint64Max), varint(kUint64Max)},
});
TestValidDataForType(FieldDescriptor::TYPE_INT32,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{longvarint(12345, 2), varint(12345)},
{longvarint(12345, 7), varint(12345)},
{varint(kInt32Max), varint(kInt32Max)},
{varint(kInt32Min), varint(kInt32Min)},
{varint(1LL << 33), varint(0)},
{varint((1LL << 33) - 1), varint(-1)},
{varint(kInt64Max), varint(-1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForType(
FieldDescriptor::TYPE_UINT32,
{
{varint(0), varint(0)},
{varint(12345), varint(12345)},
{longvarint(12345, 2), varint(12345)},
{longvarint(12345, 7), varint(12345)},
{varint(kUint32Max), varint(kUint32Max)}, // UINT32_MAX
{varint(1LL << 33), varint(0)},
{varint((1LL << 33) + 1), varint(1)},
{varint((1LL << 33) - 1), varint((1LL << 32) - 1)},
{varint(kInt64Max), varint((1LL << 32) - 1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_FIXED64,
{
{u64(0), u64(0)},
{u64(12345), u64(12345)},
{u64(kUint64Max), u64(kUint64Max)},
});
TestValidDataForType(FieldDescriptor::TYPE_FIXED32,
{
{u32(0), u32(0)},
{u32(12345), u32(12345)},
{u32(kUint32Max), u32(kUint32Max)}, // UINT32_MAX
});
TestValidDataForType(FieldDescriptor::TYPE_SFIXED64,
{
{u64(0), u64(0)},
{u64(12345), u64(12345)},
{u64(kInt64Max), u64(kInt64Max)},
{u64(kInt64Min), u64(kInt64Min)},
});
TestValidDataForType(FieldDescriptor::TYPE_SFIXED32,
{
{u32(0), u32(0)},
{u32(12345), u32(12345)},
{u32(kInt32Max), u32(kInt32Max)},
{u32(kInt32Min), u32(kInt32Min)},
});
// Bools should be serialized as 0 for false and 1 for true. Parsers should
// also interpret any nonzero value as true.
TestValidDataForType(FieldDescriptor::TYPE_BOOL,
{
{varint(0), varint(0)},
{varint(1), varint(1)},
{varint(-1), varint(1)},
{varint(12345678), varint(1)},
{varint(1LL << 33), varint(1)},
{varint(kInt64Max), varint(1)},
{varint(kInt64Min), varint(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_SINT32,
{
{zz32(0), zz32(0)},
{zz32(12345), zz32(12345)},
{zz32(kInt32Max), zz32(kInt32Max)},
{zz32(kInt32Min), zz32(kInt32Min)},
{zz64(kInt32Max + 2LL), zz32(1)},
});
TestValidDataForType(FieldDescriptor::TYPE_SINT64,
{
{zz64(0), zz64(0)},
{zz64(12345), zz64(12345)},
{zz64(kInt64Max), zz64(kInt64Max)},
{zz64(kInt64Min), zz64(kInt64Min)},
});
TestValidDataForType(
FieldDescriptor::TYPE_STRING,
{
{delim(""), delim("")},
{delim("Hello world!"), delim("Hello world!")},
{delim("\'\"\?\\\a\b\f\n\r\t\v"),
delim("\'\"\?\\\a\b\f\n\r\t\v")}, // escape
{delim("谷歌"), delim("谷歌")}, // Google in Chinese
{delim("\u8C37\u6B4C"), delim("谷歌")}, // unicode escape
{delim("\u8c37\u6b4c"), delim("谷歌")}, // lowercase unicode
{delim("\xF0\x9F\x98\x81"), delim("\xF0\x9F\x98\x81")}, // emoji: 😁
});
TestValidDataForType(FieldDescriptor::TYPE_BYTES,
{
{delim(""), delim("")},
{delim("Hello world!"), delim("Hello world!")},
{delim("\x01\x02"), delim("\x01\x02")},
{delim("\xfb"), delim("\xfb")},
});
TestValidDataForType(FieldDescriptor::TYPE_ENUM,
{
{varint(0), varint(0)},
{varint(1), varint(1)},
{varint(2), varint(2)},
{varint(-1), varint(-1)},
{varint(kInt64Max), varint(-1)},
{varint(kInt64Min + 1), varint(1)},
});
TestValidDataForRepeatedScalarMessage();
TestValidDataForType(
FieldDescriptor::TYPE_MESSAGE,
{
{delim(""), delim("")},
{delim(cat(tag(1, WireFormatLite::WIRETYPE_VARINT), varint(1234))),
delim(cat(tag(1, WireFormatLite::WIRETYPE_VARINT), varint(1234)))},
});
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_INT32);
TestValidDataForMapType(FieldDescriptor::TYPE_INT64,
FieldDescriptor::TYPE_INT64);
TestValidDataForMapType(FieldDescriptor::TYPE_UINT32,
FieldDescriptor::TYPE_UINT32);
TestValidDataForMapType(FieldDescriptor::TYPE_UINT64,
FieldDescriptor::TYPE_UINT64);
TestValidDataForMapType(FieldDescriptor::TYPE_SINT32,
FieldDescriptor::TYPE_SINT32);
TestValidDataForMapType(FieldDescriptor::TYPE_SINT64,
FieldDescriptor::TYPE_SINT64);
TestValidDataForMapType(FieldDescriptor::TYPE_FIXED32,
FieldDescriptor::TYPE_FIXED32);
TestValidDataForMapType(FieldDescriptor::TYPE_FIXED64,
FieldDescriptor::TYPE_FIXED64);
TestValidDataForMapType(FieldDescriptor::TYPE_SFIXED32,
FieldDescriptor::TYPE_SFIXED32);
TestValidDataForMapType(FieldDescriptor::TYPE_SFIXED64,
FieldDescriptor::TYPE_SFIXED64);
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_FLOAT);
TestValidDataForMapType(FieldDescriptor::TYPE_INT32,
FieldDescriptor::TYPE_DOUBLE);
TestValidDataForMapType(FieldDescriptor::TYPE_BOOL,
FieldDescriptor::TYPE_BOOL);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_STRING);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_BYTES);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_ENUM);
TestValidDataForMapType(FieldDescriptor::TYPE_STRING,
FieldDescriptor::TYPE_MESSAGE);
// Additional test to check overwriting message value map.
TestOverwriteMessageValueMap();
TestValidDataForOneofType(FieldDescriptor::TYPE_UINT32);
TestValidDataForOneofType(FieldDescriptor::TYPE_BOOL);
TestValidDataForOneofType(FieldDescriptor::TYPE_UINT64);
TestValidDataForOneofType(FieldDescriptor::TYPE_FLOAT);
TestValidDataForOneofType(FieldDescriptor::TYPE_DOUBLE);
TestValidDataForOneofType(FieldDescriptor::TYPE_STRING);
TestValidDataForOneofType(FieldDescriptor::TYPE_BYTES);
TestValidDataForOneofType(FieldDescriptor::TYPE_ENUM);
TestValidDataForOneofType(FieldDescriptor::TYPE_MESSAGE);
// Additional test to check merging oneof message.
TestMergeOneofMessage();
// TODO(haberman):
// TestValidDataForType(FieldDescriptor::TYPE_GROUP
// Unknown fields.
{
TestAllTypesProto3 messageProto3;
TestAllTypesProto2 messageProto2;
// TODO(yilunchong): update this behavior when unknown field's behavior
// changed in open source. Also delete
// Required.Proto3.ProtobufInput.UnknownVarint.ProtobufOutput
// from failure list of python_cpp python java
TestUnknownMessage(messageProto3, true);
TestUnknownMessage(messageProto2, false);
}
RunJsonTests();
}
// Flag control performance tests to keep them internal and opt-in only
if (performance_) {
RunBinaryPerformanceTests();
RunJsonPerformanceTests();
}
}
void BinaryAndJsonConformanceSuite::RunBinaryPerformanceTests() {
TestBinaryPerformanceForAlternatingUnknownFields();
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_BOOL);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_DOUBLE);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_FLOAT);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_UINT32);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_UINT64);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_STRING);
TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_BYTES);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_BOOL);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_DOUBLE);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_FLOAT);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_UINT32);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_UINT64);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_STRING);
TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
FieldDescriptor::TYPE_BYTES);
}
void BinaryAndJsonConformanceSuite::RunJsonPerformanceTests() {
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_BOOL, "true");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_DOUBLE, "123");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_FLOAT, "123");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_UINT32, "123");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_UINT64, "123");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_STRING, "\"foo\"");
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::TYPE_BYTES, "\"foo\"");
}
// This is currently considered valid input by some languages but not others
void BinaryAndJsonConformanceSuite::
TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
FieldDescriptor::Type type, string field_value) {
const string type_name =
UpperCase(string(".") + FieldDescriptor::TypeName(type));
for (int is_proto3 = 0; is_proto3 < 2; is_proto3++) {
const FieldDescriptor* field =
GetFieldForType(type, true, is_proto3, Packed::kFalse);
string field_name = field->name();
string message_field = "\"" + field_name + "\": [" + field_value + "]";
string recursive_message =
"\"recursive_message\": { " + message_field + "}";
string input = "{";
input.append(recursive_message);
for (size_t i = 1; i < kPerformanceRepeatCount; i++) {
input.append("," + recursive_message);
}
input.append("}");
RunJsonTests();
string textproto_message_field = field_name + ": " + field_value;
string expected_textproto = "recursive_message { ";
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
expected_textproto.append(textproto_message_field + " ");
}
expected_textproto.append("}");
RunValidJsonTest(
"TestJsonPerformanceMergeMessageWithRepeatedFieldForType" + type_name,
RECOMMENDED, input, expected_textproto, is_proto3);
}
}
void BinaryAndJsonConformanceSuite::RunJsonTests() {

@ -31,6 +31,7 @@
#ifndef CONFORMANCE_BINARY_JSON_CONFORMANCE_SUITE_H
#define CONFORMANCE_BINARY_JSON_CONFORMANCE_SUITE_H
#include "google/protobuf/descriptor.h"
#include "third_party/jsoncpp/json.h"
#include "conformance_test.h"
@ -43,6 +44,8 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
private:
void RunSuiteImpl() override;
void RunBinaryPerformanceTests();
void RunJsonPerformanceTests();
void RunJsonTests();
void RunJsonTestsForFieldNameConvention();
void RunJsonTestsForNonRepeatedTypes();
@ -56,6 +59,15 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
void RunValidJsonTest(const std::string& test_name, ConformanceLevel level,
const std::string& input_json,
const std::string& equivalent_text_format);
void RunValidJsonTest(const std::string& test_name, ConformanceLevel level,
const std::string& input_json,
const std::string& equivalent_text_format,
bool is_proto3);
void RunValidJsonTestWithMessage(const std::string& test_name,
ConformanceLevel level,
const std::string& input_json,
const std::string& equivalent_text_forma,
const Message& prototype);
void RunValidJsonTestWithProtobufInput(
const std::string& test_name, ConformanceLevel level,
const protobuf_test_messages::proto3::TestAllTypesProto3& input,
@ -78,6 +90,10 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
const std::string& input_protobuf,
const std::string& expected_protobuf,
bool is_proto3);
void RunBinaryPerformanceMergeMessageWithField(const std::string& test_name,
const std::string& field_proto,
bool is_proto3);
void RunValidProtobufTestWithMessage(
const std::string& test_name, ConformanceLevel level,
const Message* input, const std::string& equivalent_text_format,
@ -130,6 +146,13 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
void TestValidDataForOneofType(google::protobuf::FieldDescriptor::Type);
void TestMergeOneofMessage();
void TestOverwriteMessageValueMap();
void TestBinaryPerformanceForAlternatingUnknownFields();
void TestBinaryPerformanceMergeMessageWithRepeatedFieldForType(
google::protobuf::FieldDescriptor::Type);
void TestBinaryPerformanceMergeMessageWithUnknownFieldForType(
google::protobuf::FieldDescriptor::Type);
void TestJsonPerformanceMergeMessageWithRepeatedFieldForType(
google::protobuf::FieldDescriptor::Type, std::string field_value);
std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver_;
std::string type_url_;

@ -29,8 +29,11 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package conformance;
option java_package = "com.google.protobuf.conformance";
option objc_class_prefix = "Conformance";
// This defines the conformance testing protocol. This protocol exists between
// the conformance test suite itself and the code being tested. For each test,
@ -69,7 +72,8 @@ enum TestCategory {
// https://developers.google.com/protocol-buffers/docs/proto3#json_options
// for more detail.
JSON_IGNORE_UNKNOWN_PARSING_TEST = 3;
// Test jspb wire format. Google internal only. Opensource testees just skip it.
// Test jspb wire format. Google internal only. Opensource testees just skip
// it.
JSPB_TEST = 4;
// Test text format. For cpp, java and python, testees can already deal with
// this type. Testees of other languages can simply skip it.
@ -109,8 +113,8 @@ message ConformanceRequest {
string message_type = 4;
// Each test is given a specific test category. Some category may need
// specific support in testee programs. Refer to the definition of TestCategory
// for more information.
// specific support in testee programs. Refer to the definition of
// TestCategory for more information.
TestCategory test_category = 5;
// Specify details for how to encode jspb.
@ -136,6 +140,11 @@ message ConformanceResponse {
// this field.
string serialize_error = 6;
// This should be set if the test program timed out. The string should
// provide more information about what the child process was doing when it
// was killed.
string timeout_error = 9;
// This should be set if some other error occurred. This will always
// indicate that the test failed. The string can provide more information
// about the failure.
@ -169,4 +178,3 @@ message JspbEncodingConfig {
// Encode the value field of Any as jspb array if true, otherwise binary.
bool use_jspb_array_any_format = 1;
}

@ -77,7 +77,7 @@ static ConformanceResponse *DoTest(ConformanceRequest *request) {
if ([request.messageType isEqual:@"protobuf_test_messages.proto3.TestAllTypesProto3"]) {
msgClass = [Proto3TestAllTypesProto3 class];
} else if ([request.messageType isEqual:@"protobuf_test_messages.proto2.TestAllTypesProto2"]) {
msgClass = [TestAllTypesProto2 class];
msgClass = [Proto2TestAllTypesProto2 class];
} else {
response.runtimeError =
[NSString stringWithFormat:@"Protobuf request had an unknown message_type: %@",
@ -110,13 +110,13 @@ static ConformanceResponse *DoTest(ConformanceRequest *request) {
if (testMessage) {
switch (request.requestedOutputFormat) {
case WireFormat_GPBUnrecognizedEnumeratorValue:
case WireFormat_Unspecified:
case ConformanceWireFormat_GPBUnrecognizedEnumeratorValue:
case ConformanceWireFormat_Unspecified:
response.runtimeError =
[NSString stringWithFormat:@"Unrecognized/unspecified output format: %@", request];
break;
case WireFormat_Protobuf:
case ConformanceWireFormat_Protobuf:
response.protobufPayload = testMessage.data;
if (!response.protobufPayload) {
response.serializeError =
@ -124,17 +124,17 @@ static ConformanceResponse *DoTest(ConformanceRequest *request) {
}
break;
case WireFormat_Json:
case ConformanceWireFormat_Json:
response.skipped = @"ObjC doesn't support generating JSON";
break;
case WireFormat_Jspb:
case ConformanceWireFormat_Jspb:
response.skipped =
@"ConformanceRequest had a requested_output_format of JSPB WireFormat; that"
" isn't supposed to happen with opensource.";
break;
case WireFormat_TextFormat:
case ConformanceWireFormat_TextFormat:
// ObjC only has partial objc generation, so don't attempt any tests that need
// support.
response.skipped = @"ObjC doesn't support generating TextFormat";

@ -44,6 +44,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "conformance/conformance.pb.h"
#include "conformance/conformance.pb.h"
using conformance::ConformanceRequest;
using conformance::ConformanceResponse;
@ -176,6 +177,59 @@ string ConformanceTestSuite::ConformanceRequestSetting::
return "";
}
void ConformanceTestSuite::TruncateDebugPayload(string* payload) {
if (payload != nullptr && payload->size() > 200) {
payload->resize(200);
payload->append("...(truncated)");
}
}
const ConformanceRequest ConformanceTestSuite::TruncateRequest(
const ConformanceRequest& request) {
ConformanceRequest debug_request(request);
switch (debug_request.payload_case()) {
case ConformanceRequest::kProtobufPayload:
TruncateDebugPayload(debug_request.mutable_protobuf_payload());
break;
case ConformanceRequest::kJsonPayload:
TruncateDebugPayload(debug_request.mutable_json_payload());
break;
case ConformanceRequest::kTextPayload:
TruncateDebugPayload(debug_request.mutable_text_payload());
break;
case ConformanceRequest::kJspbPayload:
TruncateDebugPayload(debug_request.mutable_jspb_payload());
break;
default:
// Do nothing.
break;
}
return debug_request;
}
const ConformanceResponse ConformanceTestSuite::TruncateResponse(
const ConformanceResponse& response) {
ConformanceResponse debug_response(response);
switch (debug_response.result_case()) {
case ConformanceResponse::kProtobufPayload:
TruncateDebugPayload(debug_response.mutable_protobuf_payload());
break;
case ConformanceResponse::kJsonPayload:
TruncateDebugPayload(debug_response.mutable_json_payload());
break;
case ConformanceResponse::kTextPayload:
TruncateDebugPayload(debug_response.mutable_text_payload());
break;
case ConformanceResponse::kJspbPayload:
TruncateDebugPayload(debug_response.mutable_jspb_payload());
break;
default:
// Do nothing.
break;
}
return debug_response;
}
void ConformanceTestSuite::ReportSuccess(const string& test_name) {
if (expected_to_fail_.erase(test_name) != 0) {
absl::StrAppendFormat(
@ -203,9 +257,10 @@ void ConformanceTestSuite::ReportFailure(const string& test_name,
absl::StrAppendFormat(&output_, "ERROR, test=%s: ", test_name);
unexpected_failing_tests_.insert(test_name);
}
absl::StrAppendFormat(&output_, "%s request=%s, response=%s\n", message,
request.ShortDebugString(),
response.ShortDebugString());
absl::StrAppendFormat(&output_, "%s, request=%s, response=%s\n", message,
TruncateRequest(request).ShortDebugString(),
TruncateResponse(response).ShortDebugString());
}
void ConformanceTestSuite::ReportSkip(const string& test_name,
@ -261,6 +316,7 @@ void ConformanceTestSuite::VerifyResponse(
return;
case ConformanceResponse::kParseError:
case ConformanceResponse::kTimeoutError:
case ConformanceResponse::kRuntimeError:
case ConformanceResponse::kSerializeError:
ReportFailure(test_name, level, request, response,
@ -327,7 +383,8 @@ void ConformanceTestSuite::RunTest(const string& test_name,
if (verbose_) {
absl::StrAppendFormat(
&output_, "conformance test: name=%s, request=%s, response=%s\n",
test_name, request.ShortDebugString(), response->ShortDebugString());
test_name, TruncateRequest(request).ShortDebugString(),
TruncateResponse(*response).ShortDebugString());
}
}

@ -88,10 +88,12 @@ class ForkPipeRunner : public ConformanceTestRunner {
const std::vector<ConformanceTestSuite*>& suites);
ForkPipeRunner(const std::string& executable,
const std::vector<std::string>& executable_args)
const std::vector<std::string>& executable_args,
bool performance)
: child_pid_(-1),
executable_(executable),
executable_args_(executable_args) {}
executable_args_(executable_args),
performance_(performance) {}
explicit ForkPipeRunner(const std::string& executable)
: child_pid_(-1), executable_(executable) {}
@ -114,6 +116,7 @@ class ForkPipeRunner : public ConformanceTestRunner {
pid_t child_pid_;
std::string executable_;
const std::vector<std::string> executable_args_;
bool performance_;
std::string current_test_name_;
};
@ -148,10 +151,12 @@ class ConformanceTestSuite {
public:
ConformanceTestSuite()
: verbose_(false),
performance_(false),
enforce_recommended_(false),
failure_list_flag_name_("--failure_list") {}
virtual ~ConformanceTestSuite() {}
void SetPerformance(bool performance) { performance_ = performance; }
void SetVerbose(bool verbose) { verbose_ = verbose; }
// Whether to require the testee to pass RECOMMENDED tests. By default failing
@ -272,6 +277,12 @@ class ConformanceTestSuite {
const conformance::ConformanceResponse& response,
bool need_report_success, bool require_same_wire_format);
void TruncateDebugPayload(std::string* payload);
const conformance::ConformanceRequest TruncateRequest(
const conformance::ConformanceRequest& request);
const conformance::ConformanceResponse TruncateResponse(
const conformance::ConformanceResponse& response);
void ReportSuccess(const std::string& test_name);
void ReportFailure(const std::string& test_name, ConformanceLevel level,
const conformance::ConformanceRequest& request,
@ -299,6 +310,7 @@ class ConformanceTestSuite {
int successes_;
int expected_failures_;
bool verbose_;
bool performance_;
bool enforce_recommended_;
std::string output_;
std::string output_dir_;

@ -54,12 +54,15 @@
// 4. testee sends M bytes representing a ConformanceResponse proto
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <future>
#include <vector>
#include "absl/strings/str_format.h"
@ -149,7 +152,6 @@ void ForkPipeRunner::RunTest(const std::string &test_name,
if (child_pid_ < 0) {
SpawnTestProgram();
}
current_test_name_ = test_name;
uint32_t len = request.size();
@ -164,9 +166,18 @@ void ForkPipeRunner::RunTest(const std::string &test_name,
waitpid(child_pid_, &status, WEXITED);
string error_msg;
conformance::ConformanceResponse response_obj;
if (WIFEXITED(status)) {
absl::StrAppendFormat(&error_msg, "child exited, status=%d",
WEXITSTATUS(status));
if (WEXITSTATUS(status) == 0) {
absl::StrAppendFormat(&error_msg,
"child timed out, killed by signal %d",
WTERMSIG(status));
response_obj.set_timeout_error(error_msg);
} else {
absl::StrAppendFormat(&error_msg, "child exited, status=%d",
WEXITSTATUS(status));
response_obj.set_runtime_error(error_msg);
}
} else if (WIFSIGNALED(status)) {
absl::StrAppendFormat(&error_msg, "child killed by signal %d",
WTERMSIG(status));
@ -174,8 +185,6 @@ void ForkPipeRunner::RunTest(const std::string &test_name,
GOOGLE_LOG(INFO) << error_msg;
child_pid_ = -1;
conformance::ConformanceResponse response_obj;
response_obj.set_runtime_error(error_msg);
response_obj.SerializeToString(response);
return;
}
@ -197,11 +206,15 @@ int ForkPipeRunner::Run(int argc, char *argv[],
string failure_list_filename;
conformance::FailureSet failure_list;
bool performance = false;
for (int arg = 1; arg < argc; ++arg) {
if (strcmp(argv[arg], suite->GetFailureListFlagName().c_str()) == 0) {
if (++arg == argc) UsageError();
failure_list_filename = argv[arg];
ParseFailureList(argv[arg], &failure_list);
} else if (strcmp(argv[arg], "--performance") == 0) {
performance = true;
suite->SetPerformance(true);
} else if (strcmp(argv[arg], "--verbose") == 0) {
suite->SetVerbose(true);
} else if (strcmp(argv[arg], "--enforce_recommended") == 0) {
@ -230,7 +243,7 @@ int ForkPipeRunner::Run(int argc, char *argv[],
}
}
ForkPipeRunner runner(program, program_args);
ForkPipeRunner runner(program, program_args, performance);
std::string output;
all_ok = all_ok && suite->RunSuite(&runner, &output, failure_list_filename,
@ -319,8 +332,36 @@ void ForkPipeRunner::CheckedWrite(int fd, const void *buf, size_t len) {
bool ForkPipeRunner::TryRead(int fd, void *buf, size_t len) {
size_t ofs = 0;
while (len > 0) {
ssize_t bytes_read = read(fd, (char *)buf + ofs, len);
std::future<ssize_t> future = std::async(
std::launch::async,
[](int fd, void *buf, size_t ofs, size_t len) {
return read(fd, (char *)buf + ofs, len);
},
fd, buf, ofs, len);
std::future_status status;
if (performance_) {
status = future.wait_for(std::chrono::seconds(5));
if (status == std::future_status::timeout) {
GOOGLE_LOG(ERROR) << current_test_name_ << ": timeout from test program";
kill(child_pid_, SIGQUIT);
// TODO(sandyzhang): Only log in flag-guarded mode, since reading output
// from SIGQUIT is slow and verbose.
std::vector<char> err;
err.resize(5000);
ssize_t err_bytes_read;
size_t err_ofs = 0;
do {
err_bytes_read =
read(fd, (void *)&err[err_ofs], err.size() - err_ofs);
err_ofs += err_bytes_read;
} while (err_bytes_read > 0 && err_ofs < err.size());
GOOGLE_LOG(ERROR) << "child_pid_=" << child_pid_ << " SIGQUIT: \n" << &err[0];
return false;
}
} else {
future.wait();
}
ssize_t bytes_read = future.get();
if (bytes_read == 0) {
GOOGLE_LOG(ERROR) << current_test_name_ << ": unexpected EOF from test program";
return false;

@ -1,5 +1,2 @@
Recommended.Proto2.JsonInput.FieldNameExtension.Validator
Required.Proto2.JsonInput.StoresDefaultPrimitive.Validator
Required.Proto3.JsonInput.DurationNegativeNanos.JsonOutput
Required.Proto3.JsonInput.DurationNegativeNanos.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeSeconds.JsonOutput

@ -56,6 +56,3 @@ Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.PackedInput.UnpackedOu
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.UnpackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.PackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.UnpackedInput.UnpackedOutput.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeNanos.JsonOutput
Required.Proto3.JsonInput.DurationNegativeNanos.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeSeconds.JsonOutput

@ -51,6 +51,10 @@ using std::string;
namespace google {
namespace protobuf {
// The number of repetitions to use for performance tests.
// Corresponds approx to 500KB wireformat bytes.
static const size_t kPerformanceRepeatCount = 50000;
TextFormatConformanceTestSuite::TextFormatConformanceTestSuite() {
SetFailureListFlagName("--text_format_failure_list");
}
@ -166,6 +170,22 @@ void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2(
RunValidTextFormatTestWithMessage(test_name, level, input_text, prototype);
}
void TextFormatConformanceTestSuite::RunValidTextFormatTestWithExpected(
const string& test_name, ConformanceLevel level, const string& input_text,
const string& expected_text) {
TestAllTypesProto3 prototype;
RunValidTextFormatTestWithMessage(test_name, level, input_text, expected_text,
prototype);
}
void TextFormatConformanceTestSuite::RunValidTextFormatTestProto2WithExpected(
const string& test_name, ConformanceLevel level, const string& input_text,
const string& expected_text) {
TestAllTypesProto2 prototype;
RunValidTextFormatTestWithMessage(test_name, level, input_text, expected_text,
prototype);
}
void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage(
const string& test_name, ConformanceLevel level, const string& input_text,
const Message& prototype) {
@ -179,6 +199,19 @@ void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage(
RunValidInputTest(setting2, input_text);
}
void TextFormatConformanceTestSuite::RunValidTextFormatTestWithMessage(
const string& test_name, ConformanceLevel level, const string& input_text,
const string& expected_text, const Message& prototype) {
ConformanceRequestSetting setting1(
level, conformance::TEXT_FORMAT, conformance::PROTOBUF,
conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text);
RunValidInputTest(setting1, expected_text);
ConformanceRequestSetting setting2(
level, conformance::TEXT_FORMAT, conformance::TEXT_FORMAT,
conformance::TEXT_FORMAT_TEST, prototype, test_name, input_text);
RunValidInputTest(setting2, expected_text);
}
void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest(
const string& test_name, const Message& message) {
string serialized_input;
@ -201,276 +234,324 @@ void TextFormatConformanceTestSuite::RunValidUnknownTextFormatTest(
}
void TextFormatConformanceTestSuite::RunSuiteImpl() {
RunValidTextFormatTest("HelloWorld", REQUIRED,
"optional_string: 'Hello, World!'");
// Integer fields.
RunValidTextFormatTest("Int32FieldMaxValue", REQUIRED,
"optional_int32: 2147483647");
RunValidTextFormatTest("Int32FieldMinValue", REQUIRED,
"optional_int32: -2147483648");
RunValidTextFormatTest("Uint32FieldMaxValue", REQUIRED,
"optional_uint32: 4294967295");
RunValidTextFormatTest("Int64FieldMaxValue", REQUIRED,
"optional_int64: 9223372036854775807");
RunValidTextFormatTest("Int64FieldMinValue", REQUIRED,
"optional_int64: -9223372036854775808");
RunValidTextFormatTest("Uint64FieldMaxValue", REQUIRED,
"optional_uint64: 18446744073709551615");
// Parsers reject out-of-bound integer values.
ExpectParseFailure("Int32FieldTooLarge", REQUIRED,
"optional_int32: 2147483648");
ExpectParseFailure("Int32FieldTooSmall", REQUIRED,
"optional_int32: -2147483649");
ExpectParseFailure("Uint32FieldTooLarge", REQUIRED,
"optional_uint32: 4294967296");
ExpectParseFailure("Int64FieldTooLarge", REQUIRED,
"optional_int64: 9223372036854775808");
ExpectParseFailure("Int64FieldTooSmall", REQUIRED,
"optional_int64: -9223372036854775809");
ExpectParseFailure("Uint64FieldTooLarge", REQUIRED,
"optional_uint64: 18446744073709551616");
// Floating point fields
RunValidTextFormatTest("FloatField", REQUIRED,
"optional_float: 3.192837");
RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED,
"optional_float: 3.123456789123456789");
RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED,
"optional_float: 3.4028235e+38");
RunValidTextFormatTest("FloatFieldMinValue", REQUIRED,
"optional_float: 1.17549e-38");
RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED,
"optional_float: NaN");
RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED,
"optional_float: inf");
RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED,
"optional_float: -inf");
RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED,
"optional_float: 4294967296");
RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED,
"optional_float: 9223372036854775808");
RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED,
"optional_float: 3.4028235e+39");
RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED,
"optional_float: 1.17549e-39");
RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED,
"optional_float: 18446744073709551616");
// String literals x {Strings, Bytes}
for (const auto& field_type : std::vector<std::string>{"String", "Bytes"}) {
const std::string field_name =
field_type == "String" ? "optional_string" : "optional_bytes";
RunValidTextFormatTest(
absl::StrCat("StringLiteralConcat", field_type), REQUIRED,
absl::StrCat(field_name, ": 'first' \"second\"\n'third'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralBasicEscapes", field_type), REQUIRED,
absl::StrCat(field_name, ": '\\a\\b\\f\\n\\r\\t\\v\\?\\\\\\'\\\"'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralOctalEscapes", field_type), REQUIRED,
absl::StrCat(field_name, ": '\\341\\210\\264'"));
RunValidTextFormatTest(absl::StrCat("StringLiteralHexEscapes", field_type),
REQUIRED,
absl::StrCat(field_name, ": '\\xe1\\x88\\xb4'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralShortUnicodeEscape", field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\u1234'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralLongUnicodeEscapes", field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U00001234\\U00010437'"));
// String literals don't include line feeds.
ExpectParseFailure(absl::StrCat("StringLiteralIncludesLF", field_type),
REQUIRED,
absl::StrCat(field_name, ": 'first line\nsecond line'"));
// Unicode escapes don't include code points that lie beyond the planes
// (> 0x10ffff).
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeTooLarge", field_type),
REQUIRED, absl::StrCat(field_name, ": '\\U00110000'"));
// Unicode escapes don't include surrogates.
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogatePair",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\udc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateFirstOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud800'"));
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateSecondOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\udc00'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateFirstOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d800'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateSecondOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000dc00'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogatePair", field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\U00000dc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairLongShort",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\udc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairShortLong",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\U0000dc37'"));
// The following method depend on the type of field, as strings have extra
// validation.
const auto test_method =
field_type == "String"
? &TextFormatConformanceTestSuite::ExpectParseFailure
: &TextFormatConformanceTestSuite::RunValidTextFormatTest;
// String fields reject invalid UTF-8 byte sequences; bytes fields don't.
(this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Octal"),
REQUIRED, absl::StrCat(field_name, ": '\\300'"));
(this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Hex"), REQUIRED,
absl::StrCat(field_name, ": '\\xc0'"));
}
if (!performance_) {
RunValidTextFormatTest("HelloWorld", REQUIRED,
"optional_string: 'Hello, World!'");
// Integer fields.
RunValidTextFormatTest("Int32FieldMaxValue", REQUIRED,
"optional_int32: 2147483647");
RunValidTextFormatTest("Int32FieldMinValue", REQUIRED,
"optional_int32: -2147483648");
RunValidTextFormatTest("Uint32FieldMaxValue", REQUIRED,
"optional_uint32: 4294967295");
RunValidTextFormatTest("Int64FieldMaxValue", REQUIRED,
"optional_int64: 9223372036854775807");
RunValidTextFormatTest("Int64FieldMinValue", REQUIRED,
"optional_int64: -9223372036854775808");
RunValidTextFormatTest("Uint64FieldMaxValue", REQUIRED,
"optional_uint64: 18446744073709551615");
// Parsers reject out-of-bound integer values.
ExpectParseFailure("Int32FieldTooLarge", REQUIRED,
"optional_int32: 2147483648");
ExpectParseFailure("Int32FieldTooSmall", REQUIRED,
"optional_int32: -2147483649");
ExpectParseFailure("Uint32FieldTooLarge", REQUIRED,
"optional_uint32: 4294967296");
ExpectParseFailure("Int64FieldTooLarge", REQUIRED,
"optional_int64: 9223372036854775808");
ExpectParseFailure("Int64FieldTooSmall", REQUIRED,
"optional_int64: -9223372036854775809");
ExpectParseFailure("Uint64FieldTooLarge", REQUIRED,
"optional_uint64: 18446744073709551616");
// Floating point fields
RunValidTextFormatTest("FloatField", REQUIRED, "optional_float: 3.192837");
RunValidTextFormatTest("FloatFieldWithVeryPreciseNumber", REQUIRED,
"optional_float: 3.123456789123456789");
RunValidTextFormatTest("FloatFieldMaxValue", REQUIRED,
"optional_float: 3.4028235e+38");
RunValidTextFormatTest("FloatFieldMinValue", REQUIRED,
"optional_float: 1.17549e-38");
RunValidTextFormatTest("FloatFieldNaNValue", REQUIRED,
"optional_float: NaN");
RunValidTextFormatTest("FloatFieldPosInfValue", REQUIRED,
"optional_float: inf");
RunValidTextFormatTest("FloatFieldNegInfValue", REQUIRED,
"optional_float: -inf");
RunValidTextFormatTest("FloatFieldWithInt32Max", REQUIRED,
"optional_float: 4294967296");
RunValidTextFormatTest("FloatFieldLargerThanInt64", REQUIRED,
"optional_float: 9223372036854775808");
RunValidTextFormatTest("FloatFieldTooLarge", REQUIRED,
"optional_float: 3.4028235e+39");
RunValidTextFormatTest("FloatFieldTooSmall", REQUIRED,
"optional_float: 1.17549e-39");
RunValidTextFormatTest("FloatFieldLargerThanUint64", REQUIRED,
"optional_float: 18446744073709551616");
// String literals x {Strings, Bytes}
for (const auto& field_type : std::vector<std::string>{"String", "Bytes"}) {
const std::string field_name =
field_type == "String" ? "optional_string" : "optional_bytes";
RunValidTextFormatTest(
absl::StrCat("StringLiteralConcat", field_type), REQUIRED,
absl::StrCat(field_name, ": 'first' \"second\"\n'third'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralBasicEscapes", field_type), REQUIRED,
absl::StrCat(field_name, ": '\\a\\b\\f\\n\\r\\t\\v\\?\\\\\\'\\\"'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralOctalEscapes", field_type), REQUIRED,
absl::StrCat(field_name, ": '\\341\\210\\264'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralHexEscapes", field_type), REQUIRED,
absl::StrCat(field_name, ": '\\xe1\\x88\\xb4'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralShortUnicodeEscape", field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\u1234'"));
RunValidTextFormatTest(
absl::StrCat("StringLiteralLongUnicodeEscapes", field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U00001234\\U00010437'"));
// String literals don't include line feeds.
ExpectParseFailure(
absl::StrCat("StringLiteralIncludesLF", field_type), REQUIRED,
absl::StrCat(field_name, ": 'first line\nsecond line'"));
// Unicode escapes don't include code points that lie beyond the planes
// (> 0x10ffff).
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeTooLarge", field_type),
REQUIRED, absl::StrCat(field_name, ": '\\U00110000'"));
// Unicode escapes don't include surrogates.
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogatePair",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\udc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateFirstOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud800'"));
ExpectParseFailure(
absl::StrCat("StringLiteralShortUnicodeEscapeSurrogateSecondOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\udc00'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateFirstOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d800'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogateSecondOnly",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000dc00'"));
ExpectParseFailure(
absl::StrCat("StringLiteralLongUnicodeEscapeSurrogatePair",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\U00000dc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairLongShort",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\U0000d801\\udc37'"));
ExpectParseFailure(
absl::StrCat("StringLiteralUnicodeEscapeSurrogatePairShortLong",
field_type),
RECOMMENDED, absl::StrCat(field_name, ": '\\ud801\\U0000dc37'"));
// The following method depend on the type of field, as strings have extra
// validation.
const auto test_method =
field_type == "String"
? &TextFormatConformanceTestSuite::ExpectParseFailure
: &TextFormatConformanceTestSuite::RunValidTextFormatTest;
// String fields reject invalid UTF-8 byte sequences; bytes fields don't.
(this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Octal"),
REQUIRED, absl::StrCat(field_name, ": '\\300'"));
(this->*test_method)(absl::StrCat(field_type, "FieldBadUTF8Hex"),
REQUIRED, absl::StrCat(field_name, ": '\\xc0'"));
}
// Group fields
RunValidTextFormatTestProto2("GroupFieldNoColon", REQUIRED,
"Data { group_int32: 1 }");
RunValidTextFormatTestProto2("GroupFieldWithColon", REQUIRED,
"Data: { group_int32: 1 }");
RunValidTextFormatTestProto2("GroupFieldEmpty", REQUIRED,
"Data {}");
// Unknown Fields
UnknownToTestAllTypes message;
// Unable to print unknown Fixed32/Fixed64 fields as if they are known.
// Fixed32/Fixed64 fields are not added in the tests.
message.set_optional_int32(123);
message.set_optional_string("hello");
message.set_optional_bool(true);
RunValidUnknownTextFormatTest("ScalarUnknownFields", message);
message.Clear();
message.mutable_nested_message()->set_c(111);
RunValidUnknownTextFormatTest("MessageUnknownFields", message);
message.Clear();
message.mutable_optionalgroup()->set_a(321);
RunValidUnknownTextFormatTest("GroupUnknownFields", message);
message.add_repeated_int32(1);
message.add_repeated_int32(2);
message.add_repeated_int32(3);
RunValidUnknownTextFormatTest("RepeatedUnknownFields", message);
// Any fields
RunValidTextFormatTest("AnyField", REQUIRED,
R"(
optional_any: {
[type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
optional_int32: 12345
// Group fields
RunValidTextFormatTestProto2("GroupFieldNoColon", REQUIRED,
"Data { group_int32: 1 }");
RunValidTextFormatTestProto2("GroupFieldWithColon", REQUIRED,
"Data: { group_int32: 1 }");
RunValidTextFormatTestProto2("GroupFieldEmpty", REQUIRED, "Data {}");
// Unknown Fields
UnknownToTestAllTypes message;
// Unable to print unknown Fixed32/Fixed64 fields as if they are known.
// Fixed32/Fixed64 fields are not added in the tests.
message.set_optional_int32(123);
message.set_optional_string("hello");
message.set_optional_bool(true);
RunValidUnknownTextFormatTest("ScalarUnknownFields", message);
message.Clear();
message.mutable_nested_message()->set_c(111);
RunValidUnknownTextFormatTest("MessageUnknownFields", message);
message.Clear();
message.mutable_optionalgroup()->set_a(321);
RunValidUnknownTextFormatTest("GroupUnknownFields", message);
message.add_repeated_int32(1);
message.add_repeated_int32(2);
message.add_repeated_int32(3);
RunValidUnknownTextFormatTest("RepeatedUnknownFields", message);
// Any fields
RunValidTextFormatTest("AnyField", REQUIRED,
R"(
optional_any: {
[type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3] {
optional_int32: 12345
}
}
}
)");
RunValidTextFormatTest("AnyFieldWithRawBytes", REQUIRED,
R"(
optional_any: {
type_url: "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
value: "\b\271`"
}
)");
ExpectParseFailure("AnyFieldWithInvalidType", REQUIRED,
R"(
optional_any: {
[type.googleapis.com/unknown] {
optional_int32: 12345
)");
RunValidTextFormatTest("AnyFieldWithRawBytes", REQUIRED,
R"(
optional_any: {
type_url: "type.googleapis.com/protobuf_test_messages.proto3.TestAllTypesProto3"
value: "\b\271`"
}
}
)");
)");
ExpectParseFailure("AnyFieldWithInvalidType", REQUIRED,
R"(
optional_any: {
[type.googleapis.com/unknown] {
optional_int32: 12345
}
}
)");
// Map fields
TestAllTypesProto3 prototype;
(*prototype.mutable_map_string_string())["c"] = "value";
(*prototype.mutable_map_string_string())["b"] = "value";
(*prototype.mutable_map_string_string())["a"] = "value";
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapStringKeys",
REQUIRED,
R"(
map_string_string {
key: "a"
value: "value"
}
map_string_string {
key: "b"
value: "value"
}
map_string_string {
key: "c"
value: "value"
}
)",
prototype);
prototype.Clear();
(*prototype.mutable_map_int32_int32())[3] = 0;
(*prototype.mutable_map_int32_int32())[2] = 0;
(*prototype.mutable_map_int32_int32())[1] = 0;
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapIntKeys",
REQUIRED,
R"(
map_int32_int32 {
key: 1
value: 0
}
map_int32_int32 {
key: 2
value: 0
}
map_int32_int32 {
key: 3
value: 0
}
)",
prototype);
prototype.Clear();
(*prototype.mutable_map_bool_bool())[true] = false;
(*prototype.mutable_map_bool_bool())[false] = false;
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapBoolKeys",
REQUIRED,
R"(
map_bool_bool {
key: false
value: false
}
map_bool_bool {
key: true
value: false
}
)",
prototype);
prototype.Clear();
ConformanceRequestSetting setting_map(
REQUIRED, conformance::TEXT_FORMAT, conformance::PROTOBUF,
conformance::TEXT_FORMAT_TEST, prototype, "DuplicateMapKey", R"(
map_string_nested_message {
key: "duplicate"
value: { a: 123 }
}
map_string_nested_message {
key: "duplicate"
value: { corecursive: {} }
}
)");
// The last-specified value will be retained in a parsed map
RunValidInputTest(setting_map, R"(
map_string_nested_message {
key: "duplicate"
value: { corecursive: {} }
}
)");
}
// Flag control performance tests to keep them internal and opt-in only
if (performance_) {
RunTextFormatPerformanceTests();
}
}
// Map fields
TestAllTypesProto3 prototype;
(*prototype.mutable_map_string_string())["c"] = "value";
(*prototype.mutable_map_string_string())["b"] = "value";
(*prototype.mutable_map_string_string())["a"] = "value";
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapStringKeys",
REQUIRED,
R"(
map_string_string {
key: "a"
value: "value"
}
map_string_string {
key: "b"
value: "value"
}
map_string_string {
key: "c"
value: "value"
}
)",
prototype);
void TextFormatConformanceTestSuite::RunTextFormatPerformanceTests() {
TestTextFormatPerformanceMergeMessageWithRepeatedField("Bool",
"repeated_bool: true");
TestTextFormatPerformanceMergeMessageWithRepeatedField(
"Double", "repeated_double: 123");
TestTextFormatPerformanceMergeMessageWithRepeatedField(
"Int32", "repeated_uint32: 123");
TestTextFormatPerformanceMergeMessageWithRepeatedField(
"Int64", "repeated_uint64: 123");
TestTextFormatPerformanceMergeMessageWithRepeatedField(
"String", R"(repeated_string: "foo")");
TestTextFormatPerformanceMergeMessageWithRepeatedField(
"Bytes", R"(repeated_bytes: "foo")");
}
prototype.Clear();
(*prototype.mutable_map_int32_int32())[3] = 0;
(*prototype.mutable_map_int32_int32())[2] = 0;
(*prototype.mutable_map_int32_int32())[1] = 0;
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapIntKeys", REQUIRED,
R"(
map_int32_int32 {
key: 1
value: 0
}
map_int32_int32 {
key: 2
value: 0
}
map_int32_int32 {
key: 3
value: 0
}
)",
prototype);
// This is currently considered valid input by some languages but not others
void TextFormatConformanceTestSuite::
TestTextFormatPerformanceMergeMessageWithRepeatedField(
const string& test_type_name, const string& message_field) {
string recursive_message = "recursive_message { " + message_field + " }";
prototype.Clear();
(*prototype.mutable_map_bool_bool())[true] = false;
(*prototype.mutable_map_bool_bool())[false] = false;
RunValidTextFormatTestWithMessage("AlphabeticallySortedMapBoolKeys", REQUIRED,
R"(
map_bool_bool {
key: false
value: false
}
map_bool_bool {
key: true
value: false
}
)",
prototype);
string input;
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
input.append(recursive_message);
}
prototype.Clear();
ConformanceRequestSetting setting_map(
REQUIRED, conformance::TEXT_FORMAT, conformance::PROTOBUF,
conformance::TEXT_FORMAT_TEST, prototype, "DuplicateMapKey", R"(
map_string_nested_message {
key: "duplicate"
value: { a: 123 }
}
map_string_nested_message {
key: "duplicate"
value: { corecursive: {} }
}
)");
// The last-specified value will be retained in a parsed map
RunValidInputTest(setting_map, R"(
map_string_nested_message {
key: "duplicate"
value: { corecursive: {} }
}
)");
string expected = "recursive_message { ";
for (size_t i = 0; i < kPerformanceRepeatCount; i++) {
expected.append(message_field + " ");
}
expected.append("}");
RunValidTextFormatTestProto2WithExpected(
"TestTextFormatPerformanceMergeMessageWithRepeatedField" +
test_type_name + "Proto2",
RECOMMENDED, input, expected);
RunValidTextFormatTestWithExpected(
"TestTextFormatPerformanceMergeMessageWithRepeatedField" +
test_type_name + "Proto3",
RECOMMENDED, input, expected);
}
} // namespace protobuf

@ -42,15 +42,29 @@ class TextFormatConformanceTestSuite : public ConformanceTestSuite {
private:
void RunSuiteImpl() override;
void RunTextFormatPerformanceTests();
void RunValidTextFormatTest(const std::string& test_name,
ConformanceLevel level, const std::string& input);
void RunValidTextFormatTestProto2(const std::string& test_name,
ConformanceLevel level,
const std::string& input);
void RunValidTextFormatTestWithExpected(const std::string& test_name,
ConformanceLevel level,
const std::string& input,
const std::string& expected);
void RunValidTextFormatTestProto2WithExpected(const std::string& test_name,
ConformanceLevel level,
const std::string& input,
const std::string& expected);
void RunValidTextFormatTestWithMessage(const std::string& test_name,
ConformanceLevel level,
const std::string& input_text,
const Message& prototype);
void RunValidTextFormatTestWithMessage(const std::string& test_name,
ConformanceLevel level,
const std::string& input_text,
const std::string& expected_text,
const Message& prototype);
void RunValidUnknownTextFormatTest(const std::string& test_name,
const Message& message);
void ExpectParseFailure(const std::string& test_name, ConformanceLevel level,
@ -61,6 +75,8 @@ class TextFormatConformanceTestSuite : public ConformanceTestSuite {
bool ParseResponse(const conformance::ConformanceResponse& response,
const ConformanceRequestSetting& setting,
Message* test_message) override;
void TestTextFormatPerformanceMergeMessageWithRepeatedField(
const std::string& test_type_name, const std::string& message_field);
};
} // namespace protobuf

@ -5,7 +5,7 @@
<title>Google Protocol Buffers tools</title>
<summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
<description>See project site for more info.</description>
<version>3.21.6</version>
<version>3.21.7</version>
<authors>Google Inc.</authors>
<owners>protobuf-packages</owners>
<licenseUrl>https://github.com/protocolbuffers/protobuf/blob/main/LICENSE</licenseUrl>

@ -4,7 +4,7 @@
<Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
<Copyright>Copyright 2015, Google Inc.</Copyright>
<AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
<VersionPrefix>3.21.6</VersionPrefix>
<VersionPrefix>3.21.7</VersionPrefix>
<LangVersion>10.0</LangVersion>
<Authors>Google Inc.</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0;net45;net50</TargetFrameworks>

@ -0,0 +1,135 @@
# How to use `protobuf_generate`
This document explains how to use the function `protobuf_generate` which is provided by protobuf's CMake module.
## Usage
In the same directory that called `find_package(protobuf CONFIG)` and any of its subdirectories, the CMake function `protobuf_generate` is made available by
[`protobuf-generate.cmake`](../cmake/protobuf-generate.cmake). It can be used to automatically generate source files from `.proto` schema files at build time.
### Basic example
Let us see how `protobuf_generate` can be used to generate and compile the source files of a proto schema whenever an object target called `proto-objects` is built.
Given the following directory structure:
- `proto/helloworld/helloworld.proto`
- `CMakeLists.txt`
where `helloworld.proto` is a protobuf schema file and `CMakeLists.txt` contains:
```cmake
find_package(protobuf CONFIG REQUIRED)
add_library(proto-objects OBJECT "${CMAKE_CURRENT_LIST_DIR}/proto/helloworld/helloworld.proto")
target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf)
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
target_include_directories(proto-objects PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>")
protobuf_generate(
TARGET proto-objects
IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}/proto"
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}")
```
Building the target `proto-objects` will generate the files:
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.h`
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.cc`
and (depending on the build system) output:
```shell
[build] [1/2] Running cpp protocol buffer compiler on /proto/helloworld/helloworld.proto
[build] [2/2] Building CXX object /build/generated/helloworld/helloworld.pb.cc.o
```
### gRPC example
`protobuf_generate` can also be customized to invoke plugins like gRPC's `grpc_cpp_plugin`. Given the same directory structure as in the [basic example](#basic-example)
and let `CMakeLists.txt` contain:
```cmake
find_package(gRPC CONFIG REQUIRED)
add_library(proto-objects OBJECT "${CMAKE_CURRENT_LIST_DIR}/proto/helloworld/helloworld.proto")
target_link_libraries(proto-objects PUBLIC protobuf::libprotobuf gRPC::grpc++)
set(PROTO_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
set(PROTO_IMPORT_DIRS "${CMAKE_CURRENT_LIST_DIR}/proto")
target_include_directories(proto-objects PUBLIC "$<BUILD_INTERFACE:${PROTO_BINARY_DIR}>")
protobuf_generate(
TARGET proto-objects
IMPORT_DIRS ${PROTO_IMPORT_DIRS}
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}")
protobuf_generate(
TARGET proto-objects
LANGUAGE grpc
GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc
PLUGIN "protoc-gen-grpc=\$<TARGET_FILE:gRPC::grpc_cpp_plugin>"
IMPORT_DIRS ${PROTO_IMPORT_DIRS}
PROTOC_OUT_DIR "${PROTO_BINARY_DIR}")
```
Then building `proto-objects` will generate and compile:
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.h`
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.pb.cc`
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.grpc.pb.h`
- `${CMAKE_CURRENT_BINARY_DIR}/generated/helloworld/helloworld.grpc.pb.cc`
And `protoc` will automatically be re-run whenever the schema files change and `proto-objects` is built.
### Note on unity builds
Since protobuf's generated source files are unsuited for [jumbo/unity builds](https://cmake.org/cmake/help/latest/prop_tgt/UNITY_BUILD.html) it is recommended
to exclude them from such builds which can be achieved by adjusting their properties:
```cmake
protobuf_generate(
OUT_VAR PROTO_GENERATED_FILES
...)
set_source_files_properties(${PROTO_GENERATED_FILES} PROPERTIES SKIP_UNITY_BUILD_INCLUSION on)
```
## How it works
For each source file ending in `proto` of the argument provided to `TARGET` or each file provided through `PROTOS`, `protobuf_generate` will set up
a [add_custom_command](https://cmake.org/cmake/help/latest/command/add_custom_command.html) which depends on `protobuf::protoc` and the proto files.
It declares the generated source files as `OUTPUT` which means that any target that depends on them will automatically cause the custom command to execute
when it is brought up to date. The command itself is made up of the arguments for `protoc`, like the output directory, the schema files, the language to
generate for, the plugins to use, etc.
## Reference
Arguments accepted by `protobuf_generate`.
Flag arguments:
- `APPEND_PATH` — A flag that causes the base path of all proto schema files to be added to `IMPORT_DIRS`.
Single-value arguments:
- `LANGUAGE` — A single value: cpp or python. Determines what kind of source files are being generated.
- `OUT_VAR` — Name of a CMake variable that will be filled with the paths to the generated source files.
- `EXPORT_MACRO` — Name of a macro that is applied to all generated Protobuf message classes and extern variables. It can, for example, be used to declare DLL exports.
- `PROTOC_OUT_DIR` — Output directory of generated source files. Defaults to `CMAKE_CURRENT_BINARY_DIR`.
- `PLUGIN` — An optional plugin executable. This could, for example, be the path to `grpc_cpp_plugin`.
- `PLUGIN_OPTIONS` — Additional options provided to the plugin, such as `generate_mock_code=true` for the gRPC cpp plugin.
- `DEPENDENCIES` — Arguments forwarded to the `DEPENDS` of the underlying `add_custom_command` invocation.
- `TARGET` — CMake target that will have the generated files added as sources.
Multi-value arguments:
- `PROTOS` — List of proto schema files. If omitted, then every source file ending in *proto* of `TARGET` will be used.
- `IMPORT_DIRS` — A common parent directory for the schema files. For example, if the schema file is `proto/helloworld/helloworld.proto` and the import directory `proto/` then the generated files are `${PROTOC_OUT_DIR}/helloworld/helloworld.pb.h` and `${PROTOC_OUT_DIR}/helloworld/helloworld.pb.cc`.
- `GENERATE_EXTENSIONS` — If LANGUAGE is omitted then this must be set to the extensions that protoc generates.
- `PROTOC_OPTIONS` — Additional arguments that are forwarded to protoc.

@ -23,7 +23,7 @@ If you are using Maven, use the following:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</dependency>
```
@ -37,7 +37,7 @@ protobuf-java-util package:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</dependency>
```
@ -45,7 +45,7 @@ protobuf-java-util package:
If you are using Gradle, add the following to your `build.gradle` file's dependencies:
```
implementation 'com.google.protobuf:protobuf-java:3.21.6'
implementation 'com.google.protobuf:protobuf-java:3.21.7'
```
Again, be sure to check that the version number matches (or is newer than) the version number of protoc that you are using.

@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-bom</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
<packaging>pom</packaging>
<name>Protocol Buffers [BOM]</name>

@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</parent>
<artifactId>protobuf-java</artifactId>

@ -728,6 +728,10 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> {
if (descriptor.isRepeated()) {
final List<?> valueList = (List<?>) value;
if (descriptor.isPacked()) {
if (valueList.isEmpty()) {
// The tag should not be written for empty packed fields.
return;
}
output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
// Compute the total data size so the length can be written.
int dataSize = 0;
@ -893,9 +897,13 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> {
WireFormat.FieldType type = descriptor.getLiteType();
int number = descriptor.getNumber();
if (descriptor.isRepeated()) {
List<?> valueList = (List<?>) value;
if (descriptor.isPacked()) {
if (valueList.isEmpty()) {
return 0;
}
int dataSize = 0;
for (final Object element : (List<?>) value) {
for (final Object element : valueList) {
dataSize += computeElementSizeNoTag(type, element);
}
return dataSize
@ -903,7 +911,7 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> {
+ CodedOutputStream.computeUInt32SizeNoTag(dataSize);
} else {
int size = 0;
for (final Object element : (List<?>) value) {
for (final Object element : valueList) {
size += computeElementSize(type, number, element);
}
return size;

@ -44,6 +44,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
@ -230,6 +231,19 @@ public class DynamicMessageTest {
assertThat(rawBytes).isEqualTo(TestUtil.getPackedSet().toByteString());
}
@Test
public void testDynamicMessagePackedEmptySerialization() throws Exception {
Message message =
DynamicMessage.newBuilder(TestPackedTypes.getDescriptor())
.setField(
TestPackedTypes.getDescriptor()
.findFieldByNumber(TestPackedTypes.PACKED_INT64_FIELD_NUMBER),
new ArrayList<Long>())
.build();
assertThat(message.toByteString()).isEqualTo(ByteString.EMPTY);
}
@Test
public void testDynamicMessagePackedParsing() throws Exception {
TestPackedTypes.Builder builder = TestPackedTypes.newBuilder();

@ -50,7 +50,7 @@ def junit_tests(name, srcs, data = [], deps = [], package_name = "com.google.pro
if not test_name.endswith("Test") or test_name.startswith("Abstract"):
continue
if test_prefix:
test_name = "%s%s" % (test_prefix, test_name)
test_name = "%s%s" % (test_prefix, test_name)
test_names = test_names + [test_name]
suite_name = prefix + '_' + test_name
_gen_suite(

@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</parent>
<artifactId>protobuf-kotlin-lite</artifactId>

@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</parent>
<artifactId>protobuf-kotlin</artifactId>

@ -29,7 +29,7 @@ protobuf Java Lite runtime. If you are using Maven, include the following:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-javalite</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</dependency>
```

@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</parent>
<artifactId>protobuf-javalite</artifactId>

@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
<packaging>pom</packaging>
<name>Protocol Buffers [Parent]</name>

@ -0,0 +1,42 @@
# Publish pre-compiled protoc artifacts
``protoc`` is the compiler for ``.proto`` files. It generates language bindings
for the messages and/or RPC services from ``.proto`` files.
Because ``protoc`` is a native executable, the scripts under this directory
publish a ``protoc`` executable (a.k.a. artifact) to Maven repositories. The
artifact can be used by build automation tools so that users would not need to
compile and install ``protoc`` for their systems.
If you would like us to publish protoc artifact for a new platform, please
open an issue to request it.
## Maven Location
The published protoc artifacts are available on Maven here:
https://repo.maven.apache.org/maven2/com/google/protobuf/protoc/
## Versioning
The version of the ``protoc`` artifact must be the same as the version of the
Protobuf project.
## Artifact name
The name of a published ``protoc`` artifact is in the following format:
``protoc-<version>-<os>-<arch>.exe``, e.g., ``protoc-3.6.1-linux-x86_64.exe``.
Note that artifacts for linux/macos also have the `.exe` suffix but they are
not windows binaries.
## System requirement
Install [Apache Maven](http://maven.apache.org/) if you don't have it.
The scripts only work under Unix-like environments, e.g., Linux, MacOSX, and
Cygwin or MinGW for Windows. Please see ``README.md`` of the Protobuf project
for how to set up the build environment.
## Tested build environments
We have successfully built artifacts on the following environments:
- Linux x86_32 and x86_64:
- Centos 6.9 (within Docker 1.6.1)
- Ubuntu 14.04.5 64-bit
- Linux aarch_64: Cross compiled with `g++-aarch64-linux-gnu` on Ubuntu 14.04.5 64-bit
- Mac OS X x86_32 and x86_64: Mac OS X 10.9.5

@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</parent>
<artifactId>protobuf-java-util</artifactId>

@ -75,10 +75,7 @@ public final class Values {
* element in the iterable.
*/
public static Value of(Iterable<Value> values) {
Value.Builder valueBuilder = Value.newBuilder();
ListValue.Builder listValue = valueBuilder.getListValueBuilder();
listValue.addAllValues(values);
return valueBuilder.build();
return Value.newBuilder().setListValue(ListValue.newBuilder().addAllValues(values)).build();
}
private Values() {}

@ -7,7 +7,7 @@ cd $(dirname $0)/../../..
cd python
PYTHON="/opt/python/cp38-cp38/bin/python"
${PYTHON} -m pip install --user pytest auditwheel
${PYTHON} -m pip install --user pytest auditwheel numpy
# 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)

@ -8,7 +8,6 @@ fi
cd $(dirname $0)/../..
GIT_REPO_ROOT=`pwd`
rm -rf $GIT_REPO_ROOT/logs
ENVS=()
@ -25,20 +24,38 @@ if [ -n "$BAZEL_ENV" ]; then
done
fi
tmpfile=$(mktemp -u)
docker run \
--cidfile $tmpfile \
-v $GIT_REPO_ROOT:/workspace \
$CONTAINER_IMAGE \
test \
--keep_going \
--test_output=streamed \
${ENVS[@]} \
$PLATFORM_CONFIG \
$BAZEL_EXTRA_FLAGS \
$BAZEL_TARGETS
# Save logs for Kokoro
docker cp \
`cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR
function run {
local CONFIG=$1
local BAZEL_CONFIG=$2
tmpfile=$(mktemp -u)
rm -rf $GIT_REPO_ROOT/bazel-out $GIT_REPO_ROOT/bazel-bin
rm -rf $GIT_REPO_ROOT/logs
docker run \
--cidfile $tmpfile \
--cap-add=SYS_PTRACE \
-v $GIT_REPO_ROOT:/workspace \
$CONTAINER_IMAGE \
test \
--keep_going \
--test_output=streamed \
${ENVS[@]} \
$PLATFORM_CONFIG \
$BAZEL_CONFIG \
$BAZEL_EXTRA_FLAGS \
$BAZEL_TARGETS
# Save logs for Kokoro
docker cp \
`cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR/$CONFIG
}
if [ -n "$BAZEL_CONFIGS" ]; then
for config in $BAZEL_CONFIGS; do
run $config "--config=$config"
done
else
run
fi

@ -2,13 +2,28 @@
# Location of the build script in repository
build_file: "protobuf/kokoro/linux/bazel.sh"
timeout_mins: 15
timeout_mins: 120
env_vars {
key: "CONTAINER_IMAGE"
value: "gcr.io/protobuf-build/bazel/linux-san:b6bfa3bb505e83f062af0cb0ed23abf1e89b9edb"
}
env_vars {
key: "BAZEL_TARGETS"
value: "//src/... @com_google_protobuf_examples//..."
}
env_vars {
key: "BAZEL_CONFIGS"
value: "opt dbg asan kokoro-msan tsan ubsan"
}
env_vars {
key: "BAZEL_EXTRA_FLAGS"
value: "--distinct_host_configuration=false"
}
action {
define_artifacts {
regex: "**/sponge_log.*"

@ -0,0 +1,18 @@
# We run our staleness tests as release-type jobs only. They are not really
# part of the release process, but the release job type allows us to run the
# tests on a schedule only (not presubmit or postsubmit).
# Location of the build script in repository
build_file: "protobuf/kokoro/linux/bazel.sh"
timeout_mins: 15
env_vars {
key: "BAZEL_TARGETS"
value: "//src:cmake_lists_staleness_test"
}
action {
define_artifacts {
regex: "**/sponge_log.*"
}
}

@ -8,7 +8,7 @@ export HOMEBREW_PREFIX=$(brew --prefix)
##
# Select Xcode version
export DEVELOPER_DIR=/Applications/Xcode_13.3.1.app/Contents/Developer
export DEVELOPER_DIR=/Applications/Xcode_14.app/Contents/Developer
sudo xcode-select -s "${DEVELOPER_DIR}"
##

@ -233,7 +233,7 @@ TARGET_FILE="target/$OS/$ARCH/$BAZEL_TARGET.exe"
DOCKER_IMAGE=gcr.io/protobuf-build/bazel/linux@sha256:2bfd061284eff8234f2fcca16d71d43c69ccf3a22206628b54c204a6a9aac277
tmpfile=$(mktemp -u) &&
docker run --cidfile $tmpfile -v $WORKING_DIR/..:/workspace $DOCKER_IMAGE \
docker run --cidfile $tmpfile -v $WORKING_DIR/../../..:/workspace $DOCKER_IMAGE \
build //:$BAZEL_TARGET "${BAZEL_ARGS[@]}" &&
mkdir -p $(dirname $TARGET_FILE) &&
docker cp \

@ -10,7 +10,7 @@ fsutil 8dot3name set 0
@rem Reinstall Bazel due to corrupt installation in kokoro.
bazel version
choco install bazel -y -i
choco install bazel -y -i --version 5.1.0
bazel version
@rem Make paths as short as possible to avoid long path issues.

@ -16,7 +16,7 @@ die() {
exit 1
}
readonly GeneratorSrc="${ProtoRootDir}/src/google/protobuf/compiler/objectivec/objectivec_file.cc"
readonly GeneratorSrc="${ProtoRootDir}/src/google/protobuf/compiler/objectivec/file.cc"
readonly RuntimeSrc="${ProtoRootDir}/objectivec/GPBBootstrap.h"
check_constant() {

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/any.proto
// clang-format off
// source: google/protobuf/any.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/any.proto
// clang-format off
// source: google/protobuf/any.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBAny.pbobjc.h"
@ -101,4 +100,4 @@ typedef struct GPBAny__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/api.proto
// clang-format off
// source: google/protobuf/api.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/api.proto
// clang-format off
// source: google/protobuf/api.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBApi.pbobjc.h"
@ -355,4 +354,4 @@ typedef struct GPBMixin__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -681,7 +681,7 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
}
// The logic here has to match SetCommonFieldVariables() from
// objectivec_field.cc in the proto compiler.
// objectivec/field.cc in the proto compiler.
NSString *name = self.name;
NSUInteger len = [name length];
@ -918,7 +918,7 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) {
if (extraTextFormatInfo_) {
result = GPBDecodeTextFormatName(extraTextFormatInfo_, (int32_t)index, shortName);
}
// Logic here needs to match what objectivec_enum.cc does in the proto
// Logic here needs to match what objectivec/enum.cc does in the proto
// compiler.
if (result == nil) {
NSUInteger len = [shortName length];

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/duration.proto
// clang-format off
// source: google/protobuf/duration.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/duration.proto
// clang-format off
// source: google/protobuf/duration.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBDuration.pbobjc.h"
@ -96,4 +95,4 @@ typedef struct GPBDuration__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/empty.proto
// clang-format off
// source: google/protobuf/empty.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/empty.proto
// clang-format off
// source: google/protobuf/empty.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBEmpty.pbobjc.h"
@ -72,4 +71,4 @@ typedef struct GPBEmpty__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/field_mask.proto
// clang-format off
// source: google/protobuf/field_mask.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/field_mask.proto
// clang-format off
// source: google/protobuf/field_mask.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBFieldMask.pbobjc.h"
@ -85,4 +84,4 @@ typedef struct GPBFieldMask__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/source_context.proto
// clang-format off
// source: google/protobuf/source_context.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/source_context.proto
// clang-format off
// source: google/protobuf/source_context.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBSourceContext.pbobjc.h"
@ -85,4 +84,4 @@ typedef struct GPBSourceContext__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/struct.proto
// clang-format off
// source: google/protobuf/struct.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/struct.proto
// clang-format off
// source: google/protobuf/struct.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBStruct.pbobjc.h"
@ -298,4 +297,4 @@ typedef struct GPBListValue__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/timestamp.proto
// clang-format off
// source: google/protobuf/timestamp.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/timestamp.proto
// clang-format off
// source: google/protobuf/timestamp.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBTimestamp.pbobjc.h"
@ -96,4 +95,4 @@ typedef struct GPBTimestamp__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/type.proto
// clang-format off
// source: google/protobuf/type.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/type.proto
// clang-format off
// source: google/protobuf/type.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBType.pbobjc.h"
@ -710,4 +709,4 @@ typedef struct GPBOption__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/wrappers.proto
// clang-format off
// source: google/protobuf/wrappers.proto
#import "GPBDescriptor.h"
#import "GPBMessage.h"

@ -1,7 +1,6 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: google/protobuf/wrappers.proto
// clang-format off
// source: google/protobuf/wrappers.proto
#import "GPBProtocolBuffers_RuntimeSupport.h"
#import "GPBWrappers.pbobjc.h"
@ -444,4 +443,4 @@ typedef struct GPBBytesValue__storage_ {
// @@protoc_insertion_point(global_scope)
// clange-format on
// clang-format on

@ -1843,8 +1843,8 @@
}
- (void)testPropertyNaming {
// objectivec_helpers.cc has some special handing to get proper all caps
// for a few cases to meet objc developer expectations.
// names.cc has some special handing to get proper all caps for a few cases to
// meet objc developer expectations.
//
// This "test" confirms that the expected names are generated, otherwise the
// test itself will fail to compile.
@ -1861,7 +1861,7 @@
}
- (void)testEnumNaming {
// objectivec_helpers.cc has some interesting cases to deal with in
// names.cc has some interesting cases to deal with in
// EnumValueName/EnumValueShortName. Confirm that things generated as
// expected.
@ -1966,9 +1966,8 @@
}
- (void)testReservedWordNaming {
// objectivec_helpers.cc has some special handing to make sure that
// some "reserved" objc names get renamed in a way so they
// don't conflict.
// names.cc has some special handing to make sure that some "reserved" objc
// names get renamed in a way so they don't conflict.
//
// This "test" confirms that the expected names are generated,
// otherwise the test itself will fail to compile.

@ -3,6 +3,7 @@
# See also code generation logic under /src/google/protobuf/compiler/php.
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
load("@upb//cmake:build_defs.bzl", "staleness_test")
load("//build_defs:internal_shell.bzl", "inline_sh_binary")
load("//conformance:defs.bzl", "conformance_test")
@ -84,6 +85,29 @@ conformance_test(
}),
)
genrule(
name = "copy_php_amalgamation_h",
srcs = ["@upb//:php-upb.h"],
outs = ["generated-in/ext/google/protobuf/php-upb.h"],
cmd = "cp $< $@",
)
genrule(
name = "copy_php_amalgamation_c",
srcs = ["@upb//:php-upb.c"],
outs = ["generated-in/ext/google/protobuf/php-upb.c"],
cmd = "cp $< $@",
)
staleness_test(
name = "test_amalgamation_staleness",
outs = [
"ext/google/protobuf/php-upb.h",
"ext/google/protobuf/php-upb.c",
],
generated_pattern = "generated-in/%s",
)
################################################################################
# Distribution files
################################################################################

@ -646,6 +646,25 @@ PHP_METHOD(Message, clear) {
upb_Message_Clear(intern->msg, intern->desc->msgdef);
}
static bool Message_checkEncodeStatus(upb_EncodeStatus status) {
switch (status) {
case kUpb_EncodeStatus_Ok:
return true;
case kUpb_EncodeStatus_OutOfMemory:
zend_throw_exception_ex(NULL, 0, "Out of memory");
return false;
case kUpb_EncodeStatus_MaxDepthExceeded:
zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
return false;
case kUpb_EncodeStatus_MissingRequired:
zend_throw_exception_ex(NULL, 0, "Missing required field");
return false;
default:
zend_throw_exception_ex(NULL, 0, "Unknown error encoding");
return false;
}
}
/**
* Message::mergeFrom()
*
@ -673,14 +692,9 @@ PHP_METHOD(Message, mergeFrom) {
// zend_parse_parameters().
PBPHP_ASSERT(from->desc == intern->desc);
// TODO(haberman): use a temp arena for this once we can make upb_decode()
// copy strings.
pb = upb_Encode(from->msg, l, 0, arena, &size);
if (!pb) {
zend_throw_exception_ex(NULL, 0, "Max nesting exceeded");
return;
}
// TODO(haberman): use a temp arena for this.
upb_EncodeStatus status = upb_Encode(from->msg, l, 0, arena, &pb, &size);
if (!Message_checkEncodeStatus(status)) return;
ok = upb_Decode(pb, size, intern->msg, l, NULL, 0, arena) ==
kUpb_DecodeStatus_Ok;
@ -730,7 +744,9 @@ PHP_METHOD(Message, serializeToString) {
char *data;
size_t size;
data = upb_Encode(intern->msg, l, 0, tmp_arena, &size);
upb_EncodeStatus status =
upb_Encode(intern->msg, l, 0, tmp_arena, &data, &size);
if (!Message_checkEncodeStatus(status)) return;
if (!data) {
zend_throw_exception_ex(NULL, 0, "Error occurred during serialization");
@ -1232,8 +1248,12 @@ PHP_METHOD(google_protobuf_Any, pack) {
msg = (Message*)Z_OBJ_P(val);
// Serialize and set value.
value.data = upb_Encode(msg->msg, upb_MessageDef_MiniTable(msg->desc->msgdef),
0, arena, &value.size);
char* pb;
upb_EncodeStatus status =
upb_Encode(msg->msg, upb_MessageDef_MiniTable(msg->desc->msgdef), 0,
arena, &pb, &value.size);
if (!Message_checkEncodeStatus(status)) return;
value.data = pb;
Message_setval(intern, "value", StringVal(value));
// Set type url: type_url_prefix + fully_qualified_name

@ -10,11 +10,11 @@
<email>protobuf-packages@google.com</email>
<active>yes</active>
</lead>
<date>2022-09-13</date>
<time>13:50:07</time>
<date>2022-09-29</date>
<time>10:21:44</time>
<version>
<release>3.21.6</release>
<api>3.21.6</api>
<release>3.21.7</release>
<api>3.21.7</api>
</version>
<stability>
<release>stable</release>
@ -43,6 +43,7 @@
<file baseinstalldir="/" name="names.h" role="src"/>
<file baseinstalldir="/" name="php-upb.c" role="src"/>
<file baseinstalldir="/" name="php-upb.h" role="src"/>
<file baseinstalldir="/" name="php_protobuf.h" role="src"/>
<file baseinstalldir="/" name="protobuf.c" role="src"/>
<file baseinstalldir="/" name="protobuf.h" role="src"/>
<file baseinstalldir="/" name="wkt.inc" role="src"/>
@ -1418,5 +1419,20 @@ G A release.
<notes>
</notes>
</release>
<release>
<version>
<release>3.21.7</release>
<api>3.21.7</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<date>2022-09-29</date>
<time>10:21:44</time>
<license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
<notes>
</notes>
</release>
</changelog>
</package>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -127,7 +127,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setter, 0, 0, 1)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
#define PHP_PROTOBUF_VERSION "3.21.6"
#define PHP_PROTOBUF_VERSION "3.21.7"
// ptr -> PHP object cache. This is a weak map that caches lazily-created
// wrapper objects around upb types:

@ -54,6 +54,7 @@ use Google\Protobuf\NullValue;
* or extend this class or its child classes by their own. See the comment of
* specific functions for more details.
*/
#[\AllowDynamicProperties]
class Message
{
@ -1980,8 +1981,12 @@ class Message
$size += 9;
$size += $value_msg->jsonByteSize();
} else {
// Size for value. +1 for comma, -2 for "{}".
$size += $value_msg->jsonByteSize() -1;
$value_size = $value_msg->jsonByteSize();
// size === 2 it's empty message {} which is not serialized inside any
if ($value_size !== 2) {
// Size for value. +1 for comma, -2 for "{}".
$size += $value_size -1;
}
}
} elseif (get_class($this) === 'Google\Protobuf\FieldMask') {
$field_mask = GPBUtil::formatFieldMask($this);

@ -5,6 +5,7 @@ require_once('test_util.php');
use Google\Protobuf\RepeatedField;
use Google\Protobuf\GPBType;
use Foo\EmptyAnySerialization;
use Foo\TestInt32Value;
use Foo\TestInt64Value;
use Foo\TestUInt32Value;
@ -1513,4 +1514,22 @@ class EncodeDecodeTest extends TestBase
[TestStringValue::class, "a", "\"a\"", "", "\"\""],
];
}
public function testEmptyAnySerialization()
{
$m = new EmptyAnySerialization();
$any = new Any();
$any->pack($m);
$data = $any->serializeToJsonString();
$this->assertEquals('{"@type":"type.googleapis.com/foo.EmptyAnySerialization"}', $data);
$any = new Any();
$any->mergeFromJsonString($data);
$m = $any->unpack();
$this->assertInstanceOf(EmptyAnySerialization::class, $m);
$this->assertEquals('', $m->getA());
}
}

@ -168,6 +168,10 @@ message ARRAY {
int32 a = 1;
}
message EmptyAnySerialization {
string a = 1;
}
message TestPackedMessage {
repeated int32 repeated_int32 = 90 [packed = true];
repeated int64 repeated_int64 = 91 [packed = true];

@ -6,14 +6,9 @@ load(
"pkg_files",
"strip_prefix",
)
load("//:protobuf_release.bzl", "package_naming")
load(":build_systems.bzl", "gen_file_lists")
load(":cc_dist_library.bzl", "cc_dist_library")
package_naming(
name = "protobuf_pkg_naming",
)
pkg_files(
name = "wkt_protos_files",
srcs = [
@ -375,6 +370,7 @@ cc_dist_library(
"//src/google/protobuf/io:printer",
"//src/google/protobuf/io:tokenizer",
"//src/google/protobuf/io:zero_copy_sink",
"//src/google/protobuf/json",
"//src/google/protobuf/stubs",
"//src/google/protobuf/stubs:lite",
"//src/google/protobuf/util:delimited_message_util",
@ -383,15 +379,6 @@ cc_dist_library(
"//src/google/protobuf/util:json_util",
"//src/google/protobuf/util:time_util",
"//src/google/protobuf/util:type_resolver_util",
"//src/google/protobuf/util/internal:constants",
"//src/google/protobuf/util/internal:datapiece",
"//src/google/protobuf/util/internal:default_value",
"//src/google/protobuf/util/internal:field_mask_utility",
"//src/google/protobuf/util/internal:json",
"//src/google/protobuf/util/internal:object_writer",
"//src/google/protobuf/util/internal:protostream",
"//src/google/protobuf/util/internal:type_info",
"//src/google/protobuf/util/internal:utility",
],
)
@ -410,6 +397,7 @@ cc_dist_library(
"//src/google/protobuf/compiler/java:names",
"//src/google/protobuf/compiler/java:names_internal",
"//src/google/protobuf/compiler/objectivec",
"//src/google/protobuf/compiler/objectivec:line_consumer",
"//src/google/protobuf/compiler/objectivec:names",
"//src/google/protobuf/compiler/objectivec:names_internal",
"//src/google/protobuf/compiler/php",
@ -433,9 +421,6 @@ cc_dist_library(
deps = [
"//src/google/protobuf:test_util",
"//src/google/protobuf:test_util2",
"//src/google/protobuf/util/internal:expecting_objectwriter",
"//src/google/protobuf/util/internal:mock_error_listener",
"//src/google/protobuf/util/internal:type_info_test_helper",
"//src/google/protobuf/compiler:annotation_test_util",
"//src/google/protobuf/compiler/cpp:unittest_lib",
],

@ -19,6 +19,7 @@ def gen_file_lists(name, out_stem, **kwargs):
srcs = [
out_stem + ".cmake",
],
visibility = ["//src:__pkg__"],
)
################################################################################

@ -49,9 +49,9 @@ def protobuf_deps():
http_archive(
name = "zlib",
build_file = "@com_google_protobuf//:third_party/zlib.BUILD",
sha256 = "629380c90a77b964d896ed37163f5c3a34f6e6d897311f1df2a7016355c45eff",
strip_prefix = "zlib-1.2.11",
urls = ["https://github.com/madler/zlib/archive/v1.2.11.tar.gz"],
sha256 = "d8688496ea40fb61787500e863cc63c9afcbc524468cedeb478068924eb54932",
strip_prefix = "zlib-1.2.12",
urls = ["https://github.com/madler/zlib/archive/v1.2.12.tar.gz"],
)
if not native.existing_rule("rules_cc"):
@ -115,6 +115,6 @@ def protobuf_deps():
_github_archive(
name = "upb",
repo = "https://github.com/protocolbuffers/upb",
commit = "5485645125ba3783ae2b597bd7b77679721cb1c6",
sha256 = "86de85c58eb3cb04b0987a7642ce84e55629f704ab4a9a0210a660a1115f1dd0",
commit = "32c6e9baab03d584b85390fdba789118f20613fc",
#sha256 = "4c82bff4f790dbb5a11ec40b1fac44e7c95d9a63fd215a13aaf44cb27b10ac27",
)

@ -1,3 +1,3 @@
PROTOC_VERSION = '21.6'
PROTOBUF_JAVA_VERSION = '3.21.6'
PROTOBUF_PYTHON_VERSION = '4.21.6'
PROTOC_VERSION = "21.7"
PROTOBUF_JAVA_VERSION = "3.21.7"
PROTOBUF_PYTHON_VERSION = "4.21.7"

@ -8,6 +8,7 @@
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
load("@rules_python//python:defs.bzl", "py_library")
load("@pip_deps//:requirements.bzl", "requirement")
load("//:protobuf.bzl", "internal_py_proto_library")
load("//build_defs:cpp_opts.bzl", "COPTS")
load("//conformance:defs.bzl", "conformance_test")
@ -319,6 +320,16 @@ py_test(
deps = [":python_test_lib"],
)
py_test(
name = "numpy_test",
srcs = ["google/protobuf/internal/numpy_test.py"],
imports = ["."],
deps = [
":python_test_lib",
requirement("numpy"),
],
)
py_test(
name = "proto_builder_test",
srcs = ["google/protobuf/internal/proto_builder_test.py"],

@ -30,4 +30,4 @@
# Copyright 2007 Google Inc. All Rights Reserved.
__version__ = '4.21.6'
__version__ = '4.21.7'

@ -414,6 +414,20 @@ class DescriptorPoolTestBase(object):
field = file_json.message_types_by_name['class'].fields_by_name['int_field']
self.assertEqual(field.json_name, 'json_int')
def testAddSerializedFileTwice(self):
if isinstance(self, SecondaryDescriptorFromDescriptorDB):
if api_implementation.Type() != 'python':
# Cpp extension cannot call Add on a DescriptorPool
# that uses a DescriptorDatabase.
# TODO(jieluo): Fix python and cpp extension diff.
return
self.pool = descriptor_pool.DescriptorPool()
file1_first = self.pool.AddSerializedFile(
self.factory_test1_fd.SerializeToString())
file1_again = self.pool.AddSerializedFile(
self.factory_test1_fd.SerializeToString())
self.assertIs(file1_first, file1_again)
def testEnumDefaultValue(self):
"""Test the default value of enums which don't start at zero."""
def _CheckDefaultValue(file_descriptor):

@ -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.
"""Test use of numpy types with repeated and non-repeated scalar fields."""
import unittest
import numpy as np
from google.protobuf import unittest_pb2
from google.protobuf.internal import testing_refleaks
message = unittest_pb2.TestAllTypes()
np_float_scalar = np.float64(0.0)
np_1_float_array = np.zeros(shape=(1,), dtype=np.float64)
np_2_float_array = np.zeros(shape=(2,), dtype=np.float64)
np_11_float_array = np.zeros(shape=(1, 1), dtype=np.float64)
np_22_float_array = np.zeros(shape=(2, 2), dtype=np.float64)
np_int_scalar = np.int64(0)
np_1_int_array = np.zeros(shape=(1,), dtype=np.int64)
np_2_int_array = np.zeros(shape=(2,), dtype=np.int64)
np_11_int_array = np.zeros(shape=(1, 1), dtype=np.int64)
np_22_int_array = np.zeros(shape=(2, 2), dtype=np.int64)
np_uint_scalar = np.uint64(0)
np_1_uint_array = np.zeros(shape=(1,), dtype=np.uint64)
np_2_uint_array = np.zeros(shape=(2,), dtype=np.uint64)
np_11_uint_array = np.zeros(shape=(1, 1), dtype=np.uint64)
np_22_uint_array = np.zeros(shape=(2, 2), dtype=np.uint64)
np_bool_scalar = np.bool_(False)
np_1_bool_array = np.zeros(shape=(1,), dtype=np.bool_)
np_2_bool_array = np.zeros(shape=(2,), dtype=np.bool_)
np_11_bool_array = np.zeros(shape=(1, 1), dtype=np.bool_)
np_22_bool_array = np.zeros(shape=(2, 2), dtype=np.bool_)
@testing_refleaks.TestCase
class NumpyIntProtoTest(unittest.TestCase):
# Assigning dim 1 ndarray of ints to repeated field should pass
def testNumpyDim1IntArrayToRepeated_IsValid(self):
message.repeated_int64[:] = np_1_int_array
message.repeated_int64[:] = np_2_int_array
message.repeated_uint64[:] = np_1_uint_array
message.repeated_uint64[:] = np_2_uint_array
# Assigning dim 2 ndarray of ints to repeated field should fail
def testNumpyDim2IntArrayToRepeated_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.repeated_int64[:] = np_11_int_array
with self.assertRaises(TypeError):
message.repeated_int64[:] = np_22_int_array
with self.assertRaises(TypeError):
message.repeated_uint64[:] = np_11_uint_array
with self.assertRaises(TypeError):
message.repeated_uint64[:] = np_22_uint_array
# Assigning any ndarray of floats to repeated int field should fail
def testNumpyFloatArrayToRepeated_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.repeated_int64[:] = np_1_float_array
with self.assertRaises(TypeError):
message.repeated_int64[:] = np_11_float_array
with self.assertRaises(TypeError):
message.repeated_int64[:] = np_22_float_array
# Assigning any np int to scalar field should pass
def testNumpyIntScalarToScalar_IsValid(self):
message.optional_int64 = np_int_scalar
message.optional_uint64 = np_uint_scalar
# Assigning any ndarray of ints to scalar field should fail
def testNumpyIntArrayToScalar_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.optional_int64 = np_1_int_array
with self.assertRaises(TypeError):
message.optional_int64 = np_11_int_array
with self.assertRaises(TypeError):
message.optional_int64 = np_22_int_array
with self.assertRaises(TypeError):
message.optional_uint64 = np_1_uint_array
with self.assertRaises(TypeError):
message.optional_uint64 = np_11_uint_array
with self.assertRaises(TypeError):
message.optional_uint64 = np_22_uint_array
# Assigning any ndarray of floats to scalar field should fail
def testNumpyFloatArrayToScalar_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.optional_int64 = np_1_float_array
with self.assertRaises(TypeError):
message.optional_int64 = np_11_float_array
with self.assertRaises(TypeError):
message.optional_int64 = np_22_float_array
@testing_refleaks.TestCase
class NumpyFloatProtoTest(unittest.TestCase):
# Assigning dim 1 ndarray of floats to repeated field should pass
def testNumpyDim1FloatArrayToRepeated_IsValid(self):
message.repeated_float[:] = np_1_float_array
message.repeated_float[:] = np_2_float_array
# Assigning dim 2 ndarray of floats to repeated field should fail
def testNumpyDim2FloatArrayToRepeated_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.repeated_float[:] = np_11_float_array
with self.assertRaises(TypeError):
message.repeated_float[:] = np_22_float_array
# Assigning any np float to scalar field should pass
def testNumpyFloatScalarToScalar_IsValid(self):
message.optional_float = np_float_scalar
# Assigning any ndarray of float to scalar field should fail
def testNumpyFloatArrayToScalar_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.optional_float = np_1_float_array
with self.assertRaises(TypeError):
message.optional_float = np_11_float_array
with self.assertRaises(TypeError):
message.optional_float = np_22_float_array
@testing_refleaks.TestCase
class NumpyBoolProtoTest(unittest.TestCase):
# Assigning dim 1 ndarray of bool to repeated field should pass
def testNumpyDim1BoolArrayToRepeated_IsValid(self):
message.repeated_bool[:] = np_1_bool_array
message.repeated_bool[:] = np_2_bool_array
# Assigning dim 2 ndarray of bool to repeated field should fail
def testNumpyDim2BoolArrayToRepeated_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.repeated_bool[:] = np_11_bool_array
with self.assertRaises(TypeError):
message.repeated_bool[:] = np_22_bool_array
# Assigning any np bool to scalar field should pass
def testNumpyBoolScalarToScalar_IsValid(self):
message.optional_bool = np_bool_scalar
# Assigning any ndarray of bool to scalar field should fail
def testNumpyBoolArrayToScalar_RaisesTypeError(self):
with self.assertRaises(TypeError):
message.optional_bool = np_1_bool_array
with self.assertRaises(TypeError):
message.optional_bool = np_11_bool_array
with self.assertRaises(TypeError):
message.optional_bool = np_22_bool_array
@testing_refleaks.TestCase
class NumpyProtoIndexingTest(unittest.TestCase):
def testNumpyIntScalarIndexing_Passes(self):
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2])
self.assertEqual(0, data.repeated_int64[np.int64(0)])
def testNumpyNegative1IntScalarIndexing_Passes(self):
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2])
self.assertEqual(2, data.repeated_int64[np.int64(-1)])
def testNumpyFloatScalarIndexing_Fails(self):
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2])
with self.assertRaises(TypeError):
_ = data.repeated_int64[np.float64(0.0)]
def testNumpyIntArrayIndexing_Fails(self):
data = unittest_pb2.TestAllTypes(repeated_int64=[0, 1, 2])
with self.assertRaises(TypeError):
_ = data.repeated_int64[np.array([0])]
with self.assertRaises(TypeError):
_ = data.repeated_int64[np.ndarray((1,), buffer=np.array([0]), dtype=int)]
with self.assertRaises(TypeError):
_ = data.repeated_int64[np.ndarray((1, 1),
buffer=np.array([0]),
dtype=int)]
if __name__ == '__main__':
unittest.main()

@ -3,6 +3,7 @@
# See also code generation logic under /src/google/protobuf/compiler/ruby.
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
load("@upb//cmake:build_defs.bzl", "staleness_test")
load("//build_defs:internal_shell.bzl", "inline_sh_test")
load("//conformance:defs.bzl", "conformance_test")
load("//:protobuf.bzl", "internal_ruby_proto_library")
@ -161,6 +162,29 @@ conformance_test(
}),
)
genrule(
name = "copy_ruby_amalgamation_h",
srcs = ["@upb//:ruby-upb.h"],
outs = ["generated-in/ext/google/protobuf_c/ruby-upb.h"],
cmd = "cp $< $@",
)
genrule(
name = "copy_ruby_amalgamation_c",
srcs = ["@upb//:ruby-upb.c"],
outs = ["generated-in/ext/google/protobuf_c/ruby-upb.c"],
cmd = "cp $< $@",
)
staleness_test(
name = "test_amalgamation_staleness",
outs = [
"ext/google/protobuf_c/ruby-upb.h",
"ext/google/protobuf_c/ruby-upb.c",
],
generated_pattern = "generated-in/%s",
)
################################################################################
# Distribution files
################################################################################

@ -696,15 +696,20 @@ bool Message_Equal(const upb_Message* m1, const upb_Message* m2,
if (m1 == m2) return true;
size_t size1, size2;
int encode_opts = kUpb_Encode_SkipUnknown | kUpb_Encode_Deterministic;
int encode_opts =
kUpb_EncodeOption_SkipUnknown | kUpb_EncodeOption_Deterministic;
upb_Arena* arena_tmp = upb_Arena_New();
const upb_MiniTable* layout = upb_MessageDef_MiniTable(m);
// Compare deterministically serialized payloads with no unknown fields.
char* data1 = upb_Encode(m1, layout, encode_opts, arena_tmp, &size1);
char* data2 = upb_Encode(m2, layout, encode_opts, arena_tmp, &size2);
if (data1 && data2) {
char* data1;
char* data2;
upb_EncodeStatus status1 =
upb_Encode(m1, layout, encode_opts, arena_tmp, &data1, &size1);
upb_EncodeStatus status2 =
upb_Encode(m2, layout, encode_opts, arena_tmp, &data2, &size2);
if (status1 == kUpb_EncodeStatus_Ok && status2 == kUpb_EncodeStatus_Ok) {
bool ret = (size1 == size2) && (memcmp(data1, data2, size1) == 0);
upb_Arena_Free(arena_tmp);
return ret;
@ -736,15 +741,16 @@ static VALUE Message_eq(VALUE _self, VALUE _other) {
uint64_t Message_Hash(const upb_Message* msg, const upb_MessageDef* m,
uint64_t seed) {
upb_Arena* arena = upb_Arena_New();
const char* data;
char* data;
size_t size;
// Hash a deterministically serialized payloads with no unknown fields.
data = upb_Encode(msg, upb_MessageDef_MiniTable(m),
kUpb_Encode_SkipUnknown | kUpb_Encode_Deterministic, arena,
&size);
upb_EncodeStatus status = upb_Encode(
msg, upb_MessageDef_MiniTable(m),
kUpb_EncodeOption_SkipUnknown | kUpb_EncodeOption_Deterministic, arena,
&data, &size);
if (data) {
if (status == kUpb_EncodeStatus_Ok) {
uint64_t ret = _upb_Hash(data, size, seed);
upb_Arena_Free(arena);
return ret;
@ -970,7 +976,8 @@ static VALUE Message_decode(int argc, VALUE* argv, VALUE klass) {
rb_raise(rb_eArgError, "Expected hash arguments.");
}
VALUE depth = rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
VALUE depth =
rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
if (depth != Qnil && TYPE(depth) == T_FIXNUM) {
options |= UPB_DECODE_MAXDEPTH(FIX2INT(depth));
@ -984,9 +991,10 @@ static VALUE Message_decode(int argc, VALUE* argv, VALUE klass) {
VALUE msg_rb = initialize_rb_class_with_no_args(klass);
Message* msg = ruby_to_Message(msg_rb);
upb_DecodeStatus status = upb_Decode(
RSTRING_PTR(data), RSTRING_LEN(data), (upb_Message*)msg->msg,
upb_MessageDef_MiniTable(msg->msgdef), NULL, options, Arena_get(msg->arena));
upb_DecodeStatus status =
upb_Decode(RSTRING_PTR(data), RSTRING_LEN(data), (upb_Message*)msg->msg,
upb_MessageDef_MiniTable(msg->msgdef), NULL, options,
Arena_get(msg->arena));
if (status != kUpb_DecodeStatus_Ok) {
rb_raise(cParseError, "Error occurred during parsing");
@ -1070,7 +1078,7 @@ static VALUE Message_decode_json(int argc, VALUE* argv, VALUE klass) {
static VALUE Message_encode(int argc, VALUE* argv, VALUE klass) {
Message* msg = ruby_to_Message(argv[0]);
int options = 0;
const char* data;
char* data;
size_t size;
if (CLASS_OF(argv[0]) != klass) {
@ -1086,19 +1094,21 @@ static VALUE Message_encode(int argc, VALUE* argv, VALUE klass) {
if (TYPE(hash_args) != T_HASH) {
rb_raise(rb_eArgError, "Expected hash arguments.");
}
VALUE depth = rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
VALUE depth =
rb_hash_lookup(hash_args, ID2SYM(rb_intern("recursion_limit")));
if (depth != Qnil && TYPE(depth) == T_FIXNUM) {
options |= UPB_DECODE_MAXDEPTH(FIX2INT(depth));
}
}
upb_Arena *arena = upb_Arena_New();
upb_Arena* arena = upb_Arena_New();
data = upb_Encode(msg->msg, upb_MessageDef_MiniTable(msg->msgdef),
options, arena, &size);
upb_EncodeStatus status =
upb_Encode(msg->msg, upb_MessageDef_MiniTable(msg->msgdef), options,
arena, &data, &size);
if (data) {
if (status == kUpb_EncodeStatus_Ok) {
VALUE ret = rb_str_new(data, size);
rb_enc_associate(ret, rb_ascii8bit_encoding());
upb_Arena_Free(arena);
@ -1136,7 +1146,8 @@ static VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) {
if (argc == 2) {
VALUE hash_args = argv[1];
if (TYPE(hash_args) != T_HASH) {
if (RTEST(rb_funcall(hash_args, rb_intern("respond_to?"), 1, rb_str_new2("to_h")))) {
if (RTEST(rb_funcall(hash_args, rb_intern("respond_to?"), 1,
rb_str_new2("to_h")))) {
hash_args = rb_funcall(hash_args, rb_intern("to_h"), 0);
} else {
rb_raise(rb_eArgError, "Expected hash arguments.");
@ -1295,11 +1306,13 @@ upb_Message* Message_deep_copy(const upb_Message* msg, const upb_MessageDef* m,
const upb_MiniTable* layout = upb_MessageDef_MiniTable(m);
size_t size;
char* data = upb_Encode(msg, layout, 0, tmp_arena, &size);
upb_Message* new_msg = upb_Message_New(m, arena);
char* data;
if (!data || upb_Decode(data, size, new_msg, layout, NULL, 0, arena) !=
kUpb_DecodeStatus_Ok) {
if (upb_Encode(msg, layout, 0, tmp_arena, &data, &size) !=
kUpb_EncodeStatus_Ok ||
upb_Decode(data, size, new_msg, layout, NULL, 0, arena) !=
kUpb_DecodeStatus_Ok) {
upb_Arena_Free(tmp_arena);
rb_raise(cParseError, "Error occurred copying proto");
}
@ -1399,7 +1412,8 @@ static void Message_define_class(VALUE klass) {
void Message_register(VALUE protobuf) {
cParseError = rb_const_get(protobuf, rb_intern("ParseError"));
cAbstractMessage = rb_define_class_under(protobuf, "AbstractMessage", rb_cObject);
cAbstractMessage =
rb_define_class_under(protobuf, "AbstractMessage", rb_cObject);
Message_define_class(cAbstractMessage);
rb_gc_register_address(&cAbstractMessage);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "google-protobuf"
s.version = "3.21.6"
s.version = "3.21.7"
git_tag = "v#{s.version.to_s.sub('.rc.', '-rc')}" # Converts X.Y.Z.rc.N to vX.Y.Z-rcN, used for the git tag
s.licenses = ["BSD-3-Clause"]
s.summary = "Protocol Buffers"

@ -9,7 +9,7 @@
<groupId>com.google.protobuf.jruby</groupId>
<artifactId>protobuf-jruby</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
<name>Protocol Buffer JRuby native extension</name>
<description>
Protocol Buffers are a way of encoding structured data in an efficient yet
@ -76,7 +76,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.21.6</version>
<version>3.21.7</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>

@ -4,6 +4,7 @@
# 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(
@ -30,7 +31,6 @@ pkg_filegroup(
"//src/google/protobuf/stubs:dist_files",
"//src/google/protobuf/testing:dist_files",
"//src/google/protobuf/util:dist_files",
"//src/google/protobuf/util/internal:dist_files",
],
visibility = ["//pkg:__pkg__"],
)
@ -41,3 +41,24 @@ conformance_test(
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"],
)

@ -24,7 +24,6 @@ set(libprotobuf_srcs
${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_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
@ -54,6 +53,7 @@ set(libprotobuf_srcs
${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/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
@ -72,20 +72,6 @@ set(libprotobuf_srcs
${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/internal/datapiece.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/default_value_objectwriter.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/error_listener.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/field_mask_utility.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_escaping.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_objectwriter.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_stream_parser.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/object_writer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/proto_writer.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectsource.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectwriter.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/type_info.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/utility.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/json_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
@ -143,6 +129,7 @@ set(libprotobuf_hdrs
${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/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
@ -179,24 +166,6 @@ set(libprotobuf_hdrs
${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/internal/constants.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/datapiece.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/default_value_objectwriter.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/error_listener.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/field_mask_utility.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_escaping.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_objectwriter.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_stream_parser.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/location_tracker.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/object_location_tracker.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/object_source.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/object_writer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/proto_writer.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectsource.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectwriter.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/structured_objectwriter.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/type_info.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/utility.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
@ -210,7 +179,6 @@ set(libprotobuf_hdrs
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_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
@ -350,18 +318,22 @@ set(libprotoc_srcs
${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/objectivec_enum.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_extension.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_file.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_generator.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_map_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.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
@ -446,21 +418,24 @@ set(libprotoc_hdrs
${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/objectivec_enum.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_enum_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_extension.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_file.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_generator.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_helpers.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_map_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_message_field.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_nsobject_methods.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_oneof.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_options.h
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.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
@ -563,7 +538,6 @@ 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
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/type_info_test_helper.cc
)
# //pkg:test_util
@ -580,15 +554,13 @@ set(test_util_hdrs
${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/util/internal/expecting_objectwriter.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/mock_error_listener.h
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/type_info_test_helper.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
@ -679,7 +651,9 @@ set(compiler_test_files
${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/objectivec_helpers_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
@ -712,13 +686,6 @@ 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/internal/default_value_objectwriter_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_objectwriter_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/json_stream_parser_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectsource_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/protostream_objectwriter_test.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/type_info_test_helper.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/util/json_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
@ -726,17 +693,6 @@ set(util_test_files
# //src/google/protobuf/util:test_proto_srcs
set(util_test_protos_files
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/anys.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/books.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/default_value.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/default_value_test.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/field_mask.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/maps.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/oneofs.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/proto3.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/struct.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/timestamp_duration.proto
${protobuf_SOURCE_DIR}/src/google/protobuf/util/internal/testdata/wrappers.proto
${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

@ -10,6 +10,7 @@ load("//build_defs:cpp_opts.bzl", "COPTS", "LINK_OPTS")
package(
default_visibility = [
"//:__pkg__", # "public" targets are alias rules in //.
"//json:__subpackages__",
],
)
@ -162,11 +163,66 @@ cc_library(
],
)
cc_library(
name = "arena_align",
srcs = ["arena_align.cc"],
hdrs = ["arena_align.h"],
include_prefix = "google/protobuf",
visibility = [
"//:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
"//src/google/protobuf/stubs:lite",
"@com_google_absl//absl/numeric:bits",
],
)
cc_library(
name = "arena_cleanup",
hdrs = ["arena_cleanup.h"],
include_prefix = "google/protobuf",
visibility = [
"//:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
"@com_google_absl//absl/base:core_headers",
],
)
cc_library(
name = "arena_config",
srcs = ["arena_config.cc"],
hdrs = ["arena_config.h"],
include_prefix = "google/protobuf",
visibility = [
"//:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
"//src/google/protobuf/stubs:lite",
],
)
cc_library(
name = "arena_allocation_policy",
hdrs = ["arena_allocation_policy.h"],
include_prefix = "google/protobuf",
visibility = [
"//:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
":arena_config",
"//src/google/protobuf/stubs:lite",
],
)
cc_library(
name = "arena",
srcs = [
"arena.cc",
"arena_config.cc",
],
hdrs = [
"arena.h",
@ -180,6 +236,9 @@ cc_library(
"//src/google/protobuf:__subpackages__",
],
deps = [
":arena_allocation_policy",
":arena_cleanup",
":arena_config",
"//src/google/protobuf/stubs:lite",
"@com_google_absl//absl/synchronization",
],
@ -189,7 +248,6 @@ cc_library(
name = "protobuf_lite",
srcs = [
"any_lite.cc",
"arena_config.cc",
"arenastring.cc",
"arenaz_sampler.cc",
"extension_set.cc",
@ -208,7 +266,6 @@ cc_library(
hdrs = [
"any.h",
"arena.h",
"arena_config.h",
"arena_impl.h",
"arenastring.h",
"arenaz_sampler.h",
@ -251,6 +308,7 @@ cc_library(
# In Bazel 6.0+, these will be `interface_deps`:
deps = [
":arena",
":arena_config",
"//src/google/protobuf/io",
"//src/google/protobuf/stubs:lite",
"@com_google_absl//absl/container:flat_hash_set",
@ -672,6 +730,22 @@ cc_test(
],
)
cc_test(
name = "arena_align_test",
srcs = ["arena_align_test.cc"],
copts = COPTS + select({
"//build_defs:config_msvc": [],
"//conditions:default": [
"-Wno-error=sign-compare",
],
}),
deps = [
":arena_align",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "arena_unittest",
srcs = ["arena_unittest.cc"],

@ -15,7 +15,7 @@
#error "your headers."
#endif // PROTOBUF_VERSION
#if 3021006 < PROTOBUF_MIN_PROTOC_VERSION
#if 3021007 < 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."

@ -15,7 +15,7 @@
#error "your headers."
#endif // PROTOBUF_VERSION
#if 3021006 < PROTOBUF_MIN_PROTOC_VERSION
#if 3021007 < 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."

@ -38,6 +38,7 @@
#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"
@ -53,6 +54,18 @@
namespace google {
namespace protobuf {
namespace internal {
namespace {
// 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);
}
} // namespace
static SerialArena::Memory AllocateMemory(const AllocationPolicy* policy_ptr,
size_t last_size, size_t min_bytes) {
@ -105,28 +118,26 @@ class GetDeallocator {
size_t* space_allocated_;
};
constexpr ArenaBlock SerialArena::kSentryBlock;
// 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()},
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_{SentryBlock()}, parent_{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} {
: head_{b}, space_allocated_{b->size}, parent_{parent} {
if (b->IsSentry()) return;
set_ptr(b->Pointer(kBlockHeaderSize));
@ -136,9 +147,9 @@ SerialArena::SerialArena(FirstSerialArena, ArenaBlock* b,
void SerialArena::Init(ArenaBlock* b, size_t offset) {
set_ptr(b->Pointer(offset));
limit_ = b->Limit();
set_head(b);
space_used_.relaxed_set(0);
space_allocated_.relaxed_set(b->size());
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;
}
@ -155,11 +166,11 @@ SerialArena* SerialArena::New(Memory mem, ThreadSafeArena& parent) {
template <typename Deallocator>
SerialArena::Memory SerialArena::Free(Deallocator deallocator) {
ArenaBlock* b = head();
Memory mem = {b, b->size()};
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()};
mem = {b, b->size};
}
return mem;
}
@ -195,8 +206,9 @@ void SerialArena::AllocateNewBlock(size_t n) {
// Record how much used in this block.
used = static_cast<size_t>(ptr() - old_head->Pointer(kBlockHeaderSize));
wasted = old_head->size() - used;
space_used_.relaxed_set(space_used_.relaxed_get() + used);
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
@ -204,18 +216,21 @@ void SerialArena::AllocateNewBlock(size_t n) {
// but with a CPU regression. The regression might have been an artifact of
// the microbenchmark.
auto mem = AllocateMemory(parent_.AllocPolicy(), old_head->size(), n);
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_.relaxed_set(space_allocated_.relaxed_get() + mem.size);
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_head(new_head);
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());
@ -230,15 +245,15 @@ uint64_t SerialArena::SpaceUsed() const {
// 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();
const ArenaBlock* h = head_.load(std::memory_order_acquire);
if (h->IsSentry()) return 0;
const uint64_t current_block_size = h->size();
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_.relaxed_get();
return current_space_used + space_used_.load(std::memory_order_relaxed);
}
void SerialArena::CleanupList() {
@ -299,10 +314,10 @@ void SerialArena::CleanupList() {
// struct SerialArenaChunkHeader {
// SerialArenaChunk* next_chunk;
// uint32_t capacity;
// Atomic<uint32_t> size;
// std::atomic<uint32_t> size;
// } header;
// Atomic<void*> ids[];
// Atomic<SerialArena*> arenas[];
// std::atomic<void*> ids[];
// std::atomic<SerialArena*> arenas[];
// };
//
// where the size of "ids" and "arenas" is determined at runtime; hence the use
@ -313,7 +328,7 @@ struct SerialArenaChunkHeader {
ThreadSafeArena::SerialArenaChunk* next_chunk;
uint32_t capacity;
Atomic<uint32_t> size;
std::atomic<uint32_t> size;
};
class ThreadSafeArena::SerialArenaChunk {
@ -321,14 +336,14 @@ class ThreadSafeArena::SerialArenaChunk {
SerialArenaChunk(uint32_t capacity, void* me, SerialArena* serial) {
new (&header()) SerialArenaChunkHeader{capacity, 1};
new (&id(0)) Atomic<void*>{me};
new (&id(0)) std::atomic<void*>{me};
for (uint32_t i = 1; i < capacity; ++i) {
new (&id(i)) Atomic<void*>{nullptr};
new (&id(i)) std::atomic<void*>{nullptr};
}
new (&arena(0)) Atomic<SerialArena*>{serial};
new (&arena(0)) std::atomic<SerialArena*>{serial};
for (uint32_t i = 1; i < capacity; ++i) {
new (&arena(i)) Atomic<void*>{nullptr};
new (&arena(i)) std::atomic<void*>{nullptr};
}
}
@ -346,29 +361,29 @@ class ThreadSafeArena::SerialArenaChunk {
void set_capacity(uint32_t capacity) { header().capacity = capacity; }
// ids: returns up to size().
absl::Span<const Atomic<void*>> ids() const {
absl::Span<const std::atomic<void*>> ids() const {
return Layout(capacity()).Slice<kIds>(ptr()).first(safe_size());
}
absl::Span<Atomic<void*>> ids() {
absl::Span<std::atomic<void*>> ids() {
return Layout(capacity()).Slice<kIds>(ptr()).first(safe_size());
}
Atomic<void*>& id(uint32_t i) {
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 Atomic<SerialArena*>> arenas() const {
absl::Span<const std::atomic<SerialArena*>> arenas() const {
return Layout(capacity()).Slice<kArenas>(ptr()).first(safe_size());
}
absl::Span<Atomic<SerialArena*>> arenas() {
absl::Span<std::atomic<SerialArena*>> arenas() {
return Layout(capacity()).Slice<kArenas>(ptr()).first(safe_size());
}
const Atomic<SerialArena*>& arena(uint32_t i) const {
const std::atomic<SerialArena*>& arena(uint32_t i) const {
GOOGLE_DCHECK_LT(i, capacity());
return Layout(capacity()).Pointer<kArenas>(ptr())[i];
}
Atomic<SerialArena*>& arena(uint32_t i) {
std::atomic<SerialArena*>& arena(uint32_t i) {
GOOGLE_DCHECK_LT(i, capacity());
return Layout(capacity()).Pointer<kArenas>(ptr())[i];
}
@ -382,16 +397,16 @@ class ThreadSafeArena::SerialArenaChunk {
// 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().relaxed_fetch_add(1);
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().relaxed_set(capacity());
size().store(capacity(), std::memory_order_relaxed);
return false;
}
id(idx).relaxed_set(me);
arena(idx).relaxed_set(serial);
id(idx).store(me, std::memory_order_relaxed);
arena(idx).store(serial, std::memory_order_release);
return true;
}
@ -402,9 +417,8 @@ class ThreadSafeArena::SerialArenaChunk {
constexpr static int kIds = 1;
constexpr static int kArenas = 2;
using layout_type =
absl::container_internal::Layout<SerialArenaChunkHeader, Atomic<void*>,
Atomic<SerialArena*>>;
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); }
@ -416,13 +430,13 @@ class ThreadSafeArena::SerialArenaChunk {
return *layout_type::Partial().Pointer<kHeader>(ptr());
}
Atomic<uint32_t>& size() { return header().size; }
const Atomic<uint32_t>& size() const { return header().size; }
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().relaxed_get());
return std::min(capacity(), size().load(std::memory_order_relaxed));
}
constexpr static layout_type Layout(size_t n) {
@ -487,7 +501,7 @@ ThreadSafeArena::ThreadSafeArena(void* mem, size_t size,
ArenaBlock* ThreadSafeArena::FirstBlock(void* buf, size_t size) {
GOOGLE_DCHECK_EQ(reinterpret_cast<uintptr_t>(buf) & 7, 0u);
if (buf == nullptr || size <= kBlockHeaderSize) {
return SerialArena::SentryBlock();
return SentryArenaBlock();
}
// Record user-owned block.
alloc_policy_.set_is_user_owned_initial_block(true);
@ -553,7 +567,8 @@ uint64_t ThreadSafeArena::GetNextLifeCycleId() {
// 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.relaxed_fetch_add(1) * kInc;
id = lifecycle_id_generator_.id.fetch_add(1, std::memory_order_relaxed) *
kInc;
}
tc.next_lifecycle_id = id + kDelta;
return id;
@ -588,7 +603,7 @@ ThreadSafeArena::SerialArenaChunk* ThreadSafeArena::NewSerialArenaChunk(
// 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_.atomic_get();
SerialArenaChunk* head = head_.load(std::memory_order_acquire);
// Fast path without acquiring mutex.
if (!head->IsSentry() && head->insert(id, serial)) {
return;
@ -598,7 +613,7 @@ void ThreadSafeArena::AddSerialArena(void* id, SerialArena* serial) {
absl::MutexLock lock(&mutex_);
// Refetch and if someone else installed a new head, try allocating on that!
SerialArenaChunk* new_head = head_.atomic_get();
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.
@ -610,7 +625,7 @@ void ThreadSafeArena::AddSerialArena(void* id, SerialArena* serial) {
// Use "std::memory_order_release" to make sure prior stores are visible after
// this one.
head_.atomic_set(new_head);
head_.store(new_head, std::memory_order_release);
}
void ThreadSafeArena::Init() {
@ -622,7 +637,7 @@ void ThreadSafeArena::Init() {
GOOGLE_DCHECK_EQ(tag_and_id_, kMessageOwnedArena);
}
arena_stats_ = Sample();
head_.relaxed_set(SentrySerialArenaChunk());
head_.store(SentrySerialArenaChunk(), std::memory_order_relaxed);
GOOGLE_DCHECK_EQ(message_owned, IsMessageOwned());
first_owner_ = &thread_cache();
@ -659,12 +674,12 @@ SerialArena::Memory ThreadSafeArena::Free(size_t* space_allocated) {
auto deallocator = GetDeallocator(alloc_policy_.get(), space_allocated);
WalkSerialArenaChunk([deallocator](SerialArenaChunk* chunk) {
absl::Span<Atomic<SerialArena*>> span = chunk->arenas();
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->relaxed_get();
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);
@ -702,7 +717,7 @@ uint64_t ThreadSafeArena::Reset() {
: kBlockHeaderSize + kAllocPolicySize;
first_arena_.Init(new (mem.ptr) ArenaBlock{nullptr, mem.size}, offset);
} else {
first_arena_.Init(SerialArena::SentryBlock(), 0);
first_arena_.Init(SentryArenaBlock(), 0);
}
// Since the first block and potential alloc_policy on the first block is
@ -725,21 +740,21 @@ void* ThreadSafeArena::AllocateAlignedWithCleanup(size_t n, size_t align,
void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) {
SerialArena* arena;
if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) {
arena = GetSerialArenaFallback();
arena = GetSerialArenaFallback(kMaxCleanupNodeSize);
}
arena->AddCleanup(elem, cleanup, AllocPolicy(), arena_stats_.MutableStats());
arena->AddCleanup(elem, cleanup);
}
PROTOBUF_NOINLINE
void* ThreadSafeArena::AllocateAlignedWithCleanupFallback(
size_t n, size_t align, void (*destructor)(void*)) {
return GetSerialArenaFallback()->AllocateAlignedWithCleanup(n, align,
destructor);
return GetSerialArenaFallback(n + kMaxCleanupNodeSize)
->AllocateAlignedWithCleanup(n, align, destructor);
}
template <typename Functor>
void ThreadSafeArena::WalkConstSerialArenaChunk(Functor fn) const {
const SerialArenaChunk* chunk = head_.atomic_get();
const SerialArenaChunk* chunk = head_.load(std::memory_order_acquire);
for (; !chunk->IsSentry(); chunk = chunk->next_chunk()) {
fn(chunk);
@ -751,7 +766,7 @@ 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_.relaxed_get();
SerialArenaChunk* chunk = head_.load(std::memory_order_relaxed);
while (!chunk->IsSentry()) {
// Cache next chunk in case this chunk is destroyed.
@ -765,7 +780,7 @@ template <typename Functor>
void ThreadSafeArena::PerConstSerialArenaInChunk(Functor fn) const {
WalkConstSerialArenaChunk([&fn](const SerialArenaChunk* chunk) {
for (const auto& each : chunk->arenas()) {
const SerialArena* serial = each.relaxed_get();
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;
@ -796,7 +811,7 @@ uint64_t ThreadSafeArena::SpaceUsed() const {
template <AllocationClient alloc_client>
PROTOBUF_NOINLINE void* ThreadSafeArena::AllocateAlignedFallback(size_t n) {
return GetSerialArenaFallback()->AllocateAligned<alloc_client>(n);
return GetSerialArenaFallback(n)->AllocateAligned<alloc_client>(n);
}
template void* ThreadSafeArena::AllocateAlignedFallback<
@ -806,12 +821,12 @@ template void*
void ThreadSafeArena::CleanupList() {
WalkSerialArenaChunk([](SerialArenaChunk* chunk) {
absl::Span<Atomic<SerialArena*>> span = chunk->arenas();
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->relaxed_get();
SerialArena* serial = it->load(std::memory_order_relaxed);
GOOGLE_DCHECK_NE(serial, nullptr);
serial->CleanupList();
}
@ -821,7 +836,7 @@ void ThreadSafeArena::CleanupList() {
}
PROTOBUF_NOINLINE
SerialArena* ThreadSafeArena::GetSerialArenaFallback() {
SerialArena* ThreadSafeArena::GetSerialArenaFallback(size_t n) {
void* const id = &thread_cache();
if (id == first_owner_) {
CacheSerialArena(&first_arena_);
@ -831,10 +846,10 @@ SerialArena* ThreadSafeArena::GetSerialArenaFallback() {
// Search matching SerialArena.
SerialArena* serial = nullptr;
WalkConstSerialArenaChunk([&serial, id](const SerialArenaChunk* chunk) {
absl::Span<const Atomic<void*>> ids = chunk->ids();
absl::Span<const std::atomic<void*>> ids = chunk->ids();
for (uint32_t i = 0; i < ids.size(); ++i) {
if (ids[i].relaxed_get() == id) {
serial = chunk->arena(i).relaxed_get();
if (ids[i].load(std::memory_order_relaxed) == id) {
serial = chunk->arena(i).load(std::memory_order_relaxed);
GOOGLE_DCHECK_NE(serial, nullptr);
break;
}
@ -843,9 +858,10 @@ SerialArena* ThreadSafeArena::GetSerialArenaFallback() {
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.
// 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, kSerialArenaSize), *this);
AllocateMemory(alloc_policy_.get(), 0, n + kSerialArenaSize), *this);
AddSerialArena(id, serial);
}

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

@ -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` constains 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 = 8;
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__

@ -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) {
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) {
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) {
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

@ -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 inital 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__

@ -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__

@ -43,11 +43,12 @@
#include "google/protobuf/stubs/logging.h"
#include "absl/numeric/bits.h"
#include "absl/synchronization/mutex.h"
#include "google/protobuf/port.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"
@ -87,177 +88,33 @@ inline PROTOBUF_ALWAYS_INLINE void* AlignTo(void* p, size_t a) {
}
}
// Wraps std::atomic<T> to avoid accidentally accessing the atomic variable via
// a default memory order (std::memory_order_seq_cst).
template <typename T>
struct Atomic {
constexpr explicit Atomic(T v) : val(v) {}
T relaxed_get() const { return val.load(std::memory_order_relaxed); }
T relaxed_get() { return val.load(std::memory_order_relaxed); }
void relaxed_set(T v) { val.store(v, std::memory_order_relaxed); }
T atomic_get() const { return val.load(std::memory_order_acquire); }
T atomic_get() { return val.load(std::memory_order_acquire); }
void atomic_set(T v) { val.store(v, std::memory_order_release); }
T relaxed_fetch_add(T v) {
return val.fetch_add(v, std::memory_order_relaxed);
}
private:
std::atomic<T> val;
};
// 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), relaxed_size(0) {}
: next(nullptr), cleanup_nodes(this), size(0) {}
ArenaBlock(ArenaBlock* next, size_t size)
: next(next), cleanup_nodes(nullptr), relaxed_size(size) {
: next(next), cleanup_nodes(nullptr), size(size) {
GOOGLE_DCHECK_GT(size, sizeof(ArenaBlock));
}
char* Pointer(size_t n) {
GOOGLE_DCHECK_LE(n, size());
GOOGLE_DCHECK_LE(n, size);
return reinterpret_cast<char*>(this) + n;
}
char* Limit() { return Pointer(size() & static_cast<size_t>(-8)); }
char* Limit() { return Pointer(size & static_cast<size_t>(-8)); }
size_t size() const { return relaxed_size.relaxed_get(); }
bool IsSentry() const { return size() == 0; }
bool IsSentry() const { return size == 0; }
ArenaBlock* const next;
void* cleanup_nodes;
private:
const Atomic<size_t> relaxed_size;
const size_t size;
// data follows
};
namespace cleanup {
template <typename T>
void arena_destruct_object(void* object) {
reinterpret_cast<T*>(object)->~T();
}
enum class Tag : uintptr_t {
kDynamic = 0, // {void* elem, void (*destructor)(void*)}
kString = 1, // std::string* | kString
};
constexpr bool EnableSpecializedTags() {
return alignof(std::string) >= 8
;
}
// All node types must start with a `uintptr_t` that stores `Tag` in its low
// two bits.
struct DynamicNode {
uintptr_t elem;
void (*destructor)(void*);
};
struct StringNode {
uintptr_t elem;
};
inline PROTOBUF_ALWAYS_INLINE void CreateNode(Tag tag, void* pos,
const void* elem,
void (*destructor)(void*)) {
if (EnableSpecializedTags()) {
switch (tag) {
case Tag::kString: {
StringNode n = {reinterpret_cast<uintptr_t>(elem) |
static_cast<uintptr_t>(Tag::kString)};
memcpy(pos, &n, sizeof(n));
return;
}
default:
break;
}
}
DynamicNode n = {reinterpret_cast<uintptr_t>(elem), destructor};
memcpy(pos, &n, sizeof(n));
}
inline PROTOBUF_ALWAYS_INLINE void PrefetchNode(const void* elem_address) {
(void)elem_address;
}
inline PROTOBUF_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));
}
inline PROTOBUF_ALWAYS_INLINE Tag Type(void (*destructor)(void*)) {
if (EnableSpecializedTags()) {
if (destructor == &arena_destruct_object<std::string>) {
return Tag::kString;
}
}
return Tag::kDynamic;
}
inline PROTOBUF_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;
}
}
inline PROTOBUF_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);
}
}
inline PROTOBUF_ALWAYS_INLINE size_t Size(void (*destructor)(void*)) {
return destructor == nullptr ? 0 : Size(Type(destructor));
}
} // namespace cleanup
using LifecycleIdAtomic = uint64_t;
// MetricsCollector collects stats for a particular arena.
@ -293,79 +150,6 @@ class PROTOBUF_EXPORT ArenaMetricsCollector {
const bool record_allocs_;
};
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_;
};
enum class AllocationClient { kDefault, kArray };
class ThreadSafeArena;
@ -394,7 +178,9 @@ class PROTOBUF_EXPORT SerialArena {
};
void CleanupList();
uint64_t SpaceAllocated() const { return space_allocated_.relaxed_get(); }
uint64_t SpaceAllocated() const {
return space_allocated_.load(std::memory_order_relaxed);
}
uint64_t SpaceUsed() const;
bool HasSpace(size_t n) const {
@ -542,8 +328,7 @@ class PROTOBUF_EXPORT SerialArena {
}
PROTOBUF_ALWAYS_INLINE
void AddCleanup(void* elem, void (*destructor)(void*),
const AllocationPolicy* policy, ThreadSafeArenaStats* stats) {
void AddCleanup(void* elem, void (*destructor)(void*)) {
size_t required = cleanup::Size(destructor);
if (PROTOBUF_PREDICT_FALSE(!HasSpace(required))) {
return AddCleanupFallback(elem, destructor);
@ -577,13 +362,6 @@ class PROTOBUF_EXPORT SerialArena {
private:
friend class ThreadSafeArena;
static constexpr ArenaBlock kSentryBlock = {};
static ArenaBlock* SentryBlock() {
// const_cast<> is okay as kSentryBlock will never be mutated.
return const_cast<ArenaBlock*>(&kSentryBlock);
}
// 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
@ -599,13 +377,13 @@ class PROTOBUF_EXPORT SerialArena {
// 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.
Atomic<char*> ptr_{nullptr};
std::atomic<char*> ptr_{nullptr};
// Limiting address up to which memory can be allocated from the head block.
char* limit_ = nullptr;
Atomic<ArenaBlock*> head_{nullptr}; // Head of linked list of blocks.
Atomic<size_t> space_used_{0}; // Necessary for metrics.
Atomic<size_t> space_allocated_{0};
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
@ -623,13 +401,14 @@ class PROTOBUF_EXPORT SerialArena {
CachedBlock** cached_blocks_ = nullptr;
// Helper getters/setters to handle relaxed operations on atomic variables.
ArenaBlock* head() { return head_.relaxed_get(); }
const ArenaBlock* head() const { return head_.relaxed_get(); }
void set_head(ArenaBlock* head) { return head_.relaxed_set(head); }
ArenaBlock* head() { return head_.load(std::memory_order_relaxed); }
const ArenaBlock* head() const {
return head_.load(std::memory_order_relaxed);
}
char* ptr() { return ptr_.relaxed_get(); }
const char* ptr() const { return ptr_.relaxed_get(); }
void set_ptr(char* ptr) { return ptr_.relaxed_set(ptr); }
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);
@ -770,7 +549,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
// Adding a new chunk to head_ must be protected by mutex_.
absl::Mutex mutex_;
// Pointer to a linked list of SerialArenaChunk.
Atomic<SerialArenaChunk*> head_{nullptr};
std::atomic<SerialArenaChunk*> head_{nullptr};
void* first_owner_;
// Must be declared after alloc_policy_; otherwise, it may lose info on
@ -812,8 +591,9 @@ class PROTOBUF_EXPORT ThreadSafeArena {
return false;
}
// Finds SerialArena or creates one if not found.
SerialArena* GetSerialArenaFallback();
// 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);
@ -874,7 +654,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
struct alignas(kCacheAlignment) CacheAlignedLifecycleIdGenerator {
constexpr CacheAlignedLifecycleIdGenerator() : id{0} {}
Atomic<LifecycleIdAtomic> id;
std::atomic<LifecycleIdAtomic> id;
};
static CacheAlignedLifecycleIdGenerator lifecycle_id_generator_;
#if defined(GOOGLE_PROTOBUF_NO_THREADLOCAL)
@ -898,6 +678,7 @@ class PROTOBUF_EXPORT ThreadSafeArena {
(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,

@ -74,7 +74,6 @@
#include "google/protobuf/stubs/logging.h"
#include "google/protobuf/compiler/subprocess.h"
#include "google/protobuf/compiler/plugin.pb.h"
#include "google/protobuf/stubs/strutil.h"
#include "absl/strings/match.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_replace.h"

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

Loading…
Cancel
Save