Skip to content

Commit

Permalink
Use ProtobufArrayList for repeated bytes field. Presize primitive arr…
Browse files Browse the repository at this point in the history
…ays for fixed-length primitives.

PiperOrigin-RevId: 542353392
  • Loading branch information
protobuf-github-bot authored and copybara-github committed Jun 21, 2023
1 parent bcc4646 commit b0b926a
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 115 deletions.
1 change: 0 additions & 1 deletion src/google/protobuf/compiler/java/message_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ bool BitfieldTracksMutability(const FieldDescriptor* const descriptor) {
case FieldDescriptor::TYPE_GROUP:
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_ENUM:
case FieldDescriptor::TYPE_BYTES:
return true;
default:
return false;
Expand Down
185 changes: 71 additions & 114 deletions src/google/protobuf/compiler/java/primitive_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include "google/protobuf/compiler/java/doc_comment.h"
#include "google/protobuf/compiler/java/helpers.h"
#include "google/protobuf/compiler/java/name_resolver.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
#include "google/protobuf/wire_format.h"

Expand Down Expand Up @@ -71,6 +72,7 @@ void SetPrimitiveVariables(
variables->insert({"field_type", (*variables)["type"]});

std::string name = (*variables)["name"];
(*variables)["name_make_immutable"] = absl::StrCat(name, "_.makeImmutable()");
if (javaType == JAVATYPE_BOOLEAN || javaType == JAVATYPE_DOUBLE ||
javaType == JAVATYPE_FLOAT || javaType == JAVATYPE_INT ||
javaType == JAVATYPE_LONG) {
Expand All @@ -80,29 +82,18 @@ void SetPrimitiveVariables(
absl::StrCat("com.google.protobuf.Internal.", capitalized_type, "List");
(*variables)["empty_list"] =
absl::StrCat("empty", capitalized_type, "List()");
(*variables)["create_list"] =
absl::StrCat("new", capitalized_type, "List()");
(*variables)["mutable_copy_list"] =
absl::StrCat("mutableCopy(", name, "_)");
(*variables)["name_make_immutable"] =
absl::StrCat(name, "_.makeImmutable()");
(*variables)["repeated_get"] =
absl::StrCat(name, "_.get", capitalized_type);
(*variables)["repeated_add"] =
absl::StrCat(name, "_.add", capitalized_type);
(*variables)["repeated_set"] =
absl::StrCat(name, "_.set", capitalized_type);
} else {
std::string boxed_type = (*variables)["boxed_type"];
(*variables)["field_list_type"] =
absl::StrCat("java.util.List<", boxed_type, ">");
(*variables)["create_list"] =
absl::StrCat("new java.util.ArrayList<", boxed_type, ">()");
(*variables)["mutable_copy_list"] =
absl::StrCat("new java.util.ArrayList<", boxed_type, ">(", name, "_)");
(*variables)["empty_list"] = "java.util.Collections.emptyList()";
(*variables)["name_make_immutable"] = absl::StrCat(
name, "_ = java.util.Collections.unmodifiableList(", name, "_)");
"com.google.protobuf.Internal.ProtobufList<com.google.protobuf."
"ByteString>";
(*variables)["empty_list"] =
"emptyList(com.google.protobuf.ByteString.class)";
(*variables)["repeated_get"] = absl::StrCat(name, "_.get");
(*variables)["repeated_add"] = absl::StrCat(name, "_.add");
(*variables)["repeated_set"] = absl::StrCat(name, "_.set");
Expand Down Expand Up @@ -171,13 +162,6 @@ void SetPrimitiveVariables(
break;
}
}
// For repeated bytes builders, one bit is used for whether the array is
// mutable.
// TODO(b/255468704): migrate bytes field to ProtobufList so that we can
// use the bit field to track their presence instead of mutability.
(*variables)["get_mutable_bit_builder"] = GenerateGetBit(builderBitIndex);
(*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex);
(*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex);

// Always track the presence of a field explicitly in the builder, regardless
// of syntax.
Expand Down Expand Up @@ -731,57 +715,42 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateMembers(

void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderMembers(
io::Printer* printer) const {
// One field is the list and the bit field keeps track of whether the
// list is immutable. If it's immutable, the invariant is that it must
// either an instance of Collections.emptyList() or it's an ArrayList
// wrapped in a Collections.unmodifiableList() wrapper and nobody else has
// a reference to the underlying ArrayList. This invariant allows us to
// share instances of lists between protocol buffers avoiding expensive
// memory allocations. Note, immutable is a strong guarantee here -- not
// just that the list cannot be modified via the reference but that the
// list can never be modified.
// We use a ProtobufArrayList because it starts as a mutable list that can be
// switched to immutable when references are handed out. This allows copy-free
// sharing. A bit in the bitfield tracks whether there are any items in the
// list. The presence bit allows us to skip work on blocks of 32 fields by
// by checking if the entire bit-field int == 0 (none of the fields are
// present).
printer->Print(variables_,
"private $field_list_type$ $name$_ = $empty_list$;\n");

if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_,
"private void ensure$capitalized_name$IsMutable() {\n"
" if (!$get_mutable_bit_builder$) {\n"
" $name$_ = $mutable_copy_list$;\n"
" $set_mutable_bit_builder$;\n"
" }\n"
"}\n");
} else {
printer->Print(variables_,
"private void ensure$capitalized_name$IsMutable() {\n"
" if (!$name$_.isModifiable()) {\n"
" $name$_ = $mutable_copy_list$;\n"
" }\n"
" $set_has_field_bit_builder$\n"
"}\n");
"private $field_list_type$ $name$_ = $empty_list$;\n"
"private void ensure$capitalized_name$IsMutable() {\n"
" if (!$name$_.isModifiable()) {\n"
" $name$_ = makeMutableCopy($name$_);\n"
" }\n"
" $set_has_field_bit_builder$\n"
"}\n");
if (FixedSize(GetType(descriptor_)) != -1) {
printer->Print(
variables_,
"private void ensure$capitalized_name$IsMutable(int capacity) {\n"
" if (!$name$_.isModifiable()) {\n"
" $name$_ = makeMutableCopy($name$_, capacity);\n"
" }\n"
" $set_has_field_bit_builder$\n"
"}\n");
}

// Note: We return an unmodifiable list because otherwise the caller
// could hold on to the returned list and modify it after the message
// has been built, thus mutating the message which is supposed to be
// immutable.
WriteFieldAccessorDocComment(printer, descriptor_, LIST_GETTER);
if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_,
"$deprecation$public java.util.List<$boxed_type$>\n"
" ${$get$capitalized_name$List$}$() {\n"
" return $get_mutable_bit_builder$ ?\n"
" java.util.Collections.unmodifiableList($name$_) "
": $name$_;\n"
"}\n");
} else {
printer->Print(variables_,
"$deprecation$public java.util.List<$boxed_type$>\n"
" ${$get$capitalized_name$List$}$() {\n"
" $name$_.makeImmutable();\n"
" return $name$_;\n"
"}\n");
}
printer->Print(variables_,
"$deprecation$public java.util.List<$boxed_type$>\n"
" ${$get$capitalized_name$List$}$() {\n"
" $name$_.makeImmutable();\n"
" return $name$_;\n"
"}\n");
printer->Annotate("{", "}", descriptor_);
WriteFieldAccessorDocComment(printer, descriptor_, LIST_COUNT);
printer->Print(
Expand All @@ -804,54 +773,45 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderMembers(
" int index, $type$ value) {\n"
" $null_check$\n"
" ensure$capitalized_name$IsMutable();\n"
" $repeated_set$(index, value);\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
if (descriptor_->type() != FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_, " $set_has_field_bit_builder$\n");
}
printer->Print(variables_,
" $repeated_set$(index, value);\n"
" $set_has_field_bit_builder$\n"
" $on_changed$\n"
" return this;\n"
"}\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER,
/* builder */ true);
printer->Print(variables_,
"$deprecation$public Builder "
"${$add$capitalized_name$$}$($type$ value) {\n"
" $null_check$\n"
" ensure$capitalized_name$IsMutable();\n"
" $repeated_add$(value);\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
if (descriptor_->type() != FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_, " $set_has_field_bit_builder$\n");
}
printer->Print(variables_,
" $repeated_add$(value);\n"
" $set_has_field_bit_builder$\n"
" $on_changed$\n"
" return this;\n"
"}\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER,
/* builder */ true);
printer->Print(variables_,
"$deprecation$public Builder ${$addAll$capitalized_name$$}$(\n"
" java.lang.Iterable<? extends $boxed_type$> values) {\n"
" ensure$capitalized_name$IsMutable();\n"
" com.google.protobuf.AbstractMessageLite.Builder.addAll(\n"
" values, $name$_);\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
if (descriptor_->type() != FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_, " $set_has_field_bit_builder$\n");
}
printer->Print(variables_,
" values, $name$_);\n"
" $set_has_field_bit_builder$\n"
" $on_changed$\n"
" return this;\n"
"}\n");
printer->Annotate("{", "}", descriptor_, Semantic::kSet);
WriteFieldAccessorDocComment(printer, descriptor_, CLEARER,
/* builder */ true);
printer->Print(
variables_,
"$deprecation$public Builder ${$clear$capitalized_name$$}$() {\n"
" $name$_ = $empty_list$;\n"
" $clear_mutable_bit_builder$;\n"
" $clear_has_field_bit_builder$\n"
" $on_changed$\n"
" return this;\n"
"}\n");
Expand Down Expand Up @@ -977,14 +937,9 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateMergingCode(
printer->Print(variables_,
"if (!other.$name$_.isEmpty()) {\n"
" if ($name$_.isEmpty()) {\n"
" $name$_ = other.$name$_;\n");
if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_, " $clear_mutable_bit_builder$;\n");
} else {
printer->Print(variables_,
" $name$_.makeImmutable();\n"
" $set_has_field_bit_builder$\n");
}
" $name$_ = other.$name$_;\n"
" $name_make_immutable$;\n"
" $set_has_field_bit_builder$\n");
printer->Print(variables_,
" } else {\n"
" ensure$capitalized_name$IsMutable();\n"
Expand All @@ -998,20 +953,11 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuildingCode(
io::Printer* printer) const {
// The code below ensures that the result has an immutable list. If our
// list is immutable, we can just reuse it. If not, we make it immutable.
if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) {
printer->Print(variables_,
"if ($get_mutable_bit_builder$) {\n"
" $name_make_immutable$;\n"
" $clear_mutable_bit_builder$;\n"
"}\n"
"result.$name$_ = $name$_;\n");
} else {
printer->Print(variables_,
"if ($get_has_field_bit_from_local$) {\n"
" $name_make_immutable$;\n"
" result.$name$_ = $name$_;\n"
"}\n");
}
printer->Print(variables_,
"if ($get_has_field_bit_from_local$) {\n"
" $name_make_immutable$;\n"
" result.$name$_ = $name$_;\n"
"}\n");
}

