From a1f4760777467c2d5bdcadbee59541dbe0965f4d Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 5 Jan 2022 08:50:29 -0800 Subject: [PATCH 1/4] Improve performance of parsing unknown fields in Java (#9371) Credit should go to @elharo for most of these Java changes--I am just cherry-picking them from our internal codebase. The one thing I did change was to give the UTF-8 validation tests their own Bazel test target. This makes it possible to give the other tests a shorter timeout, which is important for UnknownFieldSetPerformanceTest in particular. --- Makefile.am | 1 + java/core/BUILD | 35 +- .../com/google/protobuf/UnknownFieldSet.java | 427 +++++++++--------- .../UnknownFieldSetPerformanceTest.java | 78 ++++ .../google/protobuf/UnknownFieldSetTest.java | 182 +++++++- java/lite/pom.xml | 1 + 6 files changed, 506 insertions(+), 218 deletions(-) create mode 100644 java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java diff --git a/Makefile.am b/Makefile.am index cd209f3cdca9..7ce954e04884 100644 --- a/Makefile.am +++ b/Makefile.am @@ -491,6 +491,7 @@ java_EXTRA_DIST= java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java \ java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \ java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \ + java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java \ java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \ java/core/src/test/java/com/google/protobuf/Utf8Test.java \ java/core/src/test/java/com/google/protobuf/Utf8Utils.java \ diff --git a/java/core/BUILD b/java/core/BUILD index 9308ba71af5b..5ac4b147915e 100644 --- a/java/core/BUILD +++ b/java/core/BUILD @@ -222,6 +222,7 @@ test_suite( "core_build_test", "conformance_test", "core_tests", + "utf8_tests", ], ) @@ -241,12 +242,17 @@ conformance_test( junit_tests( name = "core_tests", - srcs = glob(["src/test/java/**/*.java"], exclude = [ - "src/test/java/com/google/protobuf/TestUtil.java", - "src/test/java/com/google/protobuf/TestUtilLite.java", - ]), + size = "small", + srcs = glob( + ["src/test/java/**/*.java"], + exclude = [ + "src/test/java/com/google/protobuf/DecodeUtf8Test.java", + "src/test/java/com/google/protobuf/IsValidUtf8Test.java", + "src/test/java/com/google/protobuf/TestUtil.java", + "src/test/java/com/google/protobuf/TestUtilLite.java", + ], + ), data = ["//:testdata"], - size = "large", deps = [ ":core", ":generic_test_protos_java_proto", @@ -260,6 +266,24 @@ junit_tests( ] ) +# The UTF-8 validation tests are much slower than the other tests, so they get +# their own test target with a longer timeout. +junit_tests( + name = "utf8_tests", + size = "large", + srcs = [ + "src/test/java/com/google/protobuf/DecodeUtf8Test.java", + "src/test/java/com/google/protobuf/IsValidUtf8Test.java", + "src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java", + ], + deps = [ + ":core", + "@maven//:com_google_guava_guava", + "@maven//:com_google_truth_truth", + "@maven//:junit_junit", + ], +) + java_lite_proto_library( name = "generic_test_protos_java_proto_lite", visibility = [ @@ -342,6 +366,7 @@ LITE_TEST_EXCLUSIONS = [ "src/test/java/com/google/protobuf/TypeRegistryTest.java", "src/test/java/com/google/protobuf/UnknownEnumValueTest.java", "src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java", + "src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java", "src/test/java/com/google/protobuf/UnknownFieldSetTest.java", "src/test/java/com/google/protobuf/WellKnownTypesTest.java", "src/test/java/com/google/protobuf/WireFormatTest.java", diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java index ba2f9df08e67..5c482d62dab5 100644 --- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java +++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -43,13 +43,13 @@ import java.util.TreeMap; /** - * {@code UnknownFieldSet} is used to keep track of fields which were seen when parsing a protocol + * {@code UnknownFieldSet} keeps track of fields which were seen when parsing a protocol * message but whose field numbers or types are unrecognized. This most frequently occurs when new * fields are added to a message type and then messages containing those fields are read by old * software that was compiled before the new types were added. * *

Every {@link Message} contains an {@code UnknownFieldSet} (and every {@link Message.Builder} - * contains an {@link Builder}). + * contains a {@link Builder}). * *

