diff options
Diffstat (limited to '0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch')
-rw-r--r-- | 0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch | 1272 |
1 files changed, 0 insertions, 1272 deletions
diff --git a/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch b/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch deleted file mode 100644 index a56f88d..0000000 --- a/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch +++ /dev/null @@ -1,1272 +0,0 @@ -From 8890b0a81e2f4b1de4a33cc6d81aba07655bde1a Mon Sep 17 00:00:00 2001 -From: wangxiaochao <wangxiaochao2@huawei.com> -Date: Thu, 24 Mar 2022 20:54:36 +0800 -Subject: [PATCH] Improve performance of parsing unknown fields in Java - -Signed-off-by: wangxiaochao <wangxiaochao2@huawei.com> - ---- - Makefile.am | 1 + - .../com/google/protobuf/UnknownFieldSet.java | 427 +++++++++--------- - .../UnknownFieldSetPerformanceTest.java | 78 ++++ - .../google/protobuf/UnknownFieldSetTest.java | 171 ++++++- - java/lite/pom.xml | 3 +- - 5 files changed, 466 insertions(+), 214 deletions(-) - create mode 100644 java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java - -diff --git a/Makefile.am b/Makefile.am -index 4fc706b..908c2d2 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -487,6 +487,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/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java -index ba2f9df..5c482d6 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.Map; - 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. - * - * <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every {@link Message.Builder} -- * contains an {@link Builder}). -+ * contains a {@link Builder}). - * - * <p>Most users will never need to use this class. - * -@@ -57,9 +57,13 @@ import java.util.TreeMap; - */ - public final class UnknownFieldSet implements MessageLite { - -- private UnknownFieldSet() { -- fields = null; -- fieldsDescending = null; -+ private final TreeMap<Integer, Field> fields; -+ -+ /** -+ * Construct an {@code UnknownFieldSet} around the given map. -+ */ -+ UnknownFieldSet(TreeMap<Integer, Field> fields) { -+ this.fields = fields; - } - - /** Create a new {@link Builder}. */ -@@ -68,7 +72,7 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** 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 final class UnknownFieldSet implements MessageLite { - } - - private static final UnknownFieldSet defaultInstance = -- new UnknownFieldSet( -- Collections.<Integer, Field>emptyMap(), Collections.<Integer, Field>emptyMap()); -- -- /** -- * Construct an {@code UnknownFieldSet} around the given map. The map is expected to be immutable. -- */ -- UnknownFieldSet(final Map<Integer, Field> fields, final Map<Integer, Field> fieldsDescending) { -- this.fields = fields; -- this.fieldsDescending = fieldsDescending; -- } -- -- private final Map<Integer, Field> fields; -- -- /** A copy of {@link #fields} who's iterator order is reversed. */ -- private final Map<Integer, Field> fieldsDescending; -+ new UnknownFieldSet(new TreeMap<Integer, Field>()); - - - @Override -- public boolean equals(final Object other) { -+ public boolean equals(Object other) { - if (this == other) { - return true; - } -@@ -110,29 +100,33 @@ public final class UnknownFieldSet implements MessageLite { - - @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<Integer, Field> asMap() { -- return fields; -+ return (Map<Integer, Field>) 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<Integer, Field> entry : fields.entrySet()) { -+ public void writeTo(CodedOutputStream output) throws IOException { -+ for (Map.Entry<Integer, Field> entry : fields.entrySet()) { - Field field = entry.getValue(); - field.writeTo(entry.getKey(), output); - } -@@ -154,10 +148,10 @@ public final class UnknownFieldSet implements MessageLite { - @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 final class UnknownFieldSet implements MessageLite { - @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 final class UnknownFieldSet implements MessageLite { - * {@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 final class UnknownFieldSet implements MessageLite { - @Override - public int getSerializedSize() { - int result = 0; -- for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { -- result += entry.getValue().getSerializedSize(entry.getKey()); -+ if (!fields.isEmpty()) { -+ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fields.entrySet()) { -+ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { -+ for (Map.Entry<Integer, Field> entry : fields.entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), output); - } - } -@@ -221,7 +217,7 @@ public final class UnknownFieldSet implements MessageLite { - void writeTo(Writer writer) throws IOException { - if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { - // Write fields in descending order. -- for (Map.Entry<Integer, Field> entry : fieldsDescending.entrySet()) { -+ for (Map.Entry<Integer, Field> entry : fields.descendingMap().entrySet()) { - entry.getValue().writeTo(entry.getKey(), writer); - } - } else { -@@ -233,15 +229,15 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** 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<Integer, Field> entry : fieldsDescending.entrySet()) { -+ for (Map.Entry<Integer, Field> entry : fields.descendingMap().entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); - } - } else { - // Write fields in ascending order. -- for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { -+ for (Map.Entry<Integer, Field> entry : fields.entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); - } - } -@@ -250,7 +246,7 @@ public final class UnknownFieldSet implements MessageLite { - /** 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<Integer, Field> entry : fields.entrySet()) { -+ for (Map.Entry<Integer, Field> entry : fields.entrySet()) { - result += entry.getValue().getSerializedSizeAsMessageSetExtension(entry.getKey()); - } - return result; -@@ -264,23 +260,23 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** 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 final class UnknownFieldSet implements MessageLite { - // This constructor should never be called directly (except from 'create'). - private Builder() {} - -- private Map<Integer, Field> 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<Integer, Field.Builder> 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. -- * -- * <p>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<Integer, Field> descendingFields = null; -- descendingFields = -- Collections.unmodifiableMap(((TreeMap<Integer, Field>) fields).descendingMap()); -- result = new UnknownFieldSet(Collections.unmodifiableMap(fields), descendingFields); -+ TreeMap<Integer, Field> fields = new TreeMap<>(); -+ for (Map.Entry<Integer, Field.Builder> entry : fieldBuilders.entrySet()) { -+ fields.put(entry.getKey(), entry.getValue().build()); -+ } -+ result = new UnknownFieldSet(fields); - } -- fields = null; - return result; - } - -@@ -378,11 +353,13 @@ public final class UnknownFieldSet implements MessageLite { - - @Override - public Builder clone() { -- getFieldBuilder(0); // Force lastField to be built. -- Map<Integer, Field> descendingFields = null; -- descendingFields = -- Collections.unmodifiableMap(((TreeMap<Integer, Field>) fields).descendingMap()); -- return UnknownFieldSet.newBuilder().mergeFrom(new UnknownFieldSet(fields, descendingFields)); -+ Builder clone = UnknownFieldSet.newBuilder(); -+ for (Map.Entry<Integer, Field.Builder> 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 final class UnknownFieldSet implements MessageLite { - 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 final class UnknownFieldSet implements MessageLite { - * 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<Integer, Field> entry : other.fields.entrySet()) { -+ for (Map.Entry<Integer, Field> entry : other.fields.entrySet()) { - mergeField(entry.getKey(), entry.getValue()); - } - } -@@ -435,10 +405,12 @@ public final class UnknownFieldSet implements MessageLite { - /** - * 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 final class UnknownFieldSet implements MessageLite { - /** - * 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 final class UnknownFieldSet implements MessageLite { - * Convenience method for merging a length-delimited field. - * - * <p>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<Integer, Field>(); -- } -- fields.put(number, field); -+ fieldBuilders.put(number, Field.newBuilder(field)); - return this; - } - -@@ -509,15 +476,18 @@ public final class UnknownFieldSet implements MessageLite { - * changes may or may not be reflected in this map. - */ - public Map<Integer, Field> asMap() { -- getFieldBuilder(0); // Force lastField to be built. -+ TreeMap<Integer, Field> fields = new TreeMap<>(); -+ for (Map.Entry<Integer, Field.Builder> 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 final class UnknownFieldSet implements MessageLite { - * @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 final class UnknownFieldSet implements MessageLite { - 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 final class UnknownFieldSet implements MessageLite { - * 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 final class UnknownFieldSet implements MessageLite { - * 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 final class UnknownFieldSet implements MessageLite { - * 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 final class UnknownFieldSet implements MessageLite { - - @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 final class UnknownFieldSet implements MessageLite { - @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 final class UnknownFieldSet implements MessageLite { - } - - /** 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 final class UnknownFieldSet implements MessageLite { - } - - @Override -- public boolean equals(final Object other) { -+ public boolean equals(Object other) { - if (this == other) { - return true; - } -@@ -785,7 +755,7 @@ public final class UnknownFieldSet implements MessageLite { - 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 final class UnknownFieldSet implements MessageLite { - } - - /** 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 final class UnknownFieldSet implements MessageLite { - * 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 @@ public final class UnknownFieldSet implements MessageLite { - * 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 @@ public final class UnknownFieldSet implements MessageLite { - } - } else { - // Write in ascending field order. -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - writer.writeMessageSetItem(fieldNumber, value); - } - } -@@ -892,9 +862,9 @@ public final class UnknownFieldSet implements MessageLite { - * 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 final class UnknownFieldSet implements MessageLite { - * <p>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 final class UnknownFieldSet implements MessageLite { - * 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<Long>(); -@@ -985,19 +988,19 @@ public final class UnknownFieldSet implements MessageLite { - } - if (!other.fixed64.isEmpty()) { - if (result.fixed64 == null) { -- result.fixed64 = new ArrayList<Long>(); -+ result.fixed64 = new ArrayList<>(); - } - result.fixed64.addAll(other.fixed64); - } - if (!other.lengthDelimited.isEmpty()) { - if (result.lengthDelimited == null) { -- result.lengthDelimited = new ArrayList<ByteString>(); -+ result.lengthDelimited = new ArrayList<>(); - } - result.lengthDelimited.addAll(other.lengthDelimited); - } - if (!other.group.isEmpty()) { - if (result.group == null) { -- result.group = new ArrayList<UnknownFieldSet>(); -+ result.group = new ArrayList<>(); - } - result.group.addAll(other.group); - } -@@ -1005,45 +1008,45 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Add a varint value. */ -- public Builder addVarint(final long value) { -+ public Builder addVarint(long value) { - if (result.varint == null) { -- result.varint = new ArrayList<Long>(); -+ 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<Integer>(); -+ 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<Long>(); -+ 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<ByteString>(); -+ 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<UnknownFieldSet>(); -+ 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 0000000..6ce0fc7 ---- /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 c7eb57c..3e1e928 100644 ---- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -@@ -30,6 +30,9 @@ - - package com.google.protobuf; - -+import static com.google.common.truth.Truth.assertThat; -+import static com.google.common.truth.Truth.assertWithMessage; -+ - import protobuf_unittest.UnittestProto; - import protobuf_unittest.UnittestProto.ForeignEnum; - import protobuf_unittest.UnittestProto.TestAllExtensions; -@@ -39,8 +42,10 @@ import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions; - import protobuf_unittest.UnittestProto.TestPackedExtensions; - import protobuf_unittest.UnittestProto.TestPackedTypes; - import proto3_unittest.UnittestProto3; -+import java.util.List; - import java.util.Arrays; - import java.util.Map; -+import org.junit.Assert; - import junit.framework.TestCase; - - /** -@@ -58,7 +63,7 @@ public class UnknownFieldSetTest extends TestCase { - unknownFields = emptyMessage.getUnknownFields(); - } - -- UnknownFieldSet.Field getField(String name) { -+ private UnknownFieldSet.Field getField(String name) { - Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); - assertNotNull(field); - return unknownFields.getField(field.getNumber()); -@@ -97,6 +102,161 @@ public class UnknownFieldSetTest extends TestCase { - - // ================================================================= - -+ 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); -+ } -+ -+ 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); -+ } -+ -+ 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<ByteString> 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<ByteString> lengthDelimited2 = secondField.getLengthDelimitedList(); -+ assertThat(lengthDelimited2.get(0).toStringUtf8()).isEqualTo("some data"); -+ assertThat(lengthDelimited2.get(1).toStringUtf8()).isEqualTo("some more data"); -+ } -+ -+ 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); -+ } -+ -+ @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."); -+ } -+ } -+ -+ @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."); -+ } -+ } -+ -+ @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."); -+ } -+ } -+ -+ @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."); -+ } -+ } -+ -+ @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."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testHasField_negative() { -+ assertThat(UnknownFieldSet.newBuilder().hasField(-2)).isFalse(); -+ } -+ -+ @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."); -+ } -+ } -+ -+ public void testAddField() { -+ UnknownFieldSet.Field field = getField("optional_int32"); -+ UnknownFieldSet fieldSet = UnknownFieldSet.newBuilder().addField(1, field).build(); -+ assertThat(fieldSet.getField(1)).isEqualTo(field); -+ } -+ -+ 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<Integer> list = fieldSet.getField(1).getFixed32List(); -+ assertThat(list).hasSize(1); -+ assertThat(list.get(0)).isEqualTo(25); -+ } -+ - public void testVarint() throws Exception { - UnknownFieldSet.Field field = getField("optional_int32"); - assertEquals(1, field.getVarintList().size()); -@@ -173,6 +333,15 @@ public class UnknownFieldSetTest extends TestCase { - assertEquals("1: 1\n2: 2\n3: 3\n3: 4\n", destination.toString()); - } - -+ public void testAsMap() throws Exception { -+ UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder().mergeFrom(unknownFields); -+ Map<Integer, UnknownFieldSet.Field> mapFromBuilder = builder.asMap(); -+ assertThat(mapFromBuilder).isNotEmpty(); -+ UnknownFieldSet fields = builder.build(); -+ Map<Integer, UnknownFieldSet.Field> mapFromFieldSet = fields.asMap(); -+ assertThat(mapFromFieldSet).containsExactlyEntriesIn(mapFromBuilder); -+ } -+ - public void testClear() throws Exception { - UnknownFieldSet fields = UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); - assertTrue(fields.asMap().isEmpty()); -diff --git a/java/lite/pom.xml b/java/lite/pom.xml -index 104c5c1..d095483 100644 ---- a/java/lite/pom.xml -+++ b/java/lite/pom.xml -@@ -232,7 +232,8 @@ - <exclude>TestUtil.java</exclude> - <exclude>TypeRegistryTest.java</exclude> - <exclude>UnknownEnumValueTest.java</exclude> -- <exclude>UnknownFieldSetLiteTest.java</exclude> -+ <exclude>UnknownFieldSetLiteTest.java</exclude> -+ <exclude>UnknownFieldSetPerformanceTest.java</exclude> - <exclude>UnknownFieldSetTest.java</exclude> - <exclude>WellKnownTypesTest.java</exclude> - <exclude>WireFormatTest.java</exclude> --- -2.30.0 - |