From b73e30eafed165c8da6196228cec3ce603f2342c Mon Sep 17 00:00:00 2001 From: Kevin Li Date: Thu, 24 Oct 2024 21:38:11 +0800 Subject: [PATCH] support protobuf 28.x (#67) * support protobuf 28.x --- .github/workflows/ci.yml | 14 +- MODULE.bazel | 9 +- example/bcr-presubmit/BUILD | 8 + example/bcr-presubmit/MODULE.bazel | 5 + example/bcr-presubmit/example.cpp | 23 + .../protobuf/28.3.arenastring/MODULE.bazel | 83 + .../patches/arenastring.patch | 8469 +++++++++++++++++ .../protobuf/28.3.arenastring/source.json | 9 + src/babylon/BUILD | 24 +- src/babylon/application_context.h | 16 +- src/babylon/future.hpp | 7 + src/babylon/logging/BUILD | 1 + src/babylon/logging/rolling_file_object.cpp | 2 +- src/babylon/regex.h | 13 + src/babylon/reusable/patch/arena.28.0.inc | 1055 ++ src/babylon/reusable/patch/arena.cpp | 6 +- 16 files changed, 9712 insertions(+), 32 deletions(-) create mode 100644 example/bcr-presubmit/BUILD create mode 100644 example/bcr-presubmit/MODULE.bazel create mode 100644 example/bcr-presubmit/example.cpp create mode 100644 registry/modules/protobuf/28.3.arenastring/MODULE.bazel create mode 100644 registry/modules/protobuf/28.3.arenastring/patches/arenastring.patch create mode 100644 registry/modules/protobuf/28.3.arenastring/source.json create mode 100644 src/babylon/regex.h create mode 100644 src/babylon/reusable/patch/arena.28.0.inc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a591efd..2f95a4e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: fail-fast: false matrix: compiler: [{name: gcc, flag: --action_env=CC=gcc-14}, {name: clang, flag: --action_env=CC=clang-18}] + mode: [{name: dbg, flag: --compilation_mode=dbg}, {name: opt, flag: --compilation_mode=opt}] std: [{name: c++20, flag: --cxxopt=-std=c++20}, {name: c++14, flag: --cxxopt=-std=c++14}] stdlib: [{name: stdlibc++}, {name: libc++, flag: --cxxopt=-stdlib=libc++ --linkopt=-stdlib=libc++}] feature: [{name: asan, flag: --config=asan}, {name: tsan, flag: --features=tsan}] @@ -21,21 +22,21 @@ jobs: - std: {name: c++14} feature: {name: tsan} runs-on: ubuntu-24.04 - name: basic-${{matrix.compiler.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}} + name: basic-${{matrix.compiler.name}}-${{matrix.mode.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}} steps: - uses: actions/checkout@v4 - uses: actions/cache/restore@v4 with: path: bazel-disk - key: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}-${{github.sha}} - restore-keys: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}- + key: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.mode.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}-${{github.sha}} + restore-keys: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.mode.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}- - run: sudo apt install libc++-18-dev libc++abi-18-dev - - run: bazel test --compilation_mode=dbg --disk_cache=bazel-disk --verbose_failures --test_output=errors ${{matrix.compiler.flag}} ${{matrix.std.flag}} ${{matrix.stdlib.flag}} ${{matrix.feature.flag}} test/... + - run: bazel test --disk_cache=bazel-disk --verbose_failures --test_output=errors ${{matrix.compiler.flag}} ${{matrix.mode.flag}} ${{matrix.std.flag}} ${{matrix.stdlib.flag}} ${{matrix.feature.flag}} test/... - uses: actions/cache/save@v4 if: always() with: path: bazel-disk - key: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}-${{github.sha}} + key: bazel-disk-basic-${{matrix.compiler.name}}-${{matrix.mode.name}}-${{matrix.std.name}}-${{matrix.stdlib.name}}-${{matrix.feature.name}}-${{github.sha}} arenastring: strategy: @@ -61,7 +62,7 @@ jobs: key: bazel-disk-arenastring-${{matrix.compiler.name}}-${{matrix.stdlib.name}}-${{matrix.mutable.name}}-${{github.sha}} restore-keys: bazel-disk-arenastring-${{matrix.compiler.name}}-${{matrix.stdlib.name}}-${{matrix.mutable.name}}- - run: sudo apt install libc++-18-dev libc++abi-18-dev - - run: sed -i "/single_version_override.*protobuf/s/version = '[^']*'/version = '27.5.arenastring'/" MODULE.bazel + - run: sed -i "/single_version_override.*protobuf/s/version = '[^']*'/version = '28.3.arenastring'/" MODULE.bazel - run: bazel test --compilation_mode=opt --disk_cache=bazel-disk --verbose_failures --test_output=errors --config=asan --registry=https://bcr.bazel.build --registry=file://%workspace%/registry ${{matrix.compiler.flag}} ${{matrix.stdlib.flag}} ${{matrix.mutable.flag}} test/... - uses: actions/cache/save@v4 if: always() @@ -78,6 +79,7 @@ jobs: path: bazel-disk key: bazel-disk-c++14-coroutine-${{github.sha}} restore-keys: bazel-disk-c++14-coroutine- + - run: sed -i "/single_version_override.*protobuf/s/version = '[^']*'/version = '27.5'/" MODULE.bazel - run: bazel test --disk_cache=bazel-disk --verbose_failures --test_output=errors --action_env=CC=gcc-12 --cxxopt=-std=c++14 --cxxopt=-fcoroutines --cxxopt=-fconcepts test/... - uses: actions/cache/save@v4 if: always() diff --git a/MODULE.bazel b/MODULE.bazel index 758e43bb..6cae2a2d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -6,12 +6,11 @@ module( ################################################################################ # LOWER BOUND dependency versions. -# Bzlmod follows MVS: -# https://bazel.build/versions/6.0.0/build/bzlmod#version-resolution +# Bzlmod follows MVS: https://bazel.build/external/module # Thus the highest version in their module graph is resolved. bazel_dep(name = 'abseil-cpp', version = '20220623.1', repo_name = 'com_google_absl') bazel_dep(name = 'bazel_skylib', version = '1.0.3') -bazel_dep(name = 'boost.preprocessor', version = '1.83.0') +bazel_dep(name = 'boost.preprocessor', version = '1.83.0.bcr.1') bazel_dep(name = 'boost.spirit', version = '1.83.0') bazel_dep(name = 'protobuf', version = '3.19.6', repo_name = 'com_google_protobuf') ################################################################################ @@ -19,10 +18,10 @@ bazel_dep(name = 'protobuf', version = '3.19.6', repo_name = 'com_google_protobu ################################################################################ # test only dependency single_version_override(module_name = 'abseil-cpp', version = '20240722.0') -single_version_override(module_name = 'protobuf', version = '27.5') +single_version_override(module_name = 'protobuf', version = '28.3') bazel_dep(name = 'googletest', version = '1.15.2', repo_name = 'com_google_googletest', dev_dependency = True) bazel_dep(name = 'platforms', version = '0.0.10', dev_dependency = True) -bazel_dep(name = 'rules_cc', version = '0.0.9', dev_dependency = True) +bazel_dep(name = 'rules_cc', version = '0.0.13', dev_dependency = True) bazel_dep(name = 'rules_cuda', version = '0.2.3', dev_dependency = True) # cuda toolchain diff --git a/example/bcr-presubmit/BUILD b/example/bcr-presubmit/BUILD new file mode 100644 index 00000000..916c9d61 --- /dev/null +++ b/example/bcr-presubmit/BUILD @@ -0,0 +1,8 @@ +cc_binary( + name = 'example', + srcs = ['example.cpp'], + deps = [ + '@babylon//:future', + '@babylon//:logging', + ], +) diff --git a/example/bcr-presubmit/MODULE.bazel b/example/bcr-presubmit/MODULE.bazel new file mode 100644 index 00000000..0197af0b --- /dev/null +++ b/example/bcr-presubmit/MODULE.bazel @@ -0,0 +1,5 @@ +bazel_dep(name = 'babylon') +local_path_override( + module_name = 'babylon', + path = '../..', +) diff --git a/example/bcr-presubmit/example.cpp b/example/bcr-presubmit/example.cpp new file mode 100644 index 00000000..ae22d1f6 --- /dev/null +++ b/example/bcr-presubmit/example.cpp @@ -0,0 +1,23 @@ +#include "babylon/future.h" +#include "babylon/logging/logger.h" + +#include +#include + +int main() { + ::babylon::CountDownLatch<> latch(10); + auto future = latch.get_future(); + ::std::vector<::std::thread> threads; + for (size_t i = 0; i < 10; ++i) { + threads.emplace_back([&, i]() { + BABYLON_LOG(INFO) << "finish " << i; + latch.count_down(); + }); + } + future.get(); + BABYLON_LOG(INFO) << "finish all"; + for (auto& thread : threads) { + thread.join(); + } + return 0; +} diff --git a/registry/modules/protobuf/28.3.arenastring/MODULE.bazel b/registry/modules/protobuf/28.3.arenastring/MODULE.bazel new file mode 100644 index 00000000..3d797875 --- /dev/null +++ b/registry/modules/protobuf/28.3.arenastring/MODULE.bazel @@ -0,0 +1,83 @@ +# TODO: migrate all dependencies from WORKSPACE to MODULE.bazel +# https://github.com/protocolbuffers/protobuf/issues/14313 + +module( + name = "protobuf", + version = "28.3.arenastring", # Automatically updated on release + compatibility_level = 1, + repo_name = "com_google_protobuf", +) + +# LOWER BOUND dependency versions. +# Bzlmod follows MVS: +# https://bazel.build/versions/6.0.0/build/bzlmod#version-resolution +# Thus the highest version in their module graph is resolved. +bazel_dep(name = "abseil-cpp", version = "20230802.0.bcr.1", repo_name = "com_google_absl") +bazel_dep(name = "bazel_skylib", version = "1.4.1") +bazel_dep(name = "jsoncpp", version = "1.9.5") +bazel_dep(name = "rules_cc", version = "0.0.9") +bazel_dep(name = "rules_fuzzing", version = "0.5.2") +bazel_dep(name = "rules_java", version = "5.3.5") +bazel_dep(name = "rules_jvm_external", version = "5.1") +bazel_dep(name = "rules_pkg", version = "0.7.0") +bazel_dep(name = "rules_python", version = "0.28.0") +bazel_dep(name = "rules_rust", version = "0.45.1") +bazel_dep(name = "platforms", version = "0.0.8") +bazel_dep(name = "zlib", version = "1.3.1") + +# TODO: remove after toolchain types are moved to protobuf +bazel_dep(name = "rules_proto", version = "4.0.0") + +SUPPORTED_PYTHON_VERSIONS = [ + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", +] +python = use_extension("@rules_python//python/extensions:python.bzl", "python") +[ + python.toolchain( + is_default = python_version == SUPPORTED_PYTHON_VERSIONS[-1], + python_version = python_version, + ) + for python_version in SUPPORTED_PYTHON_VERSIONS +] +use_repo(python, system_python = "python_{}".format(SUPPORTED_PYTHON_VERSIONS[-1].replace(".", "_"))) +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +[ + pip.parse( + hub_name = "pip_deps", + python_version = python_version, + requirements_lock = "//python:requirements.txt", + ) + for python_version in SUPPORTED_PYTHON_VERSIONS +] +use_repo(pip, "pip_deps") + +rust = use_extension("@rules_rust//rust:extensions.bzl", "rust") +rust.toolchain(edition = "2021") +use_repo(rust, "rust_toolchains") +register_toolchains("@rust_toolchains//:all") +crate = use_extension("@rules_rust//crate_universe:extension.bzl", "crate") +crate.spec( + package = "googletest", + version = ">0.0.0", +) +crate.spec( + package = "paste", + version = ">=1", +) +crate.from_specs() +use_repo(crate, crate_index = "crates") + +bazel_dep(name = "googletest", version = "1.15.2", repo_name = "com_google_googletest", dev_dependency = True) +bazel_dep(name = "rules_ruby", dev_dependency = True) +archive_override( + module_name = "rules_ruby", + urls = ["https://github.com/protocolbuffers/rules_ruby/archive/b7f3e9756f3c45527be27bc38840d5a1ba690436.tar.gz"], + strip_prefix = "rules_ruby-b7f3e9756f3c45527be27bc38840d5a1ba690436", + integrity = "sha256-RNo21X/p9slOdF912FLprLnxvZHMXBTxaUDGFmm2bx8=", + patches = ["third_party/rules_ruby.patch"], + patch_strip = 1, +) diff --git a/registry/modules/protobuf/28.3.arenastring/patches/arenastring.patch b/registry/modules/protobuf/28.3.arenastring/patches/arenastring.patch new file mode 100644 index 00000000..e6f3d4d2 --- /dev/null +++ b/registry/modules/protobuf/28.3.arenastring/patches/arenastring.patch @@ -0,0 +1,8469 @@ +diff --git a/MODULE.bazel b/MODULE.bazel +index 1e626f91d..3d7978758 100644 +--- a/MODULE.bazel ++++ b/MODULE.bazel +@@ -3,7 +3,7 @@ + + module( + name = "protobuf", +- version = "28.3", # Automatically updated on release ++ version = "28.3.arenastring", # Automatically updated on release + compatibility_level = 1, + repo_name = "com_google_protobuf", + ) +@@ -70,3 +70,14 @@ crate.spec( + ) + crate.from_specs() + use_repo(crate, crate_index = "crates") ++ ++bazel_dep(name = "googletest", version = "1.15.2", repo_name = "com_google_googletest", dev_dependency = True) ++bazel_dep(name = "rules_ruby", dev_dependency = True) ++archive_override( ++ module_name = "rules_ruby", ++ urls = ["https://github.com/protocolbuffers/rules_ruby/archive/b7f3e9756f3c45527be27bc38840d5a1ba690436.tar.gz"], ++ strip_prefix = "rules_ruby-b7f3e9756f3c45527be27bc38840d5a1ba690436", ++ integrity = "sha256-RNo21X/p9slOdF912FLprLnxvZHMXBTxaUDGFmm2bx8=", ++ patches = ["third_party/rules_ruby.patch"], ++ patch_strip = 1, ++) +diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod +new file mode 100644 +index 000000000..8d1c8b69c +--- /dev/null ++++ b/WORKSPACE.bzlmod +@@ -0,0 +1 @@ ++ +diff --git a/src/google/protobuf/BUILD.bazel b/src/google/protobuf/BUILD.bazel +index 381e0e24e..7c495fa98 100644 +--- a/src/google/protobuf/BUILD.bazel ++++ b/src/google/protobuf/BUILD.bazel +@@ -464,6 +464,8 @@ cc_library( + srcs = [ + "any_lite.cc", + "arenastring.cc", ++ # ARENASTRING PATCH: add new file ++ "arenastring_impl.cc", + "arenaz_sampler.cc", + "extension_set.cc", + "generated_enum_util.cc", +@@ -483,6 +485,8 @@ cc_library( + "any.h", + "arena.h", + "arenastring.h", ++ # ARENASTRING PATCH: add new file ++ "arenastring_impl.h", + "arenaz_sampler.h", + "descriptor_lite.h", + "endian.h", +@@ -823,6 +827,9 @@ filegroup( + "map_proto2_unittest.proto", + "map_unittest.proto", + "unittest.proto", ++ # ARENASTRING PATCH: add new file ++ "unittest_arenastring.proto", ++ "unittest_arenastring_mutable.proto", + "unittest_custom_options.proto", + "unittest_embed_optimize_for.proto", + "unittest_empty.proto", +@@ -838,6 +845,9 @@ filegroup( + "unittest_optimize_for.proto", + "unittest_proto3.proto", + "unittest_proto3_arena.proto", ++ # ARENASTRING PATCH: add new file ++ "unittest_proto3_arenastring.proto", ++ "unittest_proto3_arenastring_mutable.proto", + "unittest_proto3_arena_lite.proto", + "unittest_proto3_bad_macros.proto", + "unittest_proto3_extensions.proto", +@@ -1225,6 +1235,7 @@ cc_test( + ], + ) + ++# ARENASTRING PATCH: add new unittest + cc_test( + name = "arenastring_unittest", + srcs = ["arenastring_unittest.cc"], +@@ -1241,6 +1252,23 @@ cc_test( + ], + ) + ++cc_test( ++ name = "arenastring_impl_unittest", ++ srcs = ["arenastring_impl_unittest.cc"], ++ deps = [ ++ ":port", ++ ":protobuf", ++ ":protobuf_lite", ++ ":cc_test_protos", ++ "//src/google/protobuf/io", ++ "//src/google/protobuf/stubs", ++ "@com_google_absl//absl/log:absl_check", ++ "@com_google_absl//absl/strings", ++ "@com_google_googletest//:gtest", ++ "@com_google_googletest//:gtest_main", ++ ], ++) ++ + cc_test( + name = "arenaz_sampler_test", + srcs = ["arenaz_sampler_test.cc"], +diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h +index 97855e7df..c074ea582 100644 +--- a/src/google/protobuf/any.pb.h ++++ b/src/google/protobuf/any.pb.h +@@ -271,6 +271,7 @@ class PROTOBUF_EXPORT Any final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_url( + const std::string& value); + std::string* _internal_mutable_type_url(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_type_url_accessor(); + + public: + // bytes value = 2; +@@ -287,6 +288,7 @@ class PROTOBUF_EXPORT Any final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_value( + const std::string& value); + std::string* _internal_mutable_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_value_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.Any) +@@ -357,7 +359,7 @@ inline PROTOBUF_ALWAYS_INLINE void Any::set_type_url(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Any.type_url) + } + inline std::string* Any::mutable_type_url() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_type_url(); ++ auto _s = _internal_mutable_type_url(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Any.type_url) + return _s; + } +@@ -373,10 +375,18 @@ inline std::string* Any::_internal_mutable_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.type_url_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Any::_internal_mutable_type_url_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.type_url_.MutableAccessor( GetArena()); ++} + inline std::string* Any::release_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Any.type_url) +- return _impl_.type_url_.Release(); ++ auto* released = _impl_.type_url_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.type_url_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Any::set_allocated_type_url(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -407,7 +417,7 @@ inline PROTOBUF_ALWAYS_INLINE void Any::set_value(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Any.value) + } + inline std::string* Any::mutable_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_value(); ++ auto _s = _internal_mutable_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Any.value) + return _s; + } +@@ -423,10 +433,18 @@ inline std::string* Any::_internal_mutable_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Any::_internal_mutable_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.value_.MutableAccessor( GetArena()); ++} + inline std::string* Any::release_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Any.value) +- return _impl_.value_.Release(); ++ auto* released = _impl_.value_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.value_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Any::set_allocated_value(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h +index fb730a416..8f2bc8e1d 100644 +--- a/src/google/protobuf/api.pb.h ++++ b/src/google/protobuf/api.pb.h +@@ -231,6 +231,7 @@ class PROTOBUF_EXPORT Mixin final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string root = 2; +@@ -247,6 +248,7 @@ class PROTOBUF_EXPORT Mixin final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_root( + const std::string& value); + std::string* _internal_mutable_root(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_root_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.Mixin) +@@ -463,6 +465,7 @@ class PROTOBUF_EXPORT Method final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string request_type_url = 2; +@@ -479,6 +482,7 @@ class PROTOBUF_EXPORT Method final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_request_type_url( + const std::string& value); + std::string* _internal_mutable_request_type_url(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_request_type_url_accessor(); + + public: + // string response_type_url = 4; +@@ -495,6 +499,7 @@ class PROTOBUF_EXPORT Method final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_response_type_url( + const std::string& value); + std::string* _internal_mutable_response_type_url(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_response_type_url_accessor(); + + public: + // bool request_streaming = 3; +@@ -780,6 +785,7 @@ class PROTOBUF_EXPORT Api final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string version = 4; +@@ -796,6 +802,7 @@ class PROTOBUF_EXPORT Api final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_version( + const std::string& value); + std::string* _internal_mutable_version(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_version_accessor(); + + public: + // .google.protobuf.SourceContext source_context = 5; +@@ -896,7 +903,7 @@ inline PROTOBUF_ALWAYS_INLINE void Api::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Api.name) + } + inline std::string* Api::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Api.name) + return _s; + } +@@ -912,10 +919,18 @@ inline std::string* Api::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Api::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Api::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Api.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Api::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1040,7 +1055,7 @@ inline PROTOBUF_ALWAYS_INLINE void Api::set_version(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Api.version) + } + inline std::string* Api::mutable_version() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_version(); ++ auto _s = _internal_mutable_version(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Api.version) + return _s; + } +@@ -1056,10 +1071,18 @@ inline std::string* Api::_internal_mutable_version() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.version_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Api::_internal_mutable_version_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.version_.MutableAccessor( GetArena()); ++} + inline std::string* Api::release_version() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Api.version) +- return _impl_.version_.Release(); ++ auto* released = _impl_.version_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.version_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Api::set_allocated_version(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1256,7 +1279,7 @@ inline PROTOBUF_ALWAYS_INLINE void Method::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Method.name) + } + inline std::string* Method::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Method.name) + return _s; + } +@@ -1272,10 +1295,18 @@ inline std::string* Method::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Method::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Method::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Method.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Method::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1306,7 +1337,7 @@ inline PROTOBUF_ALWAYS_INLINE void Method::set_request_type_url(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Method.request_type_url) + } + inline std::string* Method::mutable_request_type_url() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_request_type_url(); ++ auto _s = _internal_mutable_request_type_url(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Method.request_type_url) + return _s; + } +@@ -1322,10 +1353,18 @@ inline std::string* Method::_internal_mutable_request_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.request_type_url_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Method::_internal_mutable_request_type_url_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.request_type_url_.MutableAccessor( GetArena()); ++} + inline std::string* Method::release_request_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Method.request_type_url) +- return _impl_.request_type_url_.Release(); ++ auto* released = _impl_.request_type_url_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.request_type_url_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Method::set_allocated_request_type_url(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1378,7 +1417,7 @@ inline PROTOBUF_ALWAYS_INLINE void Method::set_response_type_url(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Method.response_type_url) + } + inline std::string* Method::mutable_response_type_url() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_response_type_url(); ++ auto _s = _internal_mutable_response_type_url(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Method.response_type_url) + return _s; + } +@@ -1394,10 +1433,18 @@ inline std::string* Method::_internal_mutable_response_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.response_type_url_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Method::_internal_mutable_response_type_url_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.response_type_url_.MutableAccessor( GetArena()); ++} + inline std::string* Method::release_response_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Method.response_type_url) +- return _impl_.response_type_url_.Release(); ++ auto* released = _impl_.response_type_url_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.response_type_url_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Method::set_allocated_response_type_url(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1521,7 +1568,7 @@ inline PROTOBUF_ALWAYS_INLINE void Mixin::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Mixin.name) + } + inline std::string* Mixin::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Mixin.name) + return _s; + } +@@ -1537,10 +1584,18 @@ inline std::string* Mixin::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Mixin::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Mixin::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Mixin::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1571,7 +1626,7 @@ inline PROTOBUF_ALWAYS_INLINE void Mixin::set_root(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Mixin.root) + } + inline std::string* Mixin::mutable_root() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_root(); ++ auto _s = _internal_mutable_root(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Mixin.root) + return _s; + } +@@ -1587,10 +1642,18 @@ inline std::string* Mixin::_internal_mutable_root() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.root_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Mixin::_internal_mutable_root_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.root_.MutableAccessor( GetArena()); ++} + inline std::string* Mixin::release_root() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Mixin.root) +- return _impl_.root_.Release(); ++ auto* released = _impl_.root_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.root_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Mixin::set_allocated_root(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc +index 857abf057..9500344e3 100644 +--- a/src/google/protobuf/arena_unittest.cc ++++ b/src/google/protobuf/arena_unittest.cc +@@ -1173,11 +1173,15 @@ TEST(ArenaTest, UnsafeArenaAddAllocatedToRepeatedField) { + arena1_message->Clear(); + { + std::string* s = new std::string("Test"); +- arena1_message->mutable_repeated_string()->UnsafeArenaAddAllocated(s); ++ // ARENASTRING PATCH: wrap to StringHandlerType ++ arena1_message->mutable_repeated_string()->UnsafeArenaAddAllocated( ++ internal::StringHandlerType::ToUnTagged(s)); + // Should not copy. + EXPECT_EQ(s, &arena1_message->repeated_string(0)); + EXPECT_EQ("Test", arena1_message->repeated_string(0)); +- delete arena1_message->mutable_repeated_string()->UnsafeArenaReleaseLast(); ++ // ARENASTRING PATCH: unwrap StringHandlerType ++ delete arena1_message->mutable_repeated_string() ++ ->UnsafeArenaReleaseLast()->ToStringPtr(); + } + } + +@@ -1296,7 +1300,9 @@ TEST(ArenaTest, UnsafeArenaAddAllocated) { + TestAllTypes* message = Arena::Create(&arena); + for (int i = 0; i < 10; i++) { + std::string* arena_string = Arena::Create(&arena); +- message->mutable_repeated_string()->UnsafeArenaAddAllocated(arena_string); ++ // ARENASTRING PATCH: wrap to StringHandlerType ++ message->mutable_repeated_string()->UnsafeArenaAddAllocated( ++ internal::StringHandlerType::ToUnTagged(arena_string)); + EXPECT_EQ(arena_string, message->mutable_repeated_string(i)); + } + } +@@ -1538,13 +1544,7 @@ TEST(ArenaTest, ClearOneofMessageOnArena) { + child->set_moo_int(100); + message->clear_foo_message(); + +-#ifndef PROTOBUF_ASAN + EXPECT_NE(child->moo_int(), 100); +-#else +-#if GTEST_HAS_DEATH_TEST && defined(__cpp_if_constexpr) +- EXPECT_DEATH(EXPECT_EQ(child->moo_int(), 0), "use-after-poison"); +-#endif +-#endif + } + + TEST(ArenaTest, CopyValuesWithinOneof) { +diff --git a/src/google/protobuf/arenastring.cc b/src/google/protobuf/arenastring.cc +index bf9d0bb7c..d46858eb7 100644 +--- a/src/google/protobuf/arenastring.cc ++++ b/src/google/protobuf/arenastring.cc +@@ -84,7 +84,8 @@ inline TaggedStringPtr CreateString(absl::string_view value) { + // Creates an arena allocated std::string value. + TaggedStringPtr CreateArenaString(Arena& arena, absl::string_view s) { + TaggedStringPtr res; +- res.SetMutableArena(Arena::Create(&arena, s.data(), s.length())); ++ // Initialize to FixedSizeArena state ++ res.SetFixedSizeArena(ArenaStringAccessor::create(&arena, s).underlying()); + return res; + } + +@@ -116,7 +117,7 @@ void ArenaStringPtr::Set(absl::string_view value, Arena* arena) { + old->assign("garbagedata"); + } + #else // PROTOBUF_FORCE_COPY_DEFAULT_STRING +- UnsafeMutablePointer()->assign(value.data(), value.length()); ++ Accessor(arena) = value; + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + } + } +@@ -127,8 +128,7 @@ void ArenaStringPtr::Set(const std::string& value, Arena* arena) { + if (IsDefault()) { + // If we're not on an arena, skip straight to a true string to avoid + // possible copy cost later. +- tagged_ptr_ = arena != nullptr ? CreateArenaString(*arena, value) +- : CreateString(value); ++ NewString(arena, value); + } else { + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (arena == nullptr) { +@@ -141,7 +141,7 @@ void ArenaStringPtr::Set(const std::string& value, Arena* arena) { + old->assign("garbagedata"); + } + #else // PROTOBUF_FORCE_COPY_DEFAULT_STRING +- UnsafeMutablePointer()->assign(value); ++ Accessor(arena) = value; + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + } + } +@@ -150,13 +150,8 @@ void ArenaStringPtr::Set(std::string&& value, Arena* arena) { + ScopedCheckPtrInvariants check(&tagged_ptr_); + if (IsDefault()) { + NewString(arena, std::move(value)); +- } else if (IsFixedSizeArena()) { +- std::string* current = tagged_ptr_.Get(); +- auto* s = new (current) std::string(std::move(value)); +- arena->OwnDestructor(s); +- tagged_ptr_.SetMutableArena(s); +- } else /* !IsFixedSizeArena() */ { +- *UnsafeMutablePointer() = std::move(value); ++ } else { ++ Accessor(arena) = std::move(value); + } + } + +@@ -179,6 +174,42 @@ std::string* ArenaStringPtr::Mutable(const LazyString& default_value, + } + } + ++MaybeArenaStringAccessor ArenaStringPtr::MutableAccessor(Arena* arena) { ++ ScopedCheckPtrInvariants check(&tagged_ptr_); ++ if (IsDefault()) { ++ std::string* string; ++ if (arena != nullptr) { ++ string = ArenaStringAccessor::create(arena).underlying(); ++ tagged_ptr_.SetFixedSizeArena(string); ++ } else { ++ string = new std::string; ++ tagged_ptr_.SetAllocated(string); ++ } ++ return MaybeArenaStringAccessor(arena, string); ++ } else { ++ return Accessor(arena); ++ } ++} ++ ++MaybeArenaStringAccessor ArenaStringPtr::MutableAccessor( ++ const LazyString& default_value, Arena* arena) { ++ ScopedCheckPtrInvariants check(&tagged_ptr_); ++ if (IsDefault()) { ++ std::string* string; ++ if (arena != nullptr) { ++ string = ++ ArenaStringAccessor::create(arena, default_value.get()).underlying(); ++ tagged_ptr_.SetFixedSizeArena(string); ++ } else { ++ string = new std::string(default_value.get()); ++ tagged_ptr_.SetAllocated(string); ++ } ++ return MaybeArenaStringAccessor(arena, string); ++ } else { ++ return Accessor(arena); ++ } ++} ++ + std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) { + ScopedCheckPtrInvariants check(&tagged_ptr_); + if (tagged_ptr_.IsMutable()) { +@@ -193,12 +224,29 @@ std::string* ArenaStringPtr::MutableNoCopy(Arena* arena) { + template + std::string* ArenaStringPtr::MutableSlow(::google::protobuf::Arena* arena, + const Lazy&... lazy_default) { ++ // Turn state from FixedSizeArena to MutableArena ++ if (IsFixedSizeArena()) { ++ auto ptr = Arena::Create(arena, *tagged_ptr_.Get()); ++ tagged_ptr_.SetMutableArena(ptr); ++ return ptr; ++ } ++ + ABSL_DCHECK(IsDefault()); + + // For empty defaults, this ends up calling the default constructor which is + // more efficient than a copy construction from + // GetEmptyStringAlreadyInited(). +- return NewString(arena, lazy_default.get()...); ++ if (arena == nullptr) { ++ // Turn state from Default to Allocated ++ auto ptr = new std::string(lazy_default.get()...); ++ tagged_ptr_.SetAllocated(ptr); ++ return ptr; ++ } else { ++ // Turn state from Default to MutableArena ++ auto ptr = Arena::Create(arena, lazy_default.get()...); ++ tagged_ptr_.SetMutableArena(ptr); ++ return ptr; ++ } + } + + std::string* ArenaStringPtr::Release() { +@@ -248,7 +296,7 @@ void ArenaStringPtr::ClearToEmpty() { + // UpdateArenaString uses assign when capacity is larger than the new + // value, which is trivially true in the donated string case. + // const_cast(PtrValue())->clear(); +- tagged_ptr_.Get()->clear(); ++ ClearNonDefaultToEmpty(); + } + } + +@@ -259,23 +307,43 @@ void ArenaStringPtr::ClearToDefault(const LazyString& default_value, + if (IsDefault()) { + // Already set to default -- do nothing. + } else { +- UnsafeMutablePointer()->assign(default_value.get()); ++ Accessor(arena) = default_value.get(); + } + } + +-const char* EpsCopyInputStream::ReadArenaString(const char* ptr, +- ArenaStringPtr* s, +- Arena* arena) { +- ScopedCheckPtrInvariants check(&s->tagged_ptr_); +- ABSL_DCHECK(arena != nullptr); ++// FixedSizeArena implement ++const char* EpsCopyInputStream::ReadArenaString(const char* ptr, int size, ++ ArenaStringAccessor s) { ++ auto* buffer = s.__resize_default_init(size); ++ ++ if (size <= buffer_end_ + kSlopBytes - ptr) { ++ memcpy(buffer, ptr, size); ++ return ptr + size; ++ } + +- int size = ReadSize(&ptr); ++ return AppendSize(ptr, size, [&](const char* p, int s) { ++ memcpy(buffer, p, s); ++ buffer += s; ++ }); ++} ++ ++// FixedSizeArena entry ++const char* EpsCopyInputStream::ReadArenaString(const char* ptr, ++ ArenaStringAccessor s) { ++ auto size = ReadSize(&ptr); + if (!ptr) return nullptr; ++ return ReadArenaString(ptr, size, s); ++} + +- auto* str = s->NewString(arena); +- ptr = ReadString(ptr, size, str); +- GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); +- return ptr; ++const char* EpsCopyInputStream::ReadArenaString(const char* ptr, ++ MaybeArenaStringAccessor s) { ++ if (s.arena() != nullptr) { ++ return ReadArenaString(ptr, static_cast(s)); ++ } ++ ++ auto size = ReadSize(&ptr); ++ if (!ptr) return nullptr; ++ return ReadString(ptr, size, s.underlying()); + } + + } // namespace internal +diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h +index 0c1c4e271..54ff21e61 100644 +--- a/src/google/protobuf/arenastring.h ++++ b/src/google/protobuf/arenastring.h +@@ -17,6 +17,7 @@ + #include "absl/log/absl_check.h" + #include "absl/strings/string_view.h" + #include "google/protobuf/arena.h" ++#include "google/protobuf/arenastring_impl.h" + #include "google/protobuf/explicitly_constructed.h" + #include "google/protobuf/port.h" + +@@ -321,6 +322,11 @@ struct PROTOBUF_EXPORT ArenaStringPtr { + // default value is lazily initialized. + std::string* Mutable(Arena* arena); + std::string* Mutable(const LazyString& default_value, Arena* arena); ++ // Returns a mutable arenastring when arena != nullptr ++ // keep FixedSizeArena state ++ MaybeArenaStringAccessor MutableAccessor(Arena* arena); ++ MaybeArenaStringAccessor MutableAccessor(const LazyString& default_value, ++ Arena* arena); + + // Gets a mutable pointer with unspecified contents. + // This function is identical to Mutable(), except it is optimized for the +@@ -398,14 +404,16 @@ struct PROTOBUF_EXPORT ArenaStringPtr { + auto* s = new std::string(std::forward(args)...); + return tagged_ptr_.SetAllocated(s); + } else { +- auto* s = Arena::Create(arena, std::forward(args)...); +- return tagged_ptr_.SetMutableArena(s); ++ // Initialize to FixedSizeArena state ++ auto* s = ArenaStringAccessor::create(arena, std::forward(args)...) ++ .underlying(); ++ return tagged_ptr_.SetFixedSizeArena(s); + } + } + + TaggedStringPtr tagged_ptr_; + +- bool IsFixedSizeArena() const { return false; } ++ bool IsFixedSizeArena() const { return tagged_ptr_.IsFixedSizeArena(); } + + // Swaps tagged pointer without debug hardening. This is to allow python + // protobuf to maintain pointer stability even in DEBUG builds. +@@ -414,6 +422,13 @@ struct PROTOBUF_EXPORT ArenaStringPtr { + std::swap(lhs->tagged_ptr_, rhs->tagged_ptr_); + } + ++ // Universal accessor for Allocated/MutableArena/FixedSizeArena state ++ PROTOBUF_ALWAYS_INLINE ++ inline MaybeArenaStringAccessor Accessor(Arena* arena) { ++ return MaybeArenaStringAccessor( ++ tagged_ptr_.IsFixedSizeArena() ? arena : nullptr, tagged_ptr_.Get()); ++ } ++ + friend class ::google::protobuf::internal::SwapFieldHelper; + friend class TcParser; + +@@ -522,8 +537,7 @@ inline PROTOBUF_NDEBUG_INLINE void ArenaStringPtr::InternalSwap( + } + + inline void ArenaStringPtr::ClearNonDefaultToEmpty() { +- // Unconditionally mask away the tag. +- tagged_ptr_.Get()->clear(); ++ MaybeArenaStringAccessor::clear(tagged_ptr_.Get()); + } + + inline std::string* ArenaStringPtr::UnsafeMutablePointer() { +diff --git a/src/google/protobuf/arenastring_impl.cc b/src/google/protobuf/arenastring_impl.cc +new file mode 100644 +index 000000000..c4a976bbe +--- /dev/null ++++ b/src/google/protobuf/arenastring_impl.cc +@@ -0,0 +1,155 @@ ++#include "google/protobuf/arenastring_impl.h" ++ ++#include "google/protobuf/port_def.inc" ++ ++namespace google { ++namespace protobuf { ++ ++ArenaStringAccessor& ArenaStringAccessor::assign(const_pointer data, ++ size_type size) noexcept { ++ auto* buffer = qualified_buffer(size); ++ set_size(size); ++ __builtin_memcpy(buffer, data, size); ++ buffer[size] = '\0'; ++ return *this; ++} ++ ++void ArenaStringAccessor::reserve(size_type required_capacity) noexcept { ++ if (required_capacity > capacity()) { ++ auto origin_size = size(); ++ recreate_buffer(required_capacity); ++ set_size_and_terminator(origin_size); ++ } ++} ++ ++void ArenaStringAccessor::push_back(value_type c) noexcept { ++ auto origin_size = size(); ++ auto* buffer = qualified_buffer(origin_size + 1, origin_size << 1); ++ set_size(origin_size + 1); ++ buffer[origin_size] = c; ++ buffer[origin_size + 1] = '\0'; ++} ++ ++ArenaStringAccessor& ArenaStringAccessor::append( ++ const_pointer append_data, size_type append_size) noexcept { ++ auto origin_size = size(); ++ auto* buffer = qualified_buffer(origin_size + append_size); ++ set_size(origin_size + append_size); ++ __builtin_memcpy(buffer + origin_size, append_data, append_size); ++ buffer[origin_size + append_size] = '\0'; ++ return *this; ++} ++ ++void ArenaStringAccessor::swap(ArenaStringAccessor other) noexcept { ++#if __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++ auto* tmp = representation().data; ++ *reinterpret_cast(_ptr) = other.representation().data; ++ *reinterpret_cast(other._ptr) = tmp; ++#else // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++ _ptr->swap(*other._ptr); ++#endif // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++} ++ ++void ArenaStringAccessor::resize(size_type new_size, value_type c) noexcept { ++ auto origin_size = size(); ++ auto buffer = qualified_buffer(new_size); ++ set_size_and_terminator(new_size); ++ if (new_size > origin_size) { ++ __builtin_memset(buffer + origin_size, c, new_size - origin_size); ++ } ++} ++ ++internal::StdStringRep& ArenaStringAccessor::representation() noexcept { ++#if __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++ return *(*reinterpret_cast(_ptr) - 1); ++#else // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++ return *reinterpret_cast(_ptr); ++#endif // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++} ++ ++ArenaStringAccessor::pointer ArenaStringAccessor::recreate_buffer( ++ size_type capacity) noexcept { ++#if __GLIBCXX__ ++ size_t buffer_size = capacity + 1; ++ buffer_size = (buffer_size + 7) & static_cast(-8); ++ capacity = buffer_size - 1; ++#if _GLIBCXX_USE_CXX11_ABI ++ auto* buffer = reinterpret_cast(_arena->AllocateAligned(buffer_size)); ++ __builtin_memcpy(buffer, data(), size()); ++ auto& rep = representation(); ++ rep.data = buffer; ++ rep.capacity = capacity; ++ return buffer; ++#else // !_GLIBCXX_USE_CXX11_ABI ++ auto* rep = reinterpret_cast( ++ _arena->AllocateAligned(sizeof(StdStringRep) + buffer_size)); ++ rep->capacity = capacity; ++ rep->refcount = -1; ++ __builtin_memcpy(rep->data, data(), size()); ++ *reinterpret_cast(_ptr) = rep->data; ++ return rep->data; ++#endif // !_GLIBCXX_USE_CXX11_ABI ++#else // !__GLIBCXX__ ++ capacity = (capacity + 16) & static_cast(-16); ++ auto* buffer = reinterpret_cast(_arena->AllocateAligned(capacity)); ++ __builtin_memcpy(buffer, data(), size()); ++ auto& rep = representation(); ++ rep.long_format.data = buffer; ++ rep.long_format.capacity = capacity + 1; ++ return rep.long_format.data; ++#endif // !__GLIBCXX__ ++} ++ ++ArenaStringAccessor::pointer ArenaStringAccessor::writable_buffer() noexcept { ++#if __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++ return representation().refcount <= 0 ? representation().data : &(*_ptr)[0]; ++#else // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++ return &(*_ptr)[0]; ++#endif // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++} ++ ++void ArenaStringAccessor::set_size(size_type size) noexcept { ++#if __GLIBCXX__ ++ auto& rep = representation(); ++ rep.size = size; ++#else // !__GLIBCXX__ ++ auto& rep = representation(); ++ if (rep.is_long()) { ++ rep.long_format.size = size; ++ } else { ++ rep.shot_format.size = size << 1; ++ } ++#endif // !__GLIBCXX__ ++} ++ ++void ArenaStringAccessor::set_size_and_terminator(size_type size) noexcept { ++#if __GLIBCXX__ ++ auto& rep = representation(); ++ rep.size = size; ++ rep.data[size] = '\0'; ++#else // !__GLIBCXX__ ++ auto& rep = representation(); ++ if (rep.is_long()) { ++ rep.long_format.size = size; ++ rep.long_format.data[size] = '\0'; ++ } else { ++ rep.shot_format.size = size << 1; ++ rep.shot_format.data[size] = '\0'; ++ } ++#endif // !__GLIBCXX__ ++} ++ ++void MaybeArenaStringAccessor::clear() noexcept { ++#if __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++ if (representation().refcount <= 0) { ++ ArenaStringAccessor::clear(); ++ return; ++ } ++#endif // !__GLIBCXX__ || _GLIBCXX_USE_CXX11_ABI ++ underlying()->clear(); ++} ++ ++} // namespace protobuf ++} // namespace google ++ ++#include "google/protobuf/port_undef.inc" +diff --git a/src/google/protobuf/arenastring_impl.h b/src/google/protobuf/arenastring_impl.h +new file mode 100644 +index 000000000..05324e276 +--- /dev/null ++++ b/src/google/protobuf/arenastring_impl.h +@@ -0,0 +1,522 @@ ++#pragma once ++ ++// Feature check macro ++#define GOOGLE_PROTOBUF_HAS_DONATED_STRING 1 ++ ++#include "google/protobuf/arena.h" ++#if defined(__has_include) && __has_include("google/protobuf/config.h") ++#include "google/protobuf/config.h" ++#endif ++ ++#include "absl/strings/internal/resize_uninitialized.h" ++#include "absl/strings/str_format.h" ++#include "google/protobuf/port_def.inc" ++ ++namespace google { ++namespace protobuf { ++ ++namespace internal { ++ ++#if __GLIBCXX__ ++#if _GLIBCXX_USE_CXX11_ABI ++struct StdStringRep { ++ char* data; ++ uint64_t size; ++ union { ++ uint64_t capacity; ++ char local[16]; ++ }; ++}; ++#else // !_GLIBCXX_USE_CXX11_ABI ++struct StdStringRep { ++ uint64_t size; ++ uint64_t capacity; ++ int32_t refcount; ++ uint32_t gap; ++ char data[0]; ++}; ++#endif // !_GLIBCXX_USE_CXX11_ABI ++#elif _LIBCPP_VERSION // && !__GLIBCXX__ ++#if _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT ++static_assert(false, "don not support _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT yet"); ++#endif // _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT ++#if _LIBCPP_BIG_ENDIAN ++static_assert(false, "don not support _LIBCPP_BIG_ENDIAN yet"); ++#endif // _LIBCPP_BIG_ENDIAN ++union StdStringRep { ++ struct { ++ typename ::std::string::size_type capacity; ++ typename ::std::string::size_type size; ++ typename ::std::string::pointer data; ++ } long_format; ++ struct { ++ uint8_t size; ++ typename ::std::string::value_type data[0]; ++ } shot_format; ++ ++ inline bool is_long() const noexcept { return shot_format.size & 0x01; } ++ ++ inline ::std::string::size_type long_capacity() const noexcept { ++ return long_format.capacity & ~static_cast<::std::string::size_type>(0x01); ++ } ++}; ++#endif // _LIBCPP_VERSION && !__GLIBCXX__ ++} // namespace internal ++ ++// Wrap a full arenastring pointer and arena it belongs to. ++// provide function make it look like a string* ++// ++// Full arenastring itself and it's dynamic content both placed on arena ++class ArenaStringAccessor { ++ public: ++ using value_type = ::std::string::value_type; ++ using traits_type = ::std::string::traits_type; ++ using allocator_type = ::std::string::allocator_type; ++ using size_type = ::std::string::size_type; ++ using difference_type = ::std::string::difference_type; ++ using reference = ::std::string::reference; ++ using const_reference = ::std::string::const_reference; ++ using pointer = ::std::string::pointer; ++ using const_pointer = ::std::string::const_pointer; ++ using iterator = pointer; ++ using const_iterator = const_pointer; ++ using reverse_iterator = ::std::reverse_iterator; ++ using const_reverse_iterator = ::std::reverse_iterator; ++ ++ using StdStringRep = internal::StdStringRep; ++ ++ // Disable default constructor and copy constructor ++ ArenaStringAccessor() = delete; ++ inline ArenaStringAccessor(ArenaStringAccessor&&) noexcept = default; ++ inline ArenaStringAccessor(const ArenaStringAccessor&) noexcept = default; ++ ArenaStringAccessor& operator=(ArenaStringAccessor&&) = delete; ++ ArenaStringAccessor& operator=(const ArenaStringAccessor&) = delete; ++ ~ArenaStringAccessor() noexcept = default; ++ ++ // Assign ++ inline ArenaStringAccessor& operator=(::absl::string_view other) noexcept { ++ return assign(other.data(), other.size()); ++ } ++ inline ArenaStringAccessor& assign(::absl::string_view other) noexcept { ++ return assign(other.data(), other.size()); ++ } ++ ArenaStringAccessor& assign(const_pointer data, size_type size) noexcept; ++ ++ // Element access ++ inline reference operator[](size_type position) noexcept { ++ return writable_buffer()[position]; ++ } ++ inline const_reference operator[](size_type position) const noexcept { ++ return data()[position]; ++ } ++ inline const_pointer data() const noexcept { return c_str(); } ++ inline const_pointer c_str() const noexcept { ++ return static_cast(_ptr)->c_str(); ++ } ++ inline operator ::absl::string_view() const noexcept { ++ return ::absl::string_view(data(), size()); ++ } ++ inline operator const ::std::string&() const noexcept { return *_ptr; } ++ ++ // Iterators ++ inline iterator begin() noexcept { return iterator(writable_buffer()); } ++ inline const_iterator cbegin() const noexcept { ++ return const_iterator(data()); ++ } ++ inline const_iterator end() noexcept { ++ return iterator(writable_buffer() + size()); ++ } ++ inline const_iterator cend() const noexcept { ++ return const_iterator(data() + size()); ++ } ++ ++ // Capacity ++ inline bool empty() const noexcept { return _ptr->empty(); } ++ inline size_type size() const noexcept { return _ptr->size(); } ++ void reserve(size_type required_capacity) noexcept; ++ inline size_type capacity() const noexcept { return _ptr->capacity(); } ++ ++ // Modifiers ++ inline void clear() noexcept { set_size_and_terminator(0); } ++ void push_back(value_type c) noexcept; ++ inline ArenaStringAccessor& append(::absl::string_view sv) noexcept { ++ return append(sv.data(), sv.size()); ++ } ++ ArenaStringAccessor& append(const_pointer append_data, ++ size_type append_size) noexcept; ++ inline ArenaStringAccessor& operator+=(char ch) noexcept { ++ push_back(ch); ++ return *this; ++ } ++ inline ArenaStringAccessor& operator+=(::absl::string_view sv) noexcept { ++ return append(sv.data(), sv.size()); ++ } ++ void resize(size_type new_size) noexcept { resize(new_size, '\0'); } ++ void resize(size_type new_size, value_type c) noexcept; ++ void swap(ArenaStringAccessor other) noexcept; ++ ++ // Operations ++ inline int compare(::absl::string_view other) const noexcept { ++ return static_cast<::absl::string_view>(*this).compare(other); ++ } ++ ++ //////////////////////////////////////////////////////////////////////////// ++ // Special function ++ inline static ArenaStringAccessor create(Arena* arena) noexcept { ++ auto* ptr = reinterpret_cast<::std::string*>( ++ arena->AllocateAligned(sizeof(::std::string))); ++ new (ptr)::std::string(); ++ return ArenaStringAccessor(arena, ptr); ++ } ++ ++ template ++ inline static ArenaStringAccessor create(Arena* arena, T&& value) noexcept { ++ return create(arena) = ::std::forward(value); ++ } ++ ++ // Clear function dont need arena ++ inline static void clear(::std::string* ptr) noexcept { ++ ArenaStringAccessor(nullptr, ptr).clear(); ++ } ++ ++ // Wwap function dont need arena ++ // but left and right must both on same arena ++ inline static void swap(::std::string* left, ::std::string* right) noexcept { ++ ArenaStringAccessor(nullptr, left) ++ .swap(ArenaStringAccessor(nullptr, right)); ++ } ++ ++ // Wrapper construct ++ inline ArenaStringAccessor(Arena* arena, ::std::string* ptr) noexcept ++ : _arena(arena), _ptr(ptr) {} ++ inline Arena* arena() const noexcept { return _arena; } ++ inline ::std::string* underlying() const noexcept { return _ptr; } ++ ++ // Support absl::strings_internal::STLStringResizeUninitialized ++ inline char* __resize_default_init(size_type new_size) noexcept { ++ auto buffer = qualified_buffer(new_size); ++ set_size_and_terminator(new_size); ++ return buffer; ++ } ++ ++ // Also support absl::Format(ArenaStringAccessor, ...) ++ inline operator ::absl::FormatRawSink() noexcept { ++ return ::absl::FormatRawSink(this); ++ } ++ //////////////////////////////////////////////////////////////////////////// ++ ++ protected: ++ StdStringRep& representation() noexcept; ++ ++ pointer recreate_buffer(size_type capacity) noexcept; ++ ++ pointer writable_buffer() noexcept; ++ ++ inline pointer qualified_buffer(size_type required_capacity, ++ size_type predict_capacity) noexcept { ++ return required_capacity <= capacity() ? writable_buffer() ++ : recreate_buffer(predict_capacity); ++ } ++ ++ inline pointer qualified_buffer(size_type required_capacity) noexcept { ++ return qualified_buffer(required_capacity, required_capacity); ++ } ++ ++ void set_size(size_type size) noexcept; ++ ++ void set_size_and_terminator(size_type size) noexcept; ++ ++ private: ++ // Support absl::Format(ArenaStringAccessor*, ...) ++ friend inline void AbslFormatFlush(ArenaStringAccessor* accessor, ++ ::absl::string_view sv) noexcept { ++ accessor->append(sv.data(), sv.size()); ++ } ++ ++ Arena* _arena; ++ ::std::string* _ptr; ++}; ++ ++inline bool operator==(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return *left.underlying() == *right.underlying(); ++} ++ ++inline bool operator==(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return left == *right.underlying(); ++} ++ ++inline bool operator==(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return *left.underlying() == right; ++} ++ ++inline bool operator!=(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return !(left == right); ++} ++ ++inline bool operator!=(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return !(left == right); ++} ++ ++inline bool operator!=(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return !(left == right); ++} ++ ++inline bool operator<(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return *left.underlying() < *right.underlying(); ++} ++ ++inline bool operator<(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return left < *right.underlying(); ++} ++ ++inline bool operator<(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return *left.underlying() < right; ++} ++ ++inline bool operator<=(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return *left.underlying() <= *right.underlying(); ++} ++ ++inline bool operator<=(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return left <= *right.underlying(); ++} ++ ++inline bool operator<=(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return *left.underlying() <= right; ++} ++ ++inline bool operator>(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return *left.underlying() > *right.underlying(); ++} ++ ++inline bool operator>(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return left > *right.underlying(); ++} ++ ++inline bool operator>(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return *left.underlying() > right; ++} ++ ++inline bool operator>=(const ArenaStringAccessor& left, ++ const ArenaStringAccessor& right) noexcept { ++ return *left.underlying() >= *right.underlying(); ++} ++ ++inline bool operator>=(::absl::string_view left, ++ const ArenaStringAccessor& right) noexcept { ++ return left >= *right.underlying(); ++} ++ ++inline bool operator>=(const ArenaStringAccessor& left, ++ ::absl::string_view right) noexcept { ++ return *left.underlying() >= right; ++} ++ ++class MaybeArenaStringAccessor : public ArenaStringAccessor { ++ public: ++ using ArenaStringAccessor::ArenaStringAccessor; ++ ++ MaybeArenaStringAccessor(const ArenaStringAccessor& other) noexcept ++ : ArenaStringAccessor(other) {} ++ ++ // Assign ++ template ++ inline MaybeArenaStringAccessor& operator=(T&& other) { ++ return assign(::std::forward(other)); ++ } ++ template ++ inline MaybeArenaStringAccessor& assign(T&& other) { ++ ::absl::string_view sv(::std::forward(other)); ++ return assign(sv.data(), sv.size()); ++ } ++ inline MaybeArenaStringAccessor& assign(const_pointer data, size_type size) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::assign(data, size); ++ } else { ++ underlying()->assign(data, size); ++ } ++ return *this; ++ } ++ // Deal with assign string specially. Try to keep copy on write state when ++ // using old abi ++ inline MaybeArenaStringAccessor& operator=(const ::std::string& other) { ++ return assign(other); ++ } ++ inline MaybeArenaStringAccessor& assign(const ::std::string& other) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::assign(other); ++ } else { ++ underlying()->assign(other); ++ } ++ return *this; ++ } ++ inline MaybeArenaStringAccessor& operator=(::std::string& other) { ++ return assign(static_cast(other)); ++ } ++ inline MaybeArenaStringAccessor& assign(::std::string& other) { ++ return assign(static_cast(other)); ++ } ++ inline MaybeArenaStringAccessor& operator=(::std::string&& other) { ++ return assign(::std::move(other)); ++ } ++ inline MaybeArenaStringAccessor& assign(::std::string&& other) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::assign(other); ++ } else { ++ underlying()->assign(::std::move(other)); ++ } ++ return *this; ++ } ++ template ++ inline MaybeArenaStringAccessor& operator=( ++ ::std::reference_wrapper other) { ++ return assign(other); ++ } ++ template ++ inline MaybeArenaStringAccessor& assign(::std::reference_wrapper other) { ++ return assign(other.get()); ++ } ++ ++ inline void reserve(size_type required_capacity) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::reserve(required_capacity); ++ } else if (required_capacity > capacity()) { ++ underlying()->reserve(required_capacity); ++ } ++ } ++ ++ void clear() noexcept; ++ ++ inline void push_back(value_type c) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::push_back(c); ++ } else { ++ underlying()->push_back(c); ++ } ++ } ++ ++ inline MaybeArenaStringAccessor& append(::absl::string_view sv) { ++ return append(sv.data(), sv.size()); ++ } ++ ++ inline MaybeArenaStringAccessor& append(const_pointer data, size_type size) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::append(data, size); ++ } else { ++ underlying()->append(data, size); ++ } ++ return *this; ++ } ++ ++ inline MaybeArenaStringAccessor& operator+=(char ch) noexcept { ++ push_back(ch); ++ return *this; ++ } ++ ++ inline MaybeArenaStringAccessor& operator+=(::absl::string_view sv) noexcept { ++ return append(sv.data(), sv.size()); ++ } ++ ++ inline void resize(size_type size) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::resize(size); ++ } else { ++ underlying()->resize(size); ++ } ++ } ++ inline void resize(size_type size, value_type c) { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::resize(size, c); ++ } else { ++ underlying()->resize(size, c); ++ } ++ } ++ ++ //////////////////////////////////////////////////////////////////////////// ++ // Special function ++ inline static MaybeArenaStringAccessor create(Arena* arena) { ++ if (arena != nullptr) { ++ return ArenaStringAccessor::create(arena); ++ } else { ++ return MaybeArenaStringAccessor(new ::std::string); ++ } ++ } ++ ++ template ++ inline static MaybeArenaStringAccessor create(Arena* arena, T&& value) { ++ return create(arena) = ::std::forward(value); ++ } ++ ++ inline static void clear(::std::string* ptr) noexcept { ++ MaybeArenaStringAccessor(ptr).clear(); ++ } ++ ++ // Add wrapper constructor for normal string ++ inline MaybeArenaStringAccessor(::std::string* string) noexcept ++ : ArenaStringAccessor(nullptr, string) {} ++ using ArenaStringAccessor::arena; ++ using ArenaStringAccessor::underlying; ++ ++ // Support absl::strings_internal::STLStringResizeUninitialized ++ inline void __resize_default_init(size_type new_size) noexcept { ++ if (arena() != nullptr) { ++ ArenaStringAccessor::__resize_default_init(new_size); ++ } else { ++ ::absl::strings_internal::STLStringResizeUninitialized(underlying(), ++ new_size); ++ } ++ } ++ ++ // Make operator* and operator-> both to self to imitate a string* ++ inline MaybeArenaStringAccessor* operator->() { return this; } ++ inline const MaybeArenaStringAccessor* operator->() const { return this; } ++ inline MaybeArenaStringAccessor& operator*() { return *this; } ++ inline const MaybeArenaStringAccessor& operator*() const { return *this; } ++ ++ inline void destroy() noexcept { ++ if (arena() == nullptr) { ++ delete underlying(); ++ } ++ } ++ ++ // Also support absl::Format(MaybeArenaStringAccessor, ...) ++ inline operator ::absl::FormatRawSink() noexcept { ++ return ::absl::FormatRawSink(this); ++ } ++ //////////////////////////////////////////////////////////////////////////// ++ ++ private: ++ // Support absl::Format ++ friend inline void AbslFormatFlush(MaybeArenaStringAccessor* accessor, ++ ::absl::string_view sv) noexcept { ++ accessor->append(sv.data(), sv.size()); ++ } ++}; ++ ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++using MutableStringType = MaybeArenaStringAccessor; ++using MutableStringReferenceType = MaybeArenaStringAccessor; ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++using MutableStringType = ::std::string*; ++using MutableStringReferenceType = ::std::string&; ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ ++} // namespace protobuf ++} // namespace google ++ ++#include "google/protobuf/port_undef.inc" +diff --git a/src/google/protobuf/arenastring_impl_unittest.cc b/src/google/protobuf/arenastring_impl_unittest.cc +new file mode 100644 +index 000000000..a14cc312b +--- /dev/null ++++ b/src/google/protobuf/arenastring_impl_unittest.cc +@@ -0,0 +1,1512 @@ ++#include "google/protobuf/arenastring_impl.h" ++ ++#include "google/protobuf/arena.h" ++#include "google/protobuf/descriptor.pb.h" ++#include "google/protobuf/reflection.h" ++#include "google/protobuf/unittest_arenastring.pb.h" ++#include "google/protobuf/unittest_arenastring_mutable.pb.h" ++#include "google/protobuf/unittest_proto3_arenastring.pb.h" ++#include "google/protobuf/unittest_proto3_arenastring_mutable.pb.h" ++#include "gtest/gtest.h" ++ ++using ::google::protobuf::Arena; ++using ::google::protobuf::ArenaOptions; ++using ::google::protobuf::MaybeArenaStringAccessor; ++using ::google::protobuf::RepeatedPtrField; ++ ++using ::proto2_arenastring_unittest::ArenaProto2; ++using ::proto2_arenastring_unittest::ArenaProto2Extension; ++using ::proto2_arenastring_unittest::Proto2; ++using ::proto2_arenastring_unittest::Proto2Extension; ++using ::proto3_arenastring_unittest::ArenaProto3; ++using ::proto3_arenastring_unittest::Proto3; ++ ++class ArenaStringTest : public ::testing::Test { ++ public: ++ virtual void SetUp() { ++ ArenaOptions options; ++ options.initial_block = buffer; ++ options.initial_block_size = sizeof(buffer); ++ arena = new Arena(options); ++ } ++ ++ virtual void TearDown() { delete arena; } ++ ++ void assert_address_on_arena(const void* address, bool on) { ++ if (on) { ++ ASSERT_GE(address, buffer); ++ ASSERT_LT(address, buffer + sizeof(buffer)); ++ } else { ++ ASSERT_TRUE(address < buffer || address >= buffer + sizeof(buffer)); ++ } ++ } ++ ++ void assert_on_arena(const ::std::string& string, bool on, bool content_on) { ++ assert_address_on_arena(&string, on); ++ if (string.capacity() > 0) { ++ assert_address_on_arena(string.c_str(), content_on); ++ assert_address_on_arena(string.c_str() + string.capacity() - 1, ++ content_on); ++ } ++ } ++ ++ void assert_on_arena(const ::std::string& string, bool on) { ++ assert_on_arena(string, on, on); ++ } ++ ++ char buffer[1L << 20]; ++ Arena* arena; ++}; ++ ++::std::string tiny_string = ::std::string(::std::string().capacity(), 'x'); ++::std::string short_string = ::std::string(::std::string().capacity() + 1, 'y'); ++::std::string long_string = ::std::string(::std::string().capacity() + 64, 'z'); ++ ++template ++static void create_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ { ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ ASSERT_TRUE(accessor.empty()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++ } ++ { ++ auto accessor = MaybeArenaStringAccessor::create(arena, tiny_string); ++ ASSERT_EQ(tiny_string, accessor); ++ ASSERT_EQ(tiny_string, accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++ } ++ { ++ auto accessor = MaybeArenaStringAccessor::create(arena, short_string); ++ ASSERT_EQ(short_string, accessor); ++ ASSERT_EQ(short_string, accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++ } ++ { ++ auto accessor = MaybeArenaStringAccessor::create(arena, long_string); ++ ASSERT_EQ(long_string, accessor); ++ ASSERT_EQ(long_string, accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++ } ++} ++TEST_F(ArenaStringTest, create_on_arena) { ++ create_on_arena(*this, arena); ++ create_on_arena(*this, nullptr); ++} ++ ++template ++static void correct_data_size_and_capacity(T& t, Arena* arena) { ++ for (size_t i = 0; i < long_string.size(); ++i) { ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ accessor.assign(long_string.c_str(), i); ++ ASSERT_EQ(i, ::strlen(accessor.c_str())); ++ ASSERT_EQ(0, ::memcmp(long_string.c_str(), accessor.c_str(), i)); ++ ASSERT_EQ(i, accessor.size()); ++ ASSERT_LE(i, accessor.capacity()); ++ accessor.destroy(); ++ } ++} ++TEST_F(ArenaStringTest, correct_data_size_and_capacity) { ++ correct_data_size_and_capacity(*this, arena); ++ correct_data_size_and_capacity(*this, nullptr); ++} ++ ++template ++static void assign_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ accessor = tiny_string; ++ ASSERT_EQ(tiny_string, accessor); ++ ASSERT_STREQ(tiny_string.c_str(), accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor = short_string; ++ ASSERT_EQ(short_string, accessor); ++ ASSERT_STREQ(short_string.c_str(), accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor = long_string; ++ ASSERT_EQ(long_string, accessor); ++ ASSERT_STREQ(long_string.c_str(), accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor = short_string; ++ ASSERT_EQ(short_string, accessor); ++ ASSERT_STREQ(short_string.c_str(), accessor.c_str()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++} ++TEST_F(ArenaStringTest, assign_on_arena) { ++ assign_on_arena(*this, arena); ++ assign_on_arena(*this, nullptr); ++} ++ ++template ++static void reserve_keep_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ accessor = tiny_string; ++ accessor.reserve(short_string.size()); ++ ASSERT_EQ(tiny_string, accessor); ++ ASSERT_STREQ(tiny_string.c_str(), accessor.c_str()); ++ ASSERT_LE(short_string.size(), accessor.capacity()); ++ assert_on_arena(accessor); ++ accessor.reserve(long_string.size()); ++ ASSERT_EQ(tiny_string, accessor); ++ ASSERT_STREQ(tiny_string.c_str(), accessor.c_str()); ++ ASSERT_LE(long_string.size(), accessor.capacity()); ++ assert_on_arena(accessor); ++ accessor.destroy(); ++} ++TEST_F(ArenaStringTest, reserve_keep_on_arena) { ++ reserve_keep_on_arena(*this, arena); ++ reserve_keep_on_arena(*this, nullptr); ++} ++ ++#if __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++TEST_F(ArenaStringTest, do_not_copy_on_write) { ++ auto std_string = new ::std::string(); ++ auto half_arena_string = Arena::Create<::std::string>(arena); ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ auto* arena_string = &(const ::std::string&)accessor; ++ ASSERT_EQ(::std::string(*std_string).c_str(), std_string->c_str()); ++ ASSERT_EQ(::std::string(*half_arena_string).c_str(), ++ half_arena_string->c_str()); ++ ASSERT_EQ(::std::string(*arena_string).c_str(), arena_string->c_str()); ++ std_string->assign(long_string); ++ half_arena_string->assign(long_string); ++ accessor->assign(long_string); ++ ASSERT_EQ(::std::string(*std_string).c_str(), std_string->c_str()); ++ ASSERT_EQ(::std::string(*half_arena_string).c_str(), ++ half_arena_string->c_str()); ++ ASSERT_NE(::std::string(*arena_string).c_str(), arena_string->c_str()); ++ std_string->clear(); ++ half_arena_string->clear(); ++ accessor->clear(); ++ ASSERT_EQ(::std::string(*std_string).c_str(), std_string->c_str()); ++ ASSERT_EQ(::std::string(*half_arena_string).c_str(), ++ half_arena_string->c_str()); ++ ASSERT_NE(::std::string(*arena_string).c_str(), arena_string->c_str()); ++ std_string->assign(short_string); ++ half_arena_string->assign(short_string); ++ accessor->assign(short_string); ++ ASSERT_EQ(::std::string(*std_string).c_str(), std_string->c_str()); ++ ASSERT_EQ(::std::string(*half_arena_string).c_str(), ++ half_arena_string->c_str()); ++ ASSERT_NE(::std::string(*arena_string).c_str(), arena_string->c_str()); ++ std_string->append(long_string); ++ half_arena_string->append(long_string); ++ accessor->append(long_string); ++ ASSERT_EQ(::std::string(*std_string).c_str(), std_string->c_str()); ++ ASSERT_EQ(::std::string(*half_arena_string).c_str(), ++ half_arena_string->c_str()); ++ ASSERT_NE(::std::string(*arena_string).c_str(), arena_string->c_str()); ++ delete std_string; ++} ++#endif // __GLIBCXX__ && !_GLIBCXX_USE_CXX11_ABI ++ ++TEST_F(ArenaStringTest, support_resize) { ++ auto accessor = MaybeArenaStringAccessor::create(arena, "10086"); ++ accessor.resize(4); ++ auto data = &accessor[0]; ++ ASSERT_EQ(4, accessor.size()); ++ ASSERT_EQ("1008", accessor); ++ ASSERT_EQ(data, accessor.data()); ++ accessor.resize(2); ++ data = &accessor[0]; ++ ASSERT_EQ(2, accessor.size()); ++ ASSERT_EQ("10", accessor); ++ ASSERT_EQ(data, accessor.data()); ++ accessor.resize(4); ++ data = &accessor[0]; ++ ASSERT_EQ(4, accessor.size()); ++ ASSERT_EQ(::absl::string_view("10\0\0", 4), accessor); ++ ASSERT_EQ(data, accessor.data()); ++ accessor.destroy(); ++} ++ ++TEST_F(ArenaStringTest, support_resize_uninitialized) { ++ auto accessor = MaybeArenaStringAccessor::create(arena, "10086"); ++ ::absl::strings_internal::STLStringResizeUninitialized(&accessor, 4); ++ auto data = &accessor[0]; ++ ASSERT_EQ(4, accessor.size()); ++ ASSERT_EQ("1008", accessor); ++ ASSERT_EQ(data, accessor.data()); ++ ::absl::strings_internal::STLStringResizeUninitialized(&accessor, 2); ++ data = &accessor[0]; ++ ASSERT_EQ(2, accessor.size()); ++ ASSERT_EQ("10", accessor); ++ ASSERT_EQ(data, accessor.data()); ++ ::absl::strings_internal::STLStringResizeUninitialized(&accessor, 4); ++ data = &accessor[0]; ++ ASSERT_EQ(4, accessor.size()); ++ ASSERT_EQ(::absl::string_view("10\08", 4), accessor); ++ ASSERT_EQ(data, accessor.data()); ++ accessor.destroy(); ++} ++ ++TEST_F(ArenaStringTest, support_absl_format) { ++ auto accessor = MaybeArenaStringAccessor::create(arena, "hello world"); ++ ::absl::Format(accessor, " +%d", 10086); ++ ASSERT_EQ("hello world +10086", accessor); ++ accessor.destroy(); ++} ++ ++template ++static void alter_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto accessor = MaybeArenaStringAccessor::create(arena); ++ accessor.push_back('x'); ++ assert_on_arena(accessor); ++ accessor.clear(); ++ assert_on_arena(accessor); ++ accessor.append(tiny_string); ++ assert_on_arena(accessor); ++ accessor.append(short_string); ++ assert_on_arena(accessor); ++ ::std::string tmp_string(long_string.c_str()); ++ auto tmp_ptr = tmp_string.c_str(); ++ accessor = ::std::move(tmp_string); ++ ASSERT_EQ(long_string, accessor); ++ if (arena != nullptr) ++ ASSERT_NE(tmp_ptr, accessor.c_str()); ++ else { ++ ASSERT_EQ(tmp_ptr, accessor.c_str()); ++ } ++ accessor.destroy(); ++} ++TEST_F(ArenaStringTest, alter_on_arena) { ++ alter_on_arena(*this, arena); ++ alter_on_arena(*this, nullptr); ++} ++ ++template ++static void direct_set_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ ASSERT_EQ(short_string, m->s()); ++ assert_on_arena(m->s()); ++ m->set_b(long_string.c_str(), long_string.size()); ++ ASSERT_EQ(long_string, m->b()); ++ assert_on_arena(m->b()); ++ m->set_os(long_string); ++ ASSERT_EQ(long_string, m->os()); ++ assert_on_arena(m->os()); ++ m->set_ob(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->ob()); ++ assert_on_arena(m->ob()); ++ m->add_rs(short_string); ++ ASSERT_EQ(short_string, m->rs(0)); ++ assert_on_arena(m->rs(0)); ++ m->add_rs(long_string); ++ ASSERT_EQ(long_string, m->rs(1)); ++ assert_on_arena(m->rs(1)); ++ m->add_rb(long_string.c_str(), long_string.size()); ++ ASSERT_EQ(long_string, m->rb(0)); ++ assert_on_arena(m->rb(0)); ++ m->add_rb(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->rb(1)); ++ assert_on_arena(m->rb(1)); ++ m->set_ons(short_string); ++ ASSERT_EQ(short_string, m->ons()); ++ assert_on_arena(m->ons()); ++ m->set_onb(long_string.c_str(), long_string.size()); ++ ASSERT_FALSE(m->has_ons()); ++ ASSERT_TRUE(m->ons().empty()); ++ ASSERT_EQ(long_string, m->onb()); ++ assert_on_arena(m->onb()); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, direct_set_on_arena) { ++ direct_set_on_arena(*this, arena); ++ direct_set_on_arena(*this, nullptr); ++ direct_set_on_arena(*this, arena); ++ direct_set_on_arena(*this, nullptr); ++} ++ ++template ++static void direct_set_on_arena_pb2(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ ASSERT_EQ(short_string, m->s()); ++ assert_on_arena(m->s()); ++ m->set_b(long_string.c_str(), long_string.size()); ++ ASSERT_EQ(long_string, m->b()); ++ assert_on_arena(m->b()); ++ m->set_qs(long_string); ++ ASSERT_EQ(long_string, m->qs()); ++ assert_on_arena(m->qs()); ++ m->set_qb(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->qb()); ++ assert_on_arena(m->qb()); ++ m->set_ds(short_string); ++ ASSERT_EQ(short_string, m->ds()); ++ assert_on_arena(m->ds()); ++ m->set_db(long_string.c_str(), long_string.size()); ++ ASSERT_EQ(long_string, m->db()); ++ assert_on_arena(m->db()); ++ m->add_rs(long_string); ++ ASSERT_EQ(long_string, m->rs(0)); ++ assert_on_arena(m->rs(0)); ++ m->add_rs(short_string); ++ ASSERT_EQ(short_string, m->rs(1)); ++ assert_on_arena(m->rs(1)); ++ m->add_rb(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->rb(0)); ++ assert_on_arena(m->rb(0)); ++ m->add_rb(long_string.c_str(), long_string.size()); ++ ASSERT_EQ(long_string, m->rb(1)); ++ assert_on_arena(m->rb(1)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, direct_set_on_arena_pb2) { ++ direct_set_on_arena_pb2(*this, arena); ++ direct_set_on_arena_pb2(*this, nullptr); ++ direct_set_on_arena_pb2(*this, arena); ++ direct_set_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void set_again_keep_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ m->set_s(long_string); ++ ASSERT_EQ(long_string, m->s()); ++ assert_on_arena(m->s()); ++ m->set_b(long_string.c_str(), long_string.size()); ++ m->set_b(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->b()); ++ assert_on_arena(m->b()); ++ m->set_os(short_string); ++ m->set_os(long_string); ++ ASSERT_EQ(long_string, m->os()); ++ assert_on_arena(m->os()); ++ m->set_ob(long_string.c_str(), long_string.size()); ++ m->set_ob(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->ob()); ++ assert_on_arena(m->ob()); ++ m->set_ons(short_string); ++ m->set_ons(long_string); ++ ASSERT_EQ(long_string, m->ons()); ++ assert_on_arena(m->ons()); ++ m->set_onb(long_string.c_str(), long_string.size()); ++ m->set_onb(short_string.c_str(), short_string.size()); ++ ASSERT_FALSE(m->has_ons()); ++ ASSERT_TRUE(m->ons().empty()); ++ ASSERT_EQ(short_string, m->onb()); ++ assert_on_arena(m->onb()); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, set_again_keep_on_arena) { ++ set_again_keep_on_arena(*this, arena); ++ set_again_keep_on_arena(*this, nullptr); ++ set_again_keep_on_arena(*this, arena); ++ set_again_keep_on_arena(*this, nullptr); ++} ++ ++template ++static void set_again_keep_on_arena_pb2(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ m->set_s(long_string); ++ ASSERT_EQ(long_string, m->s()); ++ assert_on_arena(m->s()); ++ m->set_b(long_string.c_str(), long_string.size()); ++ m->set_b(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->b()); ++ assert_on_arena(m->b()); ++ m->set_qs(short_string); ++ m->set_qs(long_string); ++ ASSERT_EQ(long_string, m->qs()); ++ assert_on_arena(m->qs()); ++ m->set_qb(long_string.c_str(), long_string.size()); ++ m->set_qb(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->qb()); ++ assert_on_arena(m->qb()); ++ m->set_ds(short_string); ++ m->set_ds(long_string); ++ ASSERT_EQ(long_string, m->ds()); ++ assert_on_arena(m->ds()); ++ m->set_db(long_string.c_str(), long_string.size()); ++ m->set_db(short_string.c_str(), short_string.size()); ++ ASSERT_EQ(short_string, m->db()); ++ assert_on_arena(m->db()); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, set_again_keep_on_arena_pb2) { ++ set_again_keep_on_arena_pb2(*this, arena); ++ set_again_keep_on_arena_pb2(*this, nullptr); ++ set_again_keep_on_arena_pb2(*this, arena); ++ set_again_keep_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void clear_keep_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ auto* ps = &m->s(); ++ m->set_b(long_string); ++ auto* pb = &m->b(); ++ m->add_rs(short_string); ++ auto* prs = &m->rs(0); ++ m->add_rb(long_string); ++ auto* prb = &m->rb(0); ++ m->set_ons(short_string); ++ m->set_onb(long_string); ++ m->Clear(); ++ m->set_s(long_string); ++ assert_on_arena(m->s()); ++ ASSERT_EQ(ps, &m->s()); ++ m->set_b(short_string); ++ assert_on_arena(m->b()); ++ ASSERT_EQ(pb, &m->b()); ++ m->add_rs(long_string); ++ assert_on_arena(m->rs(0)); ++ ASSERT_EQ(prs, &m->rs(0)); ++ m->add_rb(short_string); ++ assert_on_arena(m->rb(0)); ++ ASSERT_EQ(prb, &m->rb(0)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, clear_keep_on_arena) { ++ clear_keep_on_arena(*this, arena); ++ clear_keep_on_arena(*this, nullptr); ++ clear_keep_on_arena(*this, arena); ++ clear_keep_on_arena(*this, nullptr); ++ clear_keep_on_arena(*this, arena); ++ clear_keep_on_arena(*this, nullptr); ++ clear_keep_on_arena(*this, arena); ++ clear_keep_on_arena(*this, nullptr); ++} ++ ++template ++static void clear_keep_on_arena_pb2(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_ds(short_string); ++ auto* pds = &m->ds(); ++ m->set_db(long_string); ++ auto* pdb = &m->db(); ++ m->set_qs(short_string); ++ auto* pqs = &m->qs(); ++ m->set_qb(long_string); ++ auto* pqb = &m->qb(); ++ m->Clear(); ++ m->set_ds(long_string); ++ assert_on_arena(m->ds()); ++ ASSERT_EQ(pds, &m->ds()); ++ m->set_db(short_string); ++ assert_on_arena(m->db()); ++ ASSERT_EQ(pdb, &m->db()); ++ m->set_qs(long_string); ++ assert_on_arena(m->qs()); ++ ASSERT_EQ(pqs, &m->qs()); ++ m->set_qb(short_string); ++ assert_on_arena(m->qb()); ++ ASSERT_EQ(pqb, &m->qb()); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, clear_keep_on_arena_pb2) { ++ clear_keep_on_arena_pb2(*this, arena); ++ clear_keep_on_arena_pb2(*this, nullptr); ++ clear_keep_on_arena_pb2(*this, arena); ++ clear_keep_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void parse_and_merge_on_arena(T& t, Arena* farena, Arena* tarena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, tarena, tarena); ++ }; ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena( ++ s, tarena, ++ tarena && ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string()); ++ }; ++ ::std::string string; ++ auto* fm = Arena::CreateMessage(farena); ++ auto* tm = Arena::CreateMessage(tarena); ++ fm->set_s(short_string); ++ fm->set_b(long_string); ++ fm->set_os(short_string); ++ fm->set_ob(long_string); ++ fm->set_ons(short_string); ++ fm->set_onb(long_string); ++ fm->add_rs(short_string); ++ fm->add_rs(long_string); ++ fm->add_rb(long_string); ++ fm->add_rb(short_string); ++ fm->SerializeToString(&string); ++ ASSERT_TRUE(fm->SerializeToString(&string)); ++ ASSERT_TRUE(tm->ParseFromString(string)); ++ ASSERT_EQ(short_string, tm->s()); ++ assert_on_arena(tm->s()); ++ ASSERT_EQ(long_string, tm->b()); ++ assert_on_arena(tm->b()); ++ ASSERT_EQ(short_string, tm->os()); ++ assert_on_arena(tm->os()); ++ ASSERT_EQ(long_string, tm->ob()); ++ assert_on_arena(tm->ob()); ++ ASSERT_EQ(long_string, tm->onb()); ++ assert_on_arena(tm->onb()); ++ ASSERT_EQ(short_string, tm->rs(0)); ++ assert_on_arena(tm->rs(0)); ++ ASSERT_EQ(long_string, tm->rs(1)); ++ assert_on_arena(tm->rs(1)); ++ ASSERT_EQ(long_string, tm->rb(0)); ++ assert_on_arena(tm->rb(0)); ++ ASSERT_EQ(short_string, tm->rb(1)); ++ assert_on_arena(tm->rb(1)); ++ tm->mutable_s()->assign(short_string); ++ assert_mutable_on_arena(tm->s()); ++ tm->mutable_rs(0)->assign(long_string); ++ tm->mutable_rb(1)->assign(long_string); ++ ASSERT_TRUE(tm->ParseFromString(string)); ++ ASSERT_EQ(short_string, tm->s()); ++ assert_mutable_on_arena(tm->s()); ++ ASSERT_EQ(long_string, tm->b()); ++ assert_on_arena(tm->b()); ++ ASSERT_EQ(long_string, tm->onb()); ++ assert_on_arena(tm->onb()); ++ ASSERT_EQ(short_string, tm->rs(0)); ++ assert_mutable_on_arena(tm->rs(0)); ++ ASSERT_EQ(long_string, tm->rs(1)); ++ assert_on_arena(tm->rs(1)); ++ ASSERT_EQ(long_string, tm->rb(0)); ++ assert_on_arena(tm->rb(0)); ++ ASSERT_EQ(short_string, tm->rb(1)); ++ assert_mutable_on_arena(tm->rb(1)); ++ tm->CopyFrom(*fm); ++ ASSERT_EQ(short_string, tm->s()); ++ assert_mutable_on_arena(tm->s()); ++ ASSERT_EQ(long_string, tm->b()); ++ assert_on_arena(tm->b()); ++ ASSERT_EQ(long_string, tm->onb()); ++ assert_on_arena(tm->onb()); ++ ASSERT_EQ(short_string, tm->rs(0)); ++ assert_mutable_on_arena(tm->rs(0)); ++ ASSERT_EQ(long_string, tm->rs(1)); ++ assert_on_arena(tm->rs(1)); ++ ASSERT_EQ(long_string, tm->rb(0)); ++ assert_on_arena(tm->rb(0)); ++ ASSERT_EQ(short_string, tm->rb(1)); ++ assert_mutable_on_arena(tm->rb(1)); ++ if (!tarena) { ++ delete tm; ++ } ++ if (!farena) { ++ delete fm; ++ } ++} ++TEST_F(ArenaStringTest, parse_and_merge_on_arena) { ++ parse_and_merge_on_arena(*this, arena, arena); ++ parse_and_merge_on_arena(*this, arena, nullptr); ++ parse_and_merge_on_arena(*this, nullptr, arena); ++ parse_and_merge_on_arena(*this, nullptr, nullptr); ++ parse_and_merge_on_arena(*this, arena, arena); ++ parse_and_merge_on_arena(*this, arena, nullptr); ++ parse_and_merge_on_arena(*this, nullptr, arena); ++ parse_and_merge_on_arena(*this, nullptr, nullptr); ++} ++ ++template ++static void swap_on_arena(T& t, Arena* farena, Arena* tarena) { ++ auto assert_nn_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, tarena, tarena); ++ }; ++ auto assert_nm_on_arena = [&](const ::std::string& s) { ++ if (tarena != farena) { ++ t.assert_on_arena(s, tarena, tarena); ++ } else { ++ t.assert_on_arena(s, tarena, ++ tarena && M().GetDescriptor() ++ ->file() ++ ->options() ++ .cc_mutable_donated_string()); ++ } ++ }; ++ auto assert_mn_on_arena = [&](const ::std::string& s) { ++ if (tarena != farena) { ++ t.assert_on_arena(s, tarena, tarena); ++ } else { ++ t.assert_on_arena(s, tarena, tarena); ++ } ++ }; ++ auto assert_mm_on_arena = [&](const ::std::string& s) { ++ if (tarena != farena) { ++ t.assert_on_arena(s, tarena, tarena); ++ } else { ++ t.assert_on_arena(s, tarena, ++ tarena && M().GetDescriptor() ++ ->file() ++ ->options() ++ .cc_mutable_donated_string()); ++ } ++ }; ++ auto* fm = Arena::CreateMessage(farena); ++ auto* tm = Arena::CreateMessage(tarena); ++ fm->set_s(short_string); ++ fm->mutable_b()->assign(long_string); ++ fm->mutable_ons()->assign(short_string); ++ fm->add_rs(short_string); ++ fm->add_rs()->assign(long_string); ++ fm->add_rb(long_string); ++ fm->add_rb()->assign(short_string); ++ ++ tm->set_s(long_string); ++ tm->set_b(short_string); ++ tm->set_onb(long_string); ++ tm->add_rs(long_string); ++ tm->add_rs(short_string); ++ tm->add_rb()->assign(short_string); ++ tm->add_rb()->assign(long_string); ++ ++ tm->Swap(fm); ++ ASSERT_EQ(short_string, tm->s()); ++ assert_nn_on_arena(tm->s()); ++ ASSERT_EQ(long_string, tm->b()); ++ assert_nm_on_arena(tm->b()); ++ ASSERT_EQ(short_string, tm->ons()); ++ assert_nm_on_arena(tm->ons()); ++ ASSERT_EQ(short_string, tm->rs(0)); ++ assert_nn_on_arena(tm->rs(0)); ++ ASSERT_EQ(long_string, tm->rs(1)); ++ assert_nm_on_arena(tm->rs(1)); ++ ASSERT_EQ(long_string, tm->rb(0)); ++ assert_mn_on_arena(tm->rb(0)); ++ ASSERT_EQ(short_string, tm->rb(1)); ++ assert_mm_on_arena(tm->rb(1)); ++ if (!tarena) { ++ delete tm; ++ } ++ if (!farena) { ++ delete fm; ++ } ++ fm = Arena::CreateMessage(farena); ++ tm = Arena::CreateMessage(tarena); ++ fm->mutable_s()->assign(short_string); ++ fm->set_b(long_string); ++ tm->mutable_s()->assign(long_string); ++ tm->mutable_b()->assign(short_string); ++ ++ tm->Swap(fm); ++ ASSERT_EQ(short_string, tm->s()); ++ assert_mm_on_arena(tm->s()); ++ ASSERT_EQ(long_string, tm->b()); ++ assert_mn_on_arena(tm->b()); ++ if (!tarena) { ++ delete tm; ++ } ++ if (!farena) { ++ delete fm; ++ } ++} ++TEST_F(ArenaStringTest, swap_on_arena) { ++ swap_on_arena(*this, arena, arena); ++ swap_on_arena(*this, arena, nullptr); ++ swap_on_arena(*this, nullptr, arena); ++ swap_on_arena(*this, nullptr, nullptr); ++ swap_on_arena(*this, arena, arena); ++ swap_on_arena(*this, arena, nullptr); ++ swap_on_arena(*this, nullptr, arena); ++ swap_on_arena(*this, nullptr, nullptr); ++ swap_on_arena(*this, arena, arena); ++ swap_on_arena(*this, arena, nullptr); ++ swap_on_arena(*this, nullptr, arena); ++ swap_on_arena(*this, nullptr, nullptr); ++ swap_on_arena(*this, arena, arena); ++ swap_on_arena(*this, arena, nullptr); ++ swap_on_arena(*this, nullptr, arena); ++ swap_on_arena(*this, nullptr, nullptr); ++} ++ ++template ++static void set_allocated_on_arena(T& t, Arena* arena) { ++ auto cc_mutable_donated_string = ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string(); ++ auto m = Arena::CreateMessage(arena); ++ { ++ auto s = new ::std::string{short_string}; ++ auto c = s->c_str(); ++ m->set_allocated_s(s); ++ ASSERT_NE(s, &m->s()); ++ ASSERT_EQ(c, m->s().c_str()); ++ t.assert_on_arena(m->s(), arena && cc_mutable_donated_string, false); ++ auto cs = &m->s(); ++ c = cs->c_str(); ++ m->mutable_s()->assign(long_string); ++ ASSERT_EQ(cs, &m->s()); ++ ASSERT_NE(c, m->s().c_str()); ++ t.assert_on_arena(m->s(), arena && cc_mutable_donated_string, false); ++ } ++ { ++ auto s = new ::std::string{short_string}; ++ auto c = s->c_str(); ++ m->set_b(long_string); ++ m->set_allocated_b(s); ++ ASSERT_NE(s, &m->b()); ++ ASSERT_EQ(c, m->b().c_str()); ++ t.assert_on_arena(m->b(), arena && cc_mutable_donated_string, false); ++ } ++ { ++ auto s = new ::std::string{long_string}; ++ auto c = s->c_str(); ++ m->mutable_ons()->assign(short_string); ++ m->set_allocated_ons(s); ++ ASSERT_EQ(s, &m->ons()); ++ ASSERT_EQ(c, m->ons().c_str()); ++ t.assert_on_arena(m->ons(), false, false); ++ } ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, set_allocated_on_arena) { ++ set_allocated_on_arena(*this, arena); ++ set_allocated_on_arena(*this, nullptr); ++ set_allocated_on_arena(*this, arena); ++ set_allocated_on_arena(*this, nullptr); ++ set_allocated_on_arena(*this, arena); ++ set_allocated_on_arena(*this, nullptr); ++ set_allocated_on_arena(*this, arena); ++ set_allocated_on_arena(*this, nullptr); ++} ++ ++template ++static void release_on_arena(T& t, Arena* arena) { ++ auto cc_mutable_donated_string = ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string(); ++ auto m = Arena::CreateMessage(arena); ++ { ++ m->set_s(long_string); ++ auto s = &m->s(); ++ auto c = s->c_str(); ++ auto r = m->release_s(); ++ if (arena || cc_mutable_donated_string) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(long_string, *r); ++ delete r; ++ } ++ { ++ m->set_os(short_string); ++ auto s = &m->os(); ++ auto c = s->c_str(); ++ auto r = m->release_os(); ++ if (arena || cc_mutable_donated_string) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(short_string, *r); ++ delete r; ++ } ++ { ++ m->set_ons(long_string); ++ auto s = &m->ons(); ++ auto c = s->c_str(); ++ auto r = m->release_ons(); ++ if (arena) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(long_string, *r); ++ delete r; ++ } ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, release_on_arena) { ++ release_on_arena(*this, arena); ++ release_on_arena(*this, nullptr); ++ release_on_arena(*this, arena); ++ release_on_arena(*this, nullptr); ++} ++ ++template ++static void release_on_arena_pb2(T& t, Arena* arena) { ++ auto cc_mutable_donated_string = ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string(); ++ auto m = Arena::CreateMessage(arena); ++ { ++ m->set_s(long_string); ++ auto s = &m->s(); ++ auto c = s->c_str(); ++ auto r = m->release_s(); ++ if (arena || cc_mutable_donated_string) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(long_string, *r); ++ delete r; ++ } ++ { ++ m->set_qs(short_string); ++ auto s = &m->qs(); ++ auto c = s->c_str(); ++ auto r = m->release_qs(); ++ if (arena || cc_mutable_donated_string) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(short_string, *r); ++ delete r; ++ } ++ { ++ m->set_ds(long_string); ++ auto s = &m->ds(); ++ auto c = s->c_str(); ++ auto r = m->release_ds(); ++ if (arena) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(long_string, *r); ++ delete r; ++ } ++ { ++ m->set_ons(short_string); ++ auto s = &m->ons(); ++ auto c = s->c_str(); ++ auto r = m->release_ons(); ++ if (arena) { ++ ASSERT_NE(s, r); ++ } else { ++ ASSERT_EQ(s, r); ++ } ++ if (arena) { ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(c, r->c_str()); ++ } ++ ASSERT_EQ(short_string, *r); ++ delete r; ++ } ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, release_on_arena_pb2) { ++ release_on_arena_pb2(*this, arena); ++ release_on_arena_pb2(*this, nullptr); ++ release_on_arena_pb2(*this, arena); ++ release_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void reflect_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ auto* r = m->GetReflection(); ++ auto* d = m->GetDescriptor(); ++ r->SetString(m, d->FindFieldByName("s"), long_string); ++ assert_on_arena(m->s()); ++ assert_on_arena(r->GetStringReference(*m, d->FindFieldByName("s"), nullptr)); ++ r->SetString(m, d->FindFieldByName("os"), long_string); ++ assert_on_arena(m->os()); ++ assert_on_arena(r->GetStringReference(*m, d->FindFieldByName("os"), nullptr)); ++ r->AddString(m, d->FindFieldByName("rs"), long_string); ++ assert_on_arena(m->rs(0)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 0, nullptr)); ++ r->SetRepeatedString(m, d->FindFieldByName("rs"), 0, ++ long_string + long_string); ++ assert_on_arena(m->rs(0)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 0, nullptr)); ++ r->template GetMutableRepeatedFieldRef<::std::string>( ++ m, d->FindFieldByName("rs")) ++ .Add(long_string); ++ assert_on_arena(m->rs(1)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 1, nullptr)); ++ r->template GetMutableRepeatedFieldRef<::std::string>( ++ m, d->FindFieldByName("rs")) ++ .Set(1, long_string + long_string); ++ assert_on_arena(m->rs(1)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 1, nullptr)); ++ r->SetString(m, d->FindFieldByName("ons"), long_string); ++ assert_on_arena(m->ons()); ++ assert_on_arena( ++ r->GetStringReference(*m, d->FindFieldByName("ons"), nullptr)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, reflect_on_arena) { ++ reflect_on_arena(*this, arena); ++ reflect_on_arena(*this, nullptr); ++ reflect_on_arena(*this, arena); ++ reflect_on_arena(*this, nullptr); ++} ++ ++template ++static void reflect_on_arena_pb2(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena, arena); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ auto* r = m->GetReflection(); ++ auto* d = m->GetDescriptor(); ++ r->SetString(m, d->FindFieldByName("s"), long_string); ++ assert_on_arena(m->s()); ++ assert_on_arena(r->GetStringReference(*m, d->FindFieldByName("s"), nullptr)); ++ r->SetString(m, d->FindFieldByName("qs"), long_string); ++ assert_on_arena(m->qs()); ++ assert_on_arena(r->GetStringReference(*m, d->FindFieldByName("qs"), nullptr)); ++ r->SetString(m, d->FindFieldByName("ds"), long_string); ++ assert_on_arena(m->ds()); ++ assert_on_arena(r->GetStringReference(*m, d->FindFieldByName("ds"), nullptr)); ++ r->AddString(m, d->FindFieldByName("rs"), long_string); ++ assert_on_arena(m->rs(0)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 0, nullptr)); ++ r->SetRepeatedString(m, d->FindFieldByName("rs"), 0, ++ long_string + long_string); ++ assert_on_arena(m->rs(0)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 0, nullptr)); ++ r->template GetMutableRepeatedFieldRef<::std::string>( ++ m, d->FindFieldByName("rs")) ++ .Add(long_string); ++ assert_on_arena(m->rs(1)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 1, nullptr)); ++ r->template GetMutableRepeatedFieldRef<::std::string>( ++ m, d->FindFieldByName("rs")) ++ .Set(1, long_string + long_string); ++ assert_on_arena(m->rs(1)); ++ assert_on_arena( ++ r->GetRepeatedStringReference(*m, d->FindFieldByName("rs"), 1, nullptr)); ++ r->SetString(m, d->FindFieldByName("ons"), long_string); ++ assert_on_arena(m->ons()); ++ assert_on_arena( ++ r->GetStringReference(*m, d->FindFieldByName("ons"), nullptr)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, reflect_on_arena_pb2) { ++ reflect_on_arena_pb2(*this, arena); ++ reflect_on_arena_pb2(*this, nullptr); ++ reflect_on_arena_pb2(*this, arena); ++ reflect_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void repeated_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena, arena); ++ }; ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, arena, arena); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, arena, false); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ }; ++ auto m = Arena::CreateMessage(arena); ++ auto rs = m->mutable_rs(); ++ rs->Add(::std::string(short_string)); ++ ASSERT_EQ(short_string, rs->Get(0)); ++ assert_on_arena(rs->Get(0)); ++ rs->RemoveLast(); ++ rs->Add(::std::string(long_string)); ++ ASSERT_EQ(long_string, rs->Get(0)); ++ assert_on_arena(rs->Get(0)); ++ for (auto iter = m->rs().begin(); iter != m->rs().end(); ++iter) { ++ ASSERT_EQ(long_string, *iter); ++ assert_on_arena(*iter); ++ } ++ for (auto iter = rs->begin(); iter != rs->end(); ++iter) { ++ ASSERT_EQ(long_string, *iter); ++ assert_mutable_on_arena(*iter); ++ } ++ rs->Add(::std::string(short_string)); ++ ASSERT_EQ(short_string, rs->Get(1)); ++ assert_on_arena(rs->Get(1)); ++ rs->Mutable(1)->assign(long_string); ++ ASSERT_EQ(long_string, rs->Get(1)); ++ assert_mutable_on_arena(rs->Get(1)); ++ rs->Add()->assign(long_string); ++ ASSERT_EQ(long_string, rs->Get(2)); ++ assert_mutable_on_arena(rs->Get(2)); ++ rs->Add(::std::string(long_string)); ++ ASSERT_EQ(long_string, m->rs()[3]); ++ assert_on_arena(m->rs()[3]); ++ (*rs)[3].assign(long_string + long_string); ++ ASSERT_EQ(long_string + long_string, rs->Get(3)); ++ assert_mutable_on_arena(rs->Get(3)); ++ rs->Add(::std::string(long_string)); ++ ASSERT_EQ(long_string, m->rs().at(4)); ++ assert_on_arena(m->rs().at(4)); ++ rs->at(4).assign(long_string + long_string); ++ ASSERT_EQ(long_string + long_string, rs->Get(4)); ++ assert_mutable_on_arena(rs->Get(4)); ++ rs->Add(::std::string(short_string)); ++ ASSERT_EQ(short_string, rs->Get(5)); ++ assert_on_arena(rs->Get(5)); ++ rs->DeleteSubrange(1, 4); ++ ASSERT_EQ(2, rs->size()); ++ if (!arena) { ++ delete m; ++ } ++ m = Arena::CreateMessage(arena); ++ rs = m->mutable_rs(); ++ M fm; ++ fm.add_rs(short_string); ++ fm.add_rs(long_string); ++ m->MergeFrom(fm); ++ ASSERT_EQ(short_string, rs->Get(0)); ++ assert_on_arena(rs->Get(0)); ++ ASSERT_EQ(long_string, rs->Get(1)); ++ assert_on_arena(rs->Get(1)); ++ { ++ ::std::string strs[2] = {short_string, long_string}; ++ rs->Add(strs, strs + 2); ++ ASSERT_EQ(short_string, rs->Get(2)); ++ assert_on_arena(rs->Get(2)); ++ ASSERT_EQ(long_string, rs->Get(3)); ++ assert_on_arena(rs->Get(3)); ++ } ++ { ++ ::std::string strs[2] = {long_string, short_string}; ++ rs->Assign(strs, strs + 2); ++ ASSERT_EQ(long_string, rs->Get(0)); ++ assert_on_arena(rs->Get(0)); ++ ASSERT_EQ(short_string, rs->Get(1)); ++ assert_on_arena(rs->Get(1)); ++ } ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, repeated_on_arena) { ++ repeated_on_arena(*this, arena); ++ repeated_on_arena(*this, nullptr); ++ repeated_on_arena(*this, arena); ++ repeated_on_arena(*this, nullptr); ++ repeated_on_arena(*this, arena); ++ repeated_on_arena(*this, nullptr); ++ repeated_on_arena(*this, arena); ++ repeated_on_arena(*this, nullptr); ++} ++ ++template ++static void repeated_add_allocated_and_release_on_arena(T& t, Arena* arena) { ++ auto m = Arena::CreateMessage(arena); ++ auto rs = m->mutable_rs(); ++ { ++ auto s = new ::std::string(long_string); ++ auto c = s->c_str(); ++ rs->AddAllocated(s); ++ ASSERT_EQ(s, &rs->Get(0)); ++ ASSERT_EQ(c, rs->Get(0).c_str()); ++ t.assert_on_arena(rs->Get(0), false, false); ++ s = new ::std::string(long_string); ++ c = s->c_str(); ++ rs->AddAllocated(s); ++ ASSERT_EQ(s, &rs->Get(1)); ++ ASSERT_EQ(c, rs->Get(1).c_str()); ++ t.assert_on_arena(rs->Get(1), false, false); ++ rs->MutableAccessor(1)->assign(long_string + long_string); ++ ASSERT_EQ(s, &rs->Get(1)); ++ ASSERT_NE(c, rs->Get(1).c_str()); ++ t.assert_on_arena(rs->Get(1), false, false); ++ m->add_rs(long_string); ++ } ++ { ++ auto s = &m->rs(m->rs_size() - 1); ++ auto c = s->c_str(); ++ auto r = rs->ReleaseLast(); ++ ASSERT_EQ(long_string, *r); ++ if (arena) { ++ ASSERT_NE(s, r); ++ ASSERT_NE(c, r->c_str()); ++ } else { ++ ASSERT_EQ(s, r); ++ ASSERT_EQ(c, r->c_str()); ++ } ++ t.assert_on_arena(*r, false, false); ++ delete r; ++ s = &m->rs(m->rs_size() - 1); ++ c = s->c_str(); ++ r = rs->ReleaseLast(); ++ ASSERT_EQ(long_string + long_string, *r); ++ if (arena) { ++ ASSERT_NE(s, r); ++ ASSERT_EQ(c, r->c_str()); ++ } else { ++ ASSERT_EQ(s, r); ++ ASSERT_EQ(c, r->c_str()); ++ } ++ t.assert_on_arena(*r, false, false); ++ delete r; ++ s = &m->rs(m->rs_size() - 1); ++ c = s->c_str(); ++ r = rs->ReleaseLast(); ++ ASSERT_EQ(long_string, *r); ++ if (arena) { ++ ASSERT_NE(s, r); ++ ASSERT_EQ(c, r->c_str()); ++ } else { ++ ASSERT_EQ(s, r); ++ ASSERT_EQ(c, r->c_str()); ++ } ++ t.assert_on_arena(*r, false, false); ++ delete r; ++ } ++ { ++ auto mm = Arena::CreateMessage(arena); ++ mm->mutable_rs()->AddString()->assign(long_string); ++ mm->mutable_rs()->AddAccessor()->assign(long_string); ++ auto s = &mm->rs(mm->rs_size() - 1); ++ auto c = s->c_str(); ++ rs->UnsafeArenaAddAllocated(mm->mutable_rs()->UnsafeArenaReleaseLast()); ++ auto ss = &m->rs(m->rs_size() - 1); ++ auto cc = ss->c_str(); ++ ASSERT_EQ(*s, *ss); ++ ASSERT_EQ(s, ss); ++ ASSERT_EQ(c, cc); ++ t.assert_on_arena(*s, arena, arena); ++ s = &mm->rs(mm->rs_size() - 1); ++ c = s->c_str(); ++ rs->UnsafeArenaAddAllocated(mm->mutable_rs()->UnsafeArenaReleaseLast()); ++ ss = &m->rs(m->rs_size() - 1); ++ cc = ss->c_str(); ++ ASSERT_EQ(*s, *ss); ++ ASSERT_EQ(s, ss); ++ ASSERT_EQ(c, cc); ++ t.assert_on_arena(*s, arena, false); ++ if (!arena) { ++ delete mm; ++ } ++ } ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, repeated_add_allocated_and_release_on_arena) { ++ repeated_add_allocated_and_release_on_arena(*this, arena); ++ repeated_add_allocated_and_release_on_arena(*this, nullptr); ++ repeated_add_allocated_and_release_on_arena(*this, arena); ++ repeated_add_allocated_and_release_on_arena(*this, nullptr); ++ repeated_add_allocated_and_release_on_arena(*this, arena); ++ repeated_add_allocated_and_release_on_arena(*this, nullptr); ++ repeated_add_allocated_and_release_on_arena(*this, arena); ++ repeated_add_allocated_and_release_on_arena(*this, nullptr); ++} ++ ++template ++static void mutable_string_on_arena(T& t, Arena* arena) { ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena( ++ s, arena, ++ arena && ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string()); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_s(short_string); ++ m->mutable_s()->assign(long_string); ++ ASSERT_EQ(long_string, m->s()); ++ assert_mutable_on_arena(m->s()); ++ m->mutable_s()->assign(short_string); ++ ASSERT_EQ(short_string, m->s()); ++ assert_mutable_on_arena(m->s()); ++ m->mutable_s()->clear(); ++ m->mutable_s()->push_back('x'); ++ m->mutable_s()->append("10086"); ++ ASSERT_EQ("x10086", m->s()); ++ assert_mutable_on_arena(m->s()); ++ m->set_ons(short_string); ++ m->mutable_ons()->assign(long_string); ++ ASSERT_EQ(long_string, m->ons()); ++ assert_mutable_on_arena(m->ons()); ++ m->mutable_ons()->assign(short_string); ++ ASSERT_EQ(short_string, m->ons()); ++ assert_mutable_on_arena(m->ons()); ++ m->mutable_ons()->clear(); ++ m->mutable_ons()->push_back('x'); ++ m->mutable_ons()->append("10086"); ++ ASSERT_EQ("x10086", m->ons()); ++ assert_mutable_on_arena(m->ons()); ++ m->add_rs(short_string); ++ m->mutable_rs(0)->assign(long_string); ++ ASSERT_EQ(long_string, m->rs(0)); ++ assert_mutable_on_arena(m->rs(0)); ++ m->mutable_rs(0)->assign(short_string); ++ ASSERT_EQ(short_string, m->rs(0)); ++ assert_mutable_on_arena(m->rs(0)); ++ m->mutable_rs(0)->clear(); ++ m->mutable_rs(0)->push_back('x'); ++ m->mutable_rs(0)->append("10086"); ++ ASSERT_EQ("x10086", m->rs(0)); ++ assert_mutable_on_arena(m->rs(0)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, mutable_string_on_arena) { ++ mutable_string_on_arena(*this, arena); ++ mutable_string_on_arena(*this, nullptr); ++ mutable_string_on_arena(*this, arena); ++ mutable_string_on_arena(*this, nullptr); ++ mutable_string_on_arena(*this, arena); ++ mutable_string_on_arena(*this, nullptr); ++ mutable_string_on_arena(*this, arena); ++ mutable_string_on_arena(*this, nullptr); ++} ++ ++template ++static void mutable_string_on_arena_pb2(T& t, Arena* arena) { ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena( ++ s, arena, ++ arena && ++ M().GetDescriptor()->file()->options().cc_mutable_donated_string()); ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->set_ds(short_string); ++ m->mutable_ds()->assign(long_string); ++ ASSERT_EQ(long_string, m->ds()); ++ assert_mutable_on_arena(m->ds()); ++ m->mutable_ds()->assign(short_string); ++ ASSERT_EQ(short_string, m->ds()); ++ assert_mutable_on_arena(m->ds()); ++ m->mutable_ds()->clear(); ++ m->mutable_ds()->push_back('x'); ++ m->mutable_ds()->append("10086"); ++ ASSERT_EQ("x10086", m->ds()); ++ assert_mutable_on_arena(m->ds()); ++ m->set_qs(short_string); ++ m->mutable_qs()->assign(long_string); ++ ASSERT_EQ(long_string, m->qs()); ++ assert_mutable_on_arena(m->qs()); ++ m->mutable_qs()->assign(short_string); ++ ASSERT_EQ(short_string, m->qs()); ++ assert_mutable_on_arena(m->qs()); ++ m->mutable_qs()->clear(); ++ m->mutable_qs()->push_back('x'); ++ m->mutable_qs()->append("10086"); ++ ASSERT_EQ("x10086", m->qs()); ++ assert_mutable_on_arena(m->qs()); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, mutable_string_on_arena_pb2) { ++ mutable_string_on_arena_pb2(*this, arena); ++ mutable_string_on_arena_pb2(*this, nullptr); ++ mutable_string_on_arena_pb2(*this, arena); ++ mutable_string_on_arena_pb2(*this, nullptr); ++} ++ ++template ++static void extension_on_arena(T& t, Arena* arena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, arena, arena); ++ }; ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, arena, arena); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, arena, false); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ }; ++ auto* m = Arena::CreateMessage(arena); ++ m->SetExtension(E::es, short_string); ++ ASSERT_EQ(short_string, m->GetExtension(E::es)); ++ assert_on_arena(m->GetExtension(E::es)); ++ m->ClearExtension(E::es); ++ m->SetExtension(E::es, long_string); ++ ASSERT_EQ(long_string, m->GetExtension(E::es)); ++ assert_on_arena(m->GetExtension(E::es)); ++ m->MutableExtension(E::es)->append(long_string); ++ ASSERT_EQ(long_string + long_string, m->GetExtension(E::es)); ++ assert_mutable_on_arena(m->GetExtension(E::es)); ++ m->ClearExtension(E::es); ++ m->MutableExtension(E::es)->assign(long_string.c_str()); ++ ASSERT_EQ(long_string, m->GetExtension(E::es)); ++ ASSERT_LE(long_string.size() * 2, m->GetExtension(E::es).capacity()); ++ assert_mutable_on_arena(m->GetExtension(E::es)); ++ m->AddExtension(E::ers, long_string); ++ ASSERT_EQ(long_string, m->GetExtension(E::ers, 0)); ++ assert_on_arena(m->GetExtension(E::ers, 0)); ++ m->MutableExtension(E::ers, 0)->append(long_string); ++ ASSERT_EQ(long_string + long_string, m->GetExtension(E::ers, 0)); ++ assert_mutable_on_arena(m->GetExtension(E::ers, 0)); ++ if (!arena) { ++ delete m; ++ } ++} ++TEST_F(ArenaStringTest, extension_on_arena) { ++ extension_on_arena(*this, arena); ++ extension_on_arena(*this, nullptr); ++ extension_on_arena(*this, arena); ++ extension_on_arena(*this, nullptr); ++} ++ ++template ++static void extension_parse_and_merge_on_arena(T& t, Arena* farena, ++ Arena* tarena) { ++ auto assert_on_arena = [&](const ::std::string& s) { ++ t.assert_on_arena(s, tarena, tarena); ++ }; ++ auto assert_mutable_on_arena = [&](const ::std::string& s) { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, tarena, tarena); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ t.assert_on_arena(s, tarena, false); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ }; ++ ::std::string string; ++ auto* fm = Arena::CreateMessage(farena); ++ auto* tm = Arena::CreateMessage(tarena); ++ tm->AddExtension(E::ers, short_string); ++ fm->SetExtension(E::es, short_string); ++ fm->set_qs(long_string); ++ fm->set_qb(long_string); ++ fm->set_qc(long_string); ++ fm->AddExtension(E::ers, long_string); ++ fm->AddExtension(E::ers, long_string); ++ ASSERT_TRUE(fm->SerializeToString(&string)); ++ ASSERT_TRUE(tm->ParseFromString(string)); ++ ASSERT_EQ(short_string, tm->GetExtension(E::es)); ++ assert_on_arena(tm->GetExtension(E::es)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 0)); ++ assert_on_arena(tm->GetExtension(E::ers, 0)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 1)); ++ assert_on_arena(tm->GetExtension(E::ers, 1)); ++ tm->MutableExtension(E::es)->assign(long_string.c_str()); ++ tm->MutableExtension(E::ers, 0)->append(long_string); ++ ASSERT_TRUE(tm->ParseFromString(string)); ++ ASSERT_EQ(short_string, tm->GetExtension(E::es)); ++ assert_mutable_on_arena(tm->GetExtension(E::es)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 0)); ++ assert_mutable_on_arena(tm->GetExtension(E::ers, 0)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 1)); ++ assert_on_arena(tm->GetExtension(E::ers, 1)); ++ if (!tarena) { ++ delete tm; ++ } ++ tm = Arena::CreateMessage(tarena); ++ tm->MergeFrom(*fm); ++ ASSERT_EQ(short_string, tm->GetExtension(E::es)); ++ assert_on_arena(tm->GetExtension(E::es)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 0)); ++ assert_on_arena(tm->GetExtension(E::ers, 0)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 1)); ++ assert_on_arena(tm->GetExtension(E::ers, 1)); ++ tm->MutableExtension(E::es)->assign(long_string.c_str()); ++ tm->MutableExtension(E::ers, 0)->append(long_string); ++ tm->CopyFrom(*fm); ++ ASSERT_EQ(short_string, tm->GetExtension(E::es)); ++ assert_mutable_on_arena(tm->GetExtension(E::es)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 0)); ++ assert_mutable_on_arena(tm->GetExtension(E::ers, 0)); ++ ASSERT_EQ(long_string, tm->GetExtension(E::ers, 1)); ++ assert_on_arena(tm->GetExtension(E::ers, 1)); ++ if (!farena) { ++ delete fm; ++ } ++ if (!tarena) { ++ delete tm; ++ } ++} ++TEST_F(ArenaStringTest, extension_parse_and_merge_on_arena) { ++ extension_parse_and_merge_on_arena(*this, arena, ++ arena); ++ extension_parse_and_merge_on_arena(*this, arena, ++ nullptr); ++ extension_parse_and_merge_on_arena(*this, nullptr, ++ arena); ++ extension_parse_and_merge_on_arena(*this, nullptr, ++ nullptr); ++ extension_parse_and_merge_on_arena( ++ *this, arena, arena); ++ extension_parse_and_merge_on_arena( ++ *this, arena, nullptr); ++ extension_parse_and_merge_on_arena( ++ *this, nullptr, arena); ++ extension_parse_and_merge_on_arena( ++ *this, nullptr, nullptr); ++} +diff --git a/src/google/protobuf/compiler/BUILD.bazel b/src/google/protobuf/compiler/BUILD.bazel +index 3f5624d5f..64cf4f852 100644 +--- a/src/google/protobuf/compiler/BUILD.bazel ++++ b/src/google/protobuf/compiler/BUILD.bazel +@@ -347,6 +347,7 @@ cc_binary( + testonly = True, + srcs = ["fake_plugin.cc"], + deps = [ ++ ":code_generator", + ":plugin_cc_proto", + "//src/google/protobuf", + "//src/google/protobuf/io:io_win32", +diff --git a/src/google/protobuf/compiler/cpp/field.cc b/src/google/protobuf/compiler/cpp/field.cc +index b7a8d1f45..cd0481b9f 100644 +--- a/src/google/protobuf/compiler/cpp/field.cc ++++ b/src/google/protobuf/compiler/cpp/field.cc +@@ -336,7 +336,7 @@ void InlinedStringVars(const FieldDescriptor* field, const Options& opts, + : "_impl_._inlined_string_donated_"; + + vars.emplace_back("inlined_string_donated", +- absl::StrFormat("(%s[%d] & %s) != 0;", array, index, mask)); ++ absl::StrFormat("(%s[%d] & %s) != 0", array, index, mask)); + vars.emplace_back("donating_states_word", + absl::StrFormat("%s[%d]", array, index)); + vars.emplace_back("mask_for_undonate", absl::StrFormat("~%s", mask)); +diff --git a/src/google/protobuf/compiler/cpp/field_generators/string_field.cc b/src/google/protobuf/compiler/cpp/field_generators/string_field.cc +index fcca9dfc3..7bc363071 100644 +--- a/src/google/protobuf/compiler/cpp/field_generators/string_field.cc ++++ b/src/google/protobuf/compiler/cpp/field_generators/string_field.cc +@@ -78,6 +78,16 @@ class SingularString : public FieldGeneratorBase { + return is_inlined() ? ArenaDtorNeeds::kOnDemand : ArenaDtorNeeds::kNone; + } + ++ void GenerateArenaDestructorCode(io::Printer* p) const override { ++ if (!is_inlined()) return; ++ ++ p->Emit(R"cc( ++ if (!_this->_internal_$name$_donated()) { ++ _this->$field_$.~InlinedStringField(); ++ } ++ )cc"); ++ } ++ + void GeneratePrivateMembers(io::Printer* p) const override { + // Skips the automatic destruction if inlined; rather calls it explicitly if + // allocating arena is null. +@@ -110,16 +120,6 @@ class SingularString : public FieldGeneratorBase { + } + } + +- void GenerateArenaDestructorCode(io::Printer* p) const override { +- if (!is_inlined()) return; +- +- p->Emit(R"cc( +- if (!_this->_internal_$name$_donated()) { +- _this->$field_$.~InlinedStringField(); +- } +- )cc"); +- } +- + void GenerateNonInlineAccessorDefinitions(io::Printer* p) const override { + if (EmptyDefault()) return; + p->Emit(R"cc( +@@ -262,7 +262,17 @@ void SingularString::GenerateAccessorDeclarations(io::Printer* p) const { + p->Emit(R"cc( + inline PROTOBUF_ALWAYS_INLINE bool _internal_$name$_donated() const; + )cc"); +- }}}, ++ }}, ++ // ARENASTRING PATCH: change mutable_xxx function return value to ++ // MaybeArenaStringAccessor when cc_mutable_donated_string=true ++ {"mutable_return_type", [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("$pb$::MaybeArenaStringAccessor"); ++ } else { ++ p->Emit("std::string*"); ++ } ++ }}, ++ }, + R"cc( + $DEPRECATED$ const std::string& $name$() const; + //~ Using `Arg_ = const std::string&` will make the type of `arg` +@@ -271,7 +281,7 @@ void SingularString::GenerateAccessorDeclarations(io::Printer* p) const { + //~ default. + template + $DEPRECATED$ void $set_name$(Arg_&& arg, Args_... args); +- $DEPRECATED$ std::string* $mutable_name$(); ++ $DEPRECATED$ $mutable_return_type$ $mutable_name$(); + $DEPRECATED$ PROTOBUF_NODISCARD std::string* $release_name$(); + $DEPRECATED$ void $set_allocated_name$(std::string* value); + +@@ -280,6 +290,7 @@ void SingularString::GenerateAccessorDeclarations(io::Printer* p) const { + inline PROTOBUF_ALWAYS_INLINE void _internal_set_$name$( + const std::string& value); + std::string* _internal_mutable_$name$(); ++ $pb$::MaybeArenaStringAccessor _internal_mutable_$name$_accessor(); + $donated$; + + public: +@@ -326,32 +337,22 @@ void SingularString::ReleaseImpl(io::Printer* p) const { + return; + } + +- if (!HasHasbit(field_)) { +- p->Emit(R"cc( +- return $field_$.Release(); +- )cc"); +- return; +- } +- +- if (is_inlined()) { ++ if (HasHasbit(field_)) { + p->Emit(R"cc( + if (($has_hasbit$) == 0) { + return nullptr; + } + $clear_hasbit$; ++ )cc"); ++ } + ++ if (is_inlined()) { ++ p->Emit(R"cc( + return $field_$.Release(GetArena(), _internal_$name_internal$_donated()); + )cc"); + return; + } + +- p->Emit(R"cc( +- if (($has_hasbit$) == 0) { +- return nullptr; +- } +- $clear_hasbit$; +- )cc"); +- + if (!EmptyDefault()) { + p->Emit(R"cc( + return $field_$.Release(); +@@ -442,6 +443,20 @@ void SingularString::GenerateInlineAccessorDefinitions(io::Printer* p) const { + SafeFunctionName(field_->containing_type(), field_, "release_")}, + {"release_impl", [&] { ReleaseImpl(p); }}, + {"set_allocated_impl", [&] { SetAllocatedImpl(p); }}, ++ // ARENASTRING PATCH: change mutable_xxx function return value to ++ // MaybeArenaStringAccessor when cc_mutable_donated_string=true ++ {"mutable_return_type", [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("$pb$::MaybeArenaStringAccessor"); ++ } else { ++ p->Emit("std::string*"); ++ } ++ }}, ++ {"mutable_suffix", [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("_accessor"); ++ } ++ }}, + }, + R"cc( + inline const std::string& $Msg$::$name$() const +@@ -463,10 +478,10 @@ void SingularString::GenerateInlineAccessorDefinitions(io::Printer* p) const { + $annotate_set$; + // @@protoc_insertion_point(field_set:$pkg.Msg.field$) + } +- inline std::string* $Msg$::mutable_$name$() ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ inline $mutable_return_type$ $Msg$::mutable_$name$() ABSL_ATTRIBUTE_LIFETIME_BOUND { + $WeakDescriptorSelfPin$; + $PrepareSplitMessageForWrite$; +- std::string* _s = _internal_mutable_$name_internal$(); ++ auto _s = _internal_mutable_$name_internal$$mutable_suffix$(); + $annotate_mutable$; + // @@protoc_insertion_point(field_mutable:$pkg.Msg.field$) + return _s; +@@ -488,6 +503,11 @@ void SingularString::GenerateInlineAccessorDefinitions(io::Printer* p) const { + $update_hasbit$; + return $field_$.Mutable($lazy_args$, $set_args$); + } ++ inline $pb$::MaybeArenaStringAccessor $Msg$::_internal_mutable_$name_internal$_accessor() { ++ $TsanDetectConcurrentMutation$; ++ $update_hasbit$; ++ return $field_$.MutableAccessor($lazy_args$, $set_args$); ++ } + inline std::string* $Msg$::$release_name$() { + $WeakDescriptorSelfPin$; + $TsanDetectConcurrentMutation$; +@@ -837,12 +857,22 @@ void RepeatedString::GenerateAccessorDeclarations(io::Printer* p) const { + auto v3 = p->WithVars( + AnnotatedAccessors(field_, {"mutable_"}, AnnotationCollector::kAlias)); + +- p->Emit(R"cc( ++ // ARENASTRING PATCH: change mutable_xxx function return value to ++ // MaybeArenaStringAccessor when cc_mutable_donated_string=true ++ p->Emit({{"mutable_return_type", ++ [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("$pb$::MaybeArenaStringAccessor"); ++ } else { ++ p->Emit("std::string*"); ++ } ++ }}}, ++ R"cc( + $DEPRECATED$ const std::string& $name$(int index) const; +- $DEPRECATED$ std::string* $mutable_name$(int index); ++ $DEPRECATED$ $mutable_return_type$ $mutable_name$(int index); + template + $DEPRECATED$ void set_$name$(int index, Arg_&& value, Args_... args); +- $DEPRECATED$ std::string* $add_name$(); ++ $DEPRECATED$ $mutable_return_type$ $add_name$(); + template + $DEPRECATED$ void $add_name$(Arg_&& value, Args_... args); + $DEPRECATED$ const $pb$::RepeatedPtrField& $name$() const; +@@ -873,12 +903,29 @@ void RepeatedString::GenerateInlineAccessorDefinitions(io::Printer* p) const { + p->Emit(", $pbi$::BytesTag{}"); + } + }}, ++ // ARENASTRING PATCH: change mutable_xxx function return value to ++ // MaybeArenaStringAccessor when cc_mutable_donated_string=true ++ {"mutable_return_type", ++ [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("$pb$::MaybeArenaStringAccessor"); ++ } else { ++ p->Emit("std::string*"); ++ } ++ }}, ++ {"mutable_suffix", [&] { ++ if (field_->file()->options().cc_mutable_donated_string()) { ++ p->Emit("Accessor"); ++ } else { ++ p->Emit("String"); ++ } ++ }}, + }, + R"cc( +- inline std::string* $Msg$::add_$name$() ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ inline $mutable_return_type$ $Msg$::add_$name$() ABSL_ATTRIBUTE_LIFETIME_BOUND { + $WeakDescriptorSelfPin$; + $TsanDetectConcurrentMutation$; +- std::string* _s = _internal_mutable_$name_internal$()->Add(); ++ auto _s = _internal_mutable_$name_internal$()->Add$mutable_suffix$(); + $annotate_add_mutable$; + // @@protoc_insertion_point(field_add_mutable:$pkg.Msg.field$) + return _s; +@@ -890,18 +937,18 @@ void RepeatedString::GenerateInlineAccessorDefinitions(io::Printer* p) const { + // @@protoc_insertion_point(field_get:$pkg.Msg.field$) + return _internal_$name_internal$().$Get$(index$GetExtraArg$); + } +- inline std::string* $Msg$::mutable_$name$(int index) ++ inline $mutable_return_type$ $Msg$::mutable_$name$(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + $WeakDescriptorSelfPin$; + $annotate_mutable$; + // @@protoc_insertion_point(field_mutable:$pkg.Msg.field$) +- return _internal_mutable_$name_internal$()->Mutable(index); ++ return _internal_mutable_$name_internal$()->Mutable$mutable_suffix$(index); + } + template + inline void $Msg$::set_$name$(int index, Arg_&& value, Args_... args) { + $WeakDescriptorSelfPin$; + $pbi$::AssignToString( +- *_internal_mutable_$name_internal$()->Mutable(index), ++ *_internal_mutable_$name_internal$()->MutableAccessor(index), + std::forward(value), args... $bytes_tag$); + $annotate_set$; + // @@protoc_insertion_point(field_set:$pkg.Msg.field$) +diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc +index 4a3a47e10..bb07bea0e 100644 +--- a/src/google/protobuf/compiler/cpp/helpers.cc ++++ b/src/google/protobuf/compiler/cpp/helpers.cc +@@ -959,7 +959,20 @@ bool CanStringBeInlined(const FieldDescriptor* field) { + bool IsStringInlined(const FieldDescriptor* field, const Options& options) { + (void)field; + (void)options; +- return false; ++ // ARENASTRING PATCH: enable InlinedStringField when cc_mutable_donated_string=true ++ return field->file()->options().cc_mutable_donated_string() && ++ // only for string/bytes ++ field->cpp_type() == FieldDescriptor::CPPTYPE_STRING && ++ // exclude non default ctype ++ internal::cpp::EffectiveStringCType(field) == FieldOptions::STRING && ++ // exclude oneof ++ !field->real_containing_oneof() && ++ // exclude repeated ++ !field->is_repeated() && ++ // exclude map ++ !field->containing_type()->options().map_entry() && ++ // no default value ++ !field->has_default_value(); + } + + static bool HasLazyFields(const Descriptor* descriptor, const Options& options, +diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc +index 1f868e589..0e816a64f 100644 +--- a/src/google/protobuf/compiler/cpp/message.cc ++++ b/src/google/protobuf/compiler/cpp/message.cc +@@ -2139,23 +2139,30 @@ void MessageGenerator::GenerateInlineMethods(io::Printer* p) { + + void MessageGenerator::GenerateSchema(io::Printer* p, int offset, + int has_offset) { +- has_offset = !has_bit_indices_.empty() || IsMapEntryMessage(descriptor_) +- ? offset + has_offset +- : -1; ++ // ARENASTRING PATCH: schema of Message is composed as ++ // [has_bit_indices][inlined_string_indices] ++ // has_offset = sizeof(metadata) ++ // offset = base offset of metadata ++ // original implementation dont support inlined string ++ // without hasbit. modify to support that ++ int has_bit_indices_offset = ++ !has_bit_indices_.empty() || IsMapEntryMessage(descriptor_) ++ ? offset + has_offset ++ : -1; + int inlined_string_indices_offset; + if (inlined_string_indices_.empty()) { + inlined_string_indices_offset = -1; + } else { + ABSL_DCHECK_NE(has_offset, -1); + ABSL_DCHECK(!IsMapEntryMessage(descriptor_)); +- inlined_string_indices_offset = has_offset + has_bit_indices_.size(); ++ inlined_string_indices_offset = offset + has_offset + has_bit_indices_.size(); + } + + auto v = p->WithVars(ClassVars(descriptor_, options_)); + p->Emit( + { + {"offset", offset}, +- {"has_offset", has_offset}, ++ {"has_offset", has_bit_indices_offset}, + {"string_offsets", inlined_string_indices_offset}, + }, + R"cc( +diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h +index ae179a29e..5af526320 100644 +--- a/src/google/protobuf/compiler/plugin.pb.h ++++ b/src/google/protobuf/compiler/plugin.pb.h +@@ -276,6 +276,7 @@ class PROTOC_EXPORT Version final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_suffix( + const std::string& value); + std::string* _internal_mutable_suffix(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_suffix_accessor(); + + public: + // optional int32 major = 1; +@@ -509,6 +510,7 @@ class PROTOC_EXPORT CodeGeneratorResponse_File final : public ::google::protobuf + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional string insertion_point = 2; +@@ -526,6 +528,7 @@ class PROTOC_EXPORT CodeGeneratorResponse_File final : public ::google::protobuf + inline PROTOBUF_ALWAYS_INLINE void _internal_set_insertion_point( + const std::string& value); + std::string* _internal_mutable_insertion_point(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_insertion_point_accessor(); + + public: + // optional string content = 15; +@@ -543,6 +546,7 @@ class PROTOC_EXPORT CodeGeneratorResponse_File final : public ::google::protobuf + inline PROTOBUF_ALWAYS_INLINE void _internal_set_content( + const std::string& value); + std::string* _internal_mutable_content(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_content_accessor(); + + public: + // optional .google.protobuf.GeneratedCodeInfo generated_code_info = 16; +@@ -797,6 +801,7 @@ class PROTOC_EXPORT CodeGeneratorResponse final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_error( + const std::string& value); + std::string* _internal_mutable_error(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_error_accessor(); + + public: + // optional uint64 supported_features = 2; +@@ -1093,6 +1098,7 @@ class PROTOC_EXPORT CodeGeneratorRequest final : public ::google::protobuf::Mess + inline PROTOBUF_ALWAYS_INLINE void _internal_set_parameter( + const std::string& value); + std::string* _internal_mutable_parameter(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_parameter_accessor(); + + public: + // optional .google.protobuf.compiler.Version compiler_version = 3; +@@ -1271,7 +1277,7 @@ inline PROTOBUF_ALWAYS_INLINE void Version::set_suffix(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.compiler.Version.suffix) + } + inline std::string* Version::mutable_suffix() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_suffix(); ++ auto _s = _internal_mutable_suffix(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.Version.suffix) + return _s; + } +@@ -1289,6 +1295,11 @@ inline std::string* Version::_internal_mutable_suffix() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.suffix_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Version::_internal_mutable_suffix_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.suffix_.MutableAccessor( GetArena()); ++} + inline std::string* Version::release_suffix() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.Version.suffix) +@@ -1335,7 +1346,7 @@ inline void CodeGeneratorRequest::clear_file_to_generate() { + } + inline std::string* CodeGeneratorRequest::add_file_to_generate() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_file_to_generate()->Add(); ++ auto _s = _internal_mutable_file_to_generate()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) + return _s; + } +@@ -1347,12 +1358,12 @@ inline const std::string& CodeGeneratorRequest::file_to_generate(int index) cons + inline std::string* CodeGeneratorRequest::mutable_file_to_generate(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) +- return _internal_mutable_file_to_generate()->Mutable(index); ++ return _internal_mutable_file_to_generate()->MutableString(index); + } + template + inline void CodeGeneratorRequest::set_file_to_generate(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_file_to_generate()->Mutable(index), ++ *_internal_mutable_file_to_generate()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.file_to_generate) + } +@@ -1410,7 +1421,7 @@ inline PROTOBUF_ALWAYS_INLINE void CodeGeneratorRequest::set_parameter(Arg_&& ar + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorRequest.parameter) + } + inline std::string* CodeGeneratorRequest::mutable_parameter() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_parameter(); ++ auto _s = _internal_mutable_parameter(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.parameter) + return _s; + } +@@ -1428,6 +1439,11 @@ inline std::string* CodeGeneratorRequest::_internal_mutable_parameter() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.parameter_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor CodeGeneratorRequest::_internal_mutable_parameter_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.parameter_.MutableAccessor( GetArena()); ++} + inline std::string* CodeGeneratorRequest::release_parameter() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorRequest.parameter) +@@ -1671,7 +1687,7 @@ inline PROTOBUF_ALWAYS_INLINE void CodeGeneratorResponse_File::set_name(Arg_&& a + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.name) + } + inline std::string* CodeGeneratorResponse_File::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.name) + return _s; + } +@@ -1689,6 +1705,11 @@ inline std::string* CodeGeneratorResponse_File::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor CodeGeneratorResponse_File::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* CodeGeneratorResponse_File::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.name) +@@ -1742,7 +1763,7 @@ inline PROTOBUF_ALWAYS_INLINE void CodeGeneratorResponse_File::set_insertion_poi + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) + } + inline std::string* CodeGeneratorResponse_File::mutable_insertion_point() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_insertion_point(); ++ auto _s = _internal_mutable_insertion_point(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) + return _s; + } +@@ -1760,6 +1781,11 @@ inline std::string* CodeGeneratorResponse_File::_internal_mutable_insertion_poin + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.insertion_point_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor CodeGeneratorResponse_File::_internal_mutable_insertion_point_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.insertion_point_.MutableAccessor( GetArena()); ++} + inline std::string* CodeGeneratorResponse_File::release_insertion_point() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.insertion_point) +@@ -1813,7 +1839,7 @@ inline PROTOBUF_ALWAYS_INLINE void CodeGeneratorResponse_File::set_content(Arg_& + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.File.content) + } + inline std::string* CodeGeneratorResponse_File::mutable_content() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_content(); ++ auto _s = _internal_mutable_content(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.File.content) + return _s; + } +@@ -1831,6 +1857,11 @@ inline std::string* CodeGeneratorResponse_File::_internal_mutable_content() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.content_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor CodeGeneratorResponse_File::_internal_mutable_content_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.content_.MutableAccessor( GetArena()); ++} + inline std::string* CodeGeneratorResponse_File::release_content() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.File.content) +@@ -1979,7 +2010,7 @@ inline PROTOBUF_ALWAYS_INLINE void CodeGeneratorResponse::set_error(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.compiler.CodeGeneratorResponse.error) + } + inline std::string* CodeGeneratorResponse::mutable_error() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_error(); ++ auto _s = _internal_mutable_error(); + // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.error) + return _s; + } +@@ -1997,6 +2028,11 @@ inline std::string* CodeGeneratorResponse::_internal_mutable_error() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.error_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor CodeGeneratorResponse::_internal_mutable_error_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.error_.MutableAccessor( GetArena()); ++} + inline std::string* CodeGeneratorResponse::release_error() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.compiler.CodeGeneratorResponse.error) +diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc +index d626f33db..5fe419ebf 100644 +--- a/src/google/protobuf/descriptor.pb.cc ++++ b/src/google/protobuf/descriptor.pb.cc +@@ -647,6 +647,7 @@ inline constexpr FileOptions::Impl_::Impl_( + java_generic_services_{false}, + py_generic_services_{false}, + deprecated_{false}, ++ cc_mutable_donated_string_{false}, + optimize_for_{static_cast< ::google::protobuf::FileOptions_OptimizeMode >(1)}, + cc_enable_arenas_{true} {} + +@@ -1508,6 +1509,7 @@ const ::uint32_t + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.py_generic_services_), + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.deprecated_), + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.cc_enable_arenas_), ++ PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.cc_mutable_donated_string_), + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.objc_class_prefix_), + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.csharp_namespace_), + PROTOBUF_FIELD_OFFSET(::google::protobuf::FileOptions, _impl_.swift_prefix_), +@@ -1522,13 +1524,14 @@ const ::uint32_t + 11, + 12, + 13, +- 18, ++ 19, + 2, + 14, + 15, + 16, + 17, +- 19, ++ 20, ++ 18, + 3, + 4, + 5, +@@ -1856,25 +1859,25 @@ static const ::_pbi::MigrationSchema + {203, 214, -1, sizeof(::google::protobuf::EnumValueDescriptorProto)}, + {217, 228, -1, sizeof(::google::protobuf::ServiceDescriptorProto)}, + {231, 245, -1, sizeof(::google::protobuf::MethodDescriptorProto)}, +- {251, 280, -1, sizeof(::google::protobuf::FileOptions)}, +- {301, 316, -1, sizeof(::google::protobuf::MessageOptions)}, +- {323, 333, -1, sizeof(::google::protobuf::FieldOptions_EditionDefault)}, +- {335, 347, -1, sizeof(::google::protobuf::FieldOptions_FeatureSupport)}, +- {351, 373, -1, sizeof(::google::protobuf::FieldOptions)}, +- {387, 397, -1, sizeof(::google::protobuf::OneofOptions)}, +- {399, 412, -1, sizeof(::google::protobuf::EnumOptions)}, +- {417, 430, -1, sizeof(::google::protobuf::EnumValueOptions)}, +- {435, 446, -1, sizeof(::google::protobuf::ServiceOptions)}, +- {449, 461, -1, sizeof(::google::protobuf::MethodOptions)}, +- {465, 475, -1, sizeof(::google::protobuf::UninterpretedOption_NamePart)}, +- {477, 492, -1, sizeof(::google::protobuf::UninterpretedOption)}, +- {499, 513, -1, sizeof(::google::protobuf::FeatureSet)}, +- {519, 530, -1, sizeof(::google::protobuf::FeatureSetDefaults_FeatureSetEditionDefault)}, +- {533, 544, -1, sizeof(::google::protobuf::FeatureSetDefaults)}, +- {547, 560, -1, sizeof(::google::protobuf::SourceCodeInfo_Location)}, +- {565, -1, -1, sizeof(::google::protobuf::SourceCodeInfo)}, +- {574, 587, -1, sizeof(::google::protobuf::GeneratedCodeInfo_Annotation)}, +- {592, -1, -1, sizeof(::google::protobuf::GeneratedCodeInfo)}, ++ {251, 281, -1, sizeof(::google::protobuf::FileOptions)}, ++ {303, 318, -1, sizeof(::google::protobuf::MessageOptions)}, ++ {325, 335, -1, sizeof(::google::protobuf::FieldOptions_EditionDefault)}, ++ {337, 349, -1, sizeof(::google::protobuf::FieldOptions_FeatureSupport)}, ++ {353, 375, -1, sizeof(::google::protobuf::FieldOptions)}, ++ {389, 399, -1, sizeof(::google::protobuf::OneofOptions)}, ++ {401, 414, -1, sizeof(::google::protobuf::EnumOptions)}, ++ {419, 432, -1, sizeof(::google::protobuf::EnumValueOptions)}, ++ {437, 448, -1, sizeof(::google::protobuf::ServiceOptions)}, ++ {451, 463, -1, sizeof(::google::protobuf::MethodOptions)}, ++ {467, 477, -1, sizeof(::google::protobuf::UninterpretedOption_NamePart)}, ++ {479, 494, -1, sizeof(::google::protobuf::UninterpretedOption)}, ++ {501, 515, -1, sizeof(::google::protobuf::FeatureSet)}, ++ {521, 532, -1, sizeof(::google::protobuf::FeatureSetDefaults_FeatureSetEditionDefault)}, ++ {535, 546, -1, sizeof(::google::protobuf::FeatureSetDefaults)}, ++ {549, 562, -1, sizeof(::google::protobuf::SourceCodeInfo_Location)}, ++ {567, -1, -1, sizeof(::google::protobuf::SourceCodeInfo)}, ++ {576, 589, -1, sizeof(::google::protobuf::GeneratedCodeInfo_Annotation)}, ++ {594, -1, -1, sizeof(::google::protobuf::GeneratedCodeInfo)}, + }; + static const ::_pb::Message* const file_default_instances[] = { + &::google::protobuf::_FileDescriptorSet_default_instance_._instance, +@@ -1997,7 +2000,7 @@ const char descriptor_table_protodef_google_2fprotobuf_2fdescriptor_2eproto[] AB + "\001(\t\022\023\n\013output_type\030\003 \001(\t\022/\n\007options\030\004 \001(" + "\0132\036.google.protobuf.MethodOptions\022\037\n\020cli" + "ent_streaming\030\005 \001(\010:\005false\022\037\n\020server_str" +- "eaming\030\006 \001(\010:\005false\"\313\006\n\013FileOptions\022\024\n\014j" ++ "eaming\030\006 \001(\010:\005false\"\365\006\n\013FileOptions\022\024\n\014j" + "ava_package\030\001 \001(\t\022\034\n\024java_outer_classnam" + "e\030\010 \001(\t\022\"\n\023java_multiple_files\030\n \001(\010:\005fa" + "lse\022)\n\035java_generate_equals_and_hash\030\024 \001" +@@ -2008,170 +2011,171 @@ const char descriptor_table_protodef_google_2fprotobuf_2fdescriptor_2eproto[] AB + "\030\020 \001(\010:\005false\022$\n\025java_generic_services\030\021" + " \001(\010:\005false\022\"\n\023py_generic_services\030\022 \001(\010" + ":\005false\022\031\n\ndeprecated\030\027 \001(\010:\005false\022\036\n\020cc" +- "_enable_arenas\030\037 \001(\010:\004true\022\031\n\021objc_class" +- "_prefix\030$ \001(\t\022\030\n\020csharp_namespace\030% \001(\t\022" +- "\024\n\014swift_prefix\030\' \001(\t\022\030\n\020php_class_prefi" +- "x\030( \001(\t\022\025\n\rphp_namespace\030) \001(\t\022\036\n\026php_me" +- "tadata_namespace\030, \001(\t\022\024\n\014ruby_package\030-" +- " \001(\t\022-\n\010features\0302 \001(\0132\033.google.protobuf" +- ".FeatureSet\022C\n\024uninterpreted_option\030\347\007 \003" +- "(\0132$.google.protobuf.UninterpretedOption" +- "\":\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE" +- "\020\002\022\020\n\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002J\004\010*\020+J\004\010" +- "&\020\'R\024php_generic_services\"\347\002\n\016MessageOpt" +- "ions\022&\n\027message_set_wire_format\030\001 \001(\010:\005f" +- "alse\022.\n\037no_standard_descriptor_accessor\030" +- "\002 \001(\010:\005false\022\031\n\ndeprecated\030\003 \001(\010:\005false\022" +- "\021\n\tmap_entry\030\007 \001(\010\0222\n&deprecated_legacy_" +- "json_field_conflicts\030\013 \001(\010B\002\030\001\022-\n\010featur" +- "es\030\014 \001(\0132\033.google.protobuf.FeatureSet\022C\n" +- "\024uninterpreted_option\030\347\007 \003(\0132$.google.pr" +- "otobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002J\004\010" +- "\004\020\005J\004\010\005\020\006J\004\010\006\020\007J\004\010\010\020\tJ\004\010\t\020\n\"\243\013\n\014FieldOpt" +- "ions\022:\n\005ctype\030\001 \001(\0162#.google.protobuf.Fi" +- "eldOptions.CType:\006STRING\022\016\n\006packed\030\002 \001(\010" +- "\022\?\n\006jstype\030\006 \001(\0162$.google.protobuf.Field" +- "Options.JSType:\tJS_NORMAL\022\023\n\004lazy\030\005 \001(\010:" +- "\005false\022\036\n\017unverified_lazy\030\017 \001(\010:\005false\022\031" +- "\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(\010:" +- "\005false\022\033\n\014debug_redact\030\020 \001(\010:\005false\022@\n\tr" +- "etention\030\021 \001(\0162-.google.protobuf.FieldOp" +- "tions.OptionRetention\022\?\n\007targets\030\023 \003(\0162." +- ".google.protobuf.FieldOptions.OptionTarg" +- "etType\022F\n\020edition_defaults\030\024 \003(\0132,.googl" +- "e.protobuf.FieldOptions.EditionDefault\022-" +- "\n\010features\030\025 \001(\0132\033.google.protobuf.Featu" +- "reSet\022E\n\017feature_support\030\026 \001(\0132,.google." +- "protobuf.FieldOptions.FeatureSupport\022C\n\024" +- "uninterpreted_option\030\347\007 \003(\0132$.google.pro" +- "tobuf.UninterpretedOption\032J\n\016EditionDefa" +- "ult\022)\n\007edition\030\003 \001(\0162\030.google.protobuf.E" +- "dition\022\r\n\005value\030\002 \001(\t\032\314\001\n\016FeatureSupport" +- "\0224\n\022edition_introduced\030\001 \001(\0162\030.google.pr" +- "otobuf.Edition\0224\n\022edition_deprecated\030\002 \001" +- "(\0162\030.google.protobuf.Edition\022\033\n\023deprecat" +- "ion_warning\030\003 \001(\t\0221\n\017edition_removed\030\004 \001" +- "(\0162\030.google.protobuf.Edition\"/\n\005CType\022\n\n" +- "\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"5\n" +- "\006JSType\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022\r\n" +- "\tJS_NUMBER\020\002\"U\n\017OptionRetention\022\025\n\021RETEN" +- "TION_UNKNOWN\020\000\022\025\n\021RETENTION_RUNTIME\020\001\022\024\n" +- "\020RETENTION_SOURCE\020\002\"\214\002\n\020OptionTargetType" +- "\022\027\n\023TARGET_TYPE_UNKNOWN\020\000\022\024\n\020TARGET_TYPE" +- "_FILE\020\001\022\037\n\033TARGET_TYPE_EXTENSION_RANGE\020\002" +- "\022\027\n\023TARGET_TYPE_MESSAGE\020\003\022\025\n\021TARGET_TYPE" +- "_FIELD\020\004\022\025\n\021TARGET_TYPE_ONEOF\020\005\022\024\n\020TARGE" +- "T_TYPE_ENUM\020\006\022\032\n\026TARGET_TYPE_ENUM_ENTRY\020" +- "\007\022\027\n\023TARGET_TYPE_SERVICE\020\010\022\026\n\022TARGET_TYP" +- "E_METHOD\020\t*\t\010\350\007\020\200\200\200\200\002J\004\010\004\020\005J\004\010\022\020\023\"\215\001\n\014On" +- "eofOptions\022-\n\010features\030\001 \001(\0132\033.google.pr" +- "otobuf.FeatureSet\022C\n\024uninterpreted_optio" +- "n\030\347\007 \003(\0132$.google.protobuf.Uninterpreted" +- "Option*\t\010\350\007\020\200\200\200\200\002\"\366\001\n\013EnumOptions\022\023\n\013all" +- "ow_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005fals" +- "e\0222\n&deprecated_legacy_json_field_confli" +- "cts\030\006 \001(\010B\002\030\001\022-\n\010features\030\007 \001(\0132\033.google" +- ".protobuf.FeatureSet\022C\n\024uninterpreted_op" +- "tion\030\347\007 \003(\0132$.google.protobuf.Uninterpre" +- "tedOption*\t\010\350\007\020\200\200\200\200\002J\004\010\005\020\006\"\220\002\n\020EnumValue" +- "Options\022\031\n\ndeprecated\030\001 \001(\010:\005false\022-\n\010fe" +- "atures\030\002 \001(\0132\033.google.protobuf.FeatureSe" +- "t\022\033\n\014debug_redact\030\003 \001(\010:\005false\022E\n\017featur" +- "e_support\030\004 \001(\0132,.google.protobuf.FieldO" +- "ptions.FeatureSupport\022C\n\024uninterpreted_o" +- "ption\030\347\007 \003(\0132$.google.protobuf.Uninterpr" +- "etedOption*\t\010\350\007\020\200\200\200\200\002\"\252\001\n\016ServiceOptions" +- "\022-\n\010features\030\" \001(\0132\033.google.protobuf.Fea" +- "tureSet\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024un" +- "interpreted_option\030\347\007 \003(\0132$.google.proto" +- "buf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\334\002\n\rM" +- "ethodOptions\022\031\n\ndeprecated\030! \001(\010:\005false\022" +- "_\n\021idempotency_level\030\" \001(\0162/.google.prot" +- "obuf.MethodOptions.IdempotencyLevel:\023IDE" +- "MPOTENCY_UNKNOWN\022-\n\010features\030# \001(\0132\033.goo" +- "gle.protobuf.FeatureSet\022C\n\024uninterpreted" ++ "_enable_arenas\030\037 \001(\010:\004true\022(\n\031cc_mutable" ++ "_donated_string\030 \001(\010:\005false\022\031\n\021objc_cla" ++ "ss_prefix\030$ \001(\t\022\030\n\020csharp_namespace\030% \001(" ++ "\t\022\024\n\014swift_prefix\030\' \001(\t\022\030\n\020php_class_pre" ++ "fix\030( \001(\t\022\025\n\rphp_namespace\030) \001(\t\022\036\n\026php_" ++ "metadata_namespace\030, \001(\t\022\024\n\014ruby_package" ++ "\030- \001(\t\022-\n\010features\0302 \001(\0132\033.google.protob" ++ "uf.FeatureSet\022C\n\024uninterpreted_option\030\347\007" ++ " \003(\0132$.google.protobuf.UninterpretedOpti" ++ "on\":\n\014OptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SI" ++ "ZE\020\002\022\020\n\014LITE_RUNTIME\020\003*\t\010\350\007\020\200\200\200\200\002J\004\010*\020+J" ++ "\004\010&\020\'R\024php_generic_services\"\347\002\n\016MessageO" ++ "ptions\022&\n\027message_set_wire_format\030\001 \001(\010:" ++ "\005false\022.\n\037no_standard_descriptor_accesso" ++ "r\030\002 \001(\010:\005false\022\031\n\ndeprecated\030\003 \001(\010:\005fals" ++ "e\022\021\n\tmap_entry\030\007 \001(\010\0222\n&deprecated_legac" ++ "y_json_field_conflicts\030\013 \001(\010B\002\030\001\022-\n\010feat" ++ "ures\030\014 \001(\0132\033.google.protobuf.FeatureSet\022" ++ "C\n\024uninterpreted_option\030\347\007 \003(\0132$.google." ++ "protobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002J" ++ "\004\010\004\020\005J\004\010\005\020\006J\004\010\006\020\007J\004\010\010\020\tJ\004\010\t\020\n\"\243\013\n\014FieldO" ++ "ptions\022:\n\005ctype\030\001 \001(\0162#.google.protobuf." ++ "FieldOptions.CType:\006STRING\022\016\n\006packed\030\002 \001" ++ "(\010\022\?\n\006jstype\030\006 \001(\0162$.google.protobuf.Fie" ++ "ldOptions.JSType:\tJS_NORMAL\022\023\n\004lazy\030\005 \001(" ++ "\010:\005false\022\036\n\017unverified_lazy\030\017 \001(\010:\005false" ++ "\022\031\n\ndeprecated\030\003 \001(\010:\005false\022\023\n\004weak\030\n \001(" ++ "\010:\005false\022\033\n\014debug_redact\030\020 \001(\010:\005false\022@\n" ++ "\tretention\030\021 \001(\0162-.google.protobuf.Field" ++ "Options.OptionRetention\022\?\n\007targets\030\023 \003(\016" ++ "2..google.protobuf.FieldOptions.OptionTa" ++ "rgetType\022F\n\020edition_defaults\030\024 \003(\0132,.goo" ++ "gle.protobuf.FieldOptions.EditionDefault" ++ "\022-\n\010features\030\025 \001(\0132\033.google.protobuf.Fea" ++ "tureSet\022E\n\017feature_support\030\026 \001(\0132,.googl" ++ "e.protobuf.FieldOptions.FeatureSupport\022C" ++ "\n\024uninterpreted_option\030\347\007 \003(\0132$.google.p" ++ "rotobuf.UninterpretedOption\032J\n\016EditionDe" ++ "fault\022)\n\007edition\030\003 \001(\0162\030.google.protobuf" ++ ".Edition\022\r\n\005value\030\002 \001(\t\032\314\001\n\016FeatureSuppo" ++ "rt\0224\n\022edition_introduced\030\001 \001(\0162\030.google." ++ "protobuf.Edition\0224\n\022edition_deprecated\030\002" ++ " \001(\0162\030.google.protobuf.Edition\022\033\n\023deprec" ++ "ation_warning\030\003 \001(\t\0221\n\017edition_removed\030\004" ++ " \001(\0162\030.google.protobuf.Edition\"/\n\005CType\022" ++ "\n\n\006STRING\020\000\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"" ++ "5\n\006JSType\022\r\n\tJS_NORMAL\020\000\022\r\n\tJS_STRING\020\001\022" ++ "\r\n\tJS_NUMBER\020\002\"U\n\017OptionRetention\022\025\n\021RET" ++ "ENTION_UNKNOWN\020\000\022\025\n\021RETENTION_RUNTIME\020\001\022" ++ "\024\n\020RETENTION_SOURCE\020\002\"\214\002\n\020OptionTargetTy" ++ "pe\022\027\n\023TARGET_TYPE_UNKNOWN\020\000\022\024\n\020TARGET_TY" ++ "PE_FILE\020\001\022\037\n\033TARGET_TYPE_EXTENSION_RANGE" ++ "\020\002\022\027\n\023TARGET_TYPE_MESSAGE\020\003\022\025\n\021TARGET_TY" ++ "PE_FIELD\020\004\022\025\n\021TARGET_TYPE_ONEOF\020\005\022\024\n\020TAR" ++ "GET_TYPE_ENUM\020\006\022\032\n\026TARGET_TYPE_ENUM_ENTR" ++ "Y\020\007\022\027\n\023TARGET_TYPE_SERVICE\020\010\022\026\n\022TARGET_T" ++ "YPE_METHOD\020\t*\t\010\350\007\020\200\200\200\200\002J\004\010\004\020\005J\004\010\022\020\023\"\215\001\n\014" ++ "OneofOptions\022-\n\010features\030\001 \001(\0132\033.google." ++ "protobuf.FeatureSet\022C\n\024uninterpreted_opt" ++ "ion\030\347\007 \003(\0132$.google.protobuf.Uninterpret" ++ "edOption*\t\010\350\007\020\200\200\200\200\002\"\366\001\n\013EnumOptions\022\023\n\013a" ++ "llow_alias\030\002 \001(\010\022\031\n\ndeprecated\030\003 \001(\010:\005fa" ++ "lse\0222\n&deprecated_legacy_json_field_conf" ++ "licts\030\006 \001(\010B\002\030\001\022-\n\010features\030\007 \001(\0132\033.goog" ++ "le.protobuf.FeatureSet\022C\n\024uninterpreted_" ++ "option\030\347\007 \003(\0132$.google.protobuf.Uninterp" ++ "retedOption*\t\010\350\007\020\200\200\200\200\002J\004\010\005\020\006\"\220\002\n\020EnumVal" ++ "ueOptions\022\031\n\ndeprecated\030\001 \001(\010:\005false\022-\n\010" ++ "features\030\002 \001(\0132\033.google.protobuf.Feature" ++ "Set\022\033\n\014debug_redact\030\003 \001(\010:\005false\022E\n\017feat" ++ "ure_support\030\004 \001(\0132,.google.protobuf.Fiel" ++ "dOptions.FeatureSupport\022C\n\024uninterpreted" + "_option\030\347\007 \003(\0132$.google.protobuf.Uninter" +- "pretedOption\"P\n\020IdempotencyLevel\022\027\n\023IDEM" +- "POTENCY_UNKNOWN\020\000\022\023\n\017NO_SIDE_EFFECTS\020\001\022\016" +- "\n\nIDEMPOTENT\020\002*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023Uninterpre" +- "tedOption\022;\n\004name\030\002 \003(\0132-.google.protobu" +- "f.UninterpretedOption.NamePart\022\030\n\020identi" +- "fier_value\030\003 \001(\t\022\032\n\022positive_int_value\030\004" +- " \001(\004\022\032\n\022negative_int_value\030\005 \001(\003\022\024\n\014doub" +- "le_value\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027\n\017" +- "aggregate_value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tnam" +- "e_part\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010\"\311\t\n\nF" +- "eatureSet\022\202\001\n\016field_presence\030\001 \001(\0162).goo" +- "gle.protobuf.FeatureSet.FieldPresenceB\?\210" +- "\001\001\230\001\004\230\001\001\242\001\r\022\010EXPLICIT\030\204\007\242\001\r\022\010IMPLICIT\030\347\007" +- "\242\001\r\022\010EXPLICIT\030\350\007\262\001\003\010\350\007\022b\n\tenum_type\030\002 \001(" +- "\0162$.google.protobuf.FeatureSet.EnumTypeB" +- ")\210\001\001\230\001\006\230\001\001\242\001\013\022\006CLOSED\030\204\007\242\001\t\022\004OPEN\030\347\007\262\001\003\010" +- "\350\007\022\201\001\n\027repeated_field_encoding\030\003 \001(\01621.g" +- "oogle.protobuf.FeatureSet.RepeatedFieldE" +- "ncodingB-\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPANDED\030\204\007\242\001\013\022\006P" +- "ACKED\030\347\007\262\001\003\010\350\007\022n\n\017utf8_validation\030\004 \001(\0162" +- "*.google.protobuf.FeatureSet.Utf8Validat" +- "ionB)\210\001\001\230\001\004\230\001\001\242\001\t\022\004NONE\030\204\007\242\001\013\022\006VERIFY\030\347\007" +- "\262\001\003\010\350\007\022m\n\020message_encoding\030\005 \001(\0162+.googl" +- "e.protobuf.FeatureSet.MessageEncodingB&\210" +- "\001\001\230\001\004\230\001\001\242\001\024\022\017LENGTH_PREFIXED\030\204\007\262\001\003\010\350\007\022v\n" +- "\013json_format\030\006 \001(\0162&.google.protobuf.Fea" +- "tureSet.JsonFormatB9\210\001\001\230\001\003\230\001\006\230\001\001\242\001\027\022\022LEG" +- "ACY_BEST_EFFORT\030\204\007\242\001\n\022\005ALLOW\030\347\007\262\001\003\010\350\007\"\\\n" +- "\rFieldPresence\022\032\n\026FIELD_PRESENCE_UNKNOWN" +- "\020\000\022\014\n\010EXPLICIT\020\001\022\014\n\010IMPLICIT\020\002\022\023\n\017LEGACY" +- "_REQUIRED\020\003\"7\n\010EnumType\022\025\n\021ENUM_TYPE_UNK" +- "NOWN\020\000\022\010\n\004OPEN\020\001\022\n\n\006CLOSED\020\002\"V\n\025Repeated" +- "FieldEncoding\022#\n\037REPEATED_FIELD_ENCODING" +- "_UNKNOWN\020\000\022\n\n\006PACKED\020\001\022\014\n\010EXPANDED\020\002\"I\n\016" +- "Utf8Validation\022\033\n\027UTF8_VALIDATION_UNKNOW" +- "N\020\000\022\n\n\006VERIFY\020\002\022\010\n\004NONE\020\003\"\004\010\001\020\001\"S\n\017Messa" +- "geEncoding\022\034\n\030MESSAGE_ENCODING_UNKNOWN\020\000" +- "\022\023\n\017LENGTH_PREFIXED\020\001\022\r\n\tDELIMITED\020\002\"H\n\n" +- "JsonFormat\022\027\n\023JSON_FORMAT_UNKNOWN\020\000\022\t\n\005A" +- "LLOW\020\001\022\026\n\022LEGACY_BEST_EFFORT\020\002*\006\010\350\007\020\213N*\006" +- "\010\213N\020\220N*\006\010\220N\020\221NJ\006\010\347\007\020\350\007\"\230\003\n\022FeatureSetDef" +- "aults\022N\n\010defaults\030\001 \003(\0132<.google.protobu" +- "f.FeatureSetDefaults.FeatureSetEditionDe" +- "fault\0221\n\017minimum_edition\030\004 \001(\0162\030.google." +- "protobuf.Edition\0221\n\017maximum_edition\030\005 \001(" +- "\0162\030.google.protobuf.Edition\032\313\001\n\030FeatureS" +- "etEditionDefault\022)\n\007edition\030\003 \001(\0162\030.goog" +- "le.protobuf.Edition\0229\n\024overridable_featu" +- "res\030\004 \001(\0132\033.google.protobuf.FeatureSet\0223" +- "\n\016fixed_features\030\005 \001(\0132\033.google.protobuf" +- ".FeatureSetJ\004\010\001\020\002J\004\010\002\020\003R\010features\"\325\001\n\016So" +- "urceCodeInfo\022:\n\010location\030\001 \003(\0132(.google." +- "protobuf.SourceCodeInfo.Location\032\206\001\n\010Loc" +- "ation\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B\002\020" +- "\001\022\030\n\020leading_comments\030\003 \001(\t\022\031\n\021trailing_" +- "comments\030\004 \001(\t\022!\n\031leading_detached_comme" +- "nts\030\006 \003(\t\"\234\002\n\021GeneratedCodeInfo\022A\n\nannot" +- "ation\030\001 \003(\0132-.google.protobuf.GeneratedC" +- "odeInfo.Annotation\032\303\001\n\nAnnotation\022\020\n\004pat" +- "h\030\001 \003(\005B\002\020\001\022\023\n\013source_file\030\002 \001(\t\022\r\n\005begi" +- "n\030\003 \001(\005\022\013\n\003end\030\004 \001(\005\022H\n\010semantic\030\005 \001(\01626" +- ".google.protobuf.GeneratedCodeInfo.Annot" +- "ation.Semantic\"(\n\010Semantic\022\010\n\004NONE\020\000\022\007\n\003" +- "SET\020\001\022\t\n\005ALIAS\020\002*\247\002\n\007Edition\022\023\n\017EDITION_" +- "UNKNOWN\020\000\022\023\n\016EDITION_LEGACY\020\204\007\022\023\n\016EDITIO" +- "N_PROTO2\020\346\007\022\023\n\016EDITION_PROTO3\020\347\007\022\021\n\014EDIT" +- "ION_2023\020\350\007\022\021\n\014EDITION_2024\020\351\007\022\027\n\023EDITIO" +- "N_1_TEST_ONLY\020\001\022\027\n\023EDITION_2_TEST_ONLY\020\002" +- "\022\035\n\027EDITION_99997_TEST_ONLY\020\235\215\006\022\035\n\027EDITI" +- "ON_99998_TEST_ONLY\020\236\215\006\022\035\n\027EDITION_99999_" +- "TEST_ONLY\020\237\215\006\022\023\n\013EDITION_MAX\020\377\377\377\377\007B~\n\023co" +- "m.google.protobufB\020DescriptorProtosH\001Z-g" +- "oogle.golang.org/protobuf/types/descript" +- "orpb\370\001\001\242\002\003GPB\252\002\032Google.Protobuf.Reflecti" +- "on" ++ "pretedOption*\t\010\350\007\020\200\200\200\200\002\"\252\001\n\016ServiceOptio" ++ "ns\022-\n\010features\030\" \001(\0132\033.google.protobuf.F" ++ "eatureSet\022\031\n\ndeprecated\030! \001(\010:\005false\022C\n\024" ++ "uninterpreted_option\030\347\007 \003(\0132$.google.pro" ++ "tobuf.UninterpretedOption*\t\010\350\007\020\200\200\200\200\002\"\334\002\n" ++ "\rMethodOptions\022\031\n\ndeprecated\030! \001(\010:\005fals" ++ "e\022_\n\021idempotency_level\030\" \001(\0162/.google.pr" ++ "otobuf.MethodOptions.IdempotencyLevel:\023I" ++ "DEMPOTENCY_UNKNOWN\022-\n\010features\030# \001(\0132\033.g" ++ "oogle.protobuf.FeatureSet\022C\n\024uninterpret" ++ "ed_option\030\347\007 \003(\0132$.google.protobuf.Unint" ++ "erpretedOption\"P\n\020IdempotencyLevel\022\027\n\023ID" ++ "EMPOTENCY_UNKNOWN\020\000\022\023\n\017NO_SIDE_EFFECTS\020\001" ++ "\022\016\n\nIDEMPOTENT\020\002*\t\010\350\007\020\200\200\200\200\002\"\236\002\n\023Uninterp" ++ "retedOption\022;\n\004name\030\002 \003(\0132-.google.proto" ++ "buf.UninterpretedOption.NamePart\022\030\n\020iden" ++ "tifier_value\030\003 \001(\t\022\032\n\022positive_int_value" ++ "\030\004 \001(\004\022\032\n\022negative_int_value\030\005 \001(\003\022\024\n\014do" ++ "uble_value\030\006 \001(\001\022\024\n\014string_value\030\007 \001(\014\022\027" ++ "\n\017aggregate_value\030\010 \001(\t\0323\n\010NamePart\022\021\n\tn" ++ "ame_part\030\001 \002(\t\022\024\n\014is_extension\030\002 \002(\010\"\311\t\n" ++ "\nFeatureSet\022\202\001\n\016field_presence\030\001 \001(\0162).g" ++ "oogle.protobuf.FeatureSet.FieldPresenceB" ++ "\?\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPLICIT\030\204\007\242\001\r\022\010IMPLICIT\030" ++ "\347\007\242\001\r\022\010EXPLICIT\030\350\007\262\001\003\010\350\007\022b\n\tenum_type\030\002 " ++ "\001(\0162$.google.protobuf.FeatureSet.EnumTyp" ++ "eB)\210\001\001\230\001\006\230\001\001\242\001\013\022\006CLOSED\030\204\007\242\001\t\022\004OPEN\030\347\007\262\001" ++ "\003\010\350\007\022\201\001\n\027repeated_field_encoding\030\003 \001(\01621" ++ ".google.protobuf.FeatureSet.RepeatedFiel" ++ "dEncodingB-\210\001\001\230\001\004\230\001\001\242\001\r\022\010EXPANDED\030\204\007\242\001\013\022" ++ "\006PACKED\030\347\007\262\001\003\010\350\007\022n\n\017utf8_validation\030\004 \001(" ++ "\0162*.google.protobuf.FeatureSet.Utf8Valid" ++ "ationB)\210\001\001\230\001\004\230\001\001\242\001\t\022\004NONE\030\204\007\242\001\013\022\006VERIFY\030" ++ "\347\007\262\001\003\010\350\007\022m\n\020message_encoding\030\005 \001(\0162+.goo" ++ "gle.protobuf.FeatureSet.MessageEncodingB" ++ "&\210\001\001\230\001\004\230\001\001\242\001\024\022\017LENGTH_PREFIXED\030\204\007\262\001\003\010\350\007\022" ++ "v\n\013json_format\030\006 \001(\0162&.google.protobuf.F" ++ "eatureSet.JsonFormatB9\210\001\001\230\001\003\230\001\006\230\001\001\242\001\027\022\022L" ++ "EGACY_BEST_EFFORT\030\204\007\242\001\n\022\005ALLOW\030\347\007\262\001\003\010\350\007\"" ++ "\\\n\rFieldPresence\022\032\n\026FIELD_PRESENCE_UNKNO" ++ "WN\020\000\022\014\n\010EXPLICIT\020\001\022\014\n\010IMPLICIT\020\002\022\023\n\017LEGA" ++ "CY_REQUIRED\020\003\"7\n\010EnumType\022\025\n\021ENUM_TYPE_U" ++ "NKNOWN\020\000\022\010\n\004OPEN\020\001\022\n\n\006CLOSED\020\002\"V\n\025Repeat" ++ "edFieldEncoding\022#\n\037REPEATED_FIELD_ENCODI" ++ "NG_UNKNOWN\020\000\022\n\n\006PACKED\020\001\022\014\n\010EXPANDED\020\002\"I" ++ "\n\016Utf8Validation\022\033\n\027UTF8_VALIDATION_UNKN" ++ "OWN\020\000\022\n\n\006VERIFY\020\002\022\010\n\004NONE\020\003\"\004\010\001\020\001\"S\n\017Mes" ++ "sageEncoding\022\034\n\030MESSAGE_ENCODING_UNKNOWN" ++ "\020\000\022\023\n\017LENGTH_PREFIXED\020\001\022\r\n\tDELIMITED\020\002\"H" ++ "\n\nJsonFormat\022\027\n\023JSON_FORMAT_UNKNOWN\020\000\022\t\n" ++ "\005ALLOW\020\001\022\026\n\022LEGACY_BEST_EFFORT\020\002*\006\010\350\007\020\213N" ++ "*\006\010\213N\020\220N*\006\010\220N\020\221NJ\006\010\347\007\020\350\007\"\230\003\n\022FeatureSetD" ++ "efaults\022N\n\010defaults\030\001 \003(\0132<.google.proto" ++ "buf.FeatureSetDefaults.FeatureSetEdition" ++ "Default\0221\n\017minimum_edition\030\004 \001(\0162\030.googl" ++ "e.protobuf.Edition\0221\n\017maximum_edition\030\005 " ++ "\001(\0162\030.google.protobuf.Edition\032\313\001\n\030Featur" ++ "eSetEditionDefault\022)\n\007edition\030\003 \001(\0162\030.go" ++ "ogle.protobuf.Edition\0229\n\024overridable_fea" ++ "tures\030\004 \001(\0132\033.google.protobuf.FeatureSet" ++ "\0223\n\016fixed_features\030\005 \001(\0132\033.google.protob" ++ "uf.FeatureSetJ\004\010\001\020\002J\004\010\002\020\003R\010features\"\325\001\n\016" ++ "SourceCodeInfo\022:\n\010location\030\001 \003(\0132(.googl" ++ "e.protobuf.SourceCodeInfo.Location\032\206\001\n\010L" ++ "ocation\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004span\030\002 \003(\005B" ++ "\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022\031\n\021trailin" ++ "g_comments\030\004 \001(\t\022!\n\031leading_detached_com" ++ "ments\030\006 \003(\t\"\234\002\n\021GeneratedCodeInfo\022A\n\nann" ++ "otation\030\001 \003(\0132-.google.protobuf.Generate" ++ "dCodeInfo.Annotation\032\303\001\n\nAnnotation\022\020\n\004p" ++ "ath\030\001 \003(\005B\002\020\001\022\023\n\013source_file\030\002 \001(\t\022\r\n\005be" ++ "gin\030\003 \001(\005\022\013\n\003end\030\004 \001(\005\022H\n\010semantic\030\005 \001(\016" ++ "26.google.protobuf.GeneratedCodeInfo.Ann" ++ "otation.Semantic\"(\n\010Semantic\022\010\n\004NONE\020\000\022\007" ++ "\n\003SET\020\001\022\t\n\005ALIAS\020\002*\247\002\n\007Edition\022\023\n\017EDITIO" ++ "N_UNKNOWN\020\000\022\023\n\016EDITION_LEGACY\020\204\007\022\023\n\016EDIT" ++ "ION_PROTO2\020\346\007\022\023\n\016EDITION_PROTO3\020\347\007\022\021\n\014ED" ++ "ITION_2023\020\350\007\022\021\n\014EDITION_2024\020\351\007\022\027\n\023EDIT" ++ "ION_1_TEST_ONLY\020\001\022\027\n\023EDITION_2_TEST_ONLY" ++ "\020\002\022\035\n\027EDITION_99997_TEST_ONLY\020\235\215\006\022\035\n\027EDI" ++ "TION_99998_TEST_ONLY\020\236\215\006\022\035\n\027EDITION_9999" ++ "9_TEST_ONLY\020\237\215\006\022\023\n\013EDITION_MAX\020\377\377\377\377\007B~\n\023" ++ "com.google.protobufB\020DescriptorProtosH\001Z" ++ "-google.golang.org/protobuf/types/descri" ++ "ptorpb\370\001\001\242\002\003GPB\252\002\032Google.Protobuf.Reflec" ++ "tion" + }; + static ::absl::once_flag descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once; + PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable descriptor_table_google_2fprotobuf_2fdescriptor_2eproto = { + false, + false, +- 10082, ++ 10124, + descriptor_table_protodef_google_2fprotobuf_2fdescriptor_2eproto, + "google/protobuf/descriptor.proto", + &descriptor_table_google_2fprotobuf_2fdescriptor_2eproto_once, +@@ -7929,9 +7933,9 @@ inline void FileOptions::SharedCtor(::_pb::Arena* arena) { + ::memset(reinterpret_cast(&_impl_) + + offsetof(Impl_, features_), + 0, +- offsetof(Impl_, deprecated_) - ++ offsetof(Impl_, cc_mutable_donated_string_) - + offsetof(Impl_, features_) + +- sizeof(Impl_::deprecated_)); ++ sizeof(Impl_::cc_mutable_donated_string_)); + } + FileOptions::~FileOptions() { + // @@protoc_insertion_point(destructor:google.protobuf.FileOptions) +@@ -7982,15 +7986,15 @@ const ::google::protobuf::MessageLite::ClassData* FileOptions::GetClassData() co + ::google::protobuf::internal::PrefetchToLocalCache(_class_data_.tc_table); + return _class_data_.base(); + } +-constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { ++constexpr ::_pbi::TcParseTable<5, 22, 3, 202, 12> FileOptions::_table_ = { + { + PROTOBUF_FIELD_OFFSET(FileOptions, _impl_._has_bits_), + PROTOBUF_FIELD_OFFSET(FileOptions, _impl_._extensions_), + 999, 248, // max_field_number, fast_idx_mask + offsetof(decltype(_table_), field_lookup_table), +- 3149166718, // skipmap ++ 1001683070, // skipmap + offsetof(decltype(_table_), field_entries), +- 21, // num_field_entries ++ 22, // num_field_entries + 3, // num_aux_entries + offsetof(decltype(_table_), aux_entries), + _class_data_.base(), +@@ -8015,7 +8019,7 @@ constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { + {66, 1, 0, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_outer_classname_)}}, + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; + {::_pbi::TcParser::FastEr1S1, +- {72, 18, 3, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.optimize_for_)}}, ++ {72, 19, 3, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.optimize_for_)}}, + // optional bool java_multiple_files = 10 [default = false]; + {::_pbi::TcParser::SingularVarintNoZag1(), + {80, 11, 0, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_multiple_files_)}}, +@@ -8065,11 +8069,11 @@ constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { + {::_pbi::TcParser::MiniParse, {}}, + // optional bool cc_enable_arenas = 31 [default = true]; + {::_pbi::TcParser::FastV8S2, +- {504, 19, 0, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_enable_arenas_)}}, ++ {504, 20, 0, PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_enable_arenas_)}}, + }}, {{ + 36, 0, 1, +- 48324, 12,999, 0, 1, +- 65534, 20, ++ 48324, 13,999, 0, 1, ++ 65534, 21, + 65535, 65535 + }}, {{ + // optional string java_package = 1; +@@ -8079,7 +8083,7 @@ constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { + {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_outer_classname_), _Internal::kHasBitsOffset + 1, 0, + (0 | ::_fl::kFcOptional | ::_fl::kRawString | ::_fl::kRepAString)}, + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; +- {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.optimize_for_), _Internal::kHasBitsOffset + 18, 2, ++ {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.optimize_for_), _Internal::kHasBitsOffset + 19, 2, + (0 | ::_fl::kFcOptional | ::_fl::kEnumRange)}, + // optional bool java_multiple_files = 10 [default = false]; + {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_multiple_files_), _Internal::kHasBitsOffset + 11, 0, +@@ -8106,7 +8110,10 @@ constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { + {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.java_string_check_utf8_), _Internal::kHasBitsOffset + 13, 0, + (0 | ::_fl::kFcOptional | ::_fl::kBool)}, + // optional bool cc_enable_arenas = 31 [default = true]; +- {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_enable_arenas_), _Internal::kHasBitsOffset + 19, 0, ++ {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_enable_arenas_), _Internal::kHasBitsOffset + 20, 0, ++ (0 | ::_fl::kFcOptional | ::_fl::kBool)}, ++ // optional bool cc_mutable_donated_string = 32 [default = false]; ++ {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.cc_mutable_donated_string_), _Internal::kHasBitsOffset + 18, 0, + (0 | ::_fl::kFcOptional | ::_fl::kBool)}, + // optional string objc_class_prefix = 36; + {PROTOBUF_FIELD_OFFSET(FileOptions, _impl_.objc_class_prefix_), _Internal::kHasBitsOffset + 3, 0, +@@ -8140,7 +8147,7 @@ constexpr ::_pbi::TcParseTable<5, 21, 3, 202, 12> FileOptions::_table_ = { + {::_pbi::TcParser::GetTable<::google::protobuf::UninterpretedOption>()}, + {1, 3}, + }}, {{ +- "\33\14\24\0\0\12\0\0\0\0\0\0\0\21\20\14\20\15\26\14\0\0\0\0" ++ "\33\14\24\0\0\12\0\0\0\0\0\0\0\0\21\20\14\20\15\26\14\0\0\0" + "google.protobuf.FileOptions" + "java_package" + "java_outer_classname" +@@ -8208,10 +8215,10 @@ PROTOBUF_NOINLINE void FileOptions::Clear() { + reinterpret_cast(&_impl_.java_generic_services_) - + reinterpret_cast(&_impl_.java_multiple_files_)) + sizeof(_impl_.java_generic_services_)); + } +- if (cached_has_bits & 0x000f0000u) { ++ if (cached_has_bits & 0x001f0000u) { + ::memset(&_impl_.py_generic_services_, 0, static_cast<::size_t>( +- reinterpret_cast(&_impl_.deprecated_) - +- reinterpret_cast(&_impl_.py_generic_services_)) + sizeof(_impl_.deprecated_)); ++ reinterpret_cast(&_impl_.cc_mutable_donated_string_) - ++ reinterpret_cast(&_impl_.py_generic_services_)) + sizeof(_impl_.cc_mutable_donated_string_)); + _impl_.optimize_for_ = 1; + _impl_.cc_enable_arenas_ = true; + } +@@ -8252,7 +8259,7 @@ PROTOBUF_NOINLINE void FileOptions::Clear() { + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; +- if (cached_has_bits & 0x00040000u) { ++ if (cached_has_bits & 0x00080000u) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteEnumToArray( + 9, this_._internal_optimize_for(), target); +@@ -8316,12 +8323,19 @@ PROTOBUF_NOINLINE void FileOptions::Clear() { + } + + // optional bool cc_enable_arenas = 31 [default = true]; +- if (cached_has_bits & 0x00080000u) { ++ if (cached_has_bits & 0x00100000u) { + target = stream->EnsureSpace(target); + target = ::_pbi::WireFormatLite::WriteBoolToArray( + 31, this_._internal_cc_enable_arenas(), target); + } + ++ // optional bool cc_mutable_donated_string = 32 [default = false]; ++ if (cached_has_bits & 0x00040000u) { ++ target = stream->EnsureSpace(target); ++ target = ::_pbi::WireFormatLite::WriteBoolToArray( ++ 32, this_._internal_cc_mutable_donated_string(), target); ++ } ++ + // optional string objc_class_prefix = 36; + if (cached_has_bits & 0x00000008u) { + const std::string& _s = this_._internal_objc_class_prefix(); +@@ -8513,7 +8527,7 @@ PROTOBUF_NOINLINE void FileOptions::Clear() { + total_size += 3; + } + } +- if (cached_has_bits & 0x000f0000u) { ++ if (cached_has_bits & 0x001f0000u) { + // optional bool py_generic_services = 18 [default = false]; + if (cached_has_bits & 0x00010000u) { + total_size += 3; +@@ -8522,13 +8536,17 @@ PROTOBUF_NOINLINE void FileOptions::Clear() { + if (cached_has_bits & 0x00020000u) { + total_size += 3; + } +- // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; ++ // optional bool cc_mutable_donated_string = 32 [default = false]; + if (cached_has_bits & 0x00040000u) { ++ total_size += 3; ++ } ++ // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; ++ if (cached_has_bits & 0x00080000u) { + total_size += 1 + + ::_pbi::WireFormatLite::EnumSize(this_._internal_optimize_for()); + } + // optional bool cc_enable_arenas = 31 [default = true]; +- if (cached_has_bits & 0x00080000u) { ++ if (cached_has_bits & 0x00100000u) { + total_size += 3; + } + } +@@ -8606,7 +8624,7 @@ void FileOptions::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::goo + _this->_impl_.java_generic_services_ = from._impl_.java_generic_services_; + } + } +- if (cached_has_bits & 0x000f0000u) { ++ if (cached_has_bits & 0x001f0000u) { + if (cached_has_bits & 0x00010000u) { + _this->_impl_.py_generic_services_ = from._impl_.py_generic_services_; + } +@@ -8614,9 +8632,12 @@ void FileOptions::MergeImpl(::google::protobuf::MessageLite& to_msg, const ::goo + _this->_impl_.deprecated_ = from._impl_.deprecated_; + } + if (cached_has_bits & 0x00040000u) { +- _this->_impl_.optimize_for_ = from._impl_.optimize_for_; ++ _this->_impl_.cc_mutable_donated_string_ = from._impl_.cc_mutable_donated_string_; + } + if (cached_has_bits & 0x00080000u) { ++ _this->_impl_.optimize_for_ = from._impl_.optimize_for_; ++ } ++ if (cached_has_bits & 0x00100000u) { + _this->_impl_.cc_enable_arenas_ = from._impl_.cc_enable_arenas_; + } + } +diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h +index 247c54b57..f825e89f0 100644 +--- a/src/google/protobuf/descriptor.pb.h ++++ b/src/google/protobuf/descriptor.pb.h +@@ -865,6 +865,7 @@ class PROTOBUF_EXPORT UninterpretedOption_NamePart final : public ::google::prot + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name_part( + const std::string& value); + std::string* _internal_mutable_name_part(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_part_accessor(); + + public: + // required bool is_extension = 2; +@@ -1133,6 +1134,7 @@ class PROTOBUF_EXPORT SourceCodeInfo_Location final : public ::google::protobuf: + inline PROTOBUF_ALWAYS_INLINE void _internal_set_leading_comments( + const std::string& value); + std::string* _internal_mutable_leading_comments(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_leading_comments_accessor(); + + public: + // optional string trailing_comments = 4; +@@ -1150,6 +1152,7 @@ class PROTOBUF_EXPORT SourceCodeInfo_Location final : public ::google::protobuf: + inline PROTOBUF_ALWAYS_INLINE void _internal_set_trailing_comments( + const std::string& value); + std::string* _internal_mutable_trailing_comments(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_trailing_comments_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo.Location) +@@ -1392,6 +1395,7 @@ class PROTOBUF_EXPORT GeneratedCodeInfo_Annotation final : public ::google::prot + inline PROTOBUF_ALWAYS_INLINE void _internal_set_source_file( + const std::string& value); + std::string* _internal_mutable_source_file(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_source_file_accessor(); + + public: + // optional int32 begin = 3; +@@ -1627,6 +1631,7 @@ class PROTOBUF_EXPORT FieldOptions_FeatureSupport final : public ::google::proto + inline PROTOBUF_ALWAYS_INLINE void _internal_set_deprecation_warning( + const std::string& value); + std::string* _internal_mutable_deprecation_warning(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_deprecation_warning_accessor(); + + public: + // optional .google.protobuf.Edition edition_introduced = 1; +@@ -1858,6 +1863,7 @@ class PROTOBUF_EXPORT FieldOptions_EditionDefault final : public ::google::proto + inline PROTOBUF_ALWAYS_INLINE void _internal_set_value( + const std::string& value); + std::string* _internal_mutable_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_value_accessor(); + + public: + // optional .google.protobuf.Edition edition = 3; +@@ -2627,6 +2633,7 @@ class PROTOBUF_EXPORT ExtensionRangeOptions_Declaration final : public ::google: + inline PROTOBUF_ALWAYS_INLINE void _internal_set_full_name( + const std::string& value); + std::string* _internal_mutable_full_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_full_name_accessor(); + + public: + // optional string type = 3; +@@ -2644,6 +2651,7 @@ class PROTOBUF_EXPORT ExtensionRangeOptions_Declaration final : public ::google: + inline PROTOBUF_ALWAYS_INLINE void _internal_set_type( + const std::string& value); + std::string* _internal_mutable_type(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_type_accessor(); + + public: + // optional int32 number = 1; +@@ -3306,6 +3314,7 @@ class PROTOBUF_EXPORT UninterpretedOption final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_identifier_value( + const std::string& value); + std::string* _internal_mutable_identifier_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_identifier_value_accessor(); + + public: + // optional bytes string_value = 7; +@@ -3323,6 +3332,7 @@ class PROTOBUF_EXPORT UninterpretedOption final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_string_value( + const std::string& value); + std::string* _internal_mutable_string_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_string_value_accessor(); + + public: + // optional string aggregate_value = 8; +@@ -3340,6 +3350,7 @@ class PROTOBUF_EXPORT UninterpretedOption final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_aggregate_value( + const std::string& value); + std::string* _internal_mutable_aggregate_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_aggregate_value_accessor(); + + public: + // optional uint64 positive_int_value = 4; +@@ -5923,6 +5934,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + kJavaGenericServicesFieldNumber = 17, + kPyGenericServicesFieldNumber = 18, + kDeprecatedFieldNumber = 23, ++ kCcMutableDonatedStringFieldNumber = 32, + kOptimizeForFieldNumber = 9, + kCcEnableArenasFieldNumber = 31, + }; +@@ -5958,6 +5970,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_java_package( + const std::string& value); + std::string* _internal_mutable_java_package(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_java_package_accessor(); + + public: + // optional string java_outer_classname = 8; +@@ -5975,6 +5988,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_java_outer_classname( + const std::string& value); + std::string* _internal_mutable_java_outer_classname(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_java_outer_classname_accessor(); + + public: + // optional string go_package = 11; +@@ -5992,6 +6006,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_go_package( + const std::string& value); + std::string* _internal_mutable_go_package(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_go_package_accessor(); + + public: + // optional string objc_class_prefix = 36; +@@ -6009,6 +6024,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_objc_class_prefix( + const std::string& value); + std::string* _internal_mutable_objc_class_prefix(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_objc_class_prefix_accessor(); + + public: + // optional string csharp_namespace = 37; +@@ -6026,6 +6042,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_csharp_namespace( + const std::string& value); + std::string* _internal_mutable_csharp_namespace(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_csharp_namespace_accessor(); + + public: + // optional string swift_prefix = 39; +@@ -6043,6 +6060,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_swift_prefix( + const std::string& value); + std::string* _internal_mutable_swift_prefix(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_swift_prefix_accessor(); + + public: + // optional string php_class_prefix = 40; +@@ -6060,6 +6078,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_class_prefix( + const std::string& value); + std::string* _internal_mutable_php_class_prefix(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_php_class_prefix_accessor(); + + public: + // optional string php_namespace = 41; +@@ -6077,6 +6096,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_namespace( + const std::string& value); + std::string* _internal_mutable_php_namespace(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_php_namespace_accessor(); + + public: + // optional string php_metadata_namespace = 44; +@@ -6094,6 +6114,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_php_metadata_namespace( + const std::string& value); + std::string* _internal_mutable_php_metadata_namespace(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_php_metadata_namespace_accessor(); + + public: + // optional string ruby_package = 45; +@@ -6111,6 +6132,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_ruby_package( + const std::string& value); + std::string* _internal_mutable_ruby_package(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_ruby_package_accessor(); + + public: + // optional .google.protobuf.FeatureSet features = 50; +@@ -6204,6 +6226,17 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + bool _internal_deprecated() const; + void _internal_set_deprecated(bool value); + ++ public: ++ // optional bool cc_mutable_donated_string = 32 [default = false]; ++ bool has_cc_mutable_donated_string() const; ++ void clear_cc_mutable_donated_string() ; ++ bool cc_mutable_donated_string() const; ++ void set_cc_mutable_donated_string(bool value); ++ ++ private: ++ bool _internal_cc_mutable_donated_string() const; ++ void _internal_set_cc_mutable_donated_string(bool value); ++ + public: + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; + bool has_optimize_for() const; +@@ -6411,7 +6444,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + class _Internal; + friend class ::google::protobuf::internal::TcParser; + static const ::google::protobuf::internal::TcParseTable< +- 5, 21, 3, ++ 5, 22, 3, + 202, 12> + _table_; + +@@ -6452,6 +6485,7 @@ class PROTOBUF_EXPORT FileOptions final : public ::google::protobuf::Message + bool java_generic_services_; + bool py_generic_services_; + bool deprecated_; ++ bool cc_mutable_donated_string_; + int optimize_for_; + bool cc_enable_arenas_; + PROTOBUF_TSAN_DECLARE_MEMBER +@@ -8832,6 +8866,7 @@ class PROTOBUF_EXPORT OneofDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional .google.protobuf.OneofOptions options = 2; +@@ -9052,6 +9087,7 @@ class PROTOBUF_EXPORT MethodDescriptorProto final : public ::google::protobuf::M + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional string input_type = 2; +@@ -9069,6 +9105,7 @@ class PROTOBUF_EXPORT MethodDescriptorProto final : public ::google::protobuf::M + inline PROTOBUF_ALWAYS_INLINE void _internal_set_input_type( + const std::string& value); + std::string* _internal_mutable_input_type(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_input_type_accessor(); + + public: + // optional string output_type = 3; +@@ -9086,6 +9123,7 @@ class PROTOBUF_EXPORT MethodDescriptorProto final : public ::google::protobuf::M + inline PROTOBUF_ALWAYS_INLINE void _internal_set_output_type( + const std::string& value); + std::string* _internal_mutable_output_type(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_output_type_accessor(); + + public: + // optional .google.protobuf.MethodOptions options = 4; +@@ -9392,6 +9430,7 @@ class PROTOBUF_EXPORT FieldDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional string extendee = 2; +@@ -9409,6 +9448,7 @@ class PROTOBUF_EXPORT FieldDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_extendee( + const std::string& value); + std::string* _internal_mutable_extendee(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_extendee_accessor(); + + public: + // optional string type_name = 6; +@@ -9426,6 +9466,7 @@ class PROTOBUF_EXPORT FieldDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_name( + const std::string& value); + std::string* _internal_mutable_type_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_type_name_accessor(); + + public: + // optional string default_value = 7; +@@ -9443,6 +9484,7 @@ class PROTOBUF_EXPORT FieldDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_default_value( + const std::string& value); + std::string* _internal_mutable_default_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_default_value_accessor(); + + public: + // optional string json_name = 10; +@@ -9460,6 +9502,7 @@ class PROTOBUF_EXPORT FieldDescriptorProto final : public ::google::protobuf::Me + inline PROTOBUF_ALWAYS_INLINE void _internal_set_json_name( + const std::string& value); + std::string* _internal_mutable_json_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_json_name_accessor(); + + public: + // optional .google.protobuf.FieldOptions options = 8; +@@ -9741,6 +9784,7 @@ class PROTOBUF_EXPORT EnumValueDescriptorProto final : public ::google::protobuf + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional .google.protobuf.EnumValueOptions options = 3; +@@ -10210,6 +10254,7 @@ class PROTOBUF_EXPORT ServiceDescriptorProto final : public ::google::protobuf:: + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional .google.protobuf.ServiceOptions options = 3; +@@ -10487,6 +10532,7 @@ class PROTOBUF_EXPORT EnumDescriptorProto final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional .google.protobuf.EnumOptions options = 3; +@@ -10857,6 +10903,7 @@ class PROTOBUF_EXPORT DescriptorProto final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional .google.protobuf.MessageOptions options = 7; +@@ -11218,6 +11265,7 @@ class PROTOBUF_EXPORT FileDescriptorProto final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // optional string package = 2; +@@ -11235,6 +11283,7 @@ class PROTOBUF_EXPORT FileDescriptorProto final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_package( + const std::string& value); + std::string* _internal_mutable_package(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_package_accessor(); + + public: + // optional string syntax = 12; +@@ -11252,6 +11301,7 @@ class PROTOBUF_EXPORT FileDescriptorProto final : public ::google::protobuf::Mes + inline PROTOBUF_ALWAYS_INLINE void _internal_set_syntax( + const std::string& value); + std::string* _internal_mutable_syntax(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_syntax_accessor(); + + public: + // optional .google.protobuf.FileOptions options = 8; +@@ -11631,7 +11681,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.name) + } + inline std::string* FileDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.name) + return _s; + } +@@ -11649,6 +11699,11 @@ inline std::string* FileDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* FileDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.name) +@@ -11702,7 +11757,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileDescriptorProto::set_package(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.package) + } + inline std::string* FileDescriptorProto::mutable_package() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_package(); ++ auto _s = _internal_mutable_package(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.package) + return _s; + } +@@ -11720,6 +11775,11 @@ inline std::string* FileDescriptorProto::_internal_mutable_package() { + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.package_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileDescriptorProto::_internal_mutable_package_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.package_.MutableAccessor( GetArena()); ++} + inline std::string* FileDescriptorProto::release_package() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.package) +@@ -11762,7 +11822,7 @@ inline void FileDescriptorProto::clear_dependency() { + } + inline std::string* FileDescriptorProto::add_dependency() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_dependency()->Add(); ++ auto _s = _internal_mutable_dependency()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FileDescriptorProto.dependency) + return _s; + } +@@ -11774,12 +11834,12 @@ inline const std::string& FileDescriptorProto::dependency(int index) const + inline std::string* FileDescriptorProto::mutable_dependency(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.dependency) +- return _internal_mutable_dependency()->Mutable(index); ++ return _internal_mutable_dependency()->MutableString(index); + } + template + inline void FileDescriptorProto::set_dependency(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_dependency()->Mutable(index), ++ *_internal_mutable_dependency()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.dependency) + } +@@ -12315,7 +12375,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileDescriptorProto::set_syntax(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileDescriptorProto.syntax) + } + inline std::string* FileDescriptorProto::mutable_syntax() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_syntax(); ++ auto _s = _internal_mutable_syntax(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.syntax) + return _s; + } +@@ -12333,6 +12393,11 @@ inline std::string* FileDescriptorProto::_internal_mutable_syntax() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.syntax_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileDescriptorProto::_internal_mutable_syntax_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.syntax_.MutableAccessor( GetArena()); ++} + inline std::string* FileDescriptorProto::release_syntax() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileDescriptorProto.syntax) +@@ -12635,7 +12700,7 @@ inline PROTOBUF_ALWAYS_INLINE void DescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.name) + } + inline std::string* DescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.name) + return _s; + } +@@ -12653,6 +12718,11 @@ inline std::string* DescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor DescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* DescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.DescriptorProto.name) +@@ -13134,7 +13204,7 @@ inline void DescriptorProto::clear_reserved_name() { + } + inline std::string* DescriptorProto::add_reserved_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_reserved_name()->Add(); ++ auto _s = _internal_mutable_reserved_name()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.DescriptorProto.reserved_name) + return _s; + } +@@ -13146,12 +13216,12 @@ inline const std::string& DescriptorProto::reserved_name(int index) const + inline std::string* DescriptorProto::mutable_reserved_name(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_name) +- return _internal_mutable_reserved_name()->Mutable(index); ++ return _internal_mutable_reserved_name()->MutableString(index); + } + template + inline void DescriptorProto::set_reserved_name(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_reserved_name()->Mutable(index), ++ *_internal_mutable_reserved_name()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.DescriptorProto.reserved_name) + } +@@ -13241,7 +13311,7 @@ inline PROTOBUF_ALWAYS_INLINE void ExtensionRangeOptions_Declaration::set_full_n + // @@protoc_insertion_point(field_set:google.protobuf.ExtensionRangeOptions.Declaration.full_name) + } + inline std::string* ExtensionRangeOptions_Declaration::mutable_full_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_full_name(); ++ auto _s = _internal_mutable_full_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.ExtensionRangeOptions.Declaration.full_name) + return _s; + } +@@ -13259,6 +13329,11 @@ inline std::string* ExtensionRangeOptions_Declaration::_internal_mutable_full_na + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.full_name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor ExtensionRangeOptions_Declaration::_internal_mutable_full_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.full_name_.MutableAccessor( GetArena()); ++} + inline std::string* ExtensionRangeOptions_Declaration::release_full_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.ExtensionRangeOptions.Declaration.full_name) +@@ -13312,7 +13387,7 @@ inline PROTOBUF_ALWAYS_INLINE void ExtensionRangeOptions_Declaration::set_type(A + // @@protoc_insertion_point(field_set:google.protobuf.ExtensionRangeOptions.Declaration.type) + } + inline std::string* ExtensionRangeOptions_Declaration::mutable_type() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_type(); ++ auto _s = _internal_mutable_type(); + // @@protoc_insertion_point(field_mutable:google.protobuf.ExtensionRangeOptions.Declaration.type) + return _s; + } +@@ -13330,6 +13405,11 @@ inline std::string* ExtensionRangeOptions_Declaration::_internal_mutable_type() + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.type_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor ExtensionRangeOptions_Declaration::_internal_mutable_type_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.type_.MutableAccessor( GetArena()); ++} + inline std::string* ExtensionRangeOptions_Declaration::release_type() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.ExtensionRangeOptions.Declaration.type) +@@ -13670,7 +13750,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.name) + } + inline std::string* FieldDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.name) + return _s; + } +@@ -13688,6 +13768,11 @@ inline std::string* FieldDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* FieldDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.name) +@@ -13827,7 +13912,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldDescriptorProto::set_type_name(Arg_&& ar + // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.type_name) + } + inline std::string* FieldDescriptorProto::mutable_type_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_type_name(); ++ auto _s = _internal_mutable_type_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.type_name) + return _s; + } +@@ -13845,6 +13930,11 @@ inline std::string* FieldDescriptorProto::_internal_mutable_type_name() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.type_name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldDescriptorProto::_internal_mutable_type_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.type_name_.MutableAccessor( GetArena()); ++} + inline std::string* FieldDescriptorProto::release_type_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.type_name) +@@ -13898,7 +13988,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldDescriptorProto::set_extendee(Arg_&& arg + // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.extendee) + } + inline std::string* FieldDescriptorProto::mutable_extendee() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_extendee(); ++ auto _s = _internal_mutable_extendee(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.extendee) + return _s; + } +@@ -13916,6 +14006,11 @@ inline std::string* FieldDescriptorProto::_internal_mutable_extendee() { + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.extendee_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldDescriptorProto::_internal_mutable_extendee_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.extendee_.MutableAccessor( GetArena()); ++} + inline std::string* FieldDescriptorProto::release_extendee() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.extendee) +@@ -13969,7 +14064,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldDescriptorProto::set_default_value(Arg_& + // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.default_value) + } + inline std::string* FieldDescriptorProto::mutable_default_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_default_value(); ++ auto _s = _internal_mutable_default_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.default_value) + return _s; + } +@@ -13987,6 +14082,11 @@ inline std::string* FieldDescriptorProto::_internal_mutable_default_value() { + _impl_._has_bits_[0] |= 0x00000008u; + return _impl_.default_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldDescriptorProto::_internal_mutable_default_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000008u; ++ return _impl_.default_value_.MutableAccessor( GetArena()); ++} + inline std::string* FieldDescriptorProto::release_default_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.default_value) +@@ -14068,7 +14168,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldDescriptorProto::set_json_name(Arg_&& ar + // @@protoc_insertion_point(field_set:google.protobuf.FieldDescriptorProto.json_name) + } + inline std::string* FieldDescriptorProto::mutable_json_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_json_name(); ++ auto _s = _internal_mutable_json_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.json_name) + return _s; + } +@@ -14086,6 +14186,11 @@ inline std::string* FieldDescriptorProto::_internal_mutable_json_name() { + _impl_._has_bits_[0] |= 0x00000010u; + return _impl_.json_name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldDescriptorProto::_internal_mutable_json_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000010u; ++ return _impl_.json_name_.MutableAccessor( GetArena()); ++} + inline std::string* FieldDescriptorProto::release_json_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldDescriptorProto.json_name) +@@ -14267,7 +14372,7 @@ inline PROTOBUF_ALWAYS_INLINE void OneofDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.OneofDescriptorProto.name) + } + inline std::string* OneofDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.OneofDescriptorProto.name) + return _s; + } +@@ -14285,6 +14390,11 @@ inline std::string* OneofDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor OneofDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* OneofDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.OneofDescriptorProto.name) +@@ -14498,7 +14608,7 @@ inline PROTOBUF_ALWAYS_INLINE void EnumDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.name) + } + inline std::string* EnumDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.name) + return _s; + } +@@ -14516,6 +14626,11 @@ inline std::string* EnumDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor EnumDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* EnumDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.EnumDescriptorProto.name) +@@ -14752,7 +14867,7 @@ inline void EnumDescriptorProto::clear_reserved_name() { + } + inline std::string* EnumDescriptorProto::add_reserved_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_reserved_name()->Add(); ++ auto _s = _internal_mutable_reserved_name()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.EnumDescriptorProto.reserved_name) + return _s; + } +@@ -14764,12 +14879,12 @@ inline const std::string& EnumDescriptorProto::reserved_name(int index) const + inline std::string* EnumDescriptorProto::mutable_reserved_name(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.reserved_name) +- return _internal_mutable_reserved_name()->Mutable(index); ++ return _internal_mutable_reserved_name()->MutableString(index); + } + template + inline void EnumDescriptorProto::set_reserved_name(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_reserved_name()->Mutable(index), ++ *_internal_mutable_reserved_name()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.EnumDescriptorProto.reserved_name) + } +@@ -14831,7 +14946,7 @@ inline PROTOBUF_ALWAYS_INLINE void EnumValueDescriptorProto::set_name(Arg_&& arg + // @@protoc_insertion_point(field_set:google.protobuf.EnumValueDescriptorProto.name) + } + inline std::string* EnumValueDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueDescriptorProto.name) + return _s; + } +@@ -14849,6 +14964,11 @@ inline std::string* EnumValueDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor EnumValueDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* EnumValueDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.EnumValueDescriptorProto.name) +@@ -15030,7 +15150,7 @@ inline PROTOBUF_ALWAYS_INLINE void ServiceDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.ServiceDescriptorProto.name) + } + inline std::string* ServiceDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.name) + return _s; + } +@@ -15048,6 +15168,11 @@ inline std::string* ServiceDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor ServiceDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* ServiceDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.ServiceDescriptorProto.name) +@@ -15250,7 +15375,7 @@ inline PROTOBUF_ALWAYS_INLINE void MethodDescriptorProto::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.name) + } + inline std::string* MethodDescriptorProto::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.name) + return _s; + } +@@ -15268,6 +15393,11 @@ inline std::string* MethodDescriptorProto::_internal_mutable_name() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor MethodDescriptorProto::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* MethodDescriptorProto::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.name) +@@ -15321,7 +15451,7 @@ inline PROTOBUF_ALWAYS_INLINE void MethodDescriptorProto::set_input_type(Arg_&& + // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.input_type) + } + inline std::string* MethodDescriptorProto::mutable_input_type() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_input_type(); ++ auto _s = _internal_mutable_input_type(); + // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.input_type) + return _s; + } +@@ -15339,6 +15469,11 @@ inline std::string* MethodDescriptorProto::_internal_mutable_input_type() { + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.input_type_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor MethodDescriptorProto::_internal_mutable_input_type_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.input_type_.MutableAccessor( GetArena()); ++} + inline std::string* MethodDescriptorProto::release_input_type() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.input_type) +@@ -15392,7 +15527,7 @@ inline PROTOBUF_ALWAYS_INLINE void MethodDescriptorProto::set_output_type(Arg_&& + // @@protoc_insertion_point(field_set:google.protobuf.MethodDescriptorProto.output_type) + } + inline std::string* MethodDescriptorProto::mutable_output_type() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_output_type(); ++ auto _s = _internal_mutable_output_type(); + // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.output_type) + return _s; + } +@@ -15410,6 +15545,11 @@ inline std::string* MethodDescriptorProto::_internal_mutable_output_type() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.output_type_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor MethodDescriptorProto::_internal_mutable_output_type_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.output_type_.MutableAccessor( GetArena()); ++} + inline std::string* MethodDescriptorProto::release_output_type() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.MethodDescriptorProto.output_type) +@@ -15619,7 +15759,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_java_package(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_package) + } + inline std::string* FileOptions::mutable_java_package() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_java_package(); ++ auto _s = _internal_mutable_java_package(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.java_package) + return _s; + } +@@ -15637,6 +15777,11 @@ inline std::string* FileOptions::_internal_mutable_java_package() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.java_package_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_java_package_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.java_package_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_java_package() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_package) +@@ -15690,7 +15835,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_java_outer_classname(Arg_&& + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.java_outer_classname) + } + inline std::string* FileOptions::mutable_java_outer_classname() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_java_outer_classname(); ++ auto _s = _internal_mutable_java_outer_classname(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.java_outer_classname) + return _s; + } +@@ -15708,6 +15853,11 @@ inline std::string* FileOptions::_internal_mutable_java_outer_classname() { + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.java_outer_classname_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_java_outer_classname_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.java_outer_classname_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_java_outer_classname() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.java_outer_classname) +@@ -15823,13 +15973,13 @@ inline void FileOptions::_internal_set_java_string_check_utf8(bool value) { + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = SPEED]; + inline bool FileOptions::has_optimize_for() const { +- bool value = (_impl_._has_bits_[0] & 0x00040000u) != 0; ++ bool value = (_impl_._has_bits_[0] & 0x00080000u) != 0; + return value; + } + inline void FileOptions::clear_optimize_for() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.optimize_for_ = 1; +- _impl_._has_bits_[0] &= ~0x00040000u; ++ _impl_._has_bits_[0] &= ~0x00080000u; + } + inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::optimize_for() const { + // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.optimize_for) +@@ -15837,7 +15987,7 @@ inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::optimize_for() + } + inline void FileOptions::set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value) { + _internal_set_optimize_for(value); +- _impl_._has_bits_[0] |= 0x00040000u; ++ _impl_._has_bits_[0] |= 0x00080000u; + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.optimize_for) + } + inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::_internal_optimize_for() const { +@@ -15874,7 +16024,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_go_package(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.go_package) + } + inline std::string* FileOptions::mutable_go_package() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_go_package(); ++ auto _s = _internal_mutable_go_package(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.go_package) + return _s; + } +@@ -15892,6 +16042,11 @@ inline std::string* FileOptions::_internal_mutable_go_package() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.go_package_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_go_package_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.go_package_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_go_package() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.go_package) +@@ -16035,13 +16190,13 @@ inline void FileOptions::_internal_set_deprecated(bool value) { + + // optional bool cc_enable_arenas = 31 [default = true]; + inline bool FileOptions::has_cc_enable_arenas() const { +- bool value = (_impl_._has_bits_[0] & 0x00080000u) != 0; ++ bool value = (_impl_._has_bits_[0] & 0x00100000u) != 0; + return value; + } + inline void FileOptions::clear_cc_enable_arenas() { + ::google::protobuf::internal::TSanWrite(&_impl_); + _impl_.cc_enable_arenas_ = true; +- _impl_._has_bits_[0] &= ~0x00080000u; ++ _impl_._has_bits_[0] &= ~0x00100000u; + } + inline bool FileOptions::cc_enable_arenas() const { + // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.cc_enable_arenas) +@@ -16049,7 +16204,7 @@ inline bool FileOptions::cc_enable_arenas() const { + } + inline void FileOptions::set_cc_enable_arenas(bool value) { + _internal_set_cc_enable_arenas(value); +- _impl_._has_bits_[0] |= 0x00080000u; ++ _impl_._has_bits_[0] |= 0x00100000u; + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.cc_enable_arenas) + } + inline bool FileOptions::_internal_cc_enable_arenas() const { +@@ -16061,6 +16216,34 @@ inline void FileOptions::_internal_set_cc_enable_arenas(bool value) { + _impl_.cc_enable_arenas_ = value; + } + ++// optional bool cc_mutable_donated_string = 32 [default = false]; ++inline bool FileOptions::has_cc_mutable_donated_string() const { ++ bool value = (_impl_._has_bits_[0] & 0x00040000u) != 0; ++ return value; ++} ++inline void FileOptions::clear_cc_mutable_donated_string() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_.cc_mutable_donated_string_ = false; ++ _impl_._has_bits_[0] &= ~0x00040000u; ++} ++inline bool FileOptions::cc_mutable_donated_string() const { ++ // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.cc_mutable_donated_string) ++ return _internal_cc_mutable_donated_string(); ++} ++inline void FileOptions::set_cc_mutable_donated_string(bool value) { ++ _internal_set_cc_mutable_donated_string(value); ++ _impl_._has_bits_[0] |= 0x00040000u; ++ // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.cc_mutable_donated_string) ++} ++inline bool FileOptions::_internal_cc_mutable_donated_string() const { ++ ::google::protobuf::internal::TSanRead(&_impl_); ++ return _impl_.cc_mutable_donated_string_; ++} ++inline void FileOptions::_internal_set_cc_mutable_donated_string(bool value) { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_.cc_mutable_donated_string_ = value; ++} ++ + // optional string objc_class_prefix = 36; + inline bool FileOptions::has_objc_class_prefix() const { + bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0; +@@ -16085,7 +16268,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_objc_class_prefix(Arg_&& arg + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.objc_class_prefix) + } + inline std::string* FileOptions::mutable_objc_class_prefix() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_objc_class_prefix(); ++ auto _s = _internal_mutable_objc_class_prefix(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.objc_class_prefix) + return _s; + } +@@ -16103,6 +16286,11 @@ inline std::string* FileOptions::_internal_mutable_objc_class_prefix() { + _impl_._has_bits_[0] |= 0x00000008u; + return _impl_.objc_class_prefix_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_objc_class_prefix_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000008u; ++ return _impl_.objc_class_prefix_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_objc_class_prefix() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.objc_class_prefix) +@@ -16156,7 +16344,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_csharp_namespace(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.csharp_namespace) + } + inline std::string* FileOptions::mutable_csharp_namespace() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_csharp_namespace(); ++ auto _s = _internal_mutable_csharp_namespace(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.csharp_namespace) + return _s; + } +@@ -16174,6 +16362,11 @@ inline std::string* FileOptions::_internal_mutable_csharp_namespace() { + _impl_._has_bits_[0] |= 0x00000010u; + return _impl_.csharp_namespace_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_csharp_namespace_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000010u; ++ return _impl_.csharp_namespace_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_csharp_namespace() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.csharp_namespace) +@@ -16227,7 +16420,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_swift_prefix(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.swift_prefix) + } + inline std::string* FileOptions::mutable_swift_prefix() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_swift_prefix(); ++ auto _s = _internal_mutable_swift_prefix(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.swift_prefix) + return _s; + } +@@ -16245,6 +16438,11 @@ inline std::string* FileOptions::_internal_mutable_swift_prefix() { + _impl_._has_bits_[0] |= 0x00000020u; + return _impl_.swift_prefix_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_swift_prefix_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000020u; ++ return _impl_.swift_prefix_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_swift_prefix() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.swift_prefix) +@@ -16298,7 +16496,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_php_class_prefix(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_class_prefix) + } + inline std::string* FileOptions::mutable_php_class_prefix() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_php_class_prefix(); ++ auto _s = _internal_mutable_php_class_prefix(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_class_prefix) + return _s; + } +@@ -16316,6 +16514,11 @@ inline std::string* FileOptions::_internal_mutable_php_class_prefix() { + _impl_._has_bits_[0] |= 0x00000040u; + return _impl_.php_class_prefix_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_php_class_prefix_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000040u; ++ return _impl_.php_class_prefix_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_php_class_prefix() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_class_prefix) +@@ -16369,7 +16572,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_php_namespace(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_namespace) + } + inline std::string* FileOptions::mutable_php_namespace() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_php_namespace(); ++ auto _s = _internal_mutable_php_namespace(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_namespace) + return _s; + } +@@ -16387,6 +16590,11 @@ inline std::string* FileOptions::_internal_mutable_php_namespace() { + _impl_._has_bits_[0] |= 0x00000080u; + return _impl_.php_namespace_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_php_namespace_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000080u; ++ return _impl_.php_namespace_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_php_namespace() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_namespace) +@@ -16440,7 +16648,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_php_metadata_namespace(Arg_& + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.php_metadata_namespace) + } + inline std::string* FileOptions::mutable_php_metadata_namespace() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_php_metadata_namespace(); ++ auto _s = _internal_mutable_php_metadata_namespace(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.php_metadata_namespace) + return _s; + } +@@ -16458,6 +16666,11 @@ inline std::string* FileOptions::_internal_mutable_php_metadata_namespace() { + _impl_._has_bits_[0] |= 0x00000100u; + return _impl_.php_metadata_namespace_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_php_metadata_namespace_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000100u; ++ return _impl_.php_metadata_namespace_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_php_metadata_namespace() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.php_metadata_namespace) +@@ -16511,7 +16724,7 @@ inline PROTOBUF_ALWAYS_INLINE void FileOptions::set_ruby_package(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.FileOptions.ruby_package) + } + inline std::string* FileOptions::mutable_ruby_package() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_ruby_package(); ++ auto _s = _internal_mutable_ruby_package(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.ruby_package) + return _s; + } +@@ -16529,6 +16742,11 @@ inline std::string* FileOptions::_internal_mutable_ruby_package() { + _impl_._has_bits_[0] |= 0x00000200u; + return _impl_.ruby_package_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FileOptions::_internal_mutable_ruby_package_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000200u; ++ return _impl_.ruby_package_.MutableAccessor( GetArena()); ++} + inline std::string* FileOptions::release_ruby_package() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FileOptions.ruby_package) +@@ -17049,7 +17267,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldOptions_EditionDefault::set_value(Arg_&& + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.EditionDefault.value) + } + inline std::string* FieldOptions_EditionDefault::mutable_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_value(); ++ auto _s = _internal_mutable_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldOptions.EditionDefault.value) + return _s; + } +@@ -17067,6 +17285,11 @@ inline std::string* FieldOptions_EditionDefault::_internal_mutable_value() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldOptions_EditionDefault::_internal_mutable_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.value_.MutableAccessor( GetArena()); ++} + inline std::string* FieldOptions_EditionDefault::release_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldOptions.EditionDefault.value) +@@ -17182,7 +17405,7 @@ inline PROTOBUF_ALWAYS_INLINE void FieldOptions_FeatureSupport::set_deprecation_ + // @@protoc_insertion_point(field_set:google.protobuf.FieldOptions.FeatureSupport.deprecation_warning) + } + inline std::string* FieldOptions_FeatureSupport::mutable_deprecation_warning() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_deprecation_warning(); ++ auto _s = _internal_mutable_deprecation_warning(); + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldOptions.FeatureSupport.deprecation_warning) + return _s; + } +@@ -17200,6 +17423,11 @@ inline std::string* FieldOptions_FeatureSupport::_internal_mutable_deprecation_w + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.deprecation_warning_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor FieldOptions_FeatureSupport::_internal_mutable_deprecation_warning_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.deprecation_warning_.MutableAccessor( GetArena()); ++} + inline std::string* FieldOptions_FeatureSupport::release_deprecation_warning() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.FieldOptions.FeatureSupport.deprecation_warning) +@@ -18948,7 +19176,7 @@ inline PROTOBUF_ALWAYS_INLINE void UninterpretedOption_NamePart::set_name_part(A + // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.NamePart.name_part) + } + inline std::string* UninterpretedOption_NamePart::mutable_name_part() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name_part(); ++ auto _s = _internal_mutable_name_part(); + // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.NamePart.name_part) + return _s; + } +@@ -18966,6 +19194,11 @@ inline std::string* UninterpretedOption_NamePart::_internal_mutable_name_part() + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.name_part_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor UninterpretedOption_NamePart::_internal_mutable_name_part_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.name_part_.MutableAccessor( GetArena()); ++} + inline std::string* UninterpretedOption_NamePart::release_name_part() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.NamePart.name_part) +@@ -19100,7 +19333,7 @@ inline PROTOBUF_ALWAYS_INLINE void UninterpretedOption::set_identifier_value(Arg + // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.identifier_value) + } + inline std::string* UninterpretedOption::mutable_identifier_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_identifier_value(); ++ auto _s = _internal_mutable_identifier_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.identifier_value) + return _s; + } +@@ -19118,6 +19351,11 @@ inline std::string* UninterpretedOption::_internal_mutable_identifier_value() { + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.identifier_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor UninterpretedOption::_internal_mutable_identifier_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.identifier_value_.MutableAccessor( GetArena()); ++} + inline std::string* UninterpretedOption::release_identifier_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.identifier_value) +@@ -19255,7 +19493,7 @@ inline PROTOBUF_ALWAYS_INLINE void UninterpretedOption::set_string_value(Arg_&& + // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.string_value) + } + inline std::string* UninterpretedOption::mutable_string_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_string_value(); ++ auto _s = _internal_mutable_string_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.string_value) + return _s; + } +@@ -19273,6 +19511,11 @@ inline std::string* UninterpretedOption::_internal_mutable_string_value() { + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.string_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor UninterpretedOption::_internal_mutable_string_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.string_value_.MutableAccessor( GetArena()); ++} + inline std::string* UninterpretedOption::release_string_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.string_value) +@@ -19326,7 +19569,7 @@ inline PROTOBUF_ALWAYS_INLINE void UninterpretedOption::set_aggregate_value(Arg_ + // @@protoc_insertion_point(field_set:google.protobuf.UninterpretedOption.aggregate_value) + } + inline std::string* UninterpretedOption::mutable_aggregate_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_aggregate_value(); ++ auto _s = _internal_mutable_aggregate_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.aggregate_value) + return _s; + } +@@ -19344,6 +19587,11 @@ inline std::string* UninterpretedOption::_internal_mutable_aggregate_value() { + _impl_._has_bits_[0] |= 0x00000004u; + return _impl_.aggregate_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor UninterpretedOption::_internal_mutable_aggregate_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000004u; ++ return _impl_.aggregate_value_.MutableAccessor( GetArena()); ++} + inline std::string* UninterpretedOption::release_aggregate_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.UninterpretedOption.aggregate_value) +@@ -20005,7 +20253,7 @@ inline PROTOBUF_ALWAYS_INLINE void SourceCodeInfo_Location::set_leading_comments + // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.leading_comments) + } + inline std::string* SourceCodeInfo_Location::mutable_leading_comments() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_leading_comments(); ++ auto _s = _internal_mutable_leading_comments(); + // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.leading_comments) + return _s; + } +@@ -20023,6 +20271,11 @@ inline std::string* SourceCodeInfo_Location::_internal_mutable_leading_comments( + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.leading_comments_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor SourceCodeInfo_Location::_internal_mutable_leading_comments_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.leading_comments_.MutableAccessor( GetArena()); ++} + inline std::string* SourceCodeInfo_Location::release_leading_comments() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.leading_comments) +@@ -20076,7 +20329,7 @@ inline PROTOBUF_ALWAYS_INLINE void SourceCodeInfo_Location::set_trailing_comment + // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.trailing_comments) + } + inline std::string* SourceCodeInfo_Location::mutable_trailing_comments() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_trailing_comments(); ++ auto _s = _internal_mutable_trailing_comments(); + // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.trailing_comments) + return _s; + } +@@ -20094,6 +20347,11 @@ inline std::string* SourceCodeInfo_Location::_internal_mutable_trailing_comments + _impl_._has_bits_[0] |= 0x00000002u; + return _impl_.trailing_comments_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor SourceCodeInfo_Location::_internal_mutable_trailing_comments_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000002u; ++ return _impl_.trailing_comments_.MutableAccessor( GetArena()); ++} + inline std::string* SourceCodeInfo_Location::release_trailing_comments() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.SourceCodeInfo.Location.trailing_comments) +@@ -20136,7 +20394,7 @@ inline void SourceCodeInfo_Location::clear_leading_detached_comments() { + } + inline std::string* SourceCodeInfo_Location::add_leading_detached_comments() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_leading_detached_comments()->Add(); ++ auto _s = _internal_mutable_leading_detached_comments()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) + return _s; + } +@@ -20148,12 +20406,12 @@ inline const std::string& SourceCodeInfo_Location::leading_detached_comments(int + inline std::string* SourceCodeInfo_Location::mutable_leading_detached_comments(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) +- return _internal_mutable_leading_detached_comments()->Mutable(index); ++ return _internal_mutable_leading_detached_comments()->MutableString(index); + } + template + inline void SourceCodeInfo_Location::set_leading_detached_comments(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_leading_detached_comments()->Mutable(index), ++ *_internal_mutable_leading_detached_comments()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.SourceCodeInfo.Location.leading_detached_comments) + } +@@ -20313,7 +20571,7 @@ inline PROTOBUF_ALWAYS_INLINE void GeneratedCodeInfo_Annotation::set_source_file + // @@protoc_insertion_point(field_set:google.protobuf.GeneratedCodeInfo.Annotation.source_file) + } + inline std::string* GeneratedCodeInfo_Annotation::mutable_source_file() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_source_file(); ++ auto _s = _internal_mutable_source_file(); + // @@protoc_insertion_point(field_mutable:google.protobuf.GeneratedCodeInfo.Annotation.source_file) + return _s; + } +@@ -20331,6 +20589,11 @@ inline std::string* GeneratedCodeInfo_Annotation::_internal_mutable_source_file( + _impl_._has_bits_[0] |= 0x00000001u; + return _impl_.source_file_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor GeneratedCodeInfo_Annotation::_internal_mutable_source_file_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ _impl_._has_bits_[0] |= 0x00000001u; ++ return _impl_.source_file_.MutableAccessor( GetArena()); ++} + inline std::string* GeneratedCodeInfo_Annotation::release_source_file() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.GeneratedCodeInfo.Annotation.source_file) +diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto +index dfabac416..805fe4e65 100644 +--- a/src/google/protobuf/descriptor.proto ++++ b/src/google/protobuf/descriptor.proto +@@ -506,6 +506,11 @@ message FileOptions { + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default = true]; + ++ // Change mutable_xxx function to support mutation on arena ++ // false: default. copy arenastring to heap and return std::string* ++ // true: return MaybeArenaStringAccessor to support mutation on arena ++ optional bool cc_mutable_donated_string = 32 [default = false]; ++ + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; +diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc +index 21f24037a..0a8407ccd 100644 +--- a/src/google/protobuf/extension_set.cc ++++ b/src/google/protobuf/extension_set.cc +@@ -526,23 +526,62 @@ const std::string& ExtensionSet::GetString( + return default_value; + } else { + ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING); +- return *extension->string_value; ++ return *extension->string_value.Get(); + } + } + +-std::string* ExtensionSet::MutableString(int number, FieldType type, +- const FieldDescriptor* descriptor) { ++MutableStringType ExtensionSet::MutableString( ++ int number, FieldType type, const FieldDescriptor* descriptor) { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return MutableAccessor(number, type, descriptor); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + Extension* extension; ++ std::string* string; + if (MaybeNewExtension(number, descriptor, &extension)) { + extension->type = type; + ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING); + extension->is_repeated = false; +- extension->string_value = Arena::Create(arena_); ++ string = Arena::Create(arena_); ++ if (arena_ != nullptr) { ++ extension->string_value.SetMutableArena(string); ++ } else { ++ extension->string_value.SetAllocated(string); ++ } + } else { + ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING); ++ string = extension->string_value.Get(); ++ if (extension->string_value.IsFixedSizeArena()) { ++ string = Arena::Create(arena_, *string); ++ extension->string_value.SetMutableArena(string); ++ } + } + extension->is_cleared = false; +- return extension->string_value; ++ return string; ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++} ++ ++MaybeArenaStringAccessor ExtensionSet::MutableAccessor( ++ int number, FieldType type, const FieldDescriptor* descriptor) { ++ Extension* extension; ++ if (MaybeNewExtension(number, descriptor, &extension)) { ++ extension->type = type; ++ ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING); ++ extension->is_repeated = false; ++ auto accessor = MaybeArenaStringAccessor::create(arena_); ++ if (arena_ != nullptr) { ++ extension->string_value.SetFixedSizeArena(accessor.underlying()); ++ } else { ++ extension->string_value.SetAllocated(accessor.underlying()); ++ } ++ extension->is_cleared = false; ++ return accessor; ++ } else { ++ ABSL_DCHECK_TYPE(*extension, OPTIONAL_FIELD, STRING); ++ extension->is_cleared = false; ++ return MaybeArenaStringAccessor( ++ extension->string_value.IsFixedSizeArena() ? arena_ : nullptr, ++ extension->string_value.Get()); ++ } + } + + const std::string& ExtensionSet::GetRepeatedString(int number, +@@ -553,15 +592,23 @@ const std::string& ExtensionSet::GetRepeatedString(int number, + return extension->repeated_string_value->Get(index); + } + +-std::string* ExtensionSet::MutableRepeatedString(int number, int index) { ++MutableStringType ExtensionSet::MutableRepeatedString(int number, int index) { + Extension* extension = FindOrNull(number); + ABSL_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty)."; + ABSL_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING); + return extension->repeated_string_value->Mutable(index); + } + +-std::string* ExtensionSet::AddString(int number, FieldType type, +- const FieldDescriptor* descriptor) { ++MaybeArenaStringAccessor ExtensionSet::MutableRepeatedAccessor(int number, ++ int index) { ++ Extension* extension = FindOrNull(number); ++ ABSL_CHECK(extension != nullptr) << "Index out-of-bounds (field is empty)."; ++ ABSL_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING); ++ return extension->repeated_string_value->MutableAccessor(index); ++} ++ ++MutableStringType ExtensionSet::AddString(int number, FieldType type, ++ const FieldDescriptor* descriptor) { + Extension* extension; + if (MaybeNewExtension(number, descriptor, &extension)) { + extension->type = type; +@@ -576,6 +623,22 @@ std::string* ExtensionSet::AddString(int number, FieldType type, + return extension->repeated_string_value->Add(); + } + ++MaybeArenaStringAccessor ExtensionSet::AddAccessor( ++ int number, FieldType type, const FieldDescriptor* descriptor) { ++ Extension* extension; ++ if (MaybeNewExtension(number, descriptor, &extension)) { ++ extension->type = type; ++ ABSL_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_STRING); ++ extension->is_repeated = true; ++ extension->is_packed = false; ++ extension->repeated_string_value = ++ Arena::CreateMessage>(arena_); ++ } else { ++ ABSL_DCHECK_TYPE(*extension, REPEATED_FIELD, STRING); ++ } ++ return extension->repeated_string_value->AddAccessor(); ++} ++ + // ------------------------------------------------------------------- + // Messages + +@@ -1017,7 +1080,8 @@ void ExtensionSet::InternalExtensionMergeFrom(const MessageLite* extendee, + HANDLE_TYPE(ENUM, enum, Enum); + #undef HANDLE_TYPE + case WireFormatLite::CPPTYPE_STRING: +- SetString(number, other_extension.type, *other_extension.string_value, ++ SetString(number, other_extension.type, ++ *other_extension.string_value.Get(), + other_extension.descriptor); + break; + case WireFormatLite::CPPTYPE_MESSAGE: { +@@ -1291,7 +1355,7 @@ void ExtensionSet::Extension::Clear() { + if (!is_cleared) { + switch (cpp_type(type)) { + case WireFormatLite::CPPTYPE_STRING: +- string_value->clear(); ++ MaybeArenaStringAccessor::clear(string_value.Get()); + break; + case WireFormatLite::CPPTYPE_MESSAGE: + if (is_lazy) { +@@ -1420,8 +1484,8 @@ size_t ExtensionSet::Extension::ByteSize(int number) const { + HANDLE_TYPE(UINT64, UInt64, uint64_t_value); + HANDLE_TYPE(SINT32, SInt32, int32_t_value); + HANDLE_TYPE(SINT64, SInt64, int64_t_value); +- HANDLE_TYPE(STRING, String, *string_value); +- HANDLE_TYPE(BYTES, Bytes, *string_value); ++ HANDLE_TYPE(STRING, String, *string_value.Get()); ++ HANDLE_TYPE(BYTES, Bytes, *string_value.Get()); + HANDLE_TYPE(ENUM, Enum, enum_value); + HANDLE_TYPE(GROUP, Group, *message_value); + #undef HANDLE_TYPE +@@ -1500,7 +1564,7 @@ void ExtensionSet::Extension::Free() { + } else { + switch (cpp_type(type)) { + case WireFormatLite::CPPTYPE_STRING: +- delete string_value; ++ delete string_value.Get(); + break; + case WireFormatLite::CPPTYPE_MESSAGE: + if (is_lazy) { +@@ -1799,8 +1863,8 @@ uint8_t* ExtensionSet::Extension::InternalSerializeFieldWithCachedSizesToArray( + target = stream->EnsureSpace(target); \ + target = stream->WriteString(number, VALUE, target); \ + break +- HANDLE_TYPE(STRING, String, *string_value); +- HANDLE_TYPE(BYTES, Bytes, *string_value); ++ HANDLE_TYPE(STRING, String, *string_value.Get()); ++ HANDLE_TYPE(BYTES, Bytes, *string_value.Get()); + #undef HANDLE_TYPE + case WireFormatLite::TYPE_GROUP: + target = stream->EnsureSpace(target); +diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h +index 8b95b7224..f76bb60e3 100644 +--- a/src/google/protobuf/extension_set.h ++++ b/src/google/protobuf/extension_set.h +@@ -318,7 +318,8 @@ class PROTOBUF_EXPORT ExtensionSet { + void SetBool(int number, FieldType type, bool value, desc); + void SetEnum(int number, FieldType type, int value, desc); + void SetString(int number, FieldType type, std::string value, desc); +- std::string* MutableString(int number, FieldType type, desc); ++ MutableStringType MutableString(int number, FieldType type, desc); ++ MaybeArenaStringAccessor MutableAccessor(int number, FieldType type, desc); + MessageLite* MutableMessage(int number, FieldType type, + const MessageLite& prototype, desc); + MessageLite* MutableMessage(const FieldDescriptor* descriptor, +@@ -382,7 +383,8 @@ class PROTOBUF_EXPORT ExtensionSet { + void SetRepeatedBool(int number, int index, bool value); + void SetRepeatedEnum(int number, int index, int value); + void SetRepeatedString(int number, int index, std::string value); +- std::string* MutableRepeatedString(int number, int index); ++ MutableStringType MutableRepeatedString(int number, int index); ++ MaybeArenaStringAccessor MutableRepeatedAccessor(int number, int index); + MessageLite* MutableRepeatedMessage(int number, int index); + + #define desc const FieldDescriptor* descriptor // avoid line wrapping +@@ -395,7 +397,8 @@ class PROTOBUF_EXPORT ExtensionSet { + void AddBool(int number, FieldType type, bool packed, bool value, desc); + void AddEnum(int number, FieldType type, bool packed, int value, desc); + void AddString(int number, FieldType type, std::string value, desc); +- std::string* AddString(int number, FieldType type, desc); ++ MutableStringType AddString(int number, FieldType type, desc); ++ MaybeArenaStringAccessor AddAccessor(int number, FieldType type, desc); + MessageLite* AddMessage(int number, FieldType type, + const MessageLite& prototype, desc); + MessageLite* AddMessage(const FieldDescriptor* descriptor, +@@ -644,7 +647,7 @@ class PROTOBUF_EXPORT ExtensionSet { + double double_value; + bool bool_value; + int enum_value; +- std::string* string_value; ++ internal::TaggedStringPtr string_value; + MessageLite* message_value; + LazyMessageExtension* lazymessage_value; + +@@ -971,16 +974,16 @@ constexpr ExtensionSet::ExtensionSet(Arena* arena) + inline void ExtensionSet::SetString(int number, FieldType type, + std::string value, + const FieldDescriptor* descriptor) { +- MutableString(number, type, descriptor)->assign(std::move(value)); ++ MutableAccessor(number, type, descriptor)->assign(std::move(value)); + } + inline void ExtensionSet::SetRepeatedString(int number, int index, + std::string value) { +- MutableRepeatedString(number, index)->assign(std::move(value)); ++ MutableRepeatedAccessor(number, index)->assign(std::move(value)); + } + inline void ExtensionSet::AddString(int number, FieldType type, + std::string value, + const FieldDescriptor* descriptor) { +- AddString(number, type, descriptor)->assign(std::move(value)); ++ AddAccessor(number, type, descriptor)->assign(std::move(value)); + } + // =================================================================== + // Glue for generated extension accessors +@@ -1189,7 +1192,7 @@ PROTOBUF_DEFINE_PRIMITIVE_TYPE(bool, Bool) + class PROTOBUF_EXPORT StringTypeTraits { + public: + typedef const std::string& ConstType; +- typedef std::string* MutableType; ++ typedef MutableStringType MutableType; + using InitType = ConstType; + static ConstType FromInitType(InitType v) { return v; } + typedef StringTypeTraits Singular; +@@ -1207,7 +1210,7 @@ class PROTOBUF_EXPORT StringTypeTraits { + const std::string& value, ExtensionSet* set) { + set->SetString(number, field_type, value, nullptr); + } +- static inline std::string* Mutable(int number, FieldType field_type, ++ static inline MutableType Mutable(int number, FieldType field_type, + ExtensionSet* set) { + return set->MutableString(number, field_type, nullptr); + } +@@ -1216,7 +1219,7 @@ class PROTOBUF_EXPORT StringTypeTraits { + class PROTOBUF_EXPORT RepeatedStringTypeTraits { + public: + typedef const std::string& ConstType; +- typedef std::string* MutableType; ++ typedef MutableStringType MutableType; + using InitType = ConstType; + static ConstType FromInitType(InitType v) { return v; } + typedef RepeatedStringTypeTraits Repeated; +@@ -1240,14 +1243,14 @@ class PROTOBUF_EXPORT RepeatedStringTypeTraits { + ExtensionSet* set) { + set->SetRepeatedString(number, index, value); + } +- static inline std::string* Mutable(int number, int index, ExtensionSet* set) { ++ static inline MutableType Mutable(int number, int index, ExtensionSet* set) { + return set->MutableRepeatedString(number, index); + } + static inline void Add(int number, FieldType field_type, bool /*is_packed*/, + const std::string& value, ExtensionSet* set) { + set->AddString(number, field_type, value, nullptr); + } +- static inline std::string* Add(int number, FieldType field_type, ++ static inline MutableType Add(int number, FieldType field_type, + ExtensionSet* set) { + return set->AddString(number, field_type, nullptr); + } +diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc +index ea99df6b5..1579892eb 100644 +--- a/src/google/protobuf/extension_set_heavy.cc ++++ b/src/google/protobuf/extension_set_heavy.cc +@@ -399,8 +399,8 @@ size_t ExtensionSet::Extension::SpaceUsedExcludingSelfLong() const { + } else { + switch (cpp_type(type)) { + case FieldDescriptor::CPPTYPE_STRING: +- total_size += sizeof(*string_value) + +- StringSpaceUsedExcludingSelfLong(*string_value); ++ total_size += sizeof(*string_value.Get()) + ++ StringSpaceUsedExcludingSelfLong(*string_value.Get()); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + if (is_lazy) { +diff --git a/src/google/protobuf/extension_set_inl.h b/src/google/protobuf/extension_set_inl.h +index 7360f181a..e79c9be84 100644 +--- a/src/google/protobuf/extension_set_inl.h ++++ b/src/google/protobuf/extension_set_inl.h +@@ -138,15 +138,12 @@ const char* ExtensionSet::ParseFieldWithExtensionInfo( + + case WireFormatLite::TYPE_BYTES: + case WireFormatLite::TYPE_STRING: { +- std::string* value = +- extension.is_repeated +- ? AddString(number, WireFormatLite::TYPE_STRING, +- extension.descriptor) +- : MutableString(number, WireFormatLite::TYPE_STRING, +- extension.descriptor); +- int size = ReadSize(&ptr); +- GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); +- return ctx->ReadString(ptr, size, value); ++ auto value = extension.is_repeated ++ ? AddAccessor(number, WireFormatLite::TYPE_STRING, ++ extension.descriptor) ++ : MutableAccessor(number, WireFormatLite::TYPE_STRING, ++ extension.descriptor); ++ return ctx->ReadArenaString(ptr, value); + } + + case WireFormatLite::TYPE_GROUP: { +diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc +index 5a77a3bb6..d8e9b1daa 100644 +--- a/src/google/protobuf/extension_set_unittest.cc ++++ b/src/google/protobuf/extension_set_unittest.cc +@@ -1142,10 +1142,17 @@ TEST(ExtensionSetTest, RepeatedFields) { + ASSERT_EQ(x, 4321); + } + // Test one string field. ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ for (auto x : ++ *message.MutableRepeatedExtension(unittest::repeated_string_extension)) { ++ x = "test_range_based_for"; ++ } ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + for (auto& x : + *message.MutableRepeatedExtension(unittest::repeated_string_extension)) { + x = "test_range_based_for"; + } ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + for (const auto& x : + message.GetRepeatedExtension(unittest::repeated_string_extension)) { + ASSERT_TRUE(x == "test_range_based_for"); +diff --git a/src/google/protobuf/field_mask.pb.h b/src/google/protobuf/field_mask.pb.h +index eb654f5bf..284fbbddd 100644 +--- a/src/google/protobuf/field_mask.pb.h ++++ b/src/google/protobuf/field_mask.pb.h +@@ -291,7 +291,7 @@ inline void FieldMask::clear_paths() { + } + inline std::string* FieldMask::add_paths() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_paths()->Add(); ++ auto _s = _internal_mutable_paths()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.FieldMask.paths) + return _s; + } +@@ -303,12 +303,12 @@ inline const std::string& FieldMask::paths(int index) const + inline std::string* FieldMask::mutable_paths(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.FieldMask.paths) +- return _internal_mutable_paths()->Mutable(index); ++ return _internal_mutable_paths()->MutableString(index); + } + template + inline void FieldMask::set_paths(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_paths()->Mutable(index), ++ *_internal_mutable_paths()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.FieldMask.paths) + } +diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc +index 74bcfd977..10a55fd66 100644 +--- a/src/google/protobuf/generated_message_reflection.cc ++++ b/src/google/protobuf/generated_message_reflection.cc +@@ -1325,6 +1325,7 @@ void Reflection::InternalSwap(Message* lhs, Message* rhs) const { + } + + void Reflection::MaybePoisonAfterClear(Message& root) const { ++ fprintf(stderr, "Reflection::MaybePoisonAfterClear\n"); + root.Clear(); + } + +@@ -2001,9 +2002,13 @@ void Reflection::SetString(Message* message, const FieldDescriptor* field, + const absl::Cord& value) const { + USAGE_MUTABLE_CHECK_ALL(SetString, SINGULAR, STRING); + if (field->is_extension()) { +- return absl::CopyCordToString(value, +- MutableExtensionSet(message)->MutableString( +- field->number(), field->type(), field)); ++ auto accessor = MutableExtensionSet(message)->MutableAccessor( ++ field->number(), field->type(), field); ++ accessor.clear(); ++ accessor.reserve(value.size()); ++ for (auto chunk : value.Chunks()) { ++ accessor.append(chunk); ++ } + } else { + switch (field->cpp_string_type()) { + case FieldDescriptor::CppStringType::kCord: +@@ -2125,8 +2130,8 @@ void Reflection::SetRepeatedString(Message* message, + break; + case FieldDescriptor::CppStringType::kView: + case FieldDescriptor::CppStringType::kString: +- MutableRepeatedField(message, field, index) +- ->assign(std::move(value)); ++ MutableRaw>(message, field) ++ ->MutableAccessor(index)->assign(std::move(value)); + break; + } + } +@@ -2146,7 +2151,8 @@ void Reflection::AddString(Message* message, const FieldDescriptor* field, + break; + case FieldDescriptor::CppStringType::kView: + case FieldDescriptor::CppStringType::kString: +- AddField(message, field)->assign(std::move(value)); ++ MutableRaw>(message, field) ++ ->Add(std::move(value)); + break; + } + } +diff --git a/src/google/protobuf/generated_message_tctable_gen.cc b/src/google/protobuf/generated_message_tctable_gen.cc +index 0b6a1c38a..88eb985b8 100644 +--- a/src/google/protobuf/generated_message_tctable_gen.cc ++++ b/src/google/protobuf/generated_message_tctable_gen.cc +@@ -774,6 +774,9 @@ uint16_t MakeTypeCardForField( + // A repeated string field uses RepeatedPtrField + // (unless it has a ctype option; see above). + type_card |= fl::kRepSString; ++ } else if (options.is_string_inlined) { ++ // 按照is_string_inlined标记设置kRepIString类型标记 ++ type_card |= fl::kRepIString; + } else { + // Otherwise, non-repeated string fields use ArenaStringPtr. + type_card |= fl::kRepAString; +diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h +index 0e639a89a..8bb1b557d 100644 +--- a/src/google/protobuf/generated_message_tctable_impl.h ++++ b/src/google/protobuf/generated_message_tctable_impl.h +@@ -997,8 +997,10 @@ class PROTOBUF_EXPORT TcParser final { + template + PROTOBUF_CC static inline const char* RepeatedString(PROTOBUF_TC_PARAM_DECL); + ++ // 整体代码只有此处依赖SerialArena的内部实现,比较trick先不支持这种优化 ++ // 而且ArenaString实现变更后这种加速意义不大 + static inline const char* ParseRepeatedStringOnce( +- const char* ptr, SerialArena* serial_arena, ParseContext* ctx, ++ const char* ptr, Arena* arena, ParseContext* ctx, + RepeatedPtrField& field); + + PROTOBUF_NOINLINE +diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc +index 155efbbbc..dcf61830a 100644 +--- a/src/google/protobuf/generated_message_tctable_lite.cc ++++ b/src/google/protobuf/generated_message_tctable_lite.cc +@@ -1378,23 +1378,46 @@ PROTOBUF_ALWAYS_INLINE inline const char* ReadStringIntoArena( + MessageLite* /*msg*/, const char* ptr, ParseContext* ctx, + uint32_t /*aux_idx*/, const TcParseTableBase* /*table*/, + ArenaStringPtr& field, Arena* arena) { +- return ctx->ReadArenaString(ptr, &field, arena); ++ // 统一适配Allocated/MutableArena/FixedSizeArena模式 ++ return ctx->ReadArenaString(ptr, field.MutableAccessor(arena)); + } + ++// ArenaStringPtr/InlinedStringField具有类似的API,模板支持 ++template + PROTOBUF_NOINLINE + const char* ReadStringNoArena(MessageLite* /*msg*/, const char* ptr, + ParseContext* ctx, uint32_t /*aux_idx*/, + const TcParseTableBase* /*table*/, +- ArenaStringPtr& field) { ++ T& field) { + int size = ReadSize(&ptr); + if (!ptr) return nullptr; + return ctx->ReadString(ptr, size, field.MutableNoCopy(nullptr)); + } + +-PROTOBUF_ALWAYS_INLINE inline bool IsValidUTF8(ArenaStringPtr& field) { ++// ArenaStringPtr/InlinedStringField具有类似的API,模板支持 ++template ++PROTOBUF_ALWAYS_INLINE inline bool IsValidUTF8(T& field) { + return utf8_range::IsStructurallyValid(field.Get()); + } + ++// InlinedStringField需要按照协议提取donated标记之后进行访问 ++PROTOBUF_ALWAYS_INLINE inline const char* ReadStringIntoArena( ++ MessageLite* msg, const char* ptr, ParseContext* ctx, ++ uint32_t aux_idx, const TcParseTableBase* table, ++ InlinedStringField& field, Arena* arena) { ++ // 包含InlinedStringField时aux[0]固定存储了donated bit图 ++ // _inlined_string_donated_成员的偏移量 ++ auto donated_slot_offset = table->field_aux(0u)->offset; ++ // 根据序号计算bit偏移量 ++ auto donated_slot_index = aux_idx / 32; ++ donated_slot_offset += donated_slot_index << 4; ++ auto donated_slot_mask = 1 << (aux_idx % 32); ++ // 取出donated bit,相当于如下方式 ++ // msg->_inlined_string_donated_[donated_slot_offset] & donated_slot_mask ++ auto donated = TcParser::RefAt(msg, donated_slot_offset) & donated_slot_mask; ++ // 实际开始读取数据 ++ return ctx->ReadArenaString(ptr, field.MutableAccessor(arena, donated)); ++} + + } // namespace + +@@ -1467,23 +1490,32 @@ PROTOBUF_NOINLINE const char* TcParser::FastUS2(PROTOBUF_TC_PARAM_DECL) { + + // Inlined string variants: + ++// 增加和ArenaStringPtr一样的fast path支持 + const char* TcParser::FastBiS1(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + const char* TcParser::FastBiS2(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + const char* TcParser::FastSiS1(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + const char* TcParser::FastSiS2(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + const char* TcParser::FastUiS1(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + const char* TcParser::FastUiS2(PROTOBUF_TC_PARAM_DECL) { +- PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_NO_DATA_PASS); ++ PROTOBUF_MUSTTAIL return SingularString( ++ PROTOBUF_TC_PARAM_PASS); + } + + // Corded string variants: +@@ -1523,8 +1555,9 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString( + #endif + return true; + default: ++ // 切换Get接口避免进行MutableArena转换 + if (PROTOBUF_PREDICT_TRUE( +- utf8_range::IsStructurallyValid(field[field.size() - 1]))) { ++ utf8_range::IsStructurallyValid(field.Get(field.size() - 1)))) { + return true; + } + ReportFastUtf8Error(FastDecodeTag(expected_tag), table); +@@ -1534,13 +1567,13 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString( + }; + + auto* arena = field.GetArena(); +- SerialArena* serial_arena; ++ // 整体代码只有此处依赖SerialArena的内部实现,比较trick先不支持这种优化 ++ // 而且ArenaString实现变更后这种加速意义不大 + if (PROTOBUF_PREDICT_TRUE(arena != nullptr && +- arena->impl_.GetSerialArenaFast(&serial_arena) && + field.PrepareForParse())) { + do { + ptr += sizeof(TagType); +- ptr = ParseRepeatedStringOnce(ptr, serial_arena, ctx, field); ++ ptr = ParseRepeatedStringOnce(ptr, arena, ctx, field); + + if (PROTOBUF_PREDICT_FALSE(ptr == nullptr || !validate_last_string())) { + PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS); +@@ -1550,8 +1583,9 @@ PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedString( + } else { + do { + ptr += sizeof(TagType); +- std::string* str = field.Add(); +- ptr = InlineGreedyStringParser(str, ptr, ctx); ++ // 统一适配Allocated/MutableArena/FixedSizeArena模式 ++ auto str = field.AddAccessor(); ++ ptr = ctx->ReadArenaString(ptr, str); + if (PROTOBUF_PREDICT_FALSE(ptr == nullptr || !validate_last_string())) { + PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS); + } +@@ -2168,7 +2202,8 @@ PROTOBUF_NOINLINE const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) { + if (need_init) field.InitDefault(); + Arena* arena = msg->GetArena(); + if (arena) { +- ptr = ctx->ReadArenaString(ptr, &field, arena); ++ // 统一适配Allocated/MutableArena/FixedSizeArena模式 ++ ptr = ctx->ReadArenaString(ptr, field.MutableAccessor(arena)); + } else { + std::string* str = field.MutableNoCopy(nullptr); + ptr = InlineGreedyStringParser(str, ptr, ctx); +@@ -2178,6 +2213,24 @@ PROTOBUF_NOINLINE const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) { + break; + } + ++ // 增加InlinedStringField的支持分支 ++ // 内容除了类型之外和kRepAString分支完全一致 ++ case field_layout::kRepIString: { ++ auto& field = RefAt(base, entry.offset); ++ Arena* arena = msg->GetArena(); ++ if (arena) { ++ // 每个field通过aux_idx存储了自身使用的donated bit序号 ++ auto aux_idx = table->field_aux(entry.aux_idx)->offset; ++ ptr = ReadStringIntoArena( ++ msg, ptr, ctx, aux_idx, table, field, arena); ++ } else { ++ std::string* str = field.MutableNoCopy(nullptr); ++ ptr = InlineGreedyStringParser(str, ptr, ctx); ++ } ++ if (!ptr) break; ++ is_valid = MpVerifyUtf8(field.Get(), table, entry, xform_val); ++ break; ++ } + + case field_layout::kRepCord: { + absl::Cord* field; +@@ -2208,13 +2261,16 @@ PROTOBUF_NOINLINE const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) { + } + + PROTOBUF_ALWAYS_INLINE const char* TcParser::ParseRepeatedStringOnce( +- const char* ptr, SerialArena* serial_arena, ParseContext* ctx, ++ const char* ptr, Arena* arena, ParseContext* ctx, + RepeatedPtrField& field) { ++ using TypeHandler = typename RepeatedPtrField::TypeHandler; + int size = ReadSize(&ptr); + if (PROTOBUF_PREDICT_FALSE(!ptr)) return {}; +- auto* str = new (serial_arena->AllocateFromStringBlock()) std::string(); +- field.AddAllocatedForParse(str); +- ptr = ctx->ReadString(ptr, size, str); ++ // 整体代码只有此处依赖SerialArena的内部实现,比较trick先不支持这种优化 ++ // 而且ArenaString实现变更后这种加速意义不大 ++ auto str = ArenaStringAccessor::create(arena); ++ field.AddAllocatedForParse(StringHandlerType::ToTagged(str.underlying())); ++ ptr = ctx->ReadArenaString(ptr, size, str); + if (PROTOBUF_PREDICT_FALSE(!ptr)) return {}; + PROTOBUF_ASSUME(ptr != nullptr); + return ptr; +@@ -2243,16 +2299,17 @@ PROTOBUF_NOINLINE const char* TcParser::MpRepeatedString( + uint32_t next_tag; + + auto* arena = field.GetArena(); +- SerialArena* serial_arena; ++ // 整体代码只有此处依赖SerialArena的内部实现,比较trick先不支持这种优化 ++ // 而且ArenaString实现变更后这种加速意义不大 + if (PROTOBUF_PREDICT_TRUE( + arena != nullptr && +- arena->impl_.GetSerialArenaFast(&serial_arena) && + field.PrepareForParse())) { + do { + ptr = ptr2; +- ptr = ParseRepeatedStringOnce(ptr, serial_arena, ctx, field); ++ ptr = ParseRepeatedStringOnce(ptr, arena, ctx, field); ++ // 切换Get接口避免进行MutableArena转换 + if (PROTOBUF_PREDICT_FALSE(ptr == nullptr || +- !MpVerifyUtf8(field[field.size() - 1], ++ !MpVerifyUtf8(field.Get(field.size() - 1), + table, entry, xform_val))) { + PROTOBUF_MUSTTAIL return Error(PROTOBUF_TC_PARAM_NO_DATA_PASS); + } +@@ -2262,8 +2319,9 @@ PROTOBUF_NOINLINE const char* TcParser::MpRepeatedString( + } else { + do { + ptr = ptr2; +- std::string* str = field.Add(); +- ptr = InlineGreedyStringParser(str, ptr, ctx); ++ // 统一适配Allocated/MutableArena/FixedSizeArena模式 ++ auto str = field.AddAccessor(); ++ ptr = ctx->ReadArenaString(ptr, str); + if (PROTOBUF_PREDICT_FALSE( + ptr == nullptr || + !MpVerifyUtf8(*str, table, entry, xform_val))) { +diff --git a/src/google/protobuf/generated_message_util.h b/src/google/protobuf/generated_message_util.h +index 589e01057..6247d7e30 100644 +--- a/src/google/protobuf/generated_message_util.h ++++ b/src/google/protobuf/generated_message_util.h +@@ -336,27 +336,52 @@ struct BytesTag { + // Assigns to `dest` the content of `value`, optionally bounded by `size`. + // This overload set is used to implement `set_xxx()` methods for repeated + // string fields in generated code. +-inline void AssignToString(std::string& dest, const std::string& value, ++//inline void AssignToString(std::string& dest, const std::string& value, ++// BytesTag tag = BytesTag{}) { ++// dest.assign(value); ++//} ++//inline void AssignToString(std::string& dest, std::string&& value, ++// BytesTag tag = BytesTag{}) { ++// dest.assign(std::move(value)); ++//} ++//inline void AssignToString(std::string& dest, const char* value, ++// BytesTag tag = BytesTag{}) { ++// dest.assign(value); ++//} ++//inline void AssignToString(std::string& dest, const char* value, ++// std::size_t size) { ++// dest.assign(value, size); ++//} ++//inline void AssignToString(std::string& dest, const void* value, ++// std::size_t size, BytesTag tag) { ++// dest.assign(reinterpret_cast(value), size); ++//} ++//inline void AssignToString(std::string& dest, absl::string_view value, ++// BytesTag tag = BytesTag{}) { ++// dest.assign(value.data(), value.size()); ++//} ++ ++inline void AssignToString(MaybeArenaStringAccessor dest, const std::string& value, + BytesTag tag = BytesTag{}) { + dest.assign(value); + } +-inline void AssignToString(std::string& dest, std::string&& value, ++inline void AssignToString(MaybeArenaStringAccessor dest, std::string&& value, + BytesTag tag = BytesTag{}) { + dest.assign(std::move(value)); + } +-inline void AssignToString(std::string& dest, const char* value, ++inline void AssignToString(MaybeArenaStringAccessor dest, const char* value, + BytesTag tag = BytesTag{}) { + dest.assign(value); + } +-inline void AssignToString(std::string& dest, const char* value, ++inline void AssignToString(MaybeArenaStringAccessor dest, const char* value, + std::size_t size) { + dest.assign(value, size); + } +-inline void AssignToString(std::string& dest, const void* value, ++inline void AssignToString(MaybeArenaStringAccessor dest, const void* value, + std::size_t size, BytesTag tag) { + dest.assign(reinterpret_cast(value), size); + } +-inline void AssignToString(std::string& dest, absl::string_view value, ++inline void AssignToString(MaybeArenaStringAccessor dest, absl::string_view value, + BytesTag tag = BytesTag{}) { + dest.assign(value.data(), value.size()); + } +@@ -367,7 +392,7 @@ inline void AssignToString(std::string& dest, absl::string_view value, + template + void AddToRepeatedPtrField(google::protobuf::RepeatedPtrField& dest, + Arg&& value, Args... args) { +- AssignToString(*dest.Add(), std::forward(value), args...); ++ AssignToString(*dest.AddAccessor(), std::forward(value), args...); + } + inline void AddToRepeatedPtrField(google::protobuf::RepeatedPtrField& dest, + std::string&& value, +diff --git a/src/google/protobuf/inlined_string_field.cc b/src/google/protobuf/inlined_string_field.cc +index a17f922b6..1c4a47f28 100644 +--- a/src/google/protobuf/inlined_string_field.cc ++++ b/src/google/protobuf/inlined_string_field.cc +@@ -24,7 +24,6 @@ + #include "google/protobuf/message_lite.h" + #include "google/protobuf/parse_context.h" + +- + // clang-format off + #include "google/protobuf/port_def.inc" + // clang-format on +@@ -33,21 +32,10 @@ namespace google { + namespace protobuf { + namespace internal { + +-#if defined(NDEBUG) || !defined(GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE) +- +-class InlinedStringField::ScopedCheckInvariants { +- public: +- constexpr explicit ScopedCheckInvariants(const InlinedStringField*) {} +-}; +- +-#endif // NDEBUG || !GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE +- +- + std::string* InlinedStringField::Mutable(const LazyString& /*default_value*/, + Arena* arena, bool donated, + uint32_t* donating_states, + uint32_t mask, MessageLite* msg) { +- ScopedCheckInvariants invariants(this); + if (arena == nullptr || !donated) { + return UnsafeMutablePointer(); + } +@@ -57,7 +45,6 @@ std::string* InlinedStringField::Mutable(const LazyString& /*default_value*/, + std::string* InlinedStringField::Mutable(Arena* arena, bool donated, + uint32_t* donating_states, + uint32_t mask, MessageLite* msg) { +- ScopedCheckInvariants invariants(this); + if (arena == nullptr || !donated) { + return UnsafeMutablePointer(); + } +@@ -70,6 +57,9 @@ std::string* InlinedStringField::MutableSlow(::google::protobuf::Arena* arena, + uint32_t mask, MessageLite* msg) { + (void)mask; + (void)msg; ++ new (get_mutable())::std::string(*get_const()); ++ *donating_states &= mask; ++ msg->OnDemandRegisterArenaDtor(arena); + return UnsafeMutablePointer(); + } + +@@ -79,6 +69,13 @@ void InlinedStringField::SetAllocated(const std::string* default_value, + uint32_t mask, MessageLite* msg) { + (void)mask; + (void)msg; ++ if (arena != nullptr && donated) { ++ new (get_mutable())::std::string(::std::move(*value)); ++ *donating_states &= mask; ++ msg->OnDemandRegisterArenaDtor(arena); ++ delete value; ++ return; ++ } + SetAllocatedNoArena(default_value, value); + } + +@@ -88,6 +85,12 @@ void InlinedStringField::Set(std::string&& value, Arena* arena, bool donated, + (void)donating_states; + (void)mask; + (void)msg; ++ if (arena != nullptr && donated) { ++ new (get_mutable())::std::string(::std::move(value)); ++ *donating_states &= mask; ++ msg->OnDemandRegisterArenaDtor(arena); ++ return; ++ } + SetNoArena(std::move(value)); + } + +@@ -102,14 +105,14 @@ std::string* InlinedStringField::Release(Arena* arena, bool donated) { + std::string* released = (arena != nullptr && donated) + ? new std::string(*get_mutable()) + : new std::string(std::move(*get_mutable())); +- get_mutable()->clear(); ++ ClearToEmpty(); + return released; + } + + void InlinedStringField::ClearToDefault(const LazyString& default_value, + Arena* arena, bool donated) { + (void)arena; +- get_mutable()->assign(default_value.get()); ++ MutableAccessor(arena, donated)->assign(default_value.get()); + } + + +diff --git a/src/google/protobuf/inlined_string_field.h b/src/google/protobuf/inlined_string_field.h +index d894acb7e..4d2c4972d 100644 +--- a/src/google/protobuf/inlined_string_field.h ++++ b/src/google/protobuf/inlined_string_field.h +@@ -25,6 +25,8 @@ + #error "You cannot SWIG proto headers" + #endif + ++#define GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE ++ + namespace google { + namespace protobuf { + +@@ -112,6 +114,8 @@ class PROTOBUF_EXPORT InlinedStringField { + // This method never changes the `donating_states`. + void Set(absl::string_view value, Arena* arena, bool donated, + uint32_t* donating_states, uint32_t mask, MessageLite* msg); ++ void Set(const std::string& value, Arena* arena, bool donated, ++ uint32_t* donating_states, uint32_t mask, MessageLite* msg); + + // Rvalue Set. If this field is donated, this method will undonate this field + // by mutating the `donating_states` according to `mask`. +@@ -131,6 +135,8 @@ class PROTOBUF_EXPORT InlinedStringField { + + void SetBytes(absl::string_view value, Arena* arena, bool donated, + uint32_t* donating_states, uint32_t mask, MessageLite* msg); ++ void SetBytes(const std::string& value, Arena* arena, bool donated, ++ uint32_t* donating_states, uint32_t mask, MessageLite* msg); + + void SetBytes(std::string&& value, Arena* arena, bool donated, + uint32_t* donating_states, uint32_t mask, MessageLite* msg); +@@ -163,6 +169,10 @@ class PROTOBUF_EXPORT InlinedStringField { + std::string* Mutable(const LazyString& default_value, Arena* arena, + bool donated, uint32_t* donating_states, uint32_t mask, + MessageLite* msg); ++ MaybeArenaStringAccessor MutableAccessor(Arena* arena, bool donated, ++ uint32_t* donating_states, ++ uint32_t mask, MessageLite* msg); ++ MaybeArenaStringAccessor MutableAccessor(Arena* arena, bool donated); + + // Mutable(nullptr_t) is an overload to explicitly support Mutable(nullptr) + // calls used by the internal parser logic. This provides API equivalence with +@@ -320,7 +330,7 @@ class PROTOBUF_EXPORT InlinedStringField { + // always be the empty std::string. + PROTOBUF_NDEBUG_INLINE void ClearToEmpty() { ClearNonDefaultToEmpty(); } + PROTOBUF_NDEBUG_INLINE void ClearNonDefaultToEmpty() { +- get_mutable()->clear(); ++ MaybeArenaStringAccessor::clear(get_mutable()); + } + + // Clears content, but keeps allocated std::string if arena != nullptr, to +@@ -372,7 +382,6 @@ inline InlinedStringField::InlinedStringField( + new (get_mutable()) std::string(default_value); + } + +- + #ifdef GOOGLE_PROTOBUF_INTERNAL_DONATE_STEAL_INLINE + constexpr uint32_t InitDonatingStates() { return ~0u; } + inline void InternalRegisterArenaDtor(Arena*, void*, void (*)(void*)) {} +@@ -390,8 +399,8 @@ inline InlinedStringField::InlinedStringField(Arena* /*arena*/) { Init(); } + + inline InlinedStringField::InlinedStringField(Arena* arena, + const InlinedStringField& rhs) { +- const std::string& src = *rhs.get_const(); +- new (value_) std::string(src); ++ Init(); ++ MaybeArenaStringAccessor(arena, get_mutable()) = *rhs.get_const(); + } + + inline const std::string& InlinedStringField::GetNoArena() const { +@@ -441,7 +450,7 @@ inline PROTOBUF_NDEBUG_INLINE void InlinedStringField::InternalSwap( + (void)rhs_arena_dtor_registered; + (void)lhs_msg; + (void)rhs_msg; +- lhs->get_mutable()->swap(*rhs->get_mutable()); ++ MaybeArenaStringAccessor::swap(lhs->get_mutable(), rhs->get_mutable()); + #endif + } + +@@ -450,10 +459,17 @@ inline void InlinedStringField::Set(absl::string_view value, Arena* arena, + uint32_t /*mask*/, MessageLite* /*msg*/) { + (void)arena; + (void)donated; +- SetNoArena(value); ++ MutableAccessor(arena, donated) = value; ++} ++ ++inline void InlinedStringField::Set(const std::string& value, Arena* arena, ++ bool donated, uint32_t* /*donating_states*/, ++ uint32_t /*mask*/, MessageLite* /*msg*/) { ++ MutableAccessor(arena, donated) = value; + } + +-inline void InlinedStringField::Set(const char* str, ::google::protobuf::Arena* arena, ++inline void InlinedStringField::Set(const char* str, ++ ::google::protobuf::Arena* arena, + bool donated, uint32_t* donating_states, + uint32_t mask, MessageLite* msg) { + Set(absl::string_view(str), arena, donated, donating_states, mask, msg); +@@ -473,6 +489,13 @@ inline void InlinedStringField::SetBytes(absl::string_view value, Arena* arena, + Set(value, arena, donated, donating_states, mask, msg); + } + ++inline void InlinedStringField::SetBytes(const std::string& value, Arena* arena, ++ bool donated, ++ uint32_t* donating_states, ++ uint32_t mask, MessageLite* msg) { ++ Set(value, arena, donated, donating_states, mask, msg); ++} ++ + inline void InlinedStringField::SetBytes(std::string&& value, Arena* arena, + bool donated, + uint32_t* donating_states, +@@ -511,6 +534,17 @@ inline void InlinedStringField::SetBytes( + Set(const_string_ref.get(), arena, donated, donating_states, mask, msg); + } + ++inline MaybeArenaStringAccessor InlinedStringField::MutableAccessor( ++ Arena* arena, bool donated, uint32_t* /*donating_states*/, ++ uint32_t /*mask*/, MessageLite* /*msg*/) { ++ return MutableAccessor(arena, donated); ++} ++ ++inline MaybeArenaStringAccessor InlinedStringField::MutableAccessor( ++ Arena* arena, bool donated) { ++ return MaybeArenaStringAccessor(donated ? arena : nullptr, get_mutable()); ++} ++ + inline std::string* InlinedStringField::UnsafeMutablePointer() { + return get_mutable(); + } +diff --git a/src/google/protobuf/io/BUILD.bazel b/src/google/protobuf/io/BUILD.bazel +index 66819966b..fca18ed8d 100644 +--- a/src/google/protobuf/io/BUILD.bazel ++++ b/src/google/protobuf/io/BUILD.bazel +@@ -199,6 +199,7 @@ cc_test( + ":tokenizer", + "//:protobuf", + "//src/google/protobuf", ++ "//src/google/protobuf:cc_test_protos", + "//src/google/protobuf:port", + "//src/google/protobuf:test_util", + "//src/google/protobuf:test_util2", +diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc +index abbc40cd0..46127f762 100644 +--- a/src/google/protobuf/io/zero_copy_stream_unittest.cc ++++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc +@@ -58,6 +58,7 @@ + #include "google/protobuf/io/zero_copy_stream_impl.h" + #include "google/protobuf/test_util.h" + #include "google/protobuf/test_util2.h" ++#include "google/protobuf/unittest.pb.h" + + #if HAVE_ZLIB + #include "google/protobuf/io/gzip_stream.h" +diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc +index 22fe7d963..0f52113f1 100644 +--- a/src/google/protobuf/message_lite.cc ++++ b/src/google/protobuf/message_lite.cc +@@ -579,6 +579,12 @@ bool MessageLite::AppendToString(std::string* output) const { + return AppendPartialToString(output); + } + ++bool MessageLite::AppendToString(MaybeArenaStringAccessor output) const { ++ ABSL_DCHECK(IsInitialized()) ++ << InitializationErrorMessage("serialize", *this); ++ return AppendPartialToString(output); ++} ++ + bool MessageLite::AppendPartialToString(std::string* output) const { + size_t old_size = output->size(); + size_t byte_size = ByteSizeLong(); +@@ -596,11 +602,31 @@ bool MessageLite::AppendPartialToString(std::string* output) const { + return true; + } + ++bool MessageLite::AppendPartialToString(MaybeArenaStringAccessor output) const { ++ size_t old_size = output->size(); ++ size_t byte_size = ByteSizeLong(); ++ if (byte_size > INT_MAX) { ++ ABSL_LOG(ERROR) << GetTypeName() ++ << " exceeded maximum protobuf size of 2GB: " << byte_size; ++ return false; ++ } ++ ++ output.__resize_default_init(old_size + byte_size); ++ uint8_t* start = reinterpret_cast(&output[0] + old_size); ++ SerializeToArrayImpl(*this, start, byte_size); ++ return true; ++} ++ + bool MessageLite::SerializeToString(std::string* output) const { + output->clear(); + return AppendToString(output); + } + ++bool MessageLite::SerializeToString(MaybeArenaStringAccessor output) const { ++ output->clear(); ++ return AppendToString(output); ++} ++ + bool MessageLite::SerializePartialToString(std::string* output) const { + output->clear(); + return AppendPartialToString(output); +diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h +index cae4e6bbe..497cff971 100644 +--- a/src/google/protobuf/message_lite.h ++++ b/src/google/protobuf/message_lite.h +@@ -28,6 +28,7 @@ + #include "absl/strings/cord.h" + #include "absl/strings/string_view.h" + #include "google/protobuf/arena.h" ++#include "google/protobuf/arenastring_impl.h" + #include "google/protobuf/explicitly_constructed.h" + #include "google/protobuf/internal_visibility.h" + #include "google/protobuf/io/coded_stream.h" +@@ -398,6 +399,7 @@ class PROTOBUF_EXPORT MessageLite { + // Serialize the message and store it in the given string. All required + // fields must be set. + bool SerializeToString(std::string* output) const; ++ bool SerializeToString(MaybeArenaStringAccessor output) const; + // Like SerializeToString(), but allows missing required fields. + bool SerializePartialToString(std::string* output) const; + // Serialize the message and store it in the given byte array. All required +@@ -430,8 +432,10 @@ class PROTOBUF_EXPORT MessageLite { + // Like SerializeToString(), but appends to the data to the string's + // existing contents. All required fields must be set. + bool AppendToString(std::string* output) const; ++ bool AppendToString(MaybeArenaStringAccessor output) const; + // Like AppendToString(), but allows missing required fields. + bool AppendPartialToString(std::string* output) const; ++ bool AppendPartialToString(MaybeArenaStringAccessor output) const; + + // Reads a protocol buffer from a Cord and merges it into this message. + bool MergeFromCord(const absl::Cord& cord); +diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h +index d40e71c1d..6dab5c22c 100644 +--- a/src/google/protobuf/parse_context.h ++++ b/src/google/protobuf/parse_context.h +@@ -198,9 +198,12 @@ class PROTOBUF_EXPORT EpsCopyInputStream { + return AppendStringFallback(ptr, size, s); + } + // Implemented in arenastring.cc ++ PROTOBUF_NODISCARD const char* ReadArenaString(const char* ptr, int size, ++ ArenaStringAccessor s); + PROTOBUF_NODISCARD const char* ReadArenaString(const char* ptr, +- ArenaStringPtr* s, +- Arena* arena); ++ ArenaStringAccessor s); ++ PROTOBUF_NODISCARD const char* ReadArenaString(const char* ptr, ++ MaybeArenaStringAccessor s); + + PROTOBUF_NODISCARD const char* ReadCord(const char* ptr, int size, + ::absl::Cord* cord) { +@@ -1267,7 +1270,6 @@ PROTOBUF_NODISCARD inline const char* InlineCordParser(::absl::Cord* cord, + return ctx->ReadCord(ptr, size, cord); + } + +- + template + PROTOBUF_NODISCARD const char* FieldParser(uint64_t tag, T& field_parser, + const char* ptr, ParseContext* ctx) { +diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc +index 49fc0b15c..2cafd0eb0 100644 +--- a/src/google/protobuf/proto3_arena_unittest.cc ++++ b/src/google/protobuf/proto3_arena_unittest.cc +@@ -286,13 +286,7 @@ TEST(Proto3ArenaTest, CheckOneofMessageFieldIsCleared) { + child->set_bb(100); + msg->Clear(); + +-#ifndef PROTOBUF_ASAN + EXPECT_EQ(child->bb(), 0); +-#else +-#if GTEST_HAS_DEATH_TEST && defined(__cpp_if_constexpr) +- EXPECT_DEATH(EXPECT_EQ(child->bb(), 100), "use-after-poison"); +-#endif +-#endif + } + + TEST(Proto3OptionalTest, OptionalFieldDescriptor) { +diff --git a/src/google/protobuf/reflection_internal.h b/src/google/protobuf/reflection_internal.h +index ea4bac293..99f7f8c7c 100644 +--- a/src/google/protobuf/reflection_internal.h ++++ b/src/google/protobuf/reflection_internal.h +@@ -136,13 +136,39 @@ class RepeatedPtrFieldWrapper : public RandomAccessRepeatedFieldAccessor { + MutableRepeatedField(data)->Clear(); + } + void Set(Field* data, int index, const Value* value) const override { ++ DoSet(data, index, value); ++ } ++ template ::value, ++ int>::type = 0> ++ void DoSet(Field* data, int index, const Value* value) const { + ConvertToT(value, MutableRepeatedField(data)->Mutable(index)); + } ++ template ::value, ++ int>::type = 0> ++ void DoSet(Field* data, int index, const Value* value) const { ++ MutableRepeatedField(data)->MutableAccessor(index) = ++ *static_cast(value); ++ } + void Add(Field* data, const Value* value) const override { ++ DoAdd(data, value); ++ } ++ template ::value, ++ int>::type = 0> ++ void DoAdd(Field* data, const Value* value) const { + T* allocated = New(value); + ConvertToT(value, allocated); + MutableRepeatedField(data)->AddAllocated(allocated); + } ++ template ::value, ++ int>::type = 0> ++ void DoAdd(Field* data, const Value* value) const { ++ MutableRepeatedField(data)->AddAccessor() = ++ *static_cast(value); ++ } + void RemoveLast(Field* data) const override { + MutableRepeatedField(data)->RemoveLast(); + } +diff --git a/src/google/protobuf/reflection_visit_field_info.h b/src/google/protobuf/reflection_visit_field_info.h +index f0c65db8a..25c8c2ad3 100644 +--- a/src/google/protobuf/reflection_visit_field_info.h ++++ b/src/google/protobuf/reflection_visit_field_info.h +@@ -190,14 +190,18 @@ struct DynamicExtensionInfoHelper { + #undef PROTOBUF_REPEATED_PTR_FIELD_METHODS + + static absl::string_view GetStringView(const Extension& ext) { +- return *ext.string_value; ++ return *ext.string_value.Get(); + } +- static void SetStringView(Extension& ext, absl::string_view value) { +- ext.string_value->assign(value.data(), value.size()); ++ static void SetStringView(Extension& ext, Arena* arena, ++ absl::string_view value) { ++ MaybeArenaStringAccessor( ++ ext.string_value.IsFixedSizeArena() ? arena : nullptr, ++ ext.string_value.Get()) ++ ->assign(value.data(), value.size()); + } + static void ClearStringView(Extension& ext) { + ext.is_cleared = true; +- ext.string_value->clear(); ++ MaybeArenaStringAccessor::clear(ext.string_value.Get()); + } + + static const Message& GetMessage(const Extension& ext) { +@@ -486,10 +490,10 @@ struct StringDynamicFieldInfo { + //////////////////////////////////////////////////////////////////////// + // Extension string fields + //////////////////////////////////////////////////////////////////////// +-template ++template + struct StringDynamicExtensionInfo { +- constexpr StringDynamicExtensionInfo(ExtensionT& e, int n) +- : ext(e), ext_number(n) {} ++ constexpr StringDynamicExtensionInfo(ExtensionS& s, ExtensionT& e, int n) ++ : set(s), ext(e), ext_number(n) {} + + int number() const { return ext_number; } + FieldDescriptor::Type type() const { +@@ -500,7 +504,7 @@ struct StringDynamicExtensionInfo { + return DynamicExtensionInfoHelper::GetStringView(ext); + } + void Set(absl::string_view value) { +- DynamicExtensionInfoHelper::SetStringView(ext, value); ++ DynamicExtensionInfoHelper::SetStringView(ext, set.GetArena(), value); + } + void Clear() { DynamicExtensionInfoHelper::ClearStringView(ext); } + size_t FieldByteSize() const { return WireFormatLite::StringSize(Get()); } +@@ -513,6 +517,7 @@ struct StringDynamicExtensionInfo { + static constexpr bool is_oneof = false; // NOLINT + static constexpr bool is_cord = false; // NOLINT + ++ ExtensionS& set; + ExtensionT& ext; + int ext_number; + }; +diff --git a/src/google/protobuf/reflection_visit_fields.h b/src/google/protobuf/reflection_visit_fields.h +index 3b53155b5..e44dfdab6 100644 +--- a/src/google/protobuf/reflection_visit_fields.h ++++ b/src/google/protobuf/reflection_visit_fields.h +@@ -20,7 +20,6 @@ + #include "google/protobuf/repeated_field.h" + #include "google/protobuf/repeated_ptr_field.h" + +- + // Must be the last include. + #include "google/protobuf/port_def.inc" // NOLINT + +@@ -388,8 +387,9 @@ void ReflectionVisit::VisitFields(MessageT& message, CallbackFn&& func, + + case FieldDescriptor::TYPE_BYTES: + case FieldDescriptor::TYPE_STRING: +- func( +- internal::StringDynamicExtensionInfo{ext, number}); ++ func(internal::StringDynamicExtensionInfo{set, ext, ++ number}); + break; + + default: +diff --git a/src/google/protobuf/reflection_visit_fields_test.cc b/src/google/protobuf/reflection_visit_fields_test.cc +index eb69d9bb0..73e21dbb1 100644 +--- a/src/google/protobuf/reflection_visit_fields_test.cc ++++ b/src/google/protobuf/reflection_visit_fields_test.cc +@@ -164,7 +164,11 @@ void MutateNothingByVisit(Message& message) { + it.CopyFrom({it.data(), it.size()}); + } + } else { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ for (auto it : info.Mutable()) { ++#else // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + for (auto& it : info.Mutable()) { ++#endif // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + std::string tmp; + tmp = it; + it = tmp; +diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc +index 144e17c93..6cb4a056a 100644 +--- a/src/google/protobuf/repeated_field_unittest.cc ++++ b/src/google/protobuf/repeated_field_unittest.cc +@@ -1607,7 +1607,7 @@ TEST(RepeatedPtrField, SwapLargeLarge) { + } + + static int ReservedSpace(RepeatedPtrField* field) { +- const std::string* const* ptr = field->data(); ++ auto ptr = field->data(); + do { + field->Add(); + } while (field->data() == ptr); +@@ -1636,7 +1636,7 @@ TEST(RepeatedPtrField, ReserveLessThanDouble) { + TEST(RepeatedPtrField, ReserveLessThanExisting) { + RepeatedPtrField field; + field.Reserve(20); +- const std::string* const* previous_ptr = field.data(); ++ auto previous_ptr = field.data(); + field.Reserve(10); + + EXPECT_EQ(previous_ptr, field.data()); +@@ -1648,11 +1648,11 @@ TEST(RepeatedPtrField, ReserveDoesntLoseAllocated) { + // failed to copy pointers to allocated-but-cleared objects, possibly + // leading to segfaults. + RepeatedPtrField field; +- std::string* first = field.Add(); ++ std::string* first = field.AddString(); + field.RemoveLast(); + + field.Reserve(20); +- EXPECT_EQ(first, field.Add()); ++ EXPECT_EQ(first, field.AddString()); + } + + // Clearing elements is tricky with RepeatedPtrFields since the memory for +@@ -1661,7 +1661,7 @@ TEST(RepeatedPtrField, ClearedElements) { + PROTOBUF_IGNORE_DEPRECATION_START + RepeatedPtrField field; + +- std::string* original = field.Add(); ++ std::string* original = field.AddString(); + *original = "foo"; + + EXPECT_EQ(field.ClearedCount(), 0); +@@ -1670,17 +1670,19 @@ TEST(RepeatedPtrField, ClearedElements) { + EXPECT_TRUE(original->empty()); + EXPECT_EQ(field.ClearedCount(), 1); + +- EXPECT_EQ(field.Add(), ++ EXPECT_EQ(field.AddString(), + original); // Should return same string for reuse. +- EXPECT_EQ(field.UnsafeArenaReleaseLast(), original); // We take ownership. ++ EXPECT_EQ(field.UnsafeArenaReleaseLast()->ToStringPtr(), ++ original); // We take ownership. + EXPECT_EQ(field.ClearedCount(), 0); + +- EXPECT_NE(field.Add(), original); // Should NOT return the same string. ++ EXPECT_NE(field.AddString(), original); // Should NOT return the same string. + EXPECT_EQ(field.ClearedCount(), 0); + +- field.UnsafeArenaAddAllocated(original); // Give ownership back. ++ field.UnsafeArenaAddAllocated(internal::StringHandlerType::ToUnTagged( ++ original)); // Give ownership back. + EXPECT_EQ(field.ClearedCount(), 0); +- EXPECT_EQ(field.Mutable(1), original); ++ EXPECT_EQ(field.MutableString(1), original); + + field.Clear(); + EXPECT_EQ(field.ClearedCount(), 2); +@@ -1775,7 +1777,6 @@ TEST(RepeatedPtrField, MergeFrom) { + EXPECT_EQ("5", destination.Get(4)); + } + +- + TEST(RepeatedPtrField, CopyFrom) { + RepeatedPtrField source, destination; + source.Add()->assign("4"); +@@ -1912,13 +1913,17 @@ TEST(RepeatedPtrField, SmallOptimization) { + // We use UnsafeArenaAddAllocated just to grow the array without creating + // objects or causing extra cleanup costs in the arena to make the + // measurements simpler. +- array->UnsafeArenaAddAllocated(&str); ++ array->UnsafeArenaAddAllocated(internal::StringHandlerType::ToUnTagged(&str)); + // No backing array, just the string. + EXPECT_EQ(array->SpaceUsedExcludingSelf(), sizeof(str)); + // We have not used any arena space. + EXPECT_EQ(usage_before, arena.SpaceUsed()); + // Verify the string is where we think it is. ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ EXPECT_EQ(array->begin()->underlying(), &str); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + EXPECT_EQ(&*array->begin(), &str); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + EXPECT_EQ(array->pointer_begin()[0], &str); + auto is_inlined = [array]() { + return std::less_equal{}(array, &*array->pointer_begin()) && +@@ -1929,7 +1934,8 @@ TEST(RepeatedPtrField, SmallOptimization) { + + // Adding a second object stops sso. + std::string str2; +- array->UnsafeArenaAddAllocated(&str2); ++ array->UnsafeArenaAddAllocated( ++ internal::StringHandlerType::ToUnTagged(&str2)); + EXPECT_EQ(array->Capacity(), 3); + // Backing array and the strings. + EXPECT_EQ(array->SpaceUsedExcludingSelf(), +@@ -1974,7 +1980,7 @@ TEST(RepeatedPtrField, MoveConstruct) { + RepeatedPtrField source; + *source.Add() = "1"; + *source.Add() = "2"; +- const std::string* const* data = source.data(); ++ auto data = source.data(); + RepeatedPtrField destination = std::move(source); + EXPECT_EQ(data, destination.data()); + EXPECT_THAT(destination, ElementsAre("1", "2")); +@@ -2004,7 +2010,7 @@ TEST(RepeatedPtrField, MoveAssign) { + *source.Add() = "2"; + RepeatedPtrField destination; + *destination.Add() = "3"; +- const std::string* const* source_data = source.data(); ++ auto source_data = source.data(); + destination = std::move(source); + EXPECT_EQ(source_data, destination.data()); + EXPECT_THAT(destination, ElementsAre("1", "2")); +@@ -2019,7 +2025,7 @@ TEST(RepeatedPtrField, MoveAssign) { + RepeatedPtrField* destination = + Arena::Create>(&arena); + *destination->Add() = "3"; +- const std::string* const* source_data = source->data(); ++ auto source_data = source->data(); + *destination = std::move(*source); + EXPECT_EQ(source_data, destination->data()); + EXPECT_THAT(*destination, ElementsAre("1", "2")); +@@ -2075,7 +2081,7 @@ TEST(RepeatedPtrField, MoveAssign) { + RepeatedPtrField& alias = field; + *field.Add() = "1"; + *field.Add() = "2"; +- const std::string* const* data = field.data(); ++ auto data = field.data(); + field = std::move(alias); + EXPECT_EQ(data, field.data()); + EXPECT_THAT(field, ElementsAre("1", "2")); +@@ -2086,7 +2092,7 @@ TEST(RepeatedPtrField, MoveAssign) { + Arena::Create>(&arena); + *field->Add() = "1"; + *field->Add() = "2"; +- const std::string* const* data = field->data(); ++ auto data = field->data(); + *field = std::move(*field); + EXPECT_EQ(data, field->data()); + EXPECT_THAT(*field, ElementsAre("1", "2")); +@@ -2099,8 +2105,8 @@ TEST(RepeatedPtrField, MutableDataIsMutable) { + EXPECT_EQ("1", field.Get(0)); + // The fact that this line compiles would be enough, but we'll check the + // value anyway. +- std::string** data = field.mutable_data(); +- **data = "2"; ++ auto data = field.mutable_data(); ++ *(*data)->UnTaggedToStringPtr() = "2"; + EXPECT_EQ("2", field.Get(0)); + } + +@@ -2109,9 +2115,13 @@ TEST(RepeatedPtrField, SubscriptOperators) { + *field.Add() = "1"; + EXPECT_EQ("1", field.Get(0)); + EXPECT_EQ("1", field[0]); ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ EXPECT_EQ(field.MutableString(0), field[0]->underlying()); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + EXPECT_EQ(field.Mutable(0), &field[0]); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + const RepeatedPtrField& const_field = field; +- EXPECT_EQ(*field.data(), &const_field[0]); ++ EXPECT_EQ((*field.data())->ToStringPtr(), &const_field[0]); + } + + TEST(RepeatedPtrField, ExtractSubrange) { +@@ -2151,9 +2161,9 @@ TEST(RepeatedPtrField, ExtractSubrange) { + + // Does the resulting array contain the right values? + for (int i = 0; i < start; ++i) +- EXPECT_EQ(field.Mutable(i), subject[i]); ++ EXPECT_EQ(field.MutableString(i), subject[i]); + for (int i = start; i < field.size(); ++i) +- EXPECT_EQ(field.Mutable(i), subject[i + num]); ++ EXPECT_EQ(field.MutableString(i), subject[i + num]); + + // Reinstate the cleared elements. + EXPECT_EQ(field.ClearedCount(), extra); +@@ -2165,7 +2175,7 @@ TEST(RepeatedPtrField, ExtractSubrange) { + for (int i = sz; i < sz + extra; ++i) { + int count = 0; + for (int j = sz; j < sz + extra; ++j) { +- if (field.Mutable(j - num) == subject[i]) count += 1; ++ if (field.MutableString(j - num) == subject[i]) count += 1; + } + EXPECT_EQ(count, 1); + } +@@ -2776,8 +2786,8 @@ TEST_F(RepeatedFieldInsertionIteratorsTest, + *new_data = absl::StrCat("name-", i); + data.push_back(new_data); + +- new_data = goldenproto.add_repeated_string(); +- *new_data = absl::StrCat("name-", i); ++ auto new_data2 = goldenproto.add_repeated_string(); ++ *new_data2 = absl::StrCat("name-", i); + } + TestAllTypes testproto; + std::copy(data.begin(), data.end(), +@@ -2805,13 +2815,13 @@ TEST_F(RepeatedFieldInsertionIteratorsTest, + + TEST_F(RepeatedFieldInsertionIteratorsTest, + UnsafeArenaAllocatedRepeatedPtrFieldWithString) { +- std::vector data; ++ std::vector data; + Arena arena; + auto* goldenproto = Arena::Create(&arena); + for (int i = 0; i < 10; ++i) { +- auto* new_data = goldenproto->add_repeated_string(); ++ auto new_data = goldenproto->add_repeated_string(); + *new_data = absl::StrCat("name-", i); +- data.push_back(new_data); ++ data.push_back(internal::StringHandlerType::ToUnTagged(new_data)); + } + auto* testproto = Arena::Create(&arena); + std::copy(data.begin(), data.end(), +diff --git a/src/google/protobuf/repeated_ptr_field.cc b/src/google/protobuf/repeated_ptr_field.cc +index 891439c14..785701733 100644 +--- a/src/google/protobuf/repeated_ptr_field.cc ++++ b/src/google/protobuf/repeated_ptr_field.cc +@@ -130,24 +130,37 @@ memswap::value>( + char* PROTOBUF_RESTRICT, char* PROTOBUF_RESTRICT); + + template <> +-void RepeatedPtrFieldBase::MergeFrom( ++void RepeatedPtrFieldBase::MergeFrom::Type>( + const RepeatedPtrFieldBase& from) { ++ using TypeHandler = GenericTypeHandler; + ABSL_DCHECK_NE(&from, this); + int new_size = current_size_ + from.current_size_; +- auto dst = reinterpret_cast(InternalReserve(new_size)); +- auto src = reinterpret_cast(from.elements()); ++ auto dst = reinterpret_cast(InternalReserve(new_size)); ++ auto src = reinterpret_cast(from.elements()); + auto end = src + from.current_size_; + auto end_assign = src + std::min(ClearedCount(), from.current_size_); +- for (; src < end_assign; ++dst, ++src) { +- (*dst)->assign(**src); +- } ++ + if (Arena* const arena = arena_) { ++ for (; src < end_assign; ++dst, ++src) { ++ if ((*dst)->IsTagged()) { ++ ArenaStringAccessor(arena, (*dst)->ToStringPtr()) ++ .assign(*(*src)->ToStringPtr()); ++ } else { ++ (*dst)->UnTaggedToStringPtr()->assign(*(*src)->ToStringPtr()); ++ } ++ } + for (; src < end; ++dst, ++src) { +- *dst = Arena::Create(arena, **src); ++ auto accessor = ++ ArenaStringAccessor::create(arena, *(*src)->ToStringPtr()); ++ *dst = StringHandlerType::ToTagged(accessor.underlying()); + } + } else { ++ for (; src < end_assign; ++dst, ++src) { ++ (*dst)->UnTaggedToStringPtr()->assign(*(*src)->ToStringPtr()); ++ } + for (; src < end; ++dst, ++src) { +- *dst = new std::string(**src); ++ *dst = StringHandlerType::ToUnTagged( ++ new std::string(*(*src)->ToStringPtr())); + } + } + ExchangeCurrentSize(new_size); +@@ -156,6 +169,17 @@ void RepeatedPtrFieldBase::MergeFrom( + } + } + ++template <> ++void RepeatedPtrFieldBase::AddAllocatedSlowWithCopy< ++ GenericTypeHandler>(StringHandlerType* value, ++ Arena* value_arena, Arena* my_arena) { ++ using TypeHandler = GenericTypeHandler; ++ if (my_arena != nullptr) { ++ my_arena->Own(value->UnTaggedToStringPtr()); ++ } ++ ++ UnsafeArenaAddAllocated(value); ++} + + int RepeatedPtrFieldBase::MergeIntoClearedMessages( + const RepeatedPtrFieldBase& from) { +@@ -224,7 +248,7 @@ void RepeatedPtrFieldBase::MergeFrom( + } + + void* NewStringElement(Arena* arena) { +- return Arena::Create(arena); ++ return GenericTypeHandler::New(arena); + } + + } // namespace internal +diff --git a/src/google/protobuf/repeated_ptr_field.h b/src/google/protobuf/repeated_ptr_field.h +index acbdb0c16..46c5c4a47 100644 +--- a/src/google/protobuf/repeated_ptr_field.h ++++ b/src/google/protobuf/repeated_ptr_field.h +@@ -36,11 +36,11 @@ + #include "absl/log/absl_check.h" + #include "absl/meta/type_traits.h" + #include "google/protobuf/arena.h" ++#include "google/protobuf/arenastring.h" + #include "google/protobuf/internal_visibility.h" + #include "google/protobuf/message_lite.h" + #include "google/protobuf/port.h" + +- + // Must be included last. + #include "google/protobuf/port_def.inc" + +@@ -765,12 +765,40 @@ inline void RepeatedPtrFieldBase::MergeFrom( + return MergeFrom(from); + } + ++// Use 1 << 1 bit in string* mark a string as arenastring ++// store this tagged ptr as StringHandlerType to disable align optimize ++struct StringHandlerType { ++ inline std::string* ToStringPtr() noexcept { ++ return reinterpret_cast(reinterpret_cast(this) & ++ ~2); ++ } ++ inline const std::string* ToStringPtr() const noexcept { ++ return reinterpret_cast( ++ reinterpret_cast(this) & ~2); ++ } ++ inline std::string* UnTaggedToStringPtr() noexcept { ++ return reinterpret_cast(this); ++ } ++ inline const std::string* UnTaggedToStringPtr() const noexcept { ++ return reinterpret_cast(this); ++ } ++ inline bool IsTagged() const noexcept { ++ return reinterpret_cast(this) & 2; ++ } ++ inline static StringHandlerType* ToTagged(std::string* string) noexcept { ++ return reinterpret_cast( ++ reinterpret_cast(string) | 2); ++ } ++ inline static StringHandlerType* ToUnTagged(std::string* string) noexcept { ++ return reinterpret_cast(string); ++ } ++}; ++ + // Appends all `std::string` values from `from` to this instance. + template <> +-void RepeatedPtrFieldBase::MergeFrom( ++void RepeatedPtrFieldBase::MergeFrom( + const RepeatedPtrFieldBase& from); + +- + template + void* RepeatedPtrFieldBase::AddInternal(F factory) { + Arena* const arena = GetArena(); +@@ -891,36 +919,47 @@ PROTOBUF_EXPORT void* NewStringElement(Arena* arena); + template <> + class GenericTypeHandler { + public: +- typedef std::string Type; +- using Movable = IsMovable; ++ // Use StringHandlerType* instead of string* to support tagged ptr ++ using Type = StringHandlerType; ++ using Movable = IsMovable; + + static constexpr auto GetNewFunc() { return NewStringElement; } + +- static PROTOBUF_NOINLINE std::string* New(Arena* arena) { +- return Arena::Create(arena); +- } +- static PROTOBUF_NOINLINE std::string* New(Arena* arena, std::string&& value) { +- return Arena::Create(arena, std::move(value)); ++ static PROTOBUF_NOINLINE Type* New(Arena* arena) { ++ if (arena != nullptr) { ++ return Type::ToTagged(ArenaStringAccessor::create(arena).underlying()); ++ } else { ++ return Type::ToUnTagged(new std::string()); ++ } + } +- static inline std::string* NewFromPrototype(const std::string*, +- Arena* arena) { ++ static inline Type* NewFromPrototype(const Type*, Arena* arena) { + return New(arena); + } +- static inline Arena* GetArena(std::string*) { return nullptr; } +- static inline void Delete(std::string* value, Arena* arena) { ++ static inline Arena* GetArena(Type*) { return nullptr; } ++ static inline void Delete(Type* value, Arena* arena) { + if (arena == nullptr) { +- delete value; ++ delete value->UnTaggedToStringPtr(); + } + } +- static inline void Clear(std::string* value) { value->clear(); } +- static inline void Merge(const std::string& from, std::string* to) { +- *to = from; ++ static inline void Clear(Type* value) { ++ MaybeArenaStringAccessor::clear(value->ToStringPtr()); ++ } ++ static inline void Merge(const Type& from, Type* to) { ++ *to->UnTaggedToStringPtr() = *from.UnTaggedToStringPtr(); + } +- static size_t SpaceUsedLong(const std::string& value) { +- return sizeof(value) + StringSpaceUsedExcludingSelfLong(value); ++ static size_t SpaceUsedLong(const Type& value) { ++ return sizeof(std::string) + ++ StringSpaceUsedExcludingSelfLong(*value.ToStringPtr()); + } + }; + ++// Most function of RepeatedPtrFieldBase is type erased except ++// AddAllocatedSlowWithCopy ++template <> ++void RepeatedPtrFieldBase::AddAllocatedSlowWithCopy< ++ GenericTypeHandler>(StringHandlerType* value, ++ Arena* value_arena, Arena* my_arena); ++ + } // namespace internal + + // RepeatedPtrField is like RepeatedField, but used for repeated strings or +@@ -943,6 +982,10 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + "We only support string and Message types in RepeatedPtrField."); + } + ++ // Note: RepeatedPtrField SHOULD NOT be subclassed by users. ++ using TypeHandler = internal::GenericTypeHandler; ++ using StorageType = typename TypeHandler::Type; ++ + public: + using value_type = Element; + using size_type = int; +@@ -996,13 +1039,46 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + int size() const; + + const_reference Get(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ template ::value, ++ int>::type = 0> + pointer Mutable(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ // Replace return type of mutable function from string* to MutableStringType ++ template ::value, ++ int>::type = 0> ++ MutableStringType Mutable(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ template ::value>::type> ++ pointer MutableString(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ template ::value>::type> ++ MaybeArenaStringAccessor MutableAccessor(int index) ++ ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // Unlike std::vector, adding an element to a RepeatedPtrField doesn't always + // make a new element; it might re-use an element left over from when the + // field was Clear()'d or resize()'d smaller. For this reason, Add() is the + // fastest API for adding a new element. ++ template ::value, ++ int>::type = 0> + pointer Add() ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ // Replace return type of mutable function from string* to MutableStringType ++ template ::value, ++ int>::type = 0> ++ MutableStringType Add(); ++ template ::value>::type> ++ Element* AddString(); ++ template ::value>::type> ++ MaybeArenaStringAccessor AddAccessor(); + + // `Add(std::move(value));` is equivalent to `*Add() = std::move(value);` + // It will either move-construct to the end of this field, or swap value +@@ -1021,18 +1097,35 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + + // Append elements in the range [begin, end) after reserving + // the appropriate number of elements. +- template ++ template ::value, ++ int>::type = 0> ++ void Add(Iter begin, Iter end); ++ // Specialization for string ++ template ::value, ++ int>::type = 0> + void Add(Iter begin, Iter end); + + const_reference operator[](int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return Get(index); + } +- reference operator[](int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ typename std::conditional<::std::is_same::value, ++ MutableStringReferenceType, reference>::type ++ operator[](int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return *Mutable(index); + } + + const_reference at(int index) const ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ template ::value, ++ int>::type = 0> + reference at(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ // Replace return type of mutable function from string* to MutableStringType ++ template ::value, ++ int>::type = 0> ++ MutableStringReferenceType at(int index) ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // Removes the last element in the array. + // Ownership of the element is retained by the array. +@@ -1060,9 +1153,10 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + + // Gets the underlying array. This pointer is possibly invalidated by + // any add or remove operation. +- Element** +- mutable_data() ABSL_ATTRIBUTE_LIFETIME_BOUND; +- const Element* const* data() const ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ // ++ // Underlying pointer for string maybe tagged ++ StorageType** mutable_data() ABSL_ATTRIBUTE_LIFETIME_BOUND; ++ const StorageType* const* data() const ABSL_ATTRIBUTE_LIFETIME_BOUND; + + // Swaps entire contents with "other". If they are on separate arenas, then + // copies data. +@@ -1149,13 +1243,17 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + // transfers to the arena at the "AddAllocated" call and is not released + // anymore, causing a double delete. UnsafeArenaAddAllocated prevents this. + // Requires: value != nullptr +- void UnsafeArenaAddAllocated(Element* value); ++ // ++ // Underlying pointer for string maybe tagged ++ void UnsafeArenaAddAllocated(StorageType* value); + + // Removes and returns the last element. Unlike ReleaseLast, the returned + // pointer is always to the original object. This may be in an arena, in + // which case it would have the arena's lifetime. + // Requires: current_size_ > 0 +- pointer UnsafeArenaReleaseLast(); ++ // ++ // Underlying pointer for string maybe tagged ++ StorageType* UnsafeArenaReleaseLast(); + + // Extracts elements with indices in the range "[start .. start+num-1]". + // The caller assumes ownership of the extracted elements and is responsible +@@ -1178,7 +1276,9 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + // copies are ever performed. Instead, the raw object pointers are returned. + // Thus, if on an arena, the returned objects must not be freed, because they + // will not be heap-allocated objects. +- void UnsafeArenaExtractSubrange(int start, int num, Element** elements); ++ // ++ // Underlying pointer for string maybe tagged ++ void UnsafeArenaExtractSubrange(int start, int num, StorageType** elements); + + // When elements are removed by calls to RemoveLast() or Clear(), they + // are not actually freed. Instead, they are cleared and kept so that +@@ -1219,7 +1319,6 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + internal::RepeatedPtrFieldBase::InternalSwap(other); + } + +- + private: + using InternalArenaConstructable_ = void; + using DestructorSkippable_ = void; +@@ -1231,14 +1330,10 @@ class RepeatedPtrField final : private internal::RepeatedPtrFieldBase { + template + friend struct WeakRepeatedPtrField; + +- // Note: RepeatedPtrField SHOULD NOT be subclassed by users. +- using TypeHandler = internal::GenericTypeHandler; +- + RepeatedPtrField(Arena* arena, const RepeatedPtrField& rhs); + RepeatedPtrField(Arena* arena, RepeatedPtrField&& rhs); + +- +- void AddAllocatedForParse(Element* p) { ++ void AddAllocatedForParse(StorageType* p) { + return RepeatedPtrFieldBase::AddAllocatedForParse(p); + } + }; +@@ -1348,37 +1443,139 @@ inline const Element& RepeatedPtrField::Get(int index) const + return RepeatedPtrFieldBase::Get(index); + } + ++template <> ++inline const std::string& RepeatedPtrField::Get(int index) const ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ return *RepeatedPtrFieldBase::Get(index).ToStringPtr(); ++} ++ + template + inline const Element& RepeatedPtrField::at(int index) const + ABSL_ATTRIBUTE_LIFETIME_BOUND { +- return RepeatedPtrFieldBase::at(index); ++ return Get(index); + } + + template ++template ::value, int>::type> + inline Element& RepeatedPtrField::at(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return RepeatedPtrFieldBase::at(index); + } + ++template ++template ::value, int>::type> ++inline MutableStringReferenceType RepeatedPtrField::at(int index) { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return MutableAccessor(index); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return *MutableString(index); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++} + + template ++template ::value, int>::type> + inline Element* RepeatedPtrField::Mutable(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return RepeatedPtrFieldBase::Mutable(index); + } + + template +-inline Element* RepeatedPtrField::Add() ABSL_ATTRIBUTE_LIFETIME_BOUND { ++template ::value, int>::type> ++inline MutableStringType RepeatedPtrField::Mutable(int index) ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return MutableAccessor(index); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return MutableString(index); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++} ++ ++template ++template ++inline Element* RepeatedPtrField::MutableString(int index) ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ auto value = RepeatedPtrFieldBase::Mutable(index); ++ if (value->IsTagged()) { ++ auto string = Arena::Create(GetArena(), *value->ToStringPtr()); ++ raw_mutable_data()[index] = string; ++ return string; ++ } ++ return value->UnTaggedToStringPtr(); ++} ++ ++template ++template ++inline MaybeArenaStringAccessor RepeatedPtrField::MutableAccessor( ++ int index) ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ auto value = RepeatedPtrFieldBase::Mutable(index); ++ if (value->IsTagged()) { ++ return MaybeArenaStringAccessor(GetArena(), value->ToStringPtr()); ++ } ++ return MaybeArenaStringAccessor(nullptr, value->UnTaggedToStringPtr()); ++} ++ ++template ++template ::value, int>::type> ++PROTOBUF_NOINLINE Element* RepeatedPtrField::Add() ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { + return RepeatedPtrFieldBase::Add(); + } + ++template ++template ::value, int>::type> ++inline MutableStringType RepeatedPtrField::Add() ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return AddAccessor(); ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ return AddString(); ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++} ++ ++template ++template ++inline Element* RepeatedPtrField::AddString() ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ auto value = RepeatedPtrFieldBase::Add(); ++ if (value->IsTagged()) { ++ auto string = Arena::Create(GetArena(), *value->ToStringPtr()); ++ raw_mutable_data()[size() - 1] = string; ++ return string; ++ } ++ return value->UnTaggedToStringPtr(); ++} ++ ++template ++template ++inline MaybeArenaStringAccessor RepeatedPtrField::AddAccessor() ++ ABSL_ATTRIBUTE_LIFETIME_BOUND { ++ auto value = RepeatedPtrFieldBase::Add(); ++ if (value->IsTagged()) { ++ return MaybeArenaStringAccessor(GetArena(), value->ToStringPtr()); ++ } ++ return MaybeArenaStringAccessor(nullptr, value->UnTaggedToStringPtr()); ++} ++ + template + inline void RepeatedPtrField::Add(Element&& value) { + RepeatedPtrFieldBase::Add(std::move(value)); + } + ++template <> ++inline void RepeatedPtrField::Add(std::string&& value) { ++ AddAccessor() = std::move(value); ++} ++ + template +-template ++template < ++ typename Iter, typename T, ++ typename std::enable_if::value, int>::type> + inline void RepeatedPtrField::Add(Iter begin, Iter end) { + if (std::is_base_of< + std::forward_iterator_tag, +@@ -1391,6 +1588,22 @@ inline void RepeatedPtrField::Add(Iter begin, Iter end) { + } + } + ++template ++template < ++ typename Iter, typename T, ++ typename std::enable_if::value, int>::type> ++inline void RepeatedPtrField::Add(Iter begin, Iter end) { ++ if (std::is_base_of< ++ std::forward_iterator_tag, ++ typename std::iterator_traits::iterator_category>::value) { ++ int reserve = static_cast(std::distance(begin, end)); ++ Reserve(size() + reserve); ++ } ++ for (; begin != end; ++begin) { ++ AddAccessor() = *begin; ++ } ++} ++ + template + inline void RepeatedPtrField::RemoveLast() { + RepeatedPtrFieldBase::RemoveLast(); +@@ -1405,7 +1618,7 @@ inline void RepeatedPtrField::DeleteSubrange(int start, int num) { + Arena* arena = GetArena(); + for (int i = 0; i < num; ++i) { + using H = CommonHandler; +- H::Delete(static_cast(subrange[i]), arena); ++ H::Delete(static_cast(subrange[i]), arena); + } + UnsafeArenaExtractSubrange(start, num, nullptr); + } +@@ -1440,7 +1653,8 @@ inline void RepeatedPtrField::ExtractSubrange(int start, int num, + // returned elements are heap-allocated. Otherwise, just forward it. + if (arena != nullptr) { + for (int i = 0; i < num; ++i) { +- elements[i] = copy(extracted[i]); ++ elements[i] = ++ reinterpret_cast(copy(extracted[i])); + } + } else { + memcpy(elements, extracted, num * sizeof(Element*)); +@@ -1452,7 +1666,7 @@ inline void RepeatedPtrField::ExtractSubrange(int start, int num, + + template + inline void RepeatedPtrField::UnsafeArenaExtractSubrange( +- int start, int num, Element** elements) { ++ int start, int num, StorageType** elements) { + ABSL_DCHECK_GE(start, 0); + ABSL_DCHECK_GE(num, 0); + ABSL_DCHECK_LE(start + num, size()); +@@ -1475,7 +1689,7 @@ template + inline void RepeatedPtrField::MergeFrom( + const RepeatedPtrField& other) { + if (other.empty()) return; +- RepeatedPtrFieldBase::MergeFrom(other); ++ RepeatedPtrFieldBase::MergeFrom(other); + } + + template +@@ -1508,14 +1722,14 @@ RepeatedPtrField::erase(const_iterator first, const_iterator last) + } + + template +-inline Element** RepeatedPtrField::mutable_data() +- ABSL_ATTRIBUTE_LIFETIME_BOUND { ++inline typename RepeatedPtrField::StorageType** ++RepeatedPtrField::mutable_data() ABSL_ATTRIBUTE_LIFETIME_BOUND { + return RepeatedPtrFieldBase::mutable_data(); + } + + template +-inline const Element* const* RepeatedPtrField::data() const +- ABSL_ATTRIBUTE_LIFETIME_BOUND { ++inline const typename RepeatedPtrField::StorageType* const* ++RepeatedPtrField::data() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + return RepeatedPtrFieldBase::data(); + } + +@@ -1559,8 +1773,15 @@ inline void RepeatedPtrField::AddAllocated(Element* value) { + RepeatedPtrFieldBase::AddAllocated(value); + } + ++template <> ++inline void RepeatedPtrField::AddAllocated(std::string* value) { ++ RepeatedPtrFieldBase::AddAllocated( ++ internal::StringHandlerType::ToUnTagged(value)); ++} ++ + template +-inline void RepeatedPtrField::UnsafeArenaAddAllocated(Element* value) { ++inline void RepeatedPtrField::UnsafeArenaAddAllocated( ++ StorageType* value) { + RepeatedPtrFieldBase::UnsafeArenaAddAllocated(value); + } + +@@ -1569,8 +1790,20 @@ inline Element* RepeatedPtrField::ReleaseLast() { + return RepeatedPtrFieldBase::ReleaseLast(); + } + ++template <> ++inline std::string* RepeatedPtrField::ReleaseLast() { ++ auto value = RepeatedPtrFieldBase::UnsafeArenaReleaseLast(); ++ if (value->IsTagged()) { ++ return new std::string(*value->ToStringPtr()); ++ } else if (GetArena() != nullptr) { ++ return new std::string(::std::move(*value->UnTaggedToStringPtr())); ++ } ++ return value->UnTaggedToStringPtr(); ++} ++ + template +-inline Element* RepeatedPtrField::UnsafeArenaReleaseLast() { ++inline typename RepeatedPtrField::StorageType* ++RepeatedPtrField::UnsafeArenaReleaseLast() { + return RepeatedPtrFieldBase::UnsafeArenaReleaseLast(); + } + +@@ -1611,11 +1844,16 @@ class RepeatedPtrIterator { + using iterator_category = std::random_access_iterator_tag; + using value_type = typename std::remove_const::type; + using difference_type = std::ptrdiff_t; +- using pointer = Element*; +- using reference = Element&; ++ using pointer = ++ typename std::conditional::value, ++ MutableStringType, Element*>::type; ++ using reference = ++ typename std::conditional::value, ++ MutableStringReferenceType, Element&>::type; ++ using TypeHandler = GenericTypeHandler; + +- RepeatedPtrIterator() : it_(nullptr) {} +- explicit RepeatedPtrIterator(void* const* it) : it_(it) {} ++ RepeatedPtrIterator() : it_(nullptr), arena_(nullptr) {} ++ RepeatedPtrIterator(void* const* it, Arena* arena) : it_(it), arena_(arena) {} + + // Allows "upcasting" from RepeatedPtrIterator to + // RepeatedPtrIterator. +@@ -1623,7 +1861,7 @@ class RepeatedPtrIterator { + typename std::enable_if::value>::type* = nullptr> + RepeatedPtrIterator(const RepeatedPtrIterator& other) +- : it_(other.it_) {} ++ : it_(other.it_), arena_(other.arena_) {} + + // dereferenceable + reference operator*() const { return *reinterpret_cast(*it_); } +@@ -1634,12 +1872,12 @@ class RepeatedPtrIterator { + ++it_; + return *this; + } +- iterator operator++(int) { return iterator(it_++); } ++ iterator operator++(int) { return iterator(it_++, arena_); } + iterator& operator--() { + --it_; + return *this; + } +- iterator operator--(int) { return iterator(it_--); } ++ iterator operator--(int) { return iterator(it_--, arena_); } + + // equality_comparable + friend bool operator==(const iterator& x, const iterator& y) { +@@ -1699,6 +1937,7 @@ class RepeatedPtrIterator { + + // The internal iterator. + void* const* it_; ++ Arena* arena_; + }; + + template +@@ -1712,6 +1951,41 @@ struct IteratorConceptSupport ++inline const std::string& RepeatedPtrIterator::operator*() ++ const { ++ auto value = reinterpret_cast(*it_); ++ return *value->ToStringPtr(); ++} ++ ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++template <> ++inline MaybeArenaStringAccessor RepeatedPtrIterator::operator*() ++ const { ++ auto value = reinterpret_cast(*it_); ++ if (value->IsTagged()) { ++ return MaybeArenaStringAccessor(arena_, value->ToStringPtr()); ++ } ++ return MaybeArenaStringAccessor(nullptr, value->UnTaggedToStringPtr()); ++} ++template <> ++inline MaybeArenaStringAccessor RepeatedPtrIterator::operator->() ++ const { ++ return operator*(); ++} ++#else // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++template <> ++inline std::string& RepeatedPtrIterator::operator*() const { ++ auto value = reinterpret_cast(*it_); ++ if (value->IsTagged()) { ++ auto string = Arena::Create(arena_, *value->ToStringPtr()); ++ *const_cast(it_) = string; ++ return *string; ++ } ++ return *value->UnTaggedToStringPtr(); ++} ++#endif // !GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ + // Provides an iterator that operates on pointers to the underlying objects + // rather than the objects themselves as RepeatedPtrIterator does. + // Consider using this when working with stl algorithms that change +@@ -1830,12 +2104,12 @@ class RepeatedPtrOverPtrsIterator { + template + inline typename RepeatedPtrField::iterator + RepeatedPtrField::begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- return iterator(raw_data()); ++ return iterator(raw_data(), GetArena()); + } + template + inline typename RepeatedPtrField::const_iterator + RepeatedPtrField::begin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { +- return iterator(raw_data()); ++ return iterator(raw_data(), nullptr); + } + template + inline typename RepeatedPtrField::const_iterator +@@ -1845,12 +2119,12 @@ RepeatedPtrField::cbegin() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + template + inline typename RepeatedPtrField::iterator + RepeatedPtrField::end() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- return iterator(raw_data() + size()); ++ return iterator(raw_data() + size(), GetArena()); + } + template + inline typename RepeatedPtrField::const_iterator + RepeatedPtrField::end() const ABSL_ATTRIBUTE_LIFETIME_BOUND { +- return iterator(raw_data() + size()); ++ return iterator(raw_data() + size(), nullptr); + } + template + inline typename RepeatedPtrField::const_iterator +@@ -1925,6 +2199,27 @@ class RepeatedPtrFieldBackInsertIterator { + private: + RepeatedPtrField* field_; + }; ++template <> ++inline RepeatedPtrFieldBackInsertIterator& ++RepeatedPtrFieldBackInsertIterator::operator=( ++ const std::string& value) { ++ *field_->AddAccessor() = value; ++ return *this; ++} ++template <> ++inline RepeatedPtrFieldBackInsertIterator& ++RepeatedPtrFieldBackInsertIterator::operator=( ++ const std::string* const ptr_to_value) { ++ *field_->AddAccessor() = *ptr_to_value; ++ return *this; ++} ++template <> ++inline RepeatedPtrFieldBackInsertIterator& ++RepeatedPtrFieldBackInsertIterator::operator=( ++ std::string&& value) { ++ *field_->AddAccessor() = ::std::move(value); ++ return *this; ++} + + // A back inserter for RepeatedPtrFields that inserts by transferring ownership + // of a pointer. +@@ -1960,8 +2255,9 @@ class AllocatedRepeatedPtrFieldBackInsertIterator { + template + class UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator { + public: ++ using TypeHandler = GenericTypeHandler; + using iterator_category = std::output_iterator_tag; +- using value_type = T; ++ using value_type = typename TypeHandler::Type; + using pointer = void; + using reference = void; + using difference_type = std::ptrdiff_t; +@@ -1970,8 +2266,8 @@ class UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator { + RepeatedPtrField* const mutable_field) + : field_(mutable_field) {} + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator=( +- T const* const ptr_to_value) { +- field_->UnsafeArenaAddAllocated(const_cast(ptr_to_value)); ++ value_type const* const ptr_to_value) { ++ field_->UnsafeArenaAddAllocated(const_cast(ptr_to_value)); + return *this; + } + UnsafeArenaAllocatedRepeatedPtrFieldBackInsertIterator& operator*() { +@@ -2038,7 +2334,6 @@ UnsafeArenaAllocatedRepeatedPtrFieldBackInserter( + mutable_field); + } + +- + namespace internal { + // Size optimization for `memswap` - supplied below N is used by every + // `RepeatedPtrField`. +diff --git a/src/google/protobuf/source_context.pb.h b/src/google/protobuf/source_context.pb.h +index 2ec4ff97b..735ab28ab 100644 +--- a/src/google/protobuf/source_context.pb.h ++++ b/src/google/protobuf/source_context.pb.h +@@ -222,6 +222,7 @@ class PROTOBUF_EXPORT SourceContext final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_file_name( + const std::string& value); + std::string* _internal_mutable_file_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_file_name_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.SourceContext) +@@ -290,7 +291,7 @@ inline PROTOBUF_ALWAYS_INLINE void SourceContext::set_file_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.SourceContext.file_name) + } + inline std::string* SourceContext::mutable_file_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_file_name(); ++ auto _s = _internal_mutable_file_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.SourceContext.file_name) + return _s; + } +@@ -306,10 +307,18 @@ inline std::string* SourceContext::_internal_mutable_file_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.file_name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor SourceContext::_internal_mutable_file_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.file_name_.MutableAccessor( GetArena()); ++} + inline std::string* SourceContext::release_file_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.SourceContext.file_name) +- return _impl_.file_name_.Release(); ++ auto* released = _impl_.file_name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.file_name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void SourceContext::set_allocated_file_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +diff --git a/src/google/protobuf/string_view_test.cc b/src/google/protobuf/string_view_test.cc +index d348f1fbe..62f248086 100644 +--- a/src/google/protobuf/string_view_test.cc ++++ b/src/google/protobuf/string_view_test.cc +@@ -1,7 +1,6 @@ + #include + #include + #include +- + #include + #include + // clang-format off +@@ -245,7 +244,11 @@ TEST(StringViewFieldTest, RepeatedViewSetter) { + EXPECT_EQ(message.repeated_string_size(), 3); + EXPECT_THAT(message.repeated_string(), ElementsAre("000", "111", "222")); + ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ for (auto it : *message.mutable_repeated_string()) { ++#else // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + for (auto& it : *message.mutable_repeated_string()) { ++#endif // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + it.append(it); + } + +@@ -288,7 +291,11 @@ TEST(StringViewFieldTest, RepeatedSetAndGetByReflection) { + + // MutableRepeatedPtrField(). + PROTOBUF_IGNORE_DEPRECATION_START; ++#if GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING ++ for (auto it : ++#else // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + for (auto& it : ++#endif // GOOGLE_PROTOBUF_MUTABLE_DONATED_STRING + *reflection->MutableRepeatedPtrField(&message, field)) { + it.append(it); + } +diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h +index 162324466..5a0ca83da 100644 +--- a/src/google/protobuf/struct.pb.h ++++ b/src/google/protobuf/struct.pb.h +@@ -725,6 +725,7 @@ class PROTOBUF_EXPORT Value final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_string_value( + const std::string& value); + std::string* _internal_mutable_string_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_string_value_accessor(); + + public: + // bool bool_value = 4; +@@ -977,7 +978,7 @@ inline PROTOBUF_ALWAYS_INLINE void Value::set_string_value(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Value.string_value) + } + inline std::string* Value::mutable_string_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_string_value(); ++ auto _s = _internal_mutable_string_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Value.string_value) + return _s; + } +@@ -1008,6 +1009,16 @@ inline std::string* Value::_internal_mutable_string_value() { + } + return _impl_.kind_.string_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Value::_internal_mutable_string_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ if (kind_case() != kStringValue) { ++ clear_kind(); ++ ++ set_has_string_value(); ++ _impl_.kind_.string_value_.InitDefault(); ++ } ++ return _impl_.kind_.string_value_.MutableAccessor( GetArena()); ++} + inline std::string* Value::release_string_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Value.string_value) +diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h +index f44579c89..75736dc2f 100644 +--- a/src/google/protobuf/type.pb.h ++++ b/src/google/protobuf/type.pb.h +@@ -357,6 +357,7 @@ class PROTOBUF_EXPORT Option final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // .google.protobuf.Any value = 2; +@@ -649,6 +650,7 @@ class PROTOBUF_EXPORT Field final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string type_url = 6; +@@ -665,6 +667,7 @@ class PROTOBUF_EXPORT Field final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_type_url( + const std::string& value); + std::string* _internal_mutable_type_url(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_type_url_accessor(); + + public: + // string json_name = 10; +@@ -681,6 +684,7 @@ class PROTOBUF_EXPORT Field final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_json_name( + const std::string& value); + std::string* _internal_mutable_json_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_json_name_accessor(); + + public: + // string default_value = 11; +@@ -697,6 +701,7 @@ class PROTOBUF_EXPORT Field final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_default_value( + const std::string& value); + std::string* _internal_mutable_default_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_default_value_accessor(); + + public: + // .google.protobuf.Field.Kind kind = 1; +@@ -967,6 +972,7 @@ class PROTOBUF_EXPORT EnumValue final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // int32 number = 2; +@@ -1233,6 +1239,7 @@ class PROTOBUF_EXPORT Type final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string edition = 7; +@@ -1249,6 +1256,7 @@ class PROTOBUF_EXPORT Type final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_edition( + const std::string& value); + std::string* _internal_mutable_edition(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_edition_accessor(); + + public: + // .google.protobuf.SourceContext source_context = 5; +@@ -1512,6 +1520,7 @@ class PROTOBUF_EXPORT Enum final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_name( + const std::string& value); + std::string* _internal_mutable_name(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_name_accessor(); + + public: + // string edition = 6; +@@ -1528,6 +1537,7 @@ class PROTOBUF_EXPORT Enum final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_edition( + const std::string& value); + std::string* _internal_mutable_edition(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_edition_accessor(); + + public: + // .google.protobuf.SourceContext source_context = 4; +@@ -1627,7 +1637,7 @@ inline PROTOBUF_ALWAYS_INLINE void Type::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Type.name) + } + inline std::string* Type::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Type.name) + return _s; + } +@@ -1643,10 +1653,18 @@ inline std::string* Type::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Type::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Type::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Type.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Type::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -1721,7 +1739,7 @@ inline void Type::clear_oneofs() { + } + inline std::string* Type::add_oneofs() ABSL_ATTRIBUTE_LIFETIME_BOUND { + ::google::protobuf::internal::TSanWrite(&_impl_); +- std::string* _s = _internal_mutable_oneofs()->Add(); ++ auto _s = _internal_mutable_oneofs()->AddString(); + // @@protoc_insertion_point(field_add_mutable:google.protobuf.Type.oneofs) + return _s; + } +@@ -1733,12 +1751,12 @@ inline const std::string& Type::oneofs(int index) const + inline std::string* Type::mutable_oneofs(int index) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + // @@protoc_insertion_point(field_mutable:google.protobuf.Type.oneofs) +- return _internal_mutable_oneofs()->Mutable(index); ++ return _internal_mutable_oneofs()->MutableString(index); + } + template + inline void Type::set_oneofs(int index, Arg_&& value, Args_... args) { + ::google::protobuf::internal::AssignToString( +- *_internal_mutable_oneofs()->Mutable(index), ++ *_internal_mutable_oneofs()->MutableAccessor(index), + std::forward(value), args... ); + // @@protoc_insertion_point(field_set:google.protobuf.Type.oneofs) + } +@@ -1952,7 +1970,7 @@ inline PROTOBUF_ALWAYS_INLINE void Type::set_edition(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Type.edition) + } + inline std::string* Type::mutable_edition() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_edition(); ++ auto _s = _internal_mutable_edition(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Type.edition) + return _s; + } +@@ -1968,10 +1986,18 @@ inline std::string* Type::_internal_mutable_edition() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.edition_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Type::_internal_mutable_edition_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.edition_.MutableAccessor( GetArena()); ++} + inline std::string* Type::release_edition() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Type.edition) +- return _impl_.edition_.Release(); ++ auto* released = _impl_.edition_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.edition_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Type::set_allocated_edition(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2072,7 +2098,7 @@ inline PROTOBUF_ALWAYS_INLINE void Field::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Field.name) + } + inline std::string* Field::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.name) + return _s; + } +@@ -2088,10 +2114,18 @@ inline std::string* Field::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Field::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Field::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Field.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Field::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2122,7 +2156,7 @@ inline PROTOBUF_ALWAYS_INLINE void Field::set_type_url(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Field.type_url) + } + inline std::string* Field::mutable_type_url() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_type_url(); ++ auto _s = _internal_mutable_type_url(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.type_url) + return _s; + } +@@ -2138,10 +2172,18 @@ inline std::string* Field::_internal_mutable_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.type_url_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Field::_internal_mutable_type_url_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.type_url_.MutableAccessor( GetArena()); ++} + inline std::string* Field::release_type_url() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Field.type_url) +- return _impl_.type_url_.Release(); ++ auto* released = _impl_.type_url_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.type_url_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Field::set_allocated_type_url(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2265,7 +2307,7 @@ inline PROTOBUF_ALWAYS_INLINE void Field::set_json_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Field.json_name) + } + inline std::string* Field::mutable_json_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_json_name(); ++ auto _s = _internal_mutable_json_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.json_name) + return _s; + } +@@ -2281,10 +2323,18 @@ inline std::string* Field::_internal_mutable_json_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.json_name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Field::_internal_mutable_json_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.json_name_.MutableAccessor( GetArena()); ++} + inline std::string* Field::release_json_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Field.json_name) +- return _impl_.json_name_.Release(); ++ auto* released = _impl_.json_name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.json_name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Field::set_allocated_json_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2315,7 +2365,7 @@ inline PROTOBUF_ALWAYS_INLINE void Field::set_default_value(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Field.default_value) + } + inline std::string* Field::mutable_default_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_default_value(); ++ auto _s = _internal_mutable_default_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Field.default_value) + return _s; + } +@@ -2331,10 +2381,18 @@ inline std::string* Field::_internal_mutable_default_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.default_value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Field::_internal_mutable_default_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.default_value_.MutableAccessor( GetArena()); ++} + inline std::string* Field::release_default_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Field.default_value) +- return _impl_.default_value_.Release(); ++ auto* released = _impl_.default_value_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.default_value_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Field::set_allocated_default_value(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2369,7 +2427,7 @@ inline PROTOBUF_ALWAYS_INLINE void Enum::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Enum.name) + } + inline std::string* Enum::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.name) + return _s; + } +@@ -2385,10 +2443,18 @@ inline std::string* Enum::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Enum::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Enum::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Enum.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Enum::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2630,7 +2696,7 @@ inline PROTOBUF_ALWAYS_INLINE void Enum::set_edition(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Enum.edition) + } + inline std::string* Enum::mutable_edition() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_edition(); ++ auto _s = _internal_mutable_edition(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Enum.edition) + return _s; + } +@@ -2646,10 +2712,18 @@ inline std::string* Enum::_internal_mutable_edition() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.edition_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Enum::_internal_mutable_edition_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.edition_.MutableAccessor( GetArena()); ++} + inline std::string* Enum::release_edition() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Enum.edition) +- return _impl_.edition_.Release(); ++ auto* released = _impl_.edition_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.edition_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Enum::set_allocated_edition(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2684,7 +2758,7 @@ inline PROTOBUF_ALWAYS_INLINE void EnumValue::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.EnumValue.name) + } + inline std::string* EnumValue::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValue.name) + return _s; + } +@@ -2700,10 +2774,18 @@ inline std::string* EnumValue::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor EnumValue::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* EnumValue::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.EnumValue.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void EnumValue::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2809,7 +2891,7 @@ inline PROTOBUF_ALWAYS_INLINE void Option::set_name(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.Option.name) + } + inline std::string* Option::mutable_name() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_name(); ++ auto _s = _internal_mutable_name(); + // @@protoc_insertion_point(field_mutable:google.protobuf.Option.name) + return _s; + } +@@ -2825,10 +2907,18 @@ inline std::string* Option::_internal_mutable_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.name_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor Option::_internal_mutable_name_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.name_.MutableAccessor( GetArena()); ++} + inline std::string* Option::release_name() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.Option.name) +- return _impl_.name_.Release(); ++ auto* released = _impl_.name_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.name_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void Option::set_allocated_name(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +diff --git a/src/google/protobuf/unittest_arenastring.proto b/src/google/protobuf/unittest_arenastring.proto +new file mode 100644 +index 000000000..f1695f2d2 +--- /dev/null ++++ b/src/google/protobuf/unittest_arenastring.proto +@@ -0,0 +1,35 @@ ++syntax = "proto2"; ++ ++package proto2_arenastring_unittest; ++ ++option cc_mutable_donated_string = false; ++ ++message Proto2 { ++ optional string s = 1; ++ optional bytes b = 2; ++ optional bytes c = 3 [ctype = CORD]; ++ required string qs = 4; ++ required bytes qb = 5; ++ required bytes qc = 6 [ctype = CORD]; ++ optional string ds = 7 [default = "10086"]; ++ optional bytes db = 8 [default = "10010"]; ++ optional bytes dc = 9 [default = "10010", ctype = CORD]; ++ repeated string rs = 10; ++ repeated bytes rb = 11; ++ oneof o { ++ string ons = 12; ++ bytes onb = 13; ++ bytes onc = 14 [ctype = CORD]; ++ } ++ map ms = 15; ++ extensions 100 to 199; ++}; ++ ++message Proto2Extension { ++ extend Proto2 { ++ optional string es = 100; ++ optional bytes eb = 101; ++ repeated string ers = 102; ++ repeated bytes erb = 103; ++ }; ++}; +diff --git a/src/google/protobuf/unittest_arenastring_mutable.proto b/src/google/protobuf/unittest_arenastring_mutable.proto +new file mode 100644 +index 000000000..1722559b3 +--- /dev/null ++++ b/src/google/protobuf/unittest_arenastring_mutable.proto +@@ -0,0 +1,35 @@ ++syntax = "proto2"; ++ ++package proto2_arenastring_unittest; ++ ++option cc_mutable_donated_string = true; ++ ++message ArenaProto2 { ++ optional string s = 1; ++ optional bytes b = 2; ++ optional bytes c = 3 [ctype = CORD]; ++ required string qs = 4; ++ required bytes qb = 5; ++ required bytes qc = 6 [ctype = CORD]; ++ optional string ds = 7 [default = "10086"]; ++ optional bytes db = 8 [default = "10010"]; ++ optional bytes dc = 9 [default = "10010", ctype = CORD]; ++ repeated string rs = 10; ++ repeated bytes rb = 11; ++ oneof o { ++ string ons = 12; ++ bytes onb = 13; ++ bytes onc = 14 [ctype = CORD]; ++ } ++ map ms = 15; ++ extensions 100 to 199; ++}; ++ ++message ArenaProto2Extension { ++ extend ArenaProto2 { ++ optional string es = 100; ++ optional bytes eb = 101; ++ repeated string ers = 102; ++ repeated bytes erb = 103; ++ }; ++}; +diff --git a/src/google/protobuf/unittest_proto3_arenastring.proto b/src/google/protobuf/unittest_proto3_arenastring.proto +new file mode 100644 +index 000000000..56cbe0a26 +--- /dev/null ++++ b/src/google/protobuf/unittest_proto3_arenastring.proto +@@ -0,0 +1,23 @@ ++syntax = "proto3"; ++ ++package proto3_arenastring_unittest; ++ ++option cc_mutable_donated_string = false; ++ ++message Proto3 { ++ string s = 1; ++ bytes b = 2; ++ bytes c = 3 [ctype = CORD]; ++ optional string os = 4; ++ optional bytes ob = 5; ++ optional bytes oc = 6 [ctype = CORD]; ++ repeated string rs = 7; ++ repeated bytes rb = 8; ++ repeated bytes rc = 9 [ctype = CORD]; ++ oneof o { ++ string ons = 10; ++ bytes onb = 11; ++ bytes onc = 12 [ctype = CORD]; ++ } ++ map ms = 13; ++}; +diff --git a/src/google/protobuf/unittest_proto3_arenastring_mutable.proto b/src/google/protobuf/unittest_proto3_arenastring_mutable.proto +new file mode 100644 +index 000000000..de3341685 +--- /dev/null ++++ b/src/google/protobuf/unittest_proto3_arenastring_mutable.proto +@@ -0,0 +1,23 @@ ++syntax = "proto3"; ++ ++package proto3_arenastring_unittest; ++ ++option cc_mutable_donated_string = true; ++ ++message ArenaProto3 { ++ string s = 1; ++ bytes b = 2; ++ // bytes c = 3 [ctype = CORD]; ++ optional string os = 4; ++ optional bytes ob = 5; ++ // optional bytes oc = 6 [ctype = CORD]; ++ repeated string rs = 7; ++ repeated bytes rb = 8; ++ // repeated bytes rc = 9 [ctype = CORD]; ++ oneof o { ++ string ons = 10; ++ bytes onb = 11; ++ // bytes onc = 12 [ctype = CORD]; ++ } ++ map ms = 13; ++}; +diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h +index 5396021ca..f024a1ca3 100644 +--- a/src/google/protobuf/wrappers.pb.h ++++ b/src/google/protobuf/wrappers.pb.h +@@ -618,6 +618,7 @@ class PROTOBUF_EXPORT StringValue final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_value( + const std::string& value); + std::string* _internal_mutable_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_value_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.StringValue) +@@ -1554,6 +1555,7 @@ class PROTOBUF_EXPORT BytesValue final : public ::google::protobuf::Message + inline PROTOBUF_ALWAYS_INLINE void _internal_set_value( + const std::string& value); + std::string* _internal_mutable_value(); ++ ::google::protobuf::MaybeArenaStringAccessor _internal_mutable_value_accessor(); + + public: + // @@protoc_insertion_point(class_scope:google.protobuf.BytesValue) +@@ -1990,7 +1992,7 @@ inline PROTOBUF_ALWAYS_INLINE void StringValue::set_value(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.StringValue.value) + } + inline std::string* StringValue::mutable_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_value(); ++ auto _s = _internal_mutable_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.StringValue.value) + return _s; + } +@@ -2006,10 +2008,18 @@ inline std::string* StringValue::_internal_mutable_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor StringValue::_internal_mutable_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.value_.MutableAccessor( GetArena()); ++} + inline std::string* StringValue::release_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.StringValue.value) +- return _impl_.value_.Release(); ++ auto* released = _impl_.value_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.value_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void StringValue::set_allocated_value(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +@@ -2044,7 +2054,7 @@ inline PROTOBUF_ALWAYS_INLINE void BytesValue::set_value(Arg_&& arg, + // @@protoc_insertion_point(field_set:google.protobuf.BytesValue.value) + } + inline std::string* BytesValue::mutable_value() ABSL_ATTRIBUTE_LIFETIME_BOUND { +- std::string* _s = _internal_mutable_value(); ++ auto _s = _internal_mutable_value(); + // @@protoc_insertion_point(field_mutable:google.protobuf.BytesValue.value) + return _s; + } +@@ -2060,10 +2070,18 @@ inline std::string* BytesValue::_internal_mutable_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + return _impl_.value_.Mutable( GetArena()); + } ++inline ::google::protobuf::MaybeArenaStringAccessor BytesValue::_internal_mutable_value_accessor() { ++ ::google::protobuf::internal::TSanWrite(&_impl_); ++ return _impl_.value_.MutableAccessor( GetArena()); ++} + inline std::string* BytesValue::release_value() { + ::google::protobuf::internal::TSanWrite(&_impl_); + // @@protoc_insertion_point(field_release:google.protobuf.BytesValue.value) +- return _impl_.value_.Release(); ++ auto* released = _impl_.value_.Release(); ++ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ _impl_.value_.Set("", GetArena()); ++ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ++ return released; + } + inline void BytesValue::set_allocated_value(std::string* value) { + ::google::protobuf::internal::TSanWrite(&_impl_); +diff --git a/third_party/rules_ruby.patch b/third_party/rules_ruby.patch +new file mode 100644 +index 000000000..886ca1c67 +--- /dev/null ++++ b/third_party/rules_ruby.patch +@@ -0,0 +1,13 @@ ++diff --git a/MODULE.bazel b/MODULE.bazel ++new file mode 100644 ++index 0000000..746f414 ++--- /dev/null +++++ b/MODULE.bazel ++@@ -0,0 +1,7 @@ +++module( +++ name = "rules_ruby", +++) +++ +++bazel_dep(name = "bazel_skylib", version = "1.3.0") +++bazel_dep(name = "platforms", version = "0.0.5") +++bazel_dep(name = "rules_pkg", version = "0.7.0") diff --git a/registry/modules/protobuf/28.3.arenastring/source.json b/registry/modules/protobuf/28.3.arenastring/source.json new file mode 100644 index 00000000..d8995b6f --- /dev/null +++ b/registry/modules/protobuf/28.3.arenastring/source.json @@ -0,0 +1,9 @@ +{ + "url": "https://github.com/protocolbuffers/protobuf/releases/download/v28.3/protobuf-28.3.zip", + "strip_prefix": "protobuf-28.3", + "integrity": "sha256-s7TDts/nS3eurpkJ/DwTAwknF/cbwhVNfBlhrNr1/kw=", + "patch_strip": 1, + "patches": { + "arenastring.patch": "sha256-vPU3J+tUz3BbyX8fCrmVw9FvG7QYWVKyvVMOiSl1uJk=" + } +} diff --git a/src/babylon/BUILD b/src/babylon/BUILD index f5636e16..d25778d0 100644 --- a/src/babylon/BUILD +++ b/src/babylon/BUILD @@ -119,19 +119,6 @@ cc_library( ], ) -cc_library( - name = 'garbage_collector', - srcs = ['garbage_collector.cpp'], - hdrs = ['garbage_collector.h'], - copts = BABYLON_COPTS, - includes = ['//src'], - strip_include_prefix = '//src', - deps = [ - '//src/babylon/concurrent:bounded_queue', - '//src/babylon/concurrent:id_allocator', - ], -) - cc_library( name = 'mlock', srcs = ['mlock.cpp'], @@ -169,6 +156,17 @@ cc_library( ], ) +cc_library( + name = 'regex', + hdrs = ['regex.h'], + copts = BABYLON_COPTS, + includes = ['//src'], + strip_include_prefix = '//src', + deps = [ + ':environment', + ], +) + cc_library( name = 'sanitizer_helper', hdrs = ['sanitizer_helper.h'], diff --git a/src/babylon/application_context.h b/src/babylon/application_context.h index a74c7632..a3f927c8 100644 --- a/src/babylon/application_context.h +++ b/src/babylon/application_context.h @@ -153,14 +153,22 @@ class ApplicationContext::ComponentHolder { template void set_option(T&& option) noexcept; + // ComponentHoler all support use as factory, but not all usable as singleton. inline bool support_singleton() const noexcept; + // If component is convertible to type T, return address offset in convertion. + // Return PTRDIFF_MAX if not convertible. template inline ptrdiff_t offset() const noexcept; + // Type-erased component as factory interface. Any create(ApplicationContext& context) noexcept; Any create(ApplicationContext& context, const Any& option) noexcept; + // Type-erased component as singleton interface. Create and hold a singleton + // component inside when first get request comes. + // Return that singleton in a type-erased way, or empty any when creation + // failed or not support_singleton. inline Any& get(ApplicationContext& context) noexcept; void for_each_type( @@ -232,7 +240,6 @@ class ApplicationContext::ComponentHolder { // 取得下一个自增序号,用来标识初始化顺序实现逆序销毁 static size_t next_sequence() noexcept; - // 查询转换到指定类型所需的偏移量,无法转换时返回最大值PTRDIFF_MAX ptrdiff_t convert_offset(const Id* type) const noexcept; void create_singleton(ApplicationContext& context) noexcept; @@ -617,10 +624,9 @@ ApplicationContext::ComponentHolder::add_convertible_type() noexcept {} template ABSL_ATTRIBUTE_NOINLINE void ApplicationContext::ComponentHolder::add_convertible_type() noexcept { - _convert_offset[&TypeId::ID] = - reinterpret_cast( - static_cast(reinterpret_cast(alignof(T)))) - - alignof(T); + _convert_offset[&TypeId::ID] = reinterpret_cast(static_cast( + reinterpret_cast(alignof(T)))) - + alignof(T); add_convertible_type(); } diff --git a/src/babylon/future.hpp b/src/babylon/future.hpp index 6bb15e41..7b2c9d49 100644 --- a/src/babylon/future.hpp +++ b/src/babylon/future.hpp @@ -244,7 +244,14 @@ template inline void FutureContext::on_finish(C&& callback) noexcept { auto head = _head.load(::std::memory_order_acquire); if (is_sealed(head)) { +// In set_value Seal head is done in release order after value assign. This +// ensure value definitely initialized here. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" internal::future::run_callback(callback, value()); +#pragma GCC diagnostic pop return; } diff --git a/src/babylon/logging/BUILD b/src/babylon/logging/BUILD index 8546eeb1..62cae582 100644 --- a/src/babylon/logging/BUILD +++ b/src/babylon/logging/BUILD @@ -110,6 +110,7 @@ cc_library( strip_include_prefix = '//src', deps = [ ':file_object', + '//src/babylon:regex', '//src/babylon:string_view', '//src/babylon:time', '@com_google_absl//absl/time', diff --git a/src/babylon/logging/rolling_file_object.cpp b/src/babylon/logging/rolling_file_object.cpp index cbffa072..497968a2 100644 --- a/src/babylon/logging/rolling_file_object.cpp +++ b/src/babylon/logging/rolling_file_object.cpp @@ -2,6 +2,7 @@ #if __cplusplus >= 201703L +#include "babylon/regex.h" #include "babylon/time.h" // clang-format off @@ -16,7 +17,6 @@ #include #include -#include BABYLON_NAMESPACE_BEGIN diff --git a/src/babylon/regex.h b/src/babylon/regex.h new file mode 100644 index 00000000..a64b8755 --- /dev/null +++ b/src/babylon/regex.h @@ -0,0 +1,13 @@ +#pragma once + +#include "babylon/environment.h" + +// See https://github.com/baidu/babylon/issues/68 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#if !__clang__ +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#include +#pragma GCC diagnostic pop diff --git a/src/babylon/reusable/patch/arena.28.0.inc b/src/babylon/reusable/patch/arena.28.0.inc new file mode 100644 index 00000000..0dc971ba --- /dev/null +++ b/src/babylon/reusable/patch/arena.28.0.inc @@ -0,0 +1,1055 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "google/protobuf/arena.h" + +#include "babylon/reusable/memory_resource.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/prefetch.h" +#include "absl/container/internal/layout.h" +#include "absl/log/absl_check.h" +#include "absl/log/absl_log.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/span.h" +#include "google/protobuf/arena_allocation_policy.h" +#include "google/protobuf/arena_cleanup.h" +#include "google/protobuf/arenaz_sampler.h" +#include "google/protobuf/port.h" +#include "google/protobuf/serial_arena.h" +#include "google/protobuf/string_block.h" +#include "google/protobuf/thread_safe_arena.h" + + +// Must be included last. +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace internal { +namespace { +struct AllocationPolicyWithMemoryResourcePointer : public AllocationPolicy { + using SwissMemoryResource = ::babylon::SwissMemoryResource; + SwissMemoryResource* resource; +}; +constexpr size_t kAllocPolicyWithMemoryResourcePointerSize = + ArenaAlignDefault::Ceil(sizeof(AllocationPolicyWithMemoryResourcePointer)); + +inline PROTOBUF_ALWAYS_INLINE bool use_swiss_resource(const AllocationPolicy* policy) noexcept { + return policy != nullptr + && policy->block_alloc == ::babylon::SwissMemoryResource::arena_block_alloc; +} + +inline PROTOBUF_ALWAYS_INLINE AllocationPolicyWithMemoryResourcePointer::SwissMemoryResource& +swiss_resource(const AllocationPolicy* policy) noexcept { + return *static_cast(policy)->resource; +} + +#if defined(__GNUC__) && __GNUC__ >= 5 +// kSentryArenaBlock is used for arenas which can be referenced pre-main. So, +// constexpr is required. +constexpr ArenaBlock kSentryArenaBlock; + +ArenaBlock* SentryArenaBlock() { + // const_cast<> is okay as kSentryArenaBlock will never be mutated. + return const_cast(&kSentryArenaBlock); +} +#else +// TODO Remove this once we're not using GCC 4.9 for tests. +// There is a compiler bug in this version that causes the above constexpr to +// fail. This version is no longer in our support window, but we use it in +// some of our aarch64 docker images. +ArenaBlock* SentryArenaBlock() { + static const ArenaBlock kSentryArenaBlock; + // const_cast<> is okay as kSentryArenaBlock will never be mutated. + return const_cast(&kSentryArenaBlock); +} +#endif + +inline size_t AllocationSize(size_t last_size, size_t start_size, + size_t max_size) { + if (last_size == 0) return start_size; + // Double the current block size, up to a limit. + return std::min(2 * last_size, max_size); +} + +SizedPtr AllocateMemory(const AllocationPolicy& policy, size_t size) { + if (policy.block_alloc == nullptr) { + return AllocateAtLeast(size); + } + return {policy.block_alloc(size), size}; +} + +SizedPtr AllocateBlock(const AllocationPolicy* policy_ptr, size_t last_size, + size_t min_bytes) { + AllocationPolicy policy; // default policy + if (policy_ptr) policy = *policy_ptr; + size_t size = + AllocationSize(last_size, policy.start_block_size, policy.max_block_size); + // Verify that min_bytes + kBlockHeaderSize won't overflow. + ABSL_CHECK_LE(min_bytes, std::numeric_limits::max() - + SerialArena::kBlockHeaderSize); + size = std::max(size, SerialArena::kBlockHeaderSize + min_bytes); + + return AllocateMemory(policy, size); +} + +SizedPtr AllocateCleanupChunk(const AllocationPolicy* policy_ptr, + size_t last_size) { + constexpr size_t kStartSize = 64; + constexpr size_t kMaxSize = 4 << 10; + static_assert(kStartSize % sizeof(cleanup::CleanupNode) == 0, ""); + + const size_t size = AllocationSize(last_size, kStartSize, kMaxSize); + if (policy_ptr == nullptr) return AllocateAtLeast(size); + return AllocateMemory(*policy_ptr, size); +} + +class GetDeallocator { + public: + explicit GetDeallocator(const AllocationPolicy* policy) + : dealloc_(policy ? policy->block_dealloc : nullptr) {} + + void operator()(SizedPtr mem) const { + if (dealloc_) { + dealloc_(mem.p, mem.n); + } else { + internal::SizedDelete(mem.p, mem.n); + } + } + + private: + void (*dealloc_)(void*, size_t); +}; + +} // namespace + +namespace cleanup { +struct ChunkList::Chunk { + CleanupNode* First() { return reinterpret_cast(this + 1); } + CleanupNode* Last() { return First() + Capacity() - 1; } + static size_t Capacity(size_t size) { + return (size - sizeof(Chunk)) / sizeof(CleanupNode); + } + size_t Capacity() const { return Capacity(size); } + + Chunk* next; + size_t size; + // Cleanup nodes follow. +}; + +void ChunkList::AddFallback(void* elem, void (*destructor)(void*), + SerialArena& arena) { + ABSL_DCHECK_EQ(next_, limit_); + SizedPtr mem = AllocateCleanupChunk(arena.parent_.AllocPolicy(), + head_ == nullptr ? 0 : head_->size); + arena.AddSpaceAllocated(mem.n); + head_ = new (mem.p) Chunk{head_, mem.n}; + next_ = head_->First(); + prefetch_ptr_ = reinterpret_cast(next_); + limit_ = next_ + Chunk::Capacity(mem.n); + AddFromExisting(elem, destructor); +} + +void ChunkList::Cleanup(const SerialArena& arena) { + Chunk* c = head_; + if (c == nullptr) return; + GetDeallocator deallocator(arena.parent_.AllocPolicy()); + + // Iterate backwards in order to destroy in the right order. + CleanupNode* it = next_ - 1; + while (true) { + CleanupNode* first = c->First(); + // A prefetch distance of 8 here was chosen arbitrarily. + constexpr int kPrefetchDistance = 8; + CleanupNode* prefetch = it; + // Prefetch the first kPrefetchDistance nodes. + for (int i = 0; prefetch >= first && i < kPrefetchDistance; + --prefetch, ++i) { + prefetch->Prefetch(); + } + // For the middle nodes, run destructor and prefetch the node + // kPrefetchDistance after the current one. + for (; prefetch >= first; --it, --prefetch) { + it->Destroy(); + prefetch->Prefetch(); + } + // Note: we could consider prefetching `next` chunk earlier. + absl::PrefetchToLocalCacheNta(c->next); + // Destroy the rest without prefetching. + for (; it >= first; --it) { + it->Destroy(); + } + Chunk* next = c->next; + deallocator({c, c->size}); + if (next == nullptr) return; + c = next; + it = c->Last(); + }; +} + +std::vector ChunkList::PeekForTesting() { + std::vector ret; + Chunk* c = head_; + if (c == nullptr) return ret; + // Iterate backwards to match destruction order. + CleanupNode* it = next_ - 1; + while (true) { + CleanupNode* first = c->First(); + for (; it >= first; --it) { + ret.push_back(it->elem); + } + c = c->next; + if (c == nullptr) return ret; + it = c->Last(); + }; +} +} // namespace cleanup + +// 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()}, + prefetch_ptr_( + b->Pointer(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize)), + head_{b}, + space_allocated_{b->size}, + parent_{parent} { + ABSL_DCHECK(!b->IsSentry()); +} + +// It is guaranteed that this is the first SerialArena. Use sentry block. +SerialArena::SerialArena(ThreadSafeArena& parent) + : head_{SentryArenaBlock()}, parent_{parent} {} + +// It is guaranteed that this is the first SerialArena but `b` may be user +// provided or newly allocated to store AllocationPolicy. +SerialArena::SerialArena(FirstSerialArena, ArenaBlock* b, + ThreadSafeArena& parent) + : head_{b}, space_allocated_{b->size}, parent_{parent} { + if (b->IsSentry()) return; + set_range(b->Pointer(kBlockHeaderSize), b->Limit()); +} + +std::vector SerialArena::PeekCleanupListForTesting() { + return cleanup_list_.PeekForTesting(); +} + +std::vector ThreadSafeArena::PeekCleanupListForTesting() { + return GetSerialArena()->PeekCleanupListForTesting(); +} + +void SerialArena::Init(ArenaBlock* b, size_t offset) { + set_range(b->Pointer(offset), b->Limit()); + head_.store(b, std::memory_order_relaxed); + space_used_.store(0, std::memory_order_relaxed); + space_allocated_.store(b->size, std::memory_order_relaxed); + cached_block_length_ = 0; + cached_blocks_ = nullptr; + string_block_.store(nullptr, std::memory_order_relaxed); + string_block_unused_.store(0, std::memory_order_relaxed); +} + +SerialArena* SerialArena::New(SizedPtr mem, ThreadSafeArena& parent) { + ABSL_DCHECK_LE(kBlockHeaderSize + ThreadSafeArena::kSerialArenaSize, mem.n); + ThreadSafeArenaStats::RecordAllocateStats(parent.arena_stats_.MutableStats(), + /*used=*/0, /*allocated=*/mem.n, + /*wasted=*/0); + auto b = new (mem.p) ArenaBlock{nullptr, mem.n}; + return new (b->Pointer(kBlockHeaderSize)) SerialArena(b, parent); +} + +template +SizedPtr SerialArena::Free(Deallocator deallocator) { + FreeStringBlocks(); + + ArenaBlock* b = head(); + SizedPtr mem = {b, b->size}; + while (b->next) { + b = b->next; // We must first advance before deleting this block + deallocator(mem); + mem = {b, b->size}; + } + return mem; +} + +PROTOBUF_NOINLINE +void* SerialArena::AllocateAlignedFallback(size_t n) { + AllocateNewBlock(n); + void* ret = nullptr; + bool res = MaybeAllocateAligned(n, &ret); + ABSL_DCHECK(res); + return ret; +} + +PROTOBUF_NOINLINE +void* SerialArena::AllocateFromStringBlockFallback() { + if (internal::use_swiss_resource(parent_.AllocPolicy())) { + auto& resource = internal::swiss_resource(parent_.AllocPolicy()); + auto ptr = reinterpret_cast<::std::string*>( + resource.allocate(sizeof(std::string))); + resource.register_destructor(ptr); + return ptr; + } + + ABSL_DCHECK_EQ(string_block_unused_.load(std::memory_order_relaxed), 0U); + StringBlock* sb = string_block_.load(std::memory_order_relaxed); + if (sb) { + AddSpaceUsed(sb->effective_size()); + } + + void* ptr; + StringBlock* new_sb; + size_t size = StringBlock::NextSize(sb); + if (MaybeAllocateAligned(size, &ptr)) { + // Correct space_used_ to avoid double counting + AddSpaceUsed(-size); + new_sb = StringBlock::Emplace(ptr, size, sb); + } else { + new_sb = StringBlock::New(sb); + AddSpaceAllocated(new_sb->allocated_size()); + } + string_block_.store(new_sb, std::memory_order_release); + size_t unused = new_sb->effective_size() - sizeof(std::string); + string_block_unused_.store(unused, std::memory_order_relaxed); + return new_sb->AtOffset(unused); +} + +PROTOBUF_NOINLINE +void* SerialArena::AllocateAlignedWithCleanupFallback( + size_t n, size_t align, void (*destructor)(void*)) { + size_t required = AlignUpTo(n, align); + AllocateNewBlock(required); + return AllocateAlignedWithCleanup(n, align, destructor); +} + +void SerialArena::AllocateNewBlock(size_t n) { + size_t used = 0; + size_t wasted = 0; + ArenaBlock* old_head = head(); + if (!old_head->IsSentry()) { + // Record how much used in this block. + used = static_cast(ptr() - old_head->Pointer(kBlockHeaderSize)); + wasted = old_head->size - used - kBlockHeaderSize; + AddSpaceUsed(used); + } + + // TODO: Evaluate if pushing unused space into the cached blocks is a + // win. In preliminary testing showed increased memory savings as expected, + // but with a CPU regression. The regression might have been an artifact of + // the microbenchmark. + + auto mem = AllocateBlock(parent_.AllocPolicy(), old_head->size, n); + AddSpaceAllocated(mem.n); + ThreadSafeArenaStats::RecordAllocateStats(parent_.arena_stats_.MutableStats(), + /*used=*/used, + /*allocated=*/mem.n, wasted); + auto* new_head = new (mem.p) ArenaBlock{old_head, mem.n}; + set_range(new_head->Pointer(kBlockHeaderSize), new_head->Limit()); + // Previous writes must take effect before writing new head. + head_.store(new_head, std::memory_order_release); + + PROTOBUF_POISON_MEMORY_REGION(ptr(), limit_ - ptr()); +} + +uint64_t SerialArena::SpaceUsed() const { + // Note: the calculation below technically causes a race with + // AllocateNewBlock when called from another thread (which happens in + // ThreadSafeArena::SpaceUsed). However, worst-case space_used_ will have + // stale data and the calculation will incorrectly assume 100% + // usage of the *current* block. + // TODO Consider eliminating this race in exchange for a possible + // performance hit on ARM (see cl/455186837). + + uint64_t space_used = 0; + StringBlock* sb = string_block_.load(std::memory_order_acquire); + if (sb) { + size_t unused = string_block_unused_.load(std::memory_order_relaxed); + space_used += sb->effective_size() - unused; + } + const ArenaBlock* h = head_.load(std::memory_order_acquire); + if (h->IsSentry()) return space_used; + + const uint64_t current_block_size = h->size; + space_used += std::min( + static_cast( + ptr() - const_cast(h)->Pointer(kBlockHeaderSize)), + current_block_size); + return space_used + space_used_.load(std::memory_order_relaxed); +} + +size_t SerialArena::FreeStringBlocks(StringBlock* string_block, + size_t unused_bytes) { + ABSL_DCHECK(string_block != nullptr); + StringBlock* next = string_block->next(); + absl::PrefetchToLocalCacheNta(next); + std::string* end = string_block->end(); + for (std::string* s = string_block->AtOffset(unused_bytes); s != end; ++s) { + s->~basic_string(); + } + size_t deallocated = StringBlock::Delete(string_block); + + while ((string_block = next) != nullptr) { + next = string_block->next(); + absl::PrefetchToLocalCacheNta(next); + for (std::string& s : *string_block) { + s.~basic_string(); + } + deallocated += StringBlock::Delete(string_block); + } + return deallocated; +} + +// Stores arrays of void* and SerialArena* instead of linked list of +// SerialArena* to speed up traversing all SerialArena. The cost of walk is non +// trivial when there are many nodes. Separately storing "ids" minimizes cache +// footprints and more efficient when looking for matching arena. +// +// Uses absl::container_internal::Layout to emulate the following: +// +// struct SerialArenaChunk { +// struct SerialArenaChunkHeader { +// SerialArenaChunk* next_chunk; +// uint32_t capacity; +// std::atomic size; +// } header; +// std::atomic ids[]; +// std::atomic arenas[]; +// }; +// +// where the size of "ids" and "arenas" is determined at runtime; hence the use +// of Layout. +struct SerialArenaChunkHeader { + constexpr SerialArenaChunkHeader(uint32_t capacity, uint32_t size) + : next_chunk(nullptr), capacity(capacity), size(size) {} + + ThreadSafeArena::SerialArenaChunk* next_chunk; + uint32_t capacity; + std::atomic size; +}; + +class ThreadSafeArena::SerialArenaChunk { + public: + SerialArenaChunk(uint32_t capacity, void* me, SerialArena* serial) { + // We use `layout`/`ids`/`arenas` local variables to avoid recomputing + // offsets if we were to call id(i)/arena(i) repeatedly. + const layout_type layout = Layout(capacity); + new (layout.Pointer(ptr())) SerialArenaChunkHeader{capacity, 1}; + + std::atomic* ids = layout.Pointer(ptr()); + new (&ids[0]) std::atomic{me}; + for (uint32_t i = 1; i < capacity; ++i) { + new (&ids[i]) std::atomic{nullptr}; + } + + std::atomic* arenas = layout.Pointer(ptr()); + new (&arenas[0]) std::atomic{serial}; + for (uint32_t i = 1; i < capacity; ++i) { + new (&arenas[i]) std::atomic{nullptr}; + } + } + + bool IsSentry() const { return capacity() == 0; } + + // next_chunk + const SerialArenaChunk* next_chunk() const { return header().next_chunk; } + SerialArenaChunk* next_chunk() { return header().next_chunk; } + void set_next(SerialArenaChunk* next_chunk) { + header().next_chunk = next_chunk; + } + + // capacity + uint32_t capacity() const { return header().capacity; } + void set_capacity(uint32_t capacity) { header().capacity = capacity; } + + // ids: returns up to size(). + absl::Span> ids() const { + return Layout().Slice(ptr()).first(safe_size()); + } + absl::Span> ids() { + return Layout().Slice(ptr()).first(safe_size()); + } + std::atomic& id(uint32_t i) { + ABSL_DCHECK_LT(i, capacity()); + return Layout().Pointer(ptr())[i]; + } + + // arenas: returns up to size(). + absl::Span> arenas() const { + return Layout().Slice(ptr()).first(safe_size()); + } + absl::Span> arenas() { + return Layout().Slice(ptr()).first(safe_size()); + } + const std::atomic& arena(uint32_t i) const { + ABSL_DCHECK_LT(i, capacity()); + return Layout().Pointer(ptr())[i]; + } + std::atomic& arena(uint32_t i) { + ABSL_DCHECK_LT(i, capacity()); + return Layout().Pointer(ptr())[i]; + } + + // Tries to insert {id, serial} to head chunk. Returns false if the head is + // already full. + // + // Note that the updating "size", "id", "arena" is individually atomic but + // those are not protected by a mutex. This is acceptable because concurrent + // lookups from SpaceUsed or SpaceAllocated accept inaccuracy due to race. On + // other paths, either race is not possible (GetSerialArenaFallback) or must + // be prevented by users (CleanupList, Free). + bool insert(void* me, SerialArena* serial) { + uint32_t idx = size().fetch_add(1, std::memory_order_relaxed); + // Bail out if this chunk is full. + if (idx >= capacity()) { + // Write old value back to avoid potential overflow. + size().store(capacity(), std::memory_order_relaxed); + return false; + } + + id(idx).store(me, std::memory_order_relaxed); + arena(idx).store(serial, std::memory_order_release); + return true; + } + + constexpr static size_t AllocSize(size_t n) { return Layout(n).AllocSize(); } + + private: + constexpr static int kHeader = 0; + constexpr static int kIds = 1; + constexpr static int kArenas = 2; + + using layout_type = absl::container_internal::Layout< + SerialArenaChunkHeader, std::atomic, std::atomic>; + + const char* ptr() const { return reinterpret_cast(this); } + char* ptr() { return reinterpret_cast(this); } + + SerialArenaChunkHeader& header() { + return *layout_type::Partial().Pointer(ptr()); + } + const SerialArenaChunkHeader& header() const { + return *layout_type::Partial().Pointer(ptr()); + } + + std::atomic& size() { return header().size; } + const std::atomic& size() const { return header().size; } + + // Returns the size capped by the capacity as fetch_add may result in a size + // greater than capacity. + uint32_t safe_size() const { + return std::min(capacity(), size().load(std::memory_order_relaxed)); + } + + constexpr static layout_type Layout(size_t n) { + return layout_type(/*header*/ 1, /*ids*/ n, /*arenas*/ n); + } + layout_type Layout() const { return Layout(capacity()); } +}; + +constexpr SerialArenaChunkHeader kSentryArenaChunk = {0, 0}; + +ThreadSafeArena::SerialArenaChunk* ThreadSafeArena::SentrySerialArenaChunk() { + // const_cast is okay because the sentry chunk is never mutated. Also, + // reinterpret_cast is acceptable here as it should be identical to + // SerialArenaChunk with zero payload. This is a necessary trick to + // constexpr initialize kSentryArenaChunk. + return reinterpret_cast( + const_cast(&kSentryArenaChunk)); +} + + +alignas(kCacheAlignment) ABSL_CONST_INIT + std::atomic ThreadSafeArena::lifecycle_id_{0}; +#if defined(PROTOBUF_NO_THREADLOCAL) +ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() { + static internal::ThreadLocalStorage* thread_cache_ = + new internal::ThreadLocalStorage(); + return *thread_cache_->Get(); +} +#elif defined(PROTOBUF_USE_DLLS) && defined(_WIN32) +ThreadSafeArena::ThreadCache& ThreadSafeArena::thread_cache() { + static PROTOBUF_THREAD_LOCAL ThreadCache thread_cache; + return thread_cache; +} +#else +PROTOBUF_CONSTINIT PROTOBUF_THREAD_LOCAL + ThreadSafeArena::ThreadCache ThreadSafeArena::thread_cache_; +#endif + +ThreadSafeArena::ThreadSafeArena() : first_arena_(*this) { Init(); } + +ThreadSafeArena::ThreadSafeArena(char* mem, size_t size) + : first_arena_(FirstSerialArena{}, FirstBlock(mem, size), *this) { + Init(); +} + +ThreadSafeArena::ThreadSafeArena(void* mem, size_t size, + const AllocationPolicy& policy) + : first_arena_(FirstSerialArena{}, FirstBlock(mem, size, policy), *this) { + InitializeWithPolicy(policy); + if (use_swiss_resource(&policy)) { + auto policy_with_resource = + static_cast(alloc_policy_.get()); + policy_with_resource->resource = reinterpret_cast<::babylon::SwissMemoryResource*>(size); + thread_cache().last_lifecycle_id_seen = UINT64_MAX; + } +} + +ArenaBlock* ThreadSafeArena::FirstBlock(void* buf, size_t size) { + ABSL_DCHECK_EQ(reinterpret_cast(buf) & 7, 0u); + if (buf == nullptr || size <= kBlockHeaderSize) { + return SentryArenaBlock(); + } + // Record user-owned block. + alloc_policy_.set_is_user_owned_initial_block(true); + return new (buf) ArenaBlock{nullptr, size}; +} + +ArenaBlock* ThreadSafeArena::FirstBlock(void* buf, size_t size, + const AllocationPolicy& policy) { + if (policy.IsDefault()) return FirstBlock(buf, size); + + ABSL_DCHECK_EQ(reinterpret_cast(buf) & 7, 0u); + + if (use_swiss_resource(&policy)) { + auto resource = + reinterpret_cast(size); + buf = resource->allocate<8>(kBlockHeaderSize + kAllocPolicyWithMemoryResourcePointerSize); + size = kBlockHeaderSize + kAllocPolicyWithMemoryResourcePointerSize; + } + + SizedPtr mem; + if (buf == nullptr || size < kBlockHeaderSize + kAllocPolicyWithMemoryResourcePointerSize) { + mem = AllocateBlock(&policy, 0, kAllocPolicyWithMemoryResourcePointerSize); + } else { + mem = {buf, size}; + // Record user-owned block. + alloc_policy_.set_is_user_owned_initial_block(true); + } + + return new (mem.p) ArenaBlock{nullptr, mem.n}; +} + +void ThreadSafeArena::InitializeWithPolicy(const AllocationPolicy& policy) { + Init(); + + if (policy.IsDefault()) return; + +#ifndef NDEBUG + const uint64_t old_alloc_policy = alloc_policy_.get_raw(); + // If there was a policy (e.g., in Reset()), make sure flags were preserved. +#define ABSL_DCHECK_POLICY_FLAGS_() \ + if (old_alloc_policy > 3) \ + ABSL_CHECK_EQ(old_alloc_policy & 3, alloc_policy_.get_raw() & 3) +#else +#define ABSL_DCHECK_POLICY_FLAGS_() +#endif // NDEBUG + + // We ensured enough space so this cannot fail. + void* p; + if (!first_arena_.MaybeAllocateAligned(kAllocPolicyWithMemoryResourcePointerSize, &p)) { + ABSL_LOG(FATAL) << "MaybeAllocateAligned cannot fail here."; + return; + } + new (p) AllocationPolicy{policy}; + // Low bits store flags, so they mustn't be overwritten. + ABSL_DCHECK_EQ(0u, reinterpret_cast(p) & 3); + alloc_policy_.set_policy(reinterpret_cast(p)); + ABSL_DCHECK_POLICY_FLAGS_(); + +#undef ABSL_DCHECK_POLICY_FLAGS_ +} + +uint64_t ThreadSafeArena::GetNextLifeCycleId() { + ThreadCache& tc = thread_cache(); + uint64_t id = tc.next_lifecycle_id; + constexpr uint64_t kInc = ThreadCache::kPerThreadIds; + if (PROTOBUF_PREDICT_FALSE((id & (kInc - 1)) == 0)) { + // On platforms that don't support uint64_t atomics we can certainly not + // afford to increment by large intervals and expect uniqueness due to + // wrapping, hence we only add by 1. + id = lifecycle_id_.fetch_add(1, std::memory_order_relaxed) * kInc; + } + tc.next_lifecycle_id = id + 1; + return id; +} + +// We assume that #threads / arena is bimodal; i.e. majority small ones are +// single threaded but some big ones are highly concurrent. To balance between +// memory overhead and minimum pointer chasing, we start with few entries and +// exponentially (4x) grow with a limit (255 entries). Note that parameters are +// picked for x64 architectures as hint and the actual size is calculated by +// Layout. +ThreadSafeArena::SerialArenaChunk* ThreadSafeArena::NewSerialArenaChunk( + uint32_t prev_capacity, void* id, SerialArena* serial) { + constexpr size_t kMaxBytes = 4096; // Can hold up to 255 entries. + constexpr size_t kGrowthFactor = 4; + constexpr size_t kHeaderSize = SerialArenaChunk::AllocSize(0); + constexpr size_t kEntrySize = SerialArenaChunk::AllocSize(1) - kHeaderSize; + + // On x64 arch: {4, 16, 64, 256, 256, ...} * 16. + size_t prev_bytes = SerialArenaChunk::AllocSize(prev_capacity); + size_t next_bytes = std::min(kMaxBytes, prev_bytes * kGrowthFactor); + uint32_t next_capacity = + static_cast(next_bytes - kHeaderSize) / kEntrySize; + // Growth based on bytes needs to be adjusted by AllocSize. + next_bytes = SerialArenaChunk::AllocSize(next_capacity); + + // If we allocate bigger memory than requested, we should expand + // size to use that extra space, and add extra entries permitted + // by the extra space. + SizedPtr mem = AllocateAtLeast(next_bytes); + next_capacity = static_cast(mem.n - kHeaderSize) / kEntrySize; + ABSL_DCHECK_LE(SerialArenaChunk::AllocSize(next_capacity), mem.n); + return new (mem.p) SerialArenaChunk{next_capacity, id, serial}; +} + +// Tries to reserve an entry by atomic fetch_add. If the head chunk is already +// full (size >= capacity), acquires the mutex and adds a new head. +void ThreadSafeArena::AddSerialArena(void* id, SerialArena* serial) { + SerialArenaChunk* head = head_.load(std::memory_order_acquire); + // Fast path without acquiring mutex. + if (!head->IsSentry() && head->insert(id, serial)) { + return; + } + + // Slow path with acquiring mutex. + absl::MutexLock lock(&mutex_); + + // Refetch and if someone else installed a new head, try allocating on that! + SerialArenaChunk* new_head = head_.load(std::memory_order_acquire); + if (new_head != head) { + if (new_head->insert(id, serial)) return; + // Update head to link to the latest one. + head = new_head; + } + + new_head = NewSerialArenaChunk(head->capacity(), id, serial); + new_head->set_next(head); + + // Use "std::memory_order_release" to make sure prior stores are visible after + // this one. + head_.store(new_head, std::memory_order_release); +} + +void ThreadSafeArena::UnpoisonAllArenaBlocks() const { + VisitSerialArena([](const SerialArena* serial) { + for (const auto* b = serial->head(); b != nullptr && !b->IsSentry(); + b = b->next) { + PROTOBUF_UNPOISON_MEMORY_REGION(b, b->size); + } + }); +} + +void ThreadSafeArena::Init() { + tag_and_id_ = GetNextLifeCycleId(); + arena_stats_ = Sample(); + head_.store(SentrySerialArenaChunk(), std::memory_order_relaxed); + first_owner_ = &thread_cache(); + + // Record allocation for the first block that was either user-provided or + // newly allocated. + ThreadSafeArenaStats::RecordAllocateStats( + arena_stats_.MutableStats(), + /*used=*/0, + /*allocated=*/first_arena_.SpaceAllocated(), + /*wasted=*/0); + + CacheSerialArena(&first_arena_); +} + +ThreadSafeArena::~ThreadSafeArena() { + // Have to do this in a first pass, because some of the destructors might + // refer to memory in other blocks. + CleanupList(); + + auto mem = Free(); + if (alloc_policy_.is_user_owned_initial_block()) { + // Unpoison the initial block, now that it's going back to the user. + PROTOBUF_UNPOISON_MEMORY_REGION(mem.p, mem.n); + } else if (mem.n > 0) { + GetDeallocator(alloc_policy_.get())(mem); + } +} + +SizedPtr ThreadSafeArena::Free() { + auto deallocator = GetDeallocator(alloc_policy_.get()); + + WalkSerialArenaChunk([&](SerialArenaChunk* chunk) { + absl::Span> span = chunk->arenas(); + // Walks arenas backward to handle the first serial arena the last. Freeing + // in reverse-order to the order in which objects were created may not be + // necessary to Free and we should revisit this. (b/247560530) + for (auto it = span.rbegin(); it != span.rend(); ++it) { + SerialArena* serial = it->load(std::memory_order_relaxed); + ABSL_DCHECK_NE(serial, nullptr); + // Always frees the first block of "serial" as it cannot be user-provided. + SizedPtr mem = serial->Free(deallocator); + ABSL_DCHECK_NE(mem.p, nullptr); + deallocator(mem); + } + + // Delete the chunk as we're done with it. + internal::SizedDelete(chunk, + SerialArenaChunk::AllocSize(chunk->capacity())); + }); + + // The first block of the first arena is special and let the caller handle it. + return first_arena_.Free(deallocator); +} + +uint64_t ThreadSafeArena::Reset() { + const size_t space_allocated = SpaceAllocated(); + + // Have to do this in a first pass, because some of the destructors might + // refer to memory in other blocks. + CleanupList(); + // Reset the first arena's cleanup list. + first_arena_.cleanup_list_ = cleanup::ChunkList(); + + // Discard all blocks except the first one. Whether it is user-provided or + // allocated, always reuse the first block for the first arena. + auto mem = Free(); + + // Reset the first arena with the first block. This avoids redundant + // free / allocation and re-allocating for AllocationPolicy. Adjust offset if + // we need to preserve alloc_policy_. + if (alloc_policy_.is_user_owned_initial_block() || + alloc_policy_.get() != nullptr) { + size_t offset = alloc_policy_.get() == nullptr + ? kBlockHeaderSize + : kBlockHeaderSize + kAllocPolicyWithMemoryResourcePointerSize; + first_arena_.Init(new (mem.p) ArenaBlock{nullptr, mem.n}, offset); + } else { + first_arena_.Init(SentryArenaBlock(), 0); + } + + // Since the first block and potential alloc_policy on the first block is + // preserved, this can be initialized by Init(). + Init(); + + return space_allocated; +} + +void* ThreadSafeArena::AllocateAlignedWithCleanup(size_t n, size_t align, + void (*destructor)(void*)) { + SerialArena* arena; + if (PROTOBUF_PREDICT_TRUE(GetSerialArenaFast(&arena))) { + return arena->AllocateAlignedWithCleanup(n, align, destructor); + } else { + return AllocateAlignedWithCleanupFallback(n, align, destructor); + } +} + +void ThreadSafeArena::AddCleanup(void* elem, void (*cleanup)(void*)) { + if (use_swiss_resource(AllocPolicy())) { + swiss_resource(AllocPolicy()).do_register_destructor(elem, cleanup); + return; + } + GetSerialArena()->AddCleanup(elem, cleanup); +} + +SerialArena* ThreadSafeArena::GetSerialArena() { + SerialArena* arena; + if (PROTOBUF_PREDICT_FALSE(!GetSerialArenaFast(&arena))) { + arena = GetSerialArenaFallback(kMaxCleanupNodeSize); + } + return arena; +} + +PROTOBUF_NOINLINE +void* ThreadSafeArena::AllocateAlignedWithCleanupFallback( + size_t n, size_t align, void (*destructor)(void*)) { + return GetSerialArenaFallback(n + kMaxCleanupNodeSize) + ->AllocateAlignedWithCleanup(n, align, destructor); +} + +PROTOBUF_NOINLINE +void* ThreadSafeArena::AllocateFromStringBlock() { + if (internal::use_swiss_resource(AllocPolicy())) { + auto& resource = internal::swiss_resource(AllocPolicy()); + auto ptr = resource.allocate(sizeof(::std::string)); + resource.register_destructor<::std::string>(static_cast<::std::string*>(ptr)); + return ptr; + } + return GetSerialArena()->AllocateFromStringBlock(); +} + +template +void ThreadSafeArena::WalkConstSerialArenaChunk(Callback fn) const { + const SerialArenaChunk* chunk = head_.load(std::memory_order_acquire); + + for (; !chunk->IsSentry(); chunk = chunk->next_chunk()) { + // Prefetch the next chunk. + absl::PrefetchToLocalCache(chunk->next_chunk()); + fn(chunk); + } +} + +template +void ThreadSafeArena::WalkSerialArenaChunk(Callback fn) { + // By omitting an Acquire barrier we help the sanitizer that any user code + // that doesn't properly synchronize Reset() or the destructor will throw a + // TSAN warning. + SerialArenaChunk* chunk = head_.load(std::memory_order_relaxed); + + while (!chunk->IsSentry()) { + // Cache next chunk in case this chunk is destroyed. + SerialArenaChunk* next_chunk = chunk->next_chunk(); + // Prefetch the next chunk. + absl::PrefetchToLocalCache(next_chunk); + fn(chunk); + chunk = next_chunk; + } +} + +template +void ThreadSafeArena::VisitSerialArena(Callback fn) const { + // In most cases, arenas are single-threaded and "first_arena_" should be + // sufficient. + fn(&first_arena_); + + WalkConstSerialArenaChunk([&fn](const SerialArenaChunk* chunk) { + for (const auto& each : chunk->arenas()) { + const SerialArena* serial = each.load(std::memory_order_acquire); + // It is possible that newly added SerialArena is not updated although + // size was. This is acceptable for SpaceAllocated and SpaceUsed. + if (serial == nullptr) continue; + fn(serial); + } + }); +} + +uint64_t ThreadSafeArena::SpaceAllocated() const { + uint64_t space_allocated = 0; + VisitSerialArena([&space_allocated](const SerialArena* serial) { + space_allocated += serial->SpaceAllocated(); + }); + return space_allocated; +} + +uint64_t ThreadSafeArena::SpaceUsed() const { + // `first_arena_` doesn't have kSerialArenaSize overhead, so adjust it here. + uint64_t space_used = kSerialArenaSize; + VisitSerialArena([&space_used](const SerialArena* serial) { + // SerialArena on chunks directly allocated from the block and needs to be + // subtracted from SpaceUsed. + space_used += serial->SpaceUsed() - kSerialArenaSize; + }); + return space_used - (alloc_policy_.get() ? sizeof(AllocationPolicyWithMemoryResourcePointer) : 0); +} + +template +PROTOBUF_NOINLINE void* ThreadSafeArena::AllocateAlignedFallback(size_t n) { + return GetSerialArenaFallback(n)->AllocateAligned(n); +} + +template void* ThreadSafeArena::AllocateAlignedFallback< + AllocationClient::kDefault>(size_t); +template void* + ThreadSafeArena::AllocateAlignedFallback(size_t); + +void ThreadSafeArena::CleanupList() { +#ifdef PROTOBUF_ASAN + UnpoisonAllArenaBlocks(); +#endif + + WalkSerialArenaChunk([](SerialArenaChunk* chunk) { + absl::Span> span = chunk->arenas(); + // Walks arenas backward to handle the first serial arena the last. + // Destroying in reverse-order to the construction is often assumed by users + // and required not to break inter-object dependencies. (b/247560530) + for (auto it = span.rbegin(); it != span.rend(); ++it) { + SerialArena* serial = it->load(std::memory_order_relaxed); + ABSL_DCHECK_NE(serial, nullptr); + serial->CleanupList(); + } + }); + // First arena must be cleaned up last. (b/247560530) + first_arena_.CleanupList(); +} + +PROTOBUF_NOINLINE +SerialArena* ThreadSafeArena::GetSerialArenaFallback(size_t n) { + void* const id = &thread_cache(); + if (id == first_owner_) { + CacheSerialArena(&first_arena_); + return &first_arena_; + } + + // Search matching SerialArena. + SerialArena* serial = nullptr; + WalkConstSerialArenaChunk([&serial, id](const SerialArenaChunk* chunk) { + absl::Span> ids = chunk->ids(); + for (uint32_t i = 0; i < ids.size(); ++i) { + if (ids[i].load(std::memory_order_relaxed) == id) { + serial = chunk->arena(i).load(std::memory_order_relaxed); + ABSL_DCHECK_NE(serial, nullptr); + break; + } + } + }); + + if (!serial) { + // This thread doesn't have any SerialArena, which also means it doesn't + // have any blocks yet. So we'll allocate its first block now. It must be + // big enough to host SerialArena and the pending request. + serial = SerialArena::New( + AllocateBlock(alloc_policy_.get(), 0, n + kSerialArenaSize), *this); + + AddSerialArena(id, serial); + } + + CacheSerialArena(serial); + return serial; +} + +} // namespace internal + +void* Arena::Allocate(size_t n) { + if (internal::use_swiss_resource(impl_.AllocPolicy())) { + return internal::swiss_resource(impl_.AllocPolicy()).allocate<8>(n); + } + return impl_.AllocateAligned(n); +} + +void* Arena::AllocateForArray(size_t n) { + if (internal::use_swiss_resource(impl_.AllocPolicy())) { + return internal::swiss_resource(impl_.AllocPolicy()).allocate<8>(n); + } + return impl_.AllocateAligned(n); +} + +void* Arena::AllocateAlignedWithCleanup(size_t n, size_t align, + void (*destructor)(void*)) { + if (internal::use_swiss_resource(impl_.AllocPolicy())) { + auto& resource = internal::swiss_resource(impl_.AllocPolicy()); + auto ptr = resource.allocate<8>(n); + resource.do_register_destructor(ptr, destructor); + return ptr; + } + return impl_.AllocateAlignedWithCleanup(n, align, destructor); +} + +std::vector Arena::PeekCleanupListForTesting() { + return impl_.PeekCleanupListForTesting(); +} + +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" diff --git a/src/babylon/reusable/patch/arena.cpp b/src/babylon/reusable/patch/arena.cpp index 880cd7a1..425eef19 100644 --- a/src/babylon/reusable/patch/arena.cpp +++ b/src/babylon/reusable/patch/arena.cpp @@ -3,13 +3,15 @@ #if BABYLON_USE_PROTOBUF #include "google/protobuf/message.h" -#if GOOGLE_PROTOBUF_VERSION >= 5027000 && GOOGLE_PROTOBUF_VERSION < 5028000 +#if GOOGLE_PROTOBUF_VERSION >= 5028000 && GOOGLE_PROTOBUF_VERSION < 5029000 +#include "arena.28.0.inc" +#elif GOOGLE_PROTOBUF_VERSION >= 5027000 && GOOGLE_PROTOBUF_VERSION < 5028000 #include "arena.27.0.inc" #elif GOOGLE_PROTOBUF_VERSION >= 4025000 && GOOGLE_PROTOBUF_VERSION < 4026000 #include "arena.25.0.inc" #elif GOOGLE_PROTOBUF_VERSION >= 4022000 && GOOGLE_PROTOBUF_VERSION < 4023000 #include "arena.22.0.inc" -#elif GOOGLE_PROTOBUF_VERSION >= 3021000 && GOOGLE_PROTOBUF_VERSION < 3022000 +#elif GOOGLE_PROTOBUF_VERSION >= 3021000 && GOOGLE_PROTOBUF_VERSION < 4022000 #include "arena.21.0.inc" #elif GOOGLE_PROTOBUF_VERSION >= 3019000 && GOOGLE_PROTOBUF_VERSION < 3020000 #include "arena.3.19.0.inc"