Most users will never need to use this class. * @@ -57,9 +57,13 @@ */ public final class UnknownFieldSet implements MessageLite { - private UnknownFieldSet() { - fields = null; - fieldsDescending = null; + private final TreeMap fields; + + /** + * Construct an {@code UnknownFieldSet} around the given map. + */ + UnknownFieldSet(TreeMap fields) { + this.fields = fields; } /** Create a new {@link Builder}. */ @@ -68,7 +72,7 @@ public static Builder newBuilder() { } /** Create a new {@link Builder} and initialize it to be a copy of {@code copyFrom}. */ - public static Builder newBuilder(final UnknownFieldSet copyFrom) { + public static Builder newBuilder(UnknownFieldSet copyFrom) { return newBuilder().mergeFrom(copyFrom); } @@ -83,25 +87,11 @@ public UnknownFieldSet getDefaultInstanceForType() { } private static final UnknownFieldSet defaultInstance = - new UnknownFieldSet( - Collections.emptyMap(), Collections.emptyMap()); - - /** - * Construct an {@code UnknownFieldSet} around the given map. The map is expected to be immutable. - */ - UnknownFieldSet(final Map fields, final Map fieldsDescending) { - this.fields = fields; - this.fieldsDescending = fieldsDescending; - } - - private final Map fields; - - /** A copy of {@link #fields} who's iterator order is reversed. */ - private final Map fieldsDescending; + new UnknownFieldSet(new TreeMap()); @Override - public boolean equals(final Object other) { + public boolean equals(Object other) { if (this == other) { return true; } @@ -110,29 +100,33 @@ public boolean equals(final Object other) { @Override public int hashCode() { + if (fields.isEmpty()) { // avoid allocation of iterator. + // This optimization may not be helpful but it is needed for the allocation tests to pass. + return 0; + } return fields.hashCode(); } /** Get a map of fields in the set by number. */ public Map asMap() { - return fields; + return (Map) fields.clone(); } /** Check if the given field number is present in the set. */ - public boolean hasField(final int number) { + public boolean hasField(int number) { return fields.containsKey(number); } /** Get a field by number. Returns an empty field if not present. Never returns {@code null}. */ - public Field getField(final int number) { - final Field result = fields.get(number); + public Field getField(int number) { + Field result = fields.get(number); return (result == null) ? Field.getDefaultInstance() : result; } /** Serializes the set and writes it to {@code output}. */ @Override - public void writeTo(final CodedOutputStream output) throws IOException { - for (final Map.Entry entry : fields.entrySet()) { + public void writeTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : fields.entrySet()) { Field field = entry.getValue(); field.writeTo(entry.getKey(), output); } @@ -154,10 +148,10 @@ public String toString() { @Override public ByteString toByteString() { try { - final ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); + ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); writeTo(out.getCodedOutput()); return out.build(); - } catch (final IOException e) { + } catch (IOException e) { throw new RuntimeException( "Serializing to a ByteString threw an IOException (should never happen).", e); } @@ -170,12 +164,12 @@ public ByteString toByteString() { @Override public byte[] toByteArray() { try { - final byte[] result = new byte[getSerializedSize()]; - final CodedOutputStream output = CodedOutputStream.newInstance(result); + byte[] result = new byte[getSerializedSize()]; + CodedOutputStream output = CodedOutputStream.newInstance(result); writeTo(output); output.checkNoSpaceLeft(); return result; - } catch (final IOException e) { + } catch (IOException e) { throw new RuntimeException( "Serializing to a byte array threw an IOException (should never happen).", e); } @@ -186,16 +180,16 @@ public byte[] toByteArray() { * {@link #writeTo(CodedOutputStream)}. */ @Override - public void writeTo(final OutputStream output) throws IOException { - final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + public void writeTo(OutputStream output) throws IOException { + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); writeTo(codedOutput); codedOutput.flush(); } @Override public void writeDelimitedTo(OutputStream output) throws IOException { - final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); - codedOutput.writeRawVarint32(getSerializedSize()); + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + codedOutput.writeUInt32NoTag(getSerializedSize()); writeTo(codedOutput); codedOutput.flush(); } @@ -204,15 +198,17 @@ public void writeDelimitedTo(OutputStream output) throws IOException { @Override public int getSerializedSize() { int result = 0; - for (final Map.Entry entry : fields.entrySet()) { - result += entry.getValue().getSerializedSize(entry.getKey()); + if (!fields.isEmpty()) { + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSize(entry.getKey()); + } } return result; } /** Serializes the set and writes it to {@code output} using {@code MessageSet} wire format. */ - public void writeAsMessageSetTo(final CodedOutputStream output) throws IOException { - for (final Map.Entry entry : fields.entrySet()) { + public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : fields.entrySet()) { entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), output); } } @@ -221,7 +217,7 @@ public void writeAsMessageSetTo(final CodedOutputStream output) throws IOExcepti void writeTo(Writer writer) throws IOException { if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write fields in descending order. - for (Map.Entry entry : fieldsDescending.entrySet()) { + for (Map.Entry entry : fields.descendingMap().entrySet()) { entry.getValue().writeTo(entry.getKey(), writer); } } else { @@ -233,15 +229,15 @@ void writeTo(Writer writer) throws IOException { } /** Serializes the set and writes it to {@code writer} using {@code MessageSet} wire format. */ - void writeAsMessageSetTo(final Writer writer) throws IOException { + void writeAsMessageSetTo(Writer writer) throws IOException { if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write fields in descending order. - for (final Map.Entry entry : fieldsDescending.entrySet()) { + for (Map.Entry entry : fields.descendingMap().entrySet()) { entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); } } else { // Write fields in ascending order. - for (final Map.Entry entry : fields.entrySet()) { + for (Map.Entry entry : fields.entrySet()) { entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); } } @@ -250,7 +246,7 @@ void writeAsMessageSetTo(final Writer writer) throws IOException { /** Get the number of bytes required to encode this set using {@code MessageSet} wire format. */ public int getSerializedSizeAsMessageSet() { int result = 0; - for (final Map.Entry entry : fields.entrySet()) { + for (Map.Entry entry : fields.entrySet()) { result += entry.getValue().getSerializedSizeAsMessageSetExtension(entry.getKey()); } return result; @@ -264,23 +260,23 @@ public boolean isInitialized() { } /** Parse an {@code UnknownFieldSet} from the given input stream. */ - public static UnknownFieldSet parseFrom(final CodedInputStream input) throws IOException { + public static UnknownFieldSet parseFrom(CodedInputStream input) throws IOException { return newBuilder().mergeFrom(input).build(); } /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ - public static UnknownFieldSet parseFrom(final ByteString data) + public static UnknownFieldSet parseFrom(ByteString data) throws InvalidProtocolBufferException { return newBuilder().mergeFrom(data).build(); } /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ - public static UnknownFieldSet parseFrom(final byte[] data) throws InvalidProtocolBufferException { + public static UnknownFieldSet parseFrom(byte[] data) throws InvalidProtocolBufferException { return newBuilder().mergeFrom(data).build(); } /** Parse an {@code UnknownFieldSet} from {@code input} and return it. */ - public static UnknownFieldSet parseFrom(final InputStream input) throws IOException { + public static UnknownFieldSet parseFrom(InputStream input) throws IOException { return newBuilder().mergeFrom(input).build(); } @@ -309,64 +305,43 @@ public static final class Builder implements MessageLite.Builder { // This constructor should never be called directly (except from 'create'). private Builder() {} - private Map fields; - - // Optimization: We keep around a builder for the last field that was - // modified so that we can efficiently add to it multiple times in a - // row (important when parsing an unknown repeated field). - private int lastFieldNumber; - private Field.Builder lastField; + private TreeMap fieldBuilders = new TreeMap<>(); private static Builder create() { - Builder builder = new Builder(); - builder.reinitialize(); - return builder; + return new Builder(); } /** * Get a field builder for the given field number which includes any values that already exist. */ - private Field.Builder getFieldBuilder(final int number) { - if (lastField != null) { - if (number == lastFieldNumber) { - return lastField; - } - // Note: addField() will reset lastField and lastFieldNumber. - addField(lastFieldNumber, lastField.build()); - } + private Field.Builder getFieldBuilder(int number) { if (number == 0) { return null; } else { - final Field existing = fields.get(number); - lastFieldNumber = number; - lastField = Field.newBuilder(); - if (existing != null) { - lastField.mergeFrom(existing); + Field.Builder builder = fieldBuilders.get(number); + if (builder == null) { + builder = Field.newBuilder(); + fieldBuilders.put(number, builder); } - return lastField; + return builder; } } /** * Build the {@link UnknownFieldSet} and return it. - * - *

Once {@code build()} has been called, the {@code Builder} will no longer be usable. - * Calling any method after {@code build()} will result in undefined behavior and can cause a - * {@code NullPointerException} to be thrown. */ @Override public UnknownFieldSet build() { - getFieldBuilder(0); // Force lastField to be built. - final UnknownFieldSet result; - if (fields.isEmpty()) { + UnknownFieldSet result; + if (fieldBuilders.isEmpty()) { result = getDefaultInstance(); } else { - Map descendingFields = null; - descendingFields = - Collections.unmodifiableMap(((TreeMap) fields).descendingMap()); - result = new UnknownFieldSet(Collections.unmodifiableMap(fields), descendingFields); + TreeMap fields = new TreeMap<>(); + for (Map.Entry entry : fieldBuilders.entrySet()) { + fields.put(entry.getKey(), entry.getValue().build()); + } + result = new UnknownFieldSet(fields); } - fields = null; return result; } @@ -378,11 +353,13 @@ public UnknownFieldSet buildPartial() { @Override public Builder clone() { - getFieldBuilder(0); // Force lastField to be built. - Map descendingFields = null; - descendingFields = - Collections.unmodifiableMap(((TreeMap) fields).descendingMap()); - return UnknownFieldSet.newBuilder().mergeFrom(new UnknownFieldSet(fields, descendingFields)); + Builder clone = UnknownFieldSet.newBuilder(); + for (Map.Entry entry : fieldBuilders.entrySet()) { + Integer key = entry.getKey(); + Field.Builder value = entry.getValue(); + clone.fieldBuilders.put(key, value.clone()); + } + return clone; } @Override @@ -390,31 +367,24 @@ public UnknownFieldSet getDefaultInstanceForType() { return UnknownFieldSet.getDefaultInstance(); } - private void reinitialize() { - fields = Collections.emptyMap(); - lastFieldNumber = 0; - lastField = null; - } - /** Reset the builder to an empty set. */ @Override public Builder clear() { - reinitialize(); + fieldBuilders = new TreeMap<>(); return this; } - /** Clear fields from the set with a given field number. */ - public Builder clearField(final int number) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } - if (lastField != null && lastFieldNumber == number) { - // Discard this. - lastField = null; - lastFieldNumber = 0; + /** + * Clear fields from the set with a given field number. + * + * @throws IllegalArgumentException if number is not positive + */ + public Builder clearField(int number) { + if (number <= 0) { + throw new IllegalArgumentException(number + " is not a valid field number."); } - if (fields.containsKey(number)) { - fields.remove(number); + if (fieldBuilders.containsKey(number)) { + fieldBuilders.remove(number); } return this; } @@ -423,9 +393,9 @@ public Builder clearField(final int number) { * Merge the fields from {@code other} into this set. If a field number exists in both sets, * {@code other}'s values for that field will be appended to the values in this set. */ - public Builder mergeFrom(final UnknownFieldSet other) { + public Builder mergeFrom(UnknownFieldSet other) { if (other != getDefaultInstance()) { - for (final Map.Entry entry : other.fields.entrySet()) { + for (Map.Entry entry : other.fields.entrySet()) { mergeField(entry.getKey(), entry.getValue()); } } @@ -435,10 +405,12 @@ public Builder mergeFrom(final UnknownFieldSet other) { /** * Add a field to the {@code UnknownFieldSet}. If a field with the same number already exists, * the two are merged. + * + * @throws IllegalArgumentException if number is not positive */ - public Builder mergeField(final int number, final Field field) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); + public Builder mergeField(int number, final Field field) { + if (number <= 0) { + throw new IllegalArgumentException(number + " is not a valid field number."); } if (hasField(number)) { getFieldBuilder(number).mergeFrom(field); @@ -454,10 +426,12 @@ public Builder mergeField(final int number, final Field field) { /** * Convenience method for merging a new field containing a single varint value. This is used in * particular when an unknown enum value is encountered. + * + * @throws IllegalArgumentException if number is not positive */ - public Builder mergeVarintField(final int number, final int value) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); + public Builder mergeVarintField(int number, int value) { + if (number <= 0) { + throw new IllegalArgumentException(number + " is not a valid field number."); } getFieldBuilder(number).addVarint(value); return this; @@ -467,40 +441,33 @@ public Builder mergeVarintField(final int number, final int value) { * Convenience method for merging a length-delimited field. * *

For use by generated code only. + * + * @throws IllegalArgumentException if number is not positive */ - public Builder mergeLengthDelimitedField(final int number, final ByteString value) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); + public Builder mergeLengthDelimitedField(int number, ByteString value) { + if (number <= 0) { + throw new IllegalArgumentException(number + " is not a valid field number."); } getFieldBuilder(number).addLengthDelimited(value); return this; } /** Check if the given field number is present in the set. */ - public boolean hasField(final int number) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } - return number == lastFieldNumber || fields.containsKey(number); + public boolean hasField(int number) { + return fieldBuilders.containsKey(number); } /** * Add a field to the {@code UnknownFieldSet}. If a field with the same number already exists, * it is removed. + * + * @throws IllegalArgumentException if number is not positive */ - public Builder addField(final int number, final Field field) { - if (number == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } - if (lastField != null && lastFieldNumber == number) { - // Discard this. - lastField = null; - lastFieldNumber = 0; + public Builder addField(int number, Field field) { + if (number <= 0) { + throw new IllegalArgumentException(number + " is not a valid field number."); } - if (fields.isEmpty()) { - fields = new TreeMap(); - } - fields.put(number, field); + fieldBuilders.put(number, Field.newBuilder(field)); return this; } @@ -509,15 +476,18 @@ public Builder addField(final int number, final Field field) { * changes may or may not be reflected in this map. */ public Map asMap() { - getFieldBuilder(0); // Force lastField to be built. + TreeMap fields = new TreeMap<>(); + for (Map.Entry entry : fieldBuilders.entrySet()) { + fields.put(entry.getKey(), entry.getValue().build()); + } return Collections.unmodifiableMap(fields); } /** Parse an entire message from {@code input} and merge its fields into this set. */ @Override - public Builder mergeFrom(final CodedInputStream input) throws IOException { + public Builder mergeFrom(CodedInputStream input) throws IOException { while (true) { - final int tag = input.readTag(); + int tag = input.readTag(); if (tag == 0 || !mergeFieldFrom(tag, input)) { break; } @@ -531,8 +501,8 @@ public Builder mergeFrom(final CodedInputStream input) throws IOException { * @param tag The field's tag number, which was already parsed. * @return {@code false} if the tag is an end group tag. */ - public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { - final int number = WireFormat.getTagFieldNumber(tag); + public boolean mergeFieldFrom(int tag, CodedInputStream input) throws IOException { + int number = WireFormat.getTagFieldNumber(tag); switch (WireFormat.getTagWireType(tag)) { case WireFormat.WIRETYPE_VARINT: getFieldBuilder(number).addVarint(input.readInt64()); @@ -544,7 +514,7 @@ public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throw getFieldBuilder(number).addLengthDelimited(input.readBytes()); return true; case WireFormat.WIRETYPE_START_GROUP: - final Builder subBuilder = newBuilder(); + Builder subBuilder = newBuilder(); input.readGroup(number, subBuilder, ExtensionRegistry.getEmptyRegistry()); getFieldBuilder(number).addGroup(subBuilder.build()); return true; @@ -563,15 +533,15 @@ public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throw * is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. */ @Override - public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException { + public Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException { try { - final CodedInputStream input = data.newCodedInput(); + CodedInputStream input = data.newCodedInput(); mergeFrom(input); input.checkLastTagWas(0); return this; - } catch (final InvalidProtocolBufferException e) { + } catch (InvalidProtocolBufferException e) { throw e; - } catch (final IOException e) { + } catch (IOException e) { throw new RuntimeException( "Reading from a ByteString threw an IOException (should never happen).", e); } @@ -582,15 +552,15 @@ public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferExce * is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. */ @Override - public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException { + public Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException { try { - final CodedInputStream input = CodedInputStream.newInstance(data); + CodedInputStream input = CodedInputStream.newInstance(data); mergeFrom(input); input.checkLastTagWas(0); return this; - } catch (final InvalidProtocolBufferException e) { + } catch (InvalidProtocolBufferException e) { throw e; - } catch (final IOException e) { + } catch (IOException e) { throw new RuntimeException( "Reading from a byte array threw an IOException (should never happen).", e); } @@ -601,8 +571,8 @@ public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferExceptio * This is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. */ @Override - public Builder mergeFrom(final InputStream input) throws IOException { - final CodedInputStream codedInput = CodedInputStream.newInstance(input); + public Builder mergeFrom(InputStream input) throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); mergeFrom(codedInput); codedInput.checkLastTagWas(0); return this; @@ -610,12 +580,12 @@ public Builder mergeFrom(final InputStream input) throws IOException { @Override public boolean mergeDelimitedFrom(InputStream input) throws IOException { - final int firstByte = input.read(); + int firstByte = input.read(); if (firstByte == -1) { return false; } - final int size = CodedInputStream.readRawVarint32(firstByte, input); - final InputStream limitedInput = new LimitedInputStream(input, size); + int size = CodedInputStream.readRawVarint32(firstByte, input); + InputStream limitedInput = new LimitedInputStream(input, size); mergeFrom(limitedInput); return true; } @@ -644,7 +614,7 @@ public Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistr @Override public Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException { try { - final CodedInputStream input = CodedInputStream.newInstance(data, off, len); + CodedInputStream input = CodedInputStream.newInstance(data, off, len); mergeFrom(input); input.checkLastTagWas(0); return this; @@ -718,7 +688,7 @@ public static Builder newBuilder() { } /** Construct a new {@link Builder} and initialize it to a copy of {@code copyFrom}. */ - public static Builder newBuilder(final Field copyFrom) { + public static Builder newBuilder(Field copyFrom) { return newBuilder().mergeFrom(copyFrom); } @@ -758,7 +728,7 @@ public List getGroupList() { } @Override - public boolean equals(final Object other) { + public boolean equals(Object other) { if (this == other) { return true; } @@ -785,7 +755,7 @@ private Object[] getIdentityArray() { public ByteString toByteString(int fieldNumber) { try { // TODO(lukes): consider caching serialized size in a volatile long - final ByteString.CodedBuilder out = + ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize(fieldNumber)); writeTo(fieldNumber, out.getCodedOutput()); return out.build(); @@ -796,40 +766,40 @@ public ByteString toByteString(int fieldNumber) { } /** Serializes the field, including field number, and writes it to {@code output}. */ - public void writeTo(final int fieldNumber, final CodedOutputStream output) throws IOException { - for (final long value : varint) { + public void writeTo(int fieldNumber, CodedOutputStream output) throws IOException { + for (long value : varint) { output.writeUInt64(fieldNumber, value); } - for (final int value : fixed32) { + for (int value : fixed32) { output.writeFixed32(fieldNumber, value); } - for (final long value : fixed64) { + for (long value : fixed64) { output.writeFixed64(fieldNumber, value); } - for (final ByteString value : lengthDelimited) { + for (ByteString value : lengthDelimited) { output.writeBytes(fieldNumber, value); } - for (final UnknownFieldSet value : group) { + for (UnknownFieldSet value : group) { output.writeGroup(fieldNumber, value); } } /** Get the number of bytes required to encode this field, including field number. */ - public int getSerializedSize(final int fieldNumber) { + public int getSerializedSize(int fieldNumber) { int result = 0; - for (final long value : varint) { + for (long value : varint) { result += CodedOutputStream.computeUInt64Size(fieldNumber, value); } - for (final int value : fixed32) { + for (int value : fixed32) { result += CodedOutputStream.computeFixed32Size(fieldNumber, value); } - for (final long value : fixed64) { + for (long value : fixed64) { result += CodedOutputStream.computeFixed64Size(fieldNumber, value); } - for (final ByteString value : lengthDelimited) { + for (ByteString value : lengthDelimited) { result += CodedOutputStream.computeBytesSize(fieldNumber, value); } - for (final UnknownFieldSet value : group) { + for (UnknownFieldSet value : group) { result += CodedOutputStream.computeGroupSize(fieldNumber, value); } return result; @@ -839,15 +809,15 @@ public int getSerializedSize(final int fieldNumber) { * Serializes the field, including field number, and writes it to {@code output}, using {@code * MessageSet} wire format. */ - public void writeAsMessageSetExtensionTo(final int fieldNumber, final CodedOutputStream output) + public void writeAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) throws IOException { - for (final ByteString value : lengthDelimited) { + for (ByteString value : lengthDelimited) { output.writeRawMessageSetExtension(fieldNumber, value); } } /** Serializes the field, including field number, and writes it to {@code writer}. */ - void writeTo(final int fieldNumber, final Writer writer) throws IOException { + void writeTo(int fieldNumber, Writer writer) throws IOException { writer.writeInt64List(fieldNumber, varint, false); writer.writeFixed32List(fieldNumber, fixed32, false); writer.writeFixed64List(fieldNumber, fixed64, false); @@ -872,7 +842,7 @@ void writeTo(final int fieldNumber, final Writer writer) throws IOException { * Serializes the field, including field number, and writes it to {@code writer}, using {@code * MessageSet} wire format. */ - private void writeAsMessageSetExtensionTo(final int fieldNumber, final Writer writer) + private void writeAsMessageSetExtensionTo(int fieldNumber, Writer writer) throws IOException { if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { // Write in descending field order. @@ -882,7 +852,7 @@ private void writeAsMessageSetExtensionTo(final int fieldNumber, final Writer wr } } else { // Write in ascending field order. - for (final ByteString value : lengthDelimited) { + for (ByteString value : lengthDelimited) { writer.writeMessageSetItem(fieldNumber, value); } } @@ -892,9 +862,9 @@ private void writeAsMessageSetExtensionTo(final int fieldNumber, final Writer wr * Get the number of bytes required to encode this field, including field number, using {@code * MessageSet} wire format. */ - public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) { + public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { int result = 0; - for (final ByteString value : lengthDelimited) { + for (ByteString value : lengthDelimited) { result += CodedOutputStream.computeRawMessageSetExtensionSize(fieldNumber, value); } return result; @@ -912,52 +882,85 @@ public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) { *

Use {@link Field#newBuilder()} to construct a {@code Builder}. */ public static final class Builder { - // This constructor should never be called directly (except from 'create'). - private Builder() {} + // This constructor should only be called directly from 'create' and 'clone'. + private Builder() { + result = new Field(); + } private static Builder create() { Builder builder = new Builder(); - builder.result = new Field(); return builder; } private Field result; + @Override + public Builder clone() { + Field copy = new Field(); + if (result.varint == null) { + copy.varint = null; + } else { + copy.varint = new ArrayList<>(result.varint); + } + if (result.fixed32 == null) { + copy.fixed32 = null; + } else { + copy.fixed32 = new ArrayList<>(result.fixed32); + } + if (result.fixed64 == null) { + copy.fixed64 = null; + } else { + copy.fixed64 = new ArrayList<>(result.fixed64); + } + if (result.lengthDelimited == null) { + copy.lengthDelimited = null; + } else { + copy.lengthDelimited = new ArrayList<>(result.lengthDelimited); + } + if (result.group == null) { + copy.group = null; + } else { + copy.group = new ArrayList<>(result.group); + } + + Builder clone = new Builder(); + clone.result = copy; + return clone; + } + /** - * Build the field. After {@code build()} has been called, the {@code Builder} is no longer - * usable. Calling any other method will result in undefined behavior and can cause a {@code - * NullPointerException} to be thrown. + * Build the field. */ public Field build() { + Field built = new Field(); if (result.varint == null) { - result.varint = Collections.emptyList(); + built.varint = Collections.emptyList(); } else { - result.varint = Collections.unmodifiableList(result.varint); + built.varint = Collections.unmodifiableList(new ArrayList<>(result.varint)); } if (result.fixed32 == null) { - result.fixed32 = Collections.emptyList(); + built.fixed32 = Collections.emptyList(); } else { - result.fixed32 = Collections.unmodifiableList(result.fixed32); + built.fixed32 = Collections.unmodifiableList(new ArrayList<>(result.fixed32)); } if (result.fixed64 == null) { - result.fixed64 = Collections.emptyList(); + built.fixed64 = Collections.emptyList(); } else { - result.fixed64 = Collections.unmodifiableList(result.fixed64); + built.fixed64 = Collections.unmodifiableList(new ArrayList<>(result.fixed64)); } if (result.lengthDelimited == null) { - result.lengthDelimited = Collections.emptyList(); + built.lengthDelimited = Collections.emptyList(); } else { - result.lengthDelimited = Collections.unmodifiableList(result.lengthDelimited); + built.lengthDelimited = Collections.unmodifiableList( + new ArrayList<>(result.lengthDelimited)); } if (result.group == null) { - result.group = Collections.emptyList(); + built.group = Collections.emptyList(); } else { - result.group = Collections.unmodifiableList(result.group); + built.group = Collections.unmodifiableList(new ArrayList<>(result.group)); } - final Field returnMe = result; - result = null; - return returnMe; + return built; } /** Discard the field's contents. */ @@ -970,7 +973,7 @@ public Builder clear() { * Merge the values in {@code other} into this field. For each list of values, {@code other}'s * values are append to the ones in this field. */ - public Builder mergeFrom(final Field other) { + public Builder mergeFrom(Field other) { if (!other.varint.isEmpty()) { if (result.varint == null) { result.varint = new ArrayList(); @@ -985,19 +988,19 @@ public Builder mergeFrom(final Field other) { } if (!other.fixed64.isEmpty()) { if (result.fixed64 == null) { - result.fixed64 = new ArrayList(); + result.fixed64 = new ArrayList<>(); } result.fixed64.addAll(other.fixed64); } if (!other.lengthDelimited.isEmpty()) { if (result.lengthDelimited == null) { - result.lengthDelimited = new ArrayList(); + result.lengthDelimited = new ArrayList<>(); } result.lengthDelimited.addAll(other.lengthDelimited); } if (!other.group.isEmpty()) { if (result.group == null) { - result.group = new ArrayList(); + result.group = new ArrayList<>(); } result.group.addAll(other.group); } @@ -1005,45 +1008,45 @@ public Builder mergeFrom(final Field other) { } /** Add a varint value. */ - public Builder addVarint(final long value) { + public Builder addVarint(long value) { if (result.varint == null) { - result.varint = new ArrayList(); + result.varint = new ArrayList<>(); } result.varint.add(value); return this; } /** Add a fixed32 value. */ - public Builder addFixed32(final int value) { + public Builder addFixed32(int value) { if (result.fixed32 == null) { - result.fixed32 = new ArrayList(); + result.fixed32 = new ArrayList<>(); } result.fixed32.add(value); return this; } /** Add a fixed64 value. */ - public Builder addFixed64(final long value) { + public Builder addFixed64(long value) { if (result.fixed64 == null) { - result.fixed64 = new ArrayList(); + result.fixed64 = new ArrayList<>(); } result.fixed64.add(value); return this; } /** Add a length-delimited value. */ - public Builder addLengthDelimited(final ByteString value) { + public Builder addLengthDelimited(ByteString value) { if (result.lengthDelimited == null) { - result.lengthDelimited = new ArrayList(); + result.lengthDelimited = new ArrayList<>(); } result.lengthDelimited.add(value); return this; } /** Add an embedded group. */ - public Builder addGroup(final UnknownFieldSet value) { + public Builder addGroup(UnknownFieldSet value) { if (result.group == null) { - result.group = new ArrayList(); + result.group = new ArrayList<>(); } result.group.add(value); return this; diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java new file mode 100644 index 000000000000..6ce0fc7e348b --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java @@ -0,0 +1,78 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import static com.google.common.truth.Truth.assertThat; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public final class UnknownFieldSetPerformanceTest { + + private static byte[] generateBytes(int length) { + assertThat(length % 4).isEqualTo(0); + byte[] input = new byte[length]; + for (int i = 0; i < length; i += 4) { + input[i] = (byte) 0x08; // field 1, wiretype 0 + input[i + 1] = (byte) 0x08; // field 1, payload 8 + input[i + 2] = (byte) 0x20; // field 4, wiretype 0 + input[i + 3] = (byte) 0x20; // field 4, payload 32 + } + return input; + } + + @Test + // This is a performance test. Failure here is a timeout. + public void testAlternatingFieldNumbers() throws IOException { + byte[] input = generateBytes(800000); + InputStream in = new ByteArrayInputStream(input); + UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder(); + CodedInputStream codedInput = CodedInputStream.newInstance(in); + builder.mergeFrom(codedInput); + } + + @Test + // This is a performance test. Failure here is a timeout. + public void testAddField() { + UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder(); + for (int i = 1; i <= 100000; i++) { + UnknownFieldSet.Field field = UnknownFieldSet.Field.newBuilder().addFixed32(i).build(); + builder.addField(i, field); + } + UnknownFieldSet fieldSet = builder.build(); + assertThat(fieldSet.getField(100000).getFixed32List().get(0)).isEqualTo(100000); + } +} diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java index 1e5bc9649a18..fbc3bb8fcfbc 100644 --- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java +++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java @@ -42,7 +42,9 @@ import protobuf_unittest.UnittestProto.TestPackedExtensions; import protobuf_unittest.UnittestProto.TestPackedTypes; import proto3_unittest.UnittestProto3; +import java.util.List; import java.util.Map; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -61,7 +63,7 @@ public void setUp() throws Exception { unknownFields = emptyMessage.getUnknownFields(); } - UnknownFieldSet.Field getField(String name) { + private UnknownFieldSet.Field getField(String name) { Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); assertThat(field).isNotNull(); return unknownFields.getField(field.getNumber()); @@ -100,6 +102,174 @@ ByteString getBizarroData() throws Exception { // ================================================================= + @Test + public void testFieldBuildersAreReusable() { + UnknownFieldSet.Field.Builder fieldBuilder = UnknownFieldSet.Field.newBuilder(); + fieldBuilder.addFixed32(10); + UnknownFieldSet.Field first = fieldBuilder.build(); + UnknownFieldSet.Field second = fieldBuilder.build(); + fieldBuilder.addFixed32(11); + UnknownFieldSet.Field third = fieldBuilder.build(); + + assertThat(first).isEqualTo(second); + assertThat(first).isNotEqualTo(third); + } + + @Test + public void testClone() { + UnknownFieldSet.Builder unknownSetBuilder = UnknownFieldSet.newBuilder(); + UnknownFieldSet.Field.Builder fieldBuilder = UnknownFieldSet.Field.newBuilder(); + fieldBuilder.addFixed32(10); + unknownSetBuilder.addField(8, fieldBuilder.build()); + // necessary to call clone twice to expose the bug + UnknownFieldSet.Builder clone1 = unknownSetBuilder.clone(); + UnknownFieldSet.Builder clone2 = unknownSetBuilder.clone(); // failure is a NullPointerException + assertThat(clone1).isNotSameInstanceAs(clone2); + } + + @Test + public void testClone_lengthDelimited() { + UnknownFieldSet.Builder destUnknownFieldSet = + UnknownFieldSet.newBuilder() + .addField(997, UnknownFieldSet.Field.newBuilder().addVarint(99).build()) + .addField( + 999, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(ByteString.copyFromUtf8("some data")) + .addLengthDelimited(ByteString.copyFromUtf8("some more data")) + .build()); + UnknownFieldSet clone = destUnknownFieldSet.clone().build(); + assertThat(clone.getField(997)).isNotNull(); + UnknownFieldSet.Field field999 = clone.getField(999); + List lengthDelimited = field999.getLengthDelimitedList(); + assertThat(lengthDelimited.get(0).toStringUtf8()).isEqualTo("some data"); + assertThat(lengthDelimited.get(1).toStringUtf8()).isEqualTo("some more data"); + + UnknownFieldSet clone2 = destUnknownFieldSet.clone().build(); + assertThat(clone2.getField(997)).isNotNull(); + UnknownFieldSet.Field secondField = clone2.getField(999); + List lengthDelimited2 = secondField.getLengthDelimitedList(); + assertThat(lengthDelimited2.get(0).toStringUtf8()).isEqualTo("some data"); + assertThat(lengthDelimited2.get(1).toStringUtf8()).isEqualTo("some more data"); + } + + @Test + public void testReuse() { + UnknownFieldSet.Builder builder = + UnknownFieldSet.newBuilder() + .addField(997, UnknownFieldSet.Field.newBuilder().addVarint(99).build()) + .addField( + 999, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(ByteString.copyFromUtf8("some data")) + .addLengthDelimited(ByteString.copyFromUtf8("some more data")) + .build()); + + UnknownFieldSet fieldSet1 = builder.build(); + UnknownFieldSet fieldSet2 = builder.build(); + builder.addField(1000, UnknownFieldSet.Field.newBuilder().addVarint(-90).build()); + UnknownFieldSet fieldSet3 = builder.build(); + + assertThat(fieldSet1).isEqualTo(fieldSet2); + assertThat(fieldSet1).isNotEqualTo(fieldSet3); + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testAddField_zero() { + UnknownFieldSet.Field field = getField("optional_int32"); + try { + UnknownFieldSet.newBuilder().addField(0, field); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("0 is not a valid field number."); + } + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testAddField_negative() { + UnknownFieldSet.Field field = getField("optional_int32"); + try { + UnknownFieldSet.newBuilder().addField(-2, field); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); + } + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testClearField_negative() { + try { + UnknownFieldSet.newBuilder().clearField(-28); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("-28 is not a valid field number."); + } + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testMergeField_negative() { + UnknownFieldSet.Field field = getField("optional_int32"); + try { + UnknownFieldSet.newBuilder().mergeField(-2, field); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); + } + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testMergeVarintField_negative() { + try { + UnknownFieldSet.newBuilder().mergeVarintField(-2, 78); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); + } + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testHasField_negative() { + assertThat(UnknownFieldSet.newBuilder().hasField(-2)).isFalse(); + } + + @Test + @SuppressWarnings("ModifiedButNotUsed") + public void testMergeLengthDelimitedField_negative() { + ByteString byteString = ByteString.copyFromUtf8("some data"); + try { + UnknownFieldSet.newBuilder().mergeLengthDelimitedField(-2, byteString); + Assert.fail(); + } catch (IllegalArgumentException expected) { + assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); + } + } + + @Test + public void testAddField() { + UnknownFieldSet.Field field = getField("optional_int32"); + UnknownFieldSet fieldSet = UnknownFieldSet.newBuilder().addField(1, field).build(); + assertThat(fieldSet.getField(1)).isEqualTo(field); + } + + @Test + public void testAddField_withReplacement() { + UnknownFieldSet.Field first = UnknownFieldSet.Field.newBuilder().addFixed32(56).build(); + UnknownFieldSet.Field second = UnknownFieldSet.Field.newBuilder().addFixed32(25).build(); + UnknownFieldSet fieldSet = UnknownFieldSet.newBuilder() + .addField(1, first) + .addField(1, second) + .build(); + List list = fieldSet.getField(1).getFixed32List(); + assertThat(list).hasSize(1); + assertThat(list.get(0)).isEqualTo(25); + } + @Test public void testVarint() throws Exception { UnknownFieldSet.Field field = getField("optional_int32"); @@ -185,6 +355,16 @@ public void testMergeFrom() throws Exception { assertThat(destination.toString()).isEqualTo("1: 1\n2: 2\n3: 3\n3: 4\n"); } + @Test + public void testAsMap() throws Exception { + UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder().mergeFrom(unknownFields); + Map mapFromBuilder = builder.asMap(); + assertThat(mapFromBuilder).isNotEmpty(); + UnknownFieldSet fields = builder.build(); + Map mapFromFieldSet = fields.asMap(); + assertThat(mapFromFieldSet).containsExactlyEntriesIn(mapFromBuilder); + } + @Test public void testClear() throws Exception { UnknownFieldSet fields = UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); diff --git a/java/lite/pom.xml b/java/lite/pom.xml index 0990fcfb444b..5653a10efffe 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml @@ -235,6 +235,7 @@ TypeRegistryTest.java UnknownEnumValueTest.java UnknownFieldSetLiteTest.java + UnknownFieldSetPerformanceTest.java UnknownFieldSetTest.java WellKnownTypesTest.java WireFormatTest.java From 93e6d059eef0ed797e09c72f86b4c96f0d2de040 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 5 Jan 2022 16:37:20 +0000 Subject: [PATCH 2/4] Update CHANGES.txt for 3.18.2 release --- CHANGES.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 69207d6ae9df..99da751e42ba 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,8 @@ +2022-01-05 version 3.18.2 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript) + + Java + * Change implementation of UnknownFieldSet (#9371) + 2021-10-04 version 3.18.1 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript) Python From f2dadfe17bebc6a4710f0562f33612c36a0f698b Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 5 Jan 2022 17:21:31 +0000 Subject: [PATCH 3/4] Update protobuf version --- Protobuf-C++.podspec | 2 +- Protobuf.podspec | 2 +- configure.ac | 2 +- csharp/Google.Protobuf.Tools.nuspec | 2 +- .../Google.Protobuf/Google.Protobuf.csproj | 2 +- java/bom/pom.xml | 2 +- java/core/pom.xml | 2 +- java/kotlin-lite/pom.xml | 2 +- java/kotlin/pom.xml | 2 +- java/lite/pom.xml | 2 +- java/pom.xml | 2 +- java/util/pom.xml | 2 +- js/package.json | 2 +- php/ext/google/protobuf/package.xml | 25 +++++++++++++++---- php/ext/google/protobuf/protobuf.h | 2 +- protobuf_version.bzl | 2 +- protoc-artifacts/pom.xml | 2 +- python/google/protobuf/__init__.py | 2 +- ruby/google-protobuf.gemspec | 2 +- src/Makefile.am | 2 +- src/google/protobuf/any.pb.h | 2 +- src/google/protobuf/api.pb.h | 2 +- src/google/protobuf/compiler/plugin.pb.h | 2 +- src/google/protobuf/descriptor.pb.h | 2 +- src/google/protobuf/duration.pb.h | 2 +- src/google/protobuf/empty.pb.h | 2 +- src/google/protobuf/field_mask.pb.h | 2 +- src/google/protobuf/port_def.inc | 2 +- src/google/protobuf/source_context.pb.h | 2 +- src/google/protobuf/struct.pb.h | 2 +- src/google/protobuf/stubs/common.h | 2 +- src/google/protobuf/timestamp.pb.h | 2 +- src/google/protobuf/type.pb.h | 2 +- src/google/protobuf/wrappers.pb.h | 2 +- 34 files changed, 53 insertions(+), 38 deletions(-) diff --git a/Protobuf-C++.podspec b/Protobuf-C++.podspec index f285e58af8e3..271a249c6556 100644 --- a/Protobuf-C++.podspec +++ b/Protobuf-C++.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Protobuf-C++' - s.version = '3.18.1' + s.version = '3.18.2' s.summary = 'Protocol Buffers v3 runtime library for C++.' s.homepage = 'https://github.com/google/protobuf' s.license = '3-Clause BSD License' diff --git a/Protobuf.podspec b/Protobuf.podspec index 4a5f7dd9c17f..80926fb20666 100644 --- a/Protobuf.podspec +++ b/Protobuf.podspec @@ -5,7 +5,7 @@ # dependent projects use the :git notation to refer to the library. Pod::Spec.new do |s| s.name = 'Protobuf' - s.version = '3.18.1' + s.version = '3.18.2' s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.' s.homepage = 'https://github.com/protocolbuffers/protobuf' s.license = '3-Clause BSD License' diff --git a/configure.ac b/configure.ac index b7466c28b5f9..4a6d50a07853 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ AC_PREREQ(2.59) # In the SVN trunk, the version should always be the next anticipated release # version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed # the size of one file name in the dist tarfile over the 99-char limit.) -AC_INIT([Protocol Buffers],[3.18.1],[protobuf@googlegroups.com],[protobuf]) +AC_INIT([Protocol Buffers],[3.18.2],[protobuf@googlegroups.com],[protobuf]) AM_MAINTAINER_MODE([enable]) diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec index 8e5786fdbdd4..d94a2c98e8cf 100644 --- a/csharp/Google.Protobuf.Tools.nuspec +++ b/csharp/Google.Protobuf.Tools.nuspec @@ -5,7 +5,7 @@ Google Protocol Buffers tools

Tools for Protocol Buffers - Google's data interchange format. See project site for more info. - 3.18.1 + 3.18.2 Google Inc. protobuf-packages https://github.com/protocolbuffers/protobuf/blob/master/LICENSE diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj index f6d6069102ef..619811027b83 100644 --- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj +++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj @@ -4,7 +4,7 @@ C# runtime library for Protocol Buffers - Google's data interchange format. Copyright 2015, Google Inc. Google Protocol Buffers - 3.18.1 + 3.18.2 7.2 Google Inc. diff --git a/java/bom/pom.xml b/java/bom/pom.xml index c4ec6855370f..e07b849094f6 100644 --- a/java/bom/pom.xml +++ b/java/bom/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-bom - 3.18.1 + 3.18.2 pom Protocol Buffers [BOM] diff --git a/java/core/pom.xml b/java/core/pom.xml index 838f4a71310e..dce983ed1b98 100644 --- a/java/core/pom.xml +++ b/java/core/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 protobuf-java diff --git a/java/kotlin-lite/pom.xml b/java/kotlin-lite/pom.xml index 072076613277..09200595e5bb 100644 --- a/java/kotlin-lite/pom.xml +++ b/java/kotlin-lite/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 protobuf-kotlin-lite diff --git a/java/kotlin/pom.xml b/java/kotlin/pom.xml index ad25304949da..ae3296db196d 100644 --- a/java/kotlin/pom.xml +++ b/java/kotlin/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 protobuf-kotlin diff --git a/java/lite/pom.xml b/java/lite/pom.xml index 5653a10efffe..734a9ec17ade 100644 --- a/java/lite/pom.xml +++ b/java/lite/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 protobuf-javalite diff --git a/java/pom.xml b/java/pom.xml index d1464dd3ca39..32aee82b7daa 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 pom Protocol Buffers [Parent] diff --git a/java/util/pom.xml b/java/util/pom.xml index 83d8d7bb2e0a..a9a9471522c8 100644 --- a/java/util/pom.xml +++ b/java/util/pom.xml @@ -4,7 +4,7 @@ com.google.protobuf protobuf-parent - 3.18.1 + 3.18.2 protobuf-java-util diff --git a/js/package.json b/js/package.json index 33f300925553..63d4b917e84c 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "google-protobuf", - "version": "3.18.1", + "version": "3.18.2", "description": "Protocol Buffers for JavaScript", "main": "google-protobuf.js", "files": [ diff --git a/php/ext/google/protobuf/package.xml b/php/ext/google/protobuf/package.xml index f083785bb1e9..53cfbd6f8e55 100644 --- a/php/ext/google/protobuf/package.xml +++ b/php/ext/google/protobuf/package.xml @@ -10,11 +10,11 @@ protobuf-opensource@google.com yes - 2021-10-04 - + 2022-01-05 + - 3.18.1 - 3.18.1 + 3.18.2 + 3.18.2 stable @@ -22,7 +22,7 @@ 3-Clause BSD License - * No new changes in 3.18.1 + * No new changes in 3.18.2 @@ -1098,5 +1098,20 @@ G A release. + + + 3.18.2 + 3.18.2 + + + stable + stable + + 2022-01-05 + + 3-Clause BSD License + + + diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 8b5c8aa8570e..9b70d33b017c 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -91,7 +91,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setter, 0, 0, 1) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() -#define PHP_PROTOBUF_VERSION "3.18.1" +#define PHP_PROTOBUF_VERSION "3.18.2" // ptr -> PHP object cache. This is a weak map that caches lazily-created // wrapper objects around upb types: diff --git a/protobuf_version.bzl b/protobuf_version.bzl index 7f6d71460ca2..d215a862a8e1 100644 --- a/protobuf_version.bzl +++ b/protobuf_version.bzl @@ -1 +1 @@ -PROTOBUF_VERSION = '3.18.1' +PROTOBUF_VERSION = '3.18.2' diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml index c3aa37771f3e..65e5ff679559 100644 --- a/protoc-artifacts/pom.xml +++ b/protoc-artifacts/pom.xml @@ -8,7 +8,7 @@ com.google.protobuf protoc - 3.18.1 + 3.18.2 pom Protobuf Compiler diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py index b8122e82ae6f..3813eeb96663 100644 --- a/python/google/protobuf/__init__.py +++ b/python/google/protobuf/__init__.py @@ -30,4 +30,4 @@ # Copyright 2007 Google Inc. All Rights Reserved. -__version__ = '3.18.1' +__version__ = '3.18.2' diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index 7a9e3f38b761..1f2b385b56e3 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = "google-protobuf" - s.version = "3.18.1" + s.version = "3.18.2" git_tag = "v#{s.version.to_s.sub('.rc.', '-rc')}" # Converts X.Y.Z.rc.N to vX.Y.Z-rcN, used for the git tag s.licenses = ["BSD-3-Clause"] s.summary = "Protocol Buffers" diff --git a/src/Makefile.am b/src/Makefile.am index 3da92e75090b..e6b4d0fcaa15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,7 @@ else PTHREAD_DEF = endif -PROTOBUF_VERSION = 29:1:0 +PROTOBUF_VERSION = 29:2:0 if GCC # Turn on all warnings except for sign comparison (we ignore sign comparison diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h index 13c5086b7f1c..31d896fe143f 100644 --- a/src/google/protobuf/any.pb.h +++ b/src/google/protobuf/any.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h index 347a9f9bfaf2..e737a15c5d86 100644 --- a/src/google/protobuf/api.pb.h +++ b/src/google/protobuf/api.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index baa72a2d4413..9ce6a77c0e8f 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 51261e4b4d52..b62091ea4885 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/duration.pb.h b/src/google/protobuf/duration.pb.h index 4e7d6adc97ef..3db6429dcdc5 100644 --- a/src/google/protobuf/duration.pb.h +++ b/src/google/protobuf/duration.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h index 637ddf4d6520..e6f045945f9e 100644 --- a/src/google/protobuf/empty.pb.h +++ b/src/google/protobuf/empty.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/field_mask.pb.h b/src/google/protobuf/field_mask.pb.h index 849f4f2f7b01..9ad4046e90b3 100644 --- a/src/google/protobuf/field_mask.pb.h +++ b/src/google/protobuf/field_mask.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc index 01384c8e741f..ee6d67c069d8 100644 --- a/src/google/protobuf/port_def.inc +++ b/src/google/protobuf/port_def.inc @@ -153,7 +153,7 @@ #ifdef PROTOBUF_VERSION #error PROTOBUF_VERSION was previously defined #endif -#define PROTOBUF_VERSION 3018001 +#define PROTOBUF_VERSION 3018002 #ifdef PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC #error PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC was previously defined diff --git a/src/google/protobuf/source_context.pb.h b/src/google/protobuf/source_context.pb.h index 13dce1c3fb78..102ea49cea89 100644 --- a/src/google/protobuf/source_context.pb.h +++ b/src/google/protobuf/source_context.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h index e84004b73c32..6f6798cc012c 100644 --- a/src/google/protobuf/struct.pb.h +++ b/src/google/protobuf/struct.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h index ed4e0613897c..ad7dfe53a84e 100644 --- a/src/google/protobuf/stubs/common.h +++ b/src/google/protobuf/stubs/common.h @@ -82,7 +82,7 @@ namespace internal { // The current version, represented as a single integer to make comparison // easier: major * 10^6 + minor * 10^3 + micro -#define GOOGLE_PROTOBUF_VERSION 3018001 +#define GOOGLE_PROTOBUF_VERSION 3018002 // A suffix string for alpha, beta or rc releases. Empty for stable releases. #define GOOGLE_PROTOBUF_VERSION_SUFFIX "" diff --git a/src/google/protobuf/timestamp.pb.h b/src/google/protobuf/timestamp.pb.h index f5c7f9882d93..e726578c0303 100644 --- a/src/google/protobuf/timestamp.pb.h +++ b/src/google/protobuf/timestamp.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h index 4baf3760d036..523370c7157d 100644 --- a/src/google/protobuf/type.pb.h +++ b/src/google/protobuf/type.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h index 5794557c2129..3e0c8ade9ed6 100644 --- a/src/google/protobuf/wrappers.pb.h +++ b/src/google/protobuf/wrappers.pb.h @@ -13,7 +13,7 @@ #error incompatible with your Protocol Buffer headers. Please update #error your headers. #endif -#if 3018001 < PROTOBUF_MIN_PROTOC_VERSION +#if 3018002 < PROTOBUF_MIN_PROTOC_VERSION #error This file was generated by an older version of protoc which is #error incompatible with your Protocol Buffer headers. Please #error regenerate this file with a newer version of protoc. From 1811608d95e52872212e7513c166f625c96d94aa Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 5 Jan 2022 18:11:01 +0000 Subject: [PATCH 4/4] Tweak wording in CHANGES.txt --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 99da751e42ba..1abd0faf3c33 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,7 @@ 2022-01-05 version 3.18.2 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript) Java - * Change implementation of UnknownFieldSet (#9371) + * Improve performance characteristics of UnknownFieldSet parsing (#9371) 2021-10-04 version 3.18.1 (C++/Java/Python/PHP/Objective-C/C#/Ruby/JavaScript)