Down-integrate from google internal.
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index a535b71..4316efe 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -62,9 +62,8 @@ private static final long serialVersionUID = 1L; - /** For use by generated code only. */ - protected UnknownFieldSetLite unknownFields = - UnknownFieldSetLite.getDefaultInstance(); + /** For use by generated code only. Lazily initialized to reduce allocations. */ + protected UnknownFieldSetLite unknownFields = null; /** For use by generated code only. */ protected int memoizedSerializedSize = -1; @@ -84,19 +83,56 @@ return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); } + // The general strategy for unknown fields is to use an UnknownFieldSetLite that is treated as + // mutable during the parsing constructor and immutable after. This allows us to avoid + // any unnecessary intermediary allocations while reducing the generated code size. + /** - * Called by subclasses to parse an unknown field. For use by generated code - * only. + * Lazily initializes unknown fields. + */ + private final void ensureUnknownFieldsInitialized() { + if (unknownFields == null) { + unknownFields = UnknownFieldSetLite.newInstance(); + } + } + + /** + * Called by subclasses to parse an unknown field. For use by generated code only. + * * @return {@code true} unless the tag is an end-group tag. */ - protected static boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSetLite.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { + protected boolean parseUnknownField(int tag, CodedInputStream input) throws IOException { + ensureUnknownFieldsInitialized(); return unknownFields.mergeFieldFrom(tag, input); } + /** + * Called by subclasses to parse an unknown field. For use by generated code only. + */ + protected void mergeVarintField(int tag, int value) { + ensureUnknownFieldsInitialized(); + unknownFields.mergeVarintField(tag, value); + } + + /** + * Called by subclasses to parse an unknown field. For use by generated code only. + */ + protected void mergeLengthDelimitedField(int fieldNumber, ByteString value) { + ensureUnknownFieldsInitialized(); + unknownFields.mergeLengthDelimitedField(fieldNumber, value); + } + + /** + * Called by subclasses to complete parsing. For use by generated code only. + */ + protected void doneParsing() { + if (unknownFields == null) { + unknownFields = UnknownFieldSetLite.getDefaultInstance(); + } else { + unknownFields.makeImmutable(); + } + } + public final boolean isInitialized() { return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null; } @@ -171,7 +207,7 @@ * <p>For use by generated code only. */ protected final void mergeUnknownFields(UnknownFieldSetLite unknownFields) { - this.unknownFields = UnknownFieldSetLite.concat(this.unknownFields, unknownFields); + this.unknownFields = UnknownFieldSetLite.mutableCopyOf(this.unknownFields, unknownFields); } @SuppressWarnings("unchecked") @@ -225,7 +261,13 @@ //@Override (Java 1.6 override semantics, but we must support 1.5) public MessageType buildPartial() { + if (isBuilt) { + return instance; + } + instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE); + instance.unknownFields.makeImmutable(); + isBuilt = true; return instance; } @@ -249,18 +291,6 @@ public MessageType getDefaultInstanceForType() { return defaultInstance; } - - /** - * Called by subclasses to parse an unknown field. - * @return {@code true} unless the tag is an end-group tag. - */ - protected boolean parseUnknownField( - CodedInputStream input, - UnknownFieldSetLite.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - return unknownFields.mergeFieldFrom(tag, input); - } public BuilderType mergeFrom( com.google.protobuf.CodedInputStream input, @@ -334,6 +364,130 @@ extensions.mergeFrom(((ExtendableMessage) other).extensions); } + /** + * Parse an unknown field or an extension. For use by generated code only. + * + * <p>For use by generated code only. + * + * @return {@code true} unless the tag is an end-group tag. + */ + protected <MessageType extends MessageLite> boolean parseUnknownField( + MessageType defaultInstance, + CodedInputStream input, + ExtensionRegistryLite extensionRegistry, + int tag) throws IOException { + int wireType = WireFormat.getTagWireType(tag); + int fieldNumber = WireFormat.getTagFieldNumber(tag); + + // TODO(dweis): How much bytecode would be saved by not requiring the generated code to + // provide the default instance? + GeneratedExtension<MessageType, ?> extension = extensionRegistry.findLiteExtensionByNumber( + defaultInstance, fieldNumber); + + boolean unknown = false; + boolean packed = false; + if (extension == null) { + unknown = true; // Unknown field. + } else if (wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + false /* isPacked */)) { + packed = false; // Normal, unpacked value. + } else if (extension.descriptor.isRepeated && + extension.descriptor.type.isPackable() && + wireType == FieldSet.getWireFormatForFieldType( + extension.descriptor.getLiteType(), + true /* isPacked */)) { + packed = true; // Packed value. + } else { + unknown = true; // Wrong wire type. + } + + if (unknown) { // Unknown field or wrong wire type. Skip. + return parseUnknownField(tag, input); + } + + if (packed) { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { + while (input.getBytesUntilLimit() > 0) { + int rawValue = input.readEnum(); + Object value = + extension.descriptor.getEnumType().findValueByNumber(rawValue); + if (value == null) { + // If the number isn't recognized as a valid value for this + // enum, drop it (don't even add it to unknownFields). + return true; + } + extensions.addRepeatedField(extension.descriptor, + extension.singularToFieldSetType(value)); + } + } else { + while (input.getBytesUntilLimit() > 0) { + Object value = + FieldSet.readPrimitiveField(input, + extension.descriptor.getLiteType(), + /*checkUtf8=*/ false); + extensions.addRepeatedField(extension.descriptor, value); + } + } + input.popLimit(limit); + } else { + Object value; + switch (extension.descriptor.getLiteJavaType()) { + case MESSAGE: { + MessageLite.Builder subBuilder = null; + if (!extension.descriptor.isRepeated()) { + MessageLite existingValue = + (MessageLite) extensions.getField(extension.descriptor); + if (existingValue != null) { + subBuilder = existingValue.toBuilder(); + } + } + if (subBuilder == null) { + subBuilder = extension.getMessageDefaultInstance() + .newBuilderForType(); + } + if (extension.descriptor.getLiteType() == + WireFormat.FieldType.GROUP) { + input.readGroup(extension.getNumber(), + subBuilder, extensionRegistry); + } else { + input.readMessage(subBuilder, extensionRegistry); + } + value = subBuilder.build(); + break; + } + case ENUM: + int rawValue = input.readEnum(); + value = extension.descriptor.getEnumType() + .findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // write it to unknown fields object. + if (value == null) { + mergeVarintField(fieldNumber, rawValue); + return true; + } + break; + default: + value = FieldSet.readPrimitiveField(input, + extension.descriptor.getLiteType(), + /*checkUtf8=*/ false); + break; + } + + if (extension.descriptor.isRepeated()) { + extensions.addRepeatedField(extension.descriptor, + extension.singularToFieldSetType(value)); + } else { + extensions.setField(extension.descriptor, + extension.singularToFieldSetType(value)); + } + } + + return true; + } + private void verifyExtensionContainingType( final GeneratedExtension<MessageType, ?> extension) { if (extension.getContainingTypeDefaultInstance() != @@ -404,11 +558,10 @@ } - /** - * Used by parsing constructors in generated classes. - */ - protected static void makeExtensionsImmutable( - FieldSet<ExtensionDescriptor> extensions) { + @Override + protected final void doneParsing() { + super.doneParsing(); + extensions.makeImmutable(); } @@ -619,131 +772,6 @@ } } - //----------------------------------------------------------------- - - /** - * Parse an unknown field or an extension. For use by generated code only. - * @return {@code true} unless the tag is an end-group tag. - */ - protected static <MessageType extends MessageLite> - boolean parseUnknownField( - FieldSet<ExtensionDescriptor> extensions, - MessageType defaultInstance, - CodedInputStream input, - UnknownFieldSetLite.Builder unknownFields, - ExtensionRegistryLite extensionRegistry, - int tag) throws IOException { - int wireType = WireFormat.getTagWireType(tag); - int fieldNumber = WireFormat.getTagFieldNumber(tag); - - GeneratedExtension<MessageType, ?> extension = - extensionRegistry.findLiteExtensionByNumber( - defaultInstance, fieldNumber); - - boolean unknown = false; - boolean packed = false; - if (extension == null) { - unknown = true; // Unknown field. - } else if (wireType == FieldSet.getWireFormatForFieldType( - extension.descriptor.getLiteType(), - false /* isPacked */)) { - packed = false; // Normal, unpacked value. - } else if (extension.descriptor.isRepeated && - extension.descriptor.type.isPackable() && - wireType == FieldSet.getWireFormatForFieldType( - extension.descriptor.getLiteType(), - true /* isPacked */)) { - packed = true; // Packed value. - } else { - unknown = true; // Wrong wire type. - } - - if (unknown) { // Unknown field or wrong wire type. Skip. - return unknownFields.mergeFieldFrom(tag, input); - } - - if (packed) { - int length = input.readRawVarint32(); - int limit = input.pushLimit(length); - if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) { - while (input.getBytesUntilLimit() > 0) { - int rawValue = input.readEnum(); - Object value = - extension.descriptor.getEnumType().findValueByNumber(rawValue); - if (value == null) { - // If the number isn't recognized as a valid value for this - // enum, drop it (don't even add it to unknownFields). - return true; - } - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); - } - } else { - while (input.getBytesUntilLimit() > 0) { - Object value = - FieldSet.readPrimitiveField(input, - extension.descriptor.getLiteType(), - /*checkUtf8=*/ false); - extensions.addRepeatedField(extension.descriptor, value); - } - } - input.popLimit(limit); - } else { - Object value; - switch (extension.descriptor.getLiteJavaType()) { - case MESSAGE: { - MessageLite.Builder subBuilder = null; - if (!extension.descriptor.isRepeated()) { - MessageLite existingValue = - (MessageLite) extensions.getField(extension.descriptor); - if (existingValue != null) { - subBuilder = existingValue.toBuilder(); - } - } - if (subBuilder == null) { - subBuilder = extension.getMessageDefaultInstance() - .newBuilderForType(); - } - if (extension.descriptor.getLiteType() == - WireFormat.FieldType.GROUP) { - input.readGroup(extension.getNumber(), - subBuilder, extensionRegistry); - } else { - input.readMessage(subBuilder, extensionRegistry); - } - value = subBuilder.build(); - break; - } - case ENUM: - int rawValue = input.readEnum(); - value = extension.descriptor.getEnumType() - .findValueByNumber(rawValue); - // If the number isn't recognized as a valid value for this enum, - // write it to unknown fields object. - if (value == null) { - unknownFields.mergeVarintField(fieldNumber, rawValue); - return true; - } - break; - default: - value = FieldSet.readPrimitiveField(input, - extension.descriptor.getLiteType(), - /*checkUtf8=*/ false); - break; - } - - if (extension.descriptor.isRepeated()) { - extensions.addRepeatedField(extension.descriptor, - extension.singularToFieldSetType(value)); - } else { - extensions.setField(extension.descriptor, - extension.singularToFieldSetType(value)); - } - } - - return true; - } - // ----------------------------------------------------------------- /** For use by generated code only. */ @@ -893,7 +921,7 @@ extends ExtensionLite<ContainingType, Type> { /** - * Create a new isntance with the given parameters. + * Create a new instance with the given parameters. * * The last parameter {@code singularType} is only needed for enum types. * We store integer values for enum types in a {@link ExtendableMessage} @@ -905,7 +933,7 @@ final Type defaultValue, final MessageLite messageDefaultInstance, final ExtensionDescriptor descriptor, - Class singularType) { + final Class singularType) { // Defensive checks to verify the correct initialization order of // GeneratedExtensions and their related GeneratedMessages. if (containingTypeDefaultInstance == null) { @@ -921,24 +949,12 @@ this.defaultValue = defaultValue; this.messageDefaultInstance = messageDefaultInstance; this.descriptor = descriptor; - - // Use Java reflection to invoke the static method {@code valueOf} of - // enum types in order to convert integers to concrete enum objects. - this.singularType = singularType; - if (Internal.EnumLite.class.isAssignableFrom(singularType)) { - this.enumValueOf = getMethodOrDie( - singularType, "valueOf", int.class); - } else { - this.enumValueOf = null; - } } final ContainingType containingTypeDefaultInstance; final Type defaultValue; final MessageLite messageDefaultInstance; final ExtensionDescriptor descriptor; - final Class singularType; - final Method enumValueOf; /** * Default instance of the type being extended, used to identify that type. @@ -980,7 +996,7 @@ Object singularFromFieldSetType(final Object value) { if (descriptor.getLiteJavaType() == WireFormat.JavaType.ENUM) { - return invokeOrDie(enumValueOf, null, (Integer) value); + return descriptor.enumTypeMap.findValueByNumber((Integer) value); } else { return value; }
diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index b4f4ce7..44d036c 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java
@@ -119,6 +119,21 @@ } /** + * Generates a human readable form of the field, useful for debugging + * and other purposes, with no newline characters. + */ + public static String shortDebugString(final FieldDescriptor field, + final Object value) { + try { + final StringBuilder sb = new StringBuilder(); + SINGLE_LINE_PRINTER.printField(field, value, new TextGenerator(sb)); + return sb.toString().trim(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** * Generates a human readable form of the unknown fields, useful for debugging * and other purposes, with no newline characters. */
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java index 45d5fc3..435ad4d 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
@@ -45,12 +45,13 @@ * @author dweis@google.com (Daniel Weis) */ public final class UnknownFieldSetLite { - - private static final int[] EMPTY_INT_ARRAY = new int[0]; - private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + + // Arbitrarily chosen. + // TODO(dweis): Tune this number? + private static final int MIN_CAPACITY = 8; private static final UnknownFieldSetLite DEFAULT_INSTANCE = - new UnknownFieldSetLite(0, EMPTY_INT_ARRAY, EMPTY_OBJECT_ARRAY); + new UnknownFieldSetLite(0, new int[0], new Object[0], false /* isMutable */); /** * Get an empty {@code UnknownFieldSetLite}. @@ -62,25 +63,32 @@ } /** - * Create a new {@link Builder}. + * Returns an empty {@code UnknownFieldSetLite.Builder}. * * <p>For use by generated code only. */ public static Builder newBuilder() { return new Builder(); } + + /** + * Returns a new mutable instance. + */ + static UnknownFieldSetLite newInstance() { + return new UnknownFieldSetLite(); + } /** - * Returns an {@code UnknownFieldSetLite} that is the composite of {@code first} and + * Returns a mutable {@code UnknownFieldSetLite} that is the composite of {@code first} and * {@code second}. */ - static UnknownFieldSetLite concat(UnknownFieldSetLite first, UnknownFieldSetLite second) { + static UnknownFieldSetLite mutableCopyOf(UnknownFieldSetLite first, UnknownFieldSetLite second) { int count = first.count + second.count; int[] tags = Arrays.copyOf(first.tags, count); System.arraycopy(second.tags, 0, tags, first.count, second.count); Object[] objects = Arrays.copyOf(first.objects, count); System.arraycopy(second.objects, 0, objects, first.count, second.count); - return new UnknownFieldSetLite(count, tags, objects); + return new UnknownFieldSetLite(count, tags, objects, true /* isMutable */); } /** @@ -102,14 +110,45 @@ * The lazily computed serialized size of the set. */ private int memoizedSerializedSize = -1; + + /** + * Indicates that this object is mutable. + */ + private boolean isMutable; /** + * Constructs a mutable {@code UnknownFieldSetLite}. + */ + private UnknownFieldSetLite() { + this(0, new int[MIN_CAPACITY], new Object[MIN_CAPACITY], true /* isMutable */); + } + + /** * Constructs the {@code UnknownFieldSetLite}. */ - private UnknownFieldSetLite(int count, int[] tags, Object[] objects) { + private UnknownFieldSetLite(int count, int[] tags, Object[] objects, boolean isMutable) { this.count = count; this.tags = tags; this.objects = objects; + this.isMutable = isMutable; + } + + /** + * Marks this object as immutable. + * + * <p>Future calls to methods that attempt to modify this object will throw. + */ + public void makeImmutable() { + this.isMutable = false; + } + + /** + * Throws an {@link UnsupportedOperationException} if immutable. + */ + void checkMutable() { + if (!isMutable) { + throw new UnsupportedOperationException(); + } } /** @@ -223,6 +262,114 @@ return hashCode; } + private void storeField(int tag, Object value) { + ensureCapacity(); + + tags[count] = tag; + objects[count] = value; + count++; + } + + /** + * Ensures that our arrays are long enough to store more metadata. + */ + private void ensureCapacity() { + if (count == tags.length) { + int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1; + int newLength = count + increment; + + tags = Arrays.copyOf(tags, newLength); + objects = Arrays.copyOf(objects, newLength); + } + } + + /** + * Parse a single field from {@code input} and merge it into this set. + * + * <p>For use by generated code only. + * + * @param tag The field's tag number, which was already parsed. + * @return {@code false} if the tag is an end group tag. + */ + boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { + checkMutable(); + final int fieldNumber = WireFormat.getTagFieldNumber(tag); + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + storeField(tag, input.readInt64()); + return true; + case WireFormat.WIRETYPE_FIXED32: + storeField(tag, input.readFixed32()); + return true; + case WireFormat.WIRETYPE_FIXED64: + storeField(tag, input.readFixed64()); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + storeField(tag, input.readBytes()); + return true; + case WireFormat.WIRETYPE_START_GROUP: + final UnknownFieldSetLite subFieldSet = new UnknownFieldSetLite(); + subFieldSet.mergeFrom(input); + input.checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + storeField(tag, subFieldSet); + return true; + case WireFormat.WIRETYPE_END_GROUP: + return false; + default: + throw InvalidProtocolBufferException.invalidWireType(); + } + } + + /** + * Convenience method for merging a new field containing a single varint + * value. This is used in particular when an unknown enum value is + * encountered. + * + * <p>For use by generated code only. + */ + UnknownFieldSetLite mergeVarintField(int fieldNumber, int value) { + checkMutable(); + if (fieldNumber == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + + storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value); + + return this; + } + + /** + * Convenience method for merging a length-delimited field. + * + * <p>For use by generated code only. + */ + UnknownFieldSetLite mergeLengthDelimitedField(final int fieldNumber, final ByteString value) { + checkMutable(); + if (fieldNumber == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + + storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value); + + return this; + } + + /** + * Parse an entire message from {@code input} and merge its fields into + * this set. + */ + private UnknownFieldSetLite mergeFrom(final CodedInputStream input) throws IOException { + // Ensures initialization in mergeFieldFrom. + while (true) { + final int tag = input.readTag(); + if (tag == 0 || !mergeFieldFrom(tag, input)) { + break; + } + } + return this; + } + /** * Builder for {@link UnknownFieldSetLite}s. * @@ -230,54 +377,27 @@ * * <p>For use by generated code only. */ + // TODO(dweis): Update the mutable API to no longer need this builder and delete. public static final class Builder { - - // Arbitrarily chosen. - // TODO(dweis): Tune this number? - private static final int MIN_CAPACITY = 8; - - private int count = 0; - private int[] tags = EMPTY_INT_ARRAY; - private Object[] objects = EMPTY_OBJECT_ARRAY; - private boolean built; - - /** - * Constructs a {@code Builder}. - */ - private Builder() {} + private UnknownFieldSetLite set; + + private Builder() { + this.set = null; + } /** * Ensures internal state is initialized for use. */ private void ensureNotBuilt() { - if (built) { - throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders."); + if (set == null) { + set = new UnknownFieldSetLite(); } - } - - private void storeField(int tag, Object value) { - ensureCapacity(); - tags[count] = tag; - objects[count] = value; - count++; + set.checkMutable(); } /** - * Ensures that our arrays are long enough to store more metadata. - */ - private void ensureCapacity() { - if (count == tags.length) { - int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1; - int newLength = count + increment; - - tags = Arrays.copyOf(tags, newLength); - objects = Arrays.copyOf(objects, newLength); - } - } - - /** * Parse a single field from {@code input} and merge it into this set. * * <p>For use by generated code only. @@ -285,36 +405,9 @@ * @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 { + boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { ensureNotBuilt(); - - final int fieldNumber = WireFormat.getTagFieldNumber(tag); - switch (WireFormat.getTagWireType(tag)) { - case WireFormat.WIRETYPE_VARINT: - storeField(tag, input.readInt64()); - return true; - case WireFormat.WIRETYPE_FIXED32: - storeField(tag, input.readFixed32()); - return true; - case WireFormat.WIRETYPE_FIXED64: - storeField(tag, input.readFixed64()); - return true; - case WireFormat.WIRETYPE_LENGTH_DELIMITED: - storeField(tag, input.readBytes()); - return true; - case WireFormat.WIRETYPE_START_GROUP: - final Builder subBuilder = newBuilder(); - subBuilder.mergeFrom(input); - input.checkLastTagWas( - WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); - storeField(tag, subBuilder.build()); - return true; - case WireFormat.WIRETYPE_END_GROUP: - return false; - default: - throw InvalidProtocolBufferException.invalidWireType(); - } + return set.mergeFieldFrom(tag, input); } /** @@ -324,71 +417,42 @@ * * <p>For use by generated code only. */ - public Builder mergeVarintField(int fieldNumber, int value) { - if (fieldNumber == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } + Builder mergeVarintField(int fieldNumber, int value) { ensureNotBuilt(); - - storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value); - + set.mergeVarintField(fieldNumber, value); return this; } - + /** * Convenience method for merging a length-delimited field. * * <p>For use by generated code only. */ - public Builder mergeLengthDelimitedField( - final int fieldNumber, final ByteString value) { - if (fieldNumber == 0) { - throw new IllegalArgumentException("Zero is not a valid field number."); - } - ensureNotBuilt(); - - storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value); - + public Builder mergeLengthDelimitedField(final int fieldNumber, final ByteString value) { + ensureNotBuilt(); + set.mergeLengthDelimitedField(fieldNumber, value); return this; } /** - * Parse an entire message from {@code input} and merge its fields into - * this set. - */ - private Builder mergeFrom(final CodedInputStream input) throws IOException { - // Ensures initialization in mergeFieldFrom. - while (true) { - final int tag = input.readTag(); - if (tag == 0 || !mergeFieldFrom(tag, input)) { - break; - } - } - return this; - } - - /** * Build the {@link UnknownFieldSetLite} 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 IllegalStateException} to be - * thrown. + * in undefined behavior and can cause an + * {@code UnsupportedOperationException} to be thrown. * * <p>For use by generated code only. */ public UnknownFieldSetLite build() { - if (built) { - throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders."); - } - - built = true; - - if (count == 0) { + if (set == null) { return DEFAULT_INSTANCE; } + + set.checkMutable(); + set.makeImmutable(); - return new UnknownFieldSetLite(count, tags, objects); + return set; } } }
diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/src/main/java/com/google/protobuf/Utf8.java index 0699778..48c7e9e 100644 --- a/java/src/main/java/com/google/protobuf/Utf8.java +++ b/java/src/main/java/com/google/protobuf/Utf8.java
@@ -360,8 +360,8 @@ static class UnpairedSurrogateException extends IllegalArgumentException { - private UnpairedSurrogateException(int index) { - super("Unpaired surrogate at index " + index); + private UnpairedSurrogateException(int index, int length) { + super("Unpaired surrogate at index " + index + " of " + length); } } @@ -417,7 +417,7 @@ // Check that we have a well-formed surrogate pair. int cp = Character.codePointAt(sequence, i); if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - throw new UnpairedSurrogateException(i); + throw new UnpairedSurrogateException(i, utf16Length); } i++; } @@ -457,7 +457,7 @@ final char low; if (i + 1 == sequence.length() || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) { - throw new UnpairedSurrogateException((i - 1)); + throw new UnpairedSurrogateException((i - 1), utf16Length); } int codePoint = Character.toCodePoint(c, low); bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18)); @@ -470,7 +470,7 @@ if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) && (i + 1 == sequence.length() || !Character.isSurrogatePair(c, sequence.charAt(i + 1)))) { - throw new UnpairedSurrogateException(i); + throw new UnpairedSurrogateException(i, utf16Length); } throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j); }