void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderParsingCode(
Expand All @@ -1024,14 +970,25 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderParsingCode(

void RepeatedImmutablePrimitiveFieldGenerator::
GenerateBuilderParsingCodeFromPacked(io::Printer* printer) const {
printer->Print(variables_,
"int length = input.readRawVarint32();\n"
"int limit = input.pushLimit(length);\n"
"ensure$capitalized_name$IsMutable();\n"
"while (input.getBytesUntilLimit() > 0) {\n"
" $repeated_add$(input.read$capitalized_type$());\n"
"}\n"
"input.popLimit(limit);\n");
if (FixedSize(GetType(descriptor_)) != -1) {
printer->Print(variables_,
"int length = input.readRawVarint32();\n"
"int limit = input.pushLimit(length);\n"
"ensure$capitalized_name$IsMutable(length / $fixed_size$);\n"
"while (input.getBytesUntilLimit() > 0) {\n"
" $repeated_add$(input.read$capitalized_type$());\n"
"}\n"
"input.popLimit(limit);\n");
} else {
printer->Print(variables_,
"int length = input.readRawVarint32();\n"
"int limit = input.pushLimit(length);\n"
"ensure$capitalized_name$IsMutable();\n"
"while (input.getBytesUntilLimit() > 0) {\n"
" $repeated_add$(input.read$capitalized_type$());\n"
"}\n"
"input.popLimit(limit);\n");
}
}

void RepeatedImmutablePrimitiveFieldGenerator::GenerateSerializationCode(
Expand Down

0 comments on commit b0b926a

Please sign in to comment.