aboutsummaryrefslogtreecommitdiffhomepage
path: root/java/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/core/src')
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessage.java190
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java217
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractParser.java111
-rw-r--r--java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java48
-rw-r--r--java/core/src/main/java/com/google/protobuf/Android.java57
-rw-r--r--java/core/src/main/java/com/google/protobuf/BooleanArrayList.java125
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java185
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteOutput.java116
-rw-r--r--java/core/src/main/java/com/google/protobuf/ByteString.java154
-rw-r--r--java/core/src/main/java/com/google/protobuf/CodedInputStream.java4530
-rw-r--r--java/core/src/main/java/com/google/protobuf/CodedOutputStream.java3239
-rw-r--r--java/core/src/main/java/com/google/protobuf/Descriptors.java237
-rw-r--r--java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java71
-rw-r--r--java/core/src/main/java/com/google/protobuf/DoubleArrayList.java125
-rw-r--r--java/core/src/main/java/com/google/protobuf/DynamicMessage.java85
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExperimentalApi.java30
-rw-r--r--java/core/src/main/java/com/google/protobuf/Extension.java8
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java12
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java96
-rw-r--r--java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java58
-rw-r--r--java/core/src/main/java/com/google/protobuf/FieldSet.java48
-rw-r--r--java/core/src/main/java/com/google/protobuf/FloatArrayList.java123
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessage.java467
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java1782
-rw-r--r--java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java2861
-rw-r--r--java/core/src/main/java/com/google/protobuf/IntArrayList.java123
-rw-r--r--java/core/src/main/java/com/google/protobuf/Internal.java82
-rw-r--r--java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java20
-rw-r--r--java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java150
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyField.java16
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyFieldLite.java173
-rw-r--r--java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java52
-rw-r--r--java/core/src/main/java/com/google/protobuf/LongArrayList.java123
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntry.java246
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapEntryLite.java316
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapField.java423
-rw-r--r--java/core/src/main/java/com/google/protobuf/MapFieldLite.java459
-rw-r--r--java/core/src/main/java/com/google/protobuf/Message.java72
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLite.java21
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageLiteToString.java281
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java2
-rw-r--r--java/core/src/main/java/com/google/protobuf/MessageReflection.java128
-rw-r--r--java/core/src/main/java/com/google/protobuf/NioByteString.java64
-rw-r--r--java/core/src/main/java/com/google/protobuf/Parser.java16
-rw-r--r--java/core/src/main/java/com/google/protobuf/PrimitiveNonBoxingCollection.java34
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java22
-rw-r--r--java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java1
-rw-r--r--java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java8
-rw-r--r--java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java702
-rw-r--r--java/core/src/main/java/com/google/protobuf/RopeByteString.java27
-rw-r--r--java/core/src/main/java/com/google/protobuf/RpcUtil.java4
-rw-r--r--java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java2
-rw-r--r--java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java237
-rw-r--r--java/core/src/main/java/com/google/protobuf/SmallSortedMap.java115
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormat.java562
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java137
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java226
-rw-r--r--java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java104
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java116
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java199
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java50
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java73
-rw-r--r--java/core/src/main/java/com/google/protobuf/UnsafeUtil.java574
-rw-r--r--java/core/src/main/java/com/google/protobuf/Utf8.java2019
-rw-r--r--java/core/src/main/java/com/google/protobuf/WireFormat.java25
-rw-r--r--java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java58
-rw-r--r--java/core/src/test/java/com/google/protobuf/AnyTest.java45
-rw-r--r--java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java222
-rw-r--r--java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java1
-rw-r--r--java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java80
-rw-r--r--java/core/src/test/java/com/google/protobuf/ByteStringTest.java18
-rw-r--r--java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java42
-rw-r--r--java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java1061
-rw-r--r--java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java809
-rw-r--r--java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java325
-rw-r--r--java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java25
-rw-r--r--java/core/src/test/java/com/google/protobuf/DescriptorsTest.java60
-rw-r--r--java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java157
-rw-r--r--java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java181
-rw-r--r--java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java4
-rw-r--r--java/core/src/test/java/com/google/protobuf/EnumTest.java76
-rw-r--r--java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java276
-rw-r--r--java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java196
-rw-r--r--java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java181
-rw-r--r--java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java2
-rw-r--r--java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java180
-rw-r--r--java/core/src/test/java/com/google/protobuf/IntArrayListTest.java175
-rw-r--r--java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java73
-rw-r--r--java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java211
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java25
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyFieldTest.java3
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java34
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java7
-rw-r--r--java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java4
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java19
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteTest.java1719
-rw-r--r--java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java73
-rw-r--r--java/core/src/test/java/com/google/protobuf/LongArrayListTest.java215
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java722
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapForProto2Test.java743
-rw-r--r--java/core/src/test/java/com/google/protobuf/MapTest.java931
-rw-r--r--java/core/src/test/java/com/google/protobuf/MessageTest.java28
-rw-r--r--java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java6
-rw-r--r--java/core/src/test/java/com/google/protobuf/NioByteStringTest.java218
-rw-r--r--java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java202
-rw-r--r--java/core/src/test/java/com/google/protobuf/ParserTest.java137
-rw-r--r--java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java17
-rw-r--r--java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java (renamed from java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java)30
-rw-r--r--java/core/src/test/java/com/google/protobuf/ServiceTest.java38
-rw-r--r--java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java (renamed from java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java)20
-rw-r--r--java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java6
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java26
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java83
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestUtil.java777
-rw-r--r--java/core/src/test/java/com/google/protobuf/TestUtilLite.java559
-rw-r--r--java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java182
-rw-r--r--java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java86
-rw-r--r--java/core/src/test/java/com/google/protobuf/TextFormatTest.java383
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java14
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java308
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java209
-rw-r--r--java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java3
-rw-r--r--java/core/src/test/java/com/google/protobuf/WireFormatTest.java107
-rw-r--r--java/core/src/test/proto/com/google/protobuf/deprecated_file.proto38
-rw-r--r--java/core/src/test/proto/com/google/protobuf/field_presence_test.proto3
-rw-r--r--java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto23
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto48
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto48
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_lite_test.proto111
-rw-r--r--java/core/src/test/proto/com/google/protobuf/map_test.proto51
-rw-r--r--java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto18
131 files changed, 27895 insertions, 7816 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
index 9f418f2b..fc3c2a5d 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java
@@ -32,9 +32,9 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
import com.google.protobuf.Descriptors.OneofDescriptor;
import com.google.protobuf.Internal.EnumLite;
-
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
@@ -50,17 +50,51 @@ import java.util.Map;
*
* @author kenton@google.com Kenton Varda
*/
-public abstract class AbstractMessage extends AbstractMessageLite
- implements Message {
+public abstract class AbstractMessage
+ // TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType.
+ extends AbstractMessageLite
+ implements Message {
+
+ @Override
public boolean isInitialized() {
return MessageReflection.isInitialized(this);
}
+ /**
+ * Interface for the parent of a Builder that allows the builder to
+ * communicate invalidations back to the parent for use when using nested
+ * builders.
+ */
+ protected interface BuilderParent {
+
+ /**
+ * A builder becomes dirty whenever a field is modified -- including fields
+ * in nested builders -- and becomes clean when build() is called. Thus,
+ * when a builder becomes dirty, all its parents become dirty as well, and
+ * when it becomes clean, all its children become clean. The dirtiness
+ * state is used to invalidate certain cached values.
+ * <br>
+ * To this end, a builder calls markDirty() on its parent whenever it
+ * transitions from clean to dirty. The parent must propagate this call to
+ * its own parent, unless it was already dirty, in which case the
+ * grandparent must necessarily already be dirty as well. The parent can
+ * only transition back to "clean" after calling build() on all children.
+ */
+ void markDirty();
+ }
+ /** Create a nested builder. */
+ protected Message.Builder newBuilderForType(BuilderParent parent) {
+ throw new UnsupportedOperationException("Nested builder is not supported for this type.");
+ }
+
+
+ @Override
public List<String> findInitializationErrors() {
return MessageReflection.findMissingFields(this);
}
+ @Override
public String getInitializationErrorString() {
return MessageReflection.delimitWithCommas(findInitializationErrors());
}
@@ -83,12 +117,24 @@ public abstract class AbstractMessage extends AbstractMessageLite
return TextFormat.printToString(this);
}
+ @Override
public void writeTo(final CodedOutputStream output) throws IOException {
MessageReflection.writeMessageTo(this, getAllFields(), output, false);
}
protected int memoizedSize = -1;
+ @Override
+ int getMemoizedSerializedSize() {
+ return memoizedSize;
+ }
+
+ @Override
+ void setMemoizedSerializedSize(int size) {
+ memoizedSize = size;
+ }
+
+ @Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) {
@@ -127,7 +173,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
return hash;
}
-
+
private static ByteString toByteString(Object value) {
if (value instanceof byte[]) {
return ByteString.copyFrom((byte[]) value);
@@ -135,7 +181,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
return (ByteString) value;
}
}
-
+
/**
* Compares two bytes fields. The parameters must be either a byte array or a
* ByteString object. They can be of different type though.
@@ -146,7 +192,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
return toByteString(a).equals(toByteString(b));
}
-
+
/**
* Converts a list of MapEntry messages into a Map used for equals() and
* hashCode().
@@ -177,7 +223,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
return result;
}
-
+
/**
* Compares two map fields. The parameters must be a list of MapEntry
* messages.
@@ -188,13 +234,13 @@ public abstract class AbstractMessage extends AbstractMessageLite
Map mb = convertMapEntryListToMap((List) b);
return MapFieldLite.equals(ma, mb);
}
-
+
/**
* Compares two set of fields.
* This method is used to implement {@link AbstractMessage#equals(Object)}
* and {@link AbstractMutableMessage#equals(Object)}. It takes special care
* of bytes fields because immutable messages and mutable messages use
- * different Java type to reprensent a bytes field and this method should be
+ * different Java type to represent a bytes field and this method should be
* able to compare immutable messages, mutable messages and also an immutable
* message to a mutable message.
*/
@@ -240,7 +286,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
}
return true;
}
-
+
/**
* Calculates the hash code of a map field. {@code value} must be a list of
* MapEntry messages.
@@ -288,12 +334,16 @@ public abstract class AbstractMessage extends AbstractMessageLite
* other methods.
*/
@SuppressWarnings("unchecked")
- public static abstract class Builder<BuilderType extends Builder>
- extends AbstractMessageLite.Builder<BuilderType>
+ public static abstract class Builder<BuilderType extends Builder<BuilderType>>
+ extends AbstractMessageLite.Builder
implements Message.Builder {
// The compiler produces an error if this is not declared explicitly.
+ // Method isn't abstract to bypass Java 1.6 compiler issue:
+ // http://bugs.java.com/view_bug.do?bug_id=6908259
@Override
- public abstract BuilderType clone();
+ public BuilderType clone() {
+ throw new UnsupportedOperationException("clone() should be implemented in subclasses.");
+ }
/** TODO(jieluo): Clear it when all subclasses have implemented this method. */
@Override
@@ -314,6 +364,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
throw new UnsupportedOperationException("clearOneof() is not implemented.");
}
+ @Override
public BuilderType clear() {
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
@@ -322,15 +373,27 @@ public abstract class AbstractMessage extends AbstractMessageLite
return (BuilderType) this;
}
+ @Override
public List<String> findInitializationErrors() {
return MessageReflection.findMissingFields(this);
}
+ @Override
public String getInitializationErrorString() {
return MessageReflection.delimitWithCommas(findInitializationErrors());
}
+ @Override
+ protected BuilderType internalMergeFrom(AbstractMessageLite other) {
+ return mergeFrom((Message) other);
+ }
+
+ @Override
public BuilderType mergeFrom(final Message other) {
+ return mergeFrom(other, other.getAllFields());
+ }
+
+ BuilderType mergeFrom(final Message other, Map<FieldDescriptor, Object> allFields) {
if (other.getDescriptorForType() != getDescriptorForType()) {
throw new IllegalArgumentException(
"mergeFrom(Message) can only merge messages of the same type.");
@@ -345,8 +408,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
// which allows people to make secure deep copies of messages.
- for (final Map.Entry<FieldDescriptor, Object> entry :
- other.getAllFields().entrySet()) {
+ for (final Map.Entry<FieldDescriptor, Object> entry : allFields.entrySet()) {
final FieldDescriptor field = entry.getKey();
if (field.isRepeated()) {
for (final Object element : (List)entry.getValue()) {
@@ -384,8 +446,12 @@ public abstract class AbstractMessage extends AbstractMessageLite
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
+ boolean discardUnknown =
+ getDescriptorForType().getFile().getSyntax() == Syntax.PROTO3
+ ? input.shouldDiscardUnknownFieldsProto3()
+ : input.shouldDiscardUnknownFields();
final UnknownFieldSet.Builder unknownFields =
- UnknownFieldSet.newBuilder(getUnknownFields());
+ discardUnknown ? null : UnknownFieldSet.newBuilder(getUnknownFields());
while (true) {
final int tag = input.readTag();
if (tag == 0) {
@@ -403,10 +469,13 @@ public abstract class AbstractMessage extends AbstractMessageLite
break;
}
}
- setUnknownFields(unknownFields.build());
+ if (unknownFields != null) {
+ setUnknownFields(unknownFields.build());
+ }
return (BuilderType) this;
}
+ @Override
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
setUnknownFields(
UnknownFieldSet.newBuilder(getUnknownFields())
@@ -415,17 +484,19 @@ public abstract class AbstractMessage extends AbstractMessageLite
return (BuilderType) this;
}
+ @Override
public Message.Builder getFieldBuilder(final FieldDescriptor field) {
throw new UnsupportedOperationException(
"getFieldBuilder() called on an unsupported message type.");
}
- public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field,
- int index) {
+ @Override
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) {
throw new UnsupportedOperationException(
"getRepeatedFieldBuilder() called on an unsupported message type.");
}
+ @Override
public String toString() {
return TextFormat.printToString(this);
}
@@ -440,6 +511,31 @@ public abstract class AbstractMessage extends AbstractMessageLite
MessageReflection.findMissingFields(message));
}
+ /**
+ * Used to support nested builders and called to mark this builder as clean.
+ * Clean builders will propagate the {@link BuilderParent#markDirty()} event
+ * to their parent builders, while dirty builders will not, as their parents
+ * should be dirty already.
+ *
+ * NOTE: Implementations that don't support nested builders don't need to
+ * override this method.
+ */
+ void markClean() {
+ throw new IllegalStateException("Should be overridden by subclasses.");
+ }
+
+ /**
+ * Used to support nested builders and called when this nested builder is
+ * no longer used by its parent builder and should release the reference
+ * to its parent builder.
+ *
+ * NOTE: Implementations that don't support nested builders don't need to
+ * override this method.
+ */
+ void dispose() {
+ throw new IllegalStateException("Should be overridden by subclasses.");
+ }
+
// ===============================================================
// The following definitions seem to be required in order to make javac
// not produce weird errors like:
@@ -462,7 +558,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
@Override
public BuilderType mergeFrom(final ByteString data)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data);
+ return (BuilderType) super.mergeFrom(data);
}
@Override
@@ -470,20 +566,20 @@ public abstract class AbstractMessage extends AbstractMessageLite
final ByteString data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data, extensionRegistry);
+ return (BuilderType) super.mergeFrom(data, extensionRegistry);
}
@Override
public BuilderType mergeFrom(final byte[] data)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data);
+ return (BuilderType) super.mergeFrom(data);
}
@Override
public BuilderType mergeFrom(
final byte[] data, final int off, final int len)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data, off, len);
+ return (BuilderType) super.mergeFrom(data, off, len);
}
@Override
@@ -491,7 +587,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
final byte[] data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data, extensionRegistry);
+ return (BuilderType) super.mergeFrom(data, extensionRegistry);
}
@Override
@@ -499,13 +595,13 @@ public abstract class AbstractMessage extends AbstractMessageLite
final byte[] data, final int off, final int len,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
- return super.mergeFrom(data, off, len, extensionRegistry);
+ return (BuilderType) super.mergeFrom(data, off, len, extensionRegistry);
}
@Override
public BuilderType mergeFrom(final InputStream input)
throws IOException {
- return super.mergeFrom(input);
+ return (BuilderType) super.mergeFrom(input);
}
@Override
@@ -513,7 +609,7 @@ public abstract class AbstractMessage extends AbstractMessageLite
final InputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
- return super.mergeFrom(input, extensionRegistry);
+ return (BuilderType) super.mergeFrom(input, extensionRegistry);
}
@Override
@@ -530,4 +626,44 @@ public abstract class AbstractMessage extends AbstractMessageLite
return super.mergeDelimitedFrom(input, extensionRegistry);
}
}
+
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashLong(long n) {
+ return (int) (n ^ (n >>> 32));
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashBoolean(boolean b) {
+ return b ? 1231 : 1237;
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashEnum(EnumLite e) {
+ return e.getNumber();
+ }
+ //
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1
+ * generated code.
+ */
+ @Deprecated
+ protected static int hashEnumList(List<? extends EnumLite> list) {
+ int hash = 1;
+ for (EnumLite e : list) {
+ hash = 31 * hash + hashEnum(e);
+ }
+ return hash;
+ }
}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
index 12384983..b22bbaab 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessageLite.java
@@ -30,11 +30,15 @@
package com.google.protobuf;
+import static com.google.protobuf.Internal.checkNotNull;
+
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
* A partial implementation of the {@link MessageLite} interface which
@@ -43,9 +47,12 @@ import java.util.Collection;
*
* @author kenton@google.com Kenton Varda
*/
-public abstract class AbstractMessageLite implements MessageLite {
+public abstract class AbstractMessageLite<
+ MessageType extends AbstractMessageLite<MessageType, BuilderType>,
+ BuilderType extends AbstractMessageLite.Builder<MessageType, BuilderType>>
+ implements MessageLite {
protected int memoizedHashCode = 0;
-
+ @Override
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
@@ -53,12 +60,11 @@ public abstract class AbstractMessageLite implements MessageLite {
writeTo(out.getCodedOutput());
return out.build();
} catch (IOException e) {
- throw new RuntimeException(
- "Serializing to a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getSerializingExceptionMessage("ByteString"), e);
}
}
+ @Override
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
@@ -67,12 +73,11 @@ public abstract class AbstractMessageLite implements MessageLite {
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
- throw new RuntimeException(
- "Serializing to a byte array threw an IOException " +
- "(should never happen).", e);
+ throw new RuntimeException(getSerializingExceptionMessage("byte array"), e);
}
}
+ @Override
public void writeTo(final OutputStream output) throws IOException {
final int bufferSize =
CodedOutputStream.computePreferredBufferSize(getSerializedSize());
@@ -82,6 +87,7 @@ public abstract class AbstractMessageLite implements MessageLite {
codedOutput.flush();
}
+ @Override
public void writeDelimitedTo(final OutputStream output) throws IOException {
final int serialized = getSerializedSize();
final int bufferSize = CodedOutputStream.computePreferredBufferSize(
@@ -93,6 +99,16 @@ public abstract class AbstractMessageLite implements MessageLite {
codedOutput.flush();
}
+ // We'd like these to be abstract but some folks are extending this class directly. They shouldn't
+ // be doing that and they should feel bad.
+ int getMemoizedSerializedSize() {
+ throw new UnsupportedOperationException();
+ }
+
+ void setMemoizedSerializedSize(int size) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Package private helper method for AbstractParser to create
@@ -102,6 +118,11 @@ public abstract class AbstractMessageLite implements MessageLite {
return new UninitializedMessageException(this);
}
+ private String getSerializingExceptionMessage(String target) {
+ return "Serializing " + getClass().getName() + " to a " + target
+ + " threw an IOException (should never happen).";
+ }
+
protected static void checkByteStringIsUtf8(ByteString byteString)
throws IllegalArgumentException {
if (!byteString.isValidUtf8()) {
@@ -109,36 +130,43 @@ public abstract class AbstractMessageLite implements MessageLite {
}
}
- protected static <T> void addAll(final Iterable<T> values,
- final Collection<? super T> list) {
+ // For binary compatibility
+ @Deprecated
+ protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
+ Builder.addAll(values, (List) list);
+ }
+
+ protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
Builder.addAll(values, list);
}
-
+
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
* other methods.
*/
@SuppressWarnings("unchecked")
- public static abstract class Builder<BuilderType extends Builder>
+ public abstract static class Builder<
+ MessageType extends AbstractMessageLite<MessageType, BuilderType>,
+ BuilderType extends Builder<MessageType, BuilderType>>
implements MessageLite.Builder {
// The compiler produces an error if this is not declared explicitly.
@Override
public abstract BuilderType clone();
- public BuilderType mergeFrom(final CodedInputStream input)
- throws IOException {
+ @Override
+ public BuilderType mergeFrom(final CodedInputStream input) throws IOException {
return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
}
// Re-defined here for return type covariance.
+ @Override
public abstract BuilderType mergeFrom(
- final CodedInputStream input,
- final ExtensionRegistryLite extensionRegistry)
+ final CodedInputStream input, final ExtensionRegistryLite extensionRegistry)
throws IOException;
- public BuilderType mergeFrom(final ByteString data)
- throws InvalidProtocolBufferException {
+ @Override
+ public BuilderType mergeFrom(final ByteString data) throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
mergeFrom(input);
@@ -147,15 +175,13 @@ public abstract class AbstractMessageLite implements MessageLite {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
+ @Override
public BuilderType mergeFrom(
- final ByteString data,
- final ExtensionRegistryLite extensionRegistry)
+ final ByteString data, final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
@@ -165,20 +191,18 @@ public abstract class AbstractMessageLite implements MessageLite {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a ByteString threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("ByteString"), e);
}
}
- public BuilderType mergeFrom(final byte[] data)
- throws InvalidProtocolBufferException {
+ @Override
+ public BuilderType mergeFrom(final byte[] data) throws InvalidProtocolBufferException {
return mergeFrom(data, 0, data.length);
}
- public BuilderType mergeFrom(final byte[] data, final int off,
- final int len)
- throws InvalidProtocolBufferException {
+ @Override
+ public BuilderType mergeFrom(final byte[] data, final int off, final int len)
+ throws InvalidProtocolBufferException {
try {
final CodedInputStream input =
CodedInputStream.newInstance(data, off, len);
@@ -188,21 +212,21 @@ public abstract class AbstractMessageLite implements MessageLite {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a byte array threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
- public BuilderType mergeFrom(
- final byte[] data,
- final ExtensionRegistryLite extensionRegistry)
+ @Override
+ public BuilderType mergeFrom(final byte[] data, final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return mergeFrom(data, 0, data.length, extensionRegistry);
}
+ @Override
public BuilderType mergeFrom(
- final byte[] data, final int off, final int len,
+ final byte[] data,
+ final int off,
+ final int len,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
@@ -214,12 +238,11 @@ public abstract class AbstractMessageLite implements MessageLite {
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
- throw new RuntimeException(
- "Reading from a byte array threw an IOException (should " +
- "never happen).", e);
+ throw new RuntimeException(getReadingExceptionMessage("byte array"), e);
}
}
+ @Override
public BuilderType mergeFrom(final InputStream input) throws IOException {
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
mergeFrom(codedInput);
@@ -227,10 +250,9 @@ public abstract class AbstractMessageLite implements MessageLite {
return (BuilderType) this;
}
+ @Override
public BuilderType mergeFrom(
- final InputStream input,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
+ final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
mergeFrom(codedInput, extensionRegistry);
codedInput.checkLastTagWas(0);
@@ -292,10 +314,9 @@ public abstract class AbstractMessageLite implements MessageLite {
}
}
+ @Override
public boolean mergeDelimitedFrom(
- final InputStream input,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
+ final InputStream input, final ExtensionRegistryLite extensionRegistry) throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
return false;
@@ -306,12 +327,49 @@ public abstract class AbstractMessageLite implements MessageLite {
return true;
}
- public boolean mergeDelimitedFrom(final InputStream input)
- throws IOException {
+ @Override
+ public boolean mergeDelimitedFrom(final InputStream input) throws IOException {
return mergeDelimitedFrom(input,
ExtensionRegistryLite.getEmptyRegistry());
}
+ @Override
+ @SuppressWarnings("unchecked") // isInstance takes care of this
+ public BuilderType mergeFrom(final MessageLite other) {
+ if (!getDefaultInstanceForType().getClass().isInstance(other)) {
+ throw new IllegalArgumentException(
+ "mergeFrom(MessageLite) can only merge messages of the same type.");
+ }
+
+ return internalMergeFrom((MessageType) other);
+ }
+
+ protected abstract BuilderType internalMergeFrom(MessageType message);
+
+ private String getReadingExceptionMessage(String target) {
+ return "Reading " + getClass().getName() + " from a " + target
+ + " threw an IOException (should never happen).";
+ }
+
+ // We check nulls as we iterate to avoid iterating over values twice.
+ private static <T> void addAllCheckingNulls(Iterable<T> values, List<? super T> list) {
+ if (list instanceof ArrayList && values instanceof Collection) {
+ ((ArrayList<T>) list).ensureCapacity(list.size() + ((Collection<T>) values).size());
+ }
+ int begin = list.size();
+ for (T value : values) {
+ if (value == null) {
+ // encountered a null value so we must undo our modifications prior to throwing
+ String message = "Element at index " + (list.size() - begin) + " is null.";
+ for (int i = list.size() - 1; i >= begin; i--) {
+ list.remove(i);
+ }
+ throw new NullPointerException(message);
+ }
+ list.add(value);
+ }
+ }
+
/**
* Construct an UninitializedMessageException reporting missing fields in
* the given message.
@@ -321,41 +379,50 @@ public abstract class AbstractMessageLite implements MessageLite {
return new UninitializedMessageException(message);
}
+ // For binary compatibility.
+ @Deprecated
+ protected static <T> void addAll(final Iterable<T> values, final Collection<? super T> list) {
+ addAll(values, (List<T>) list);
+ }
+
/**
- * Adds the {@code values} to the {@code list}. This is a helper method
- * used by generated code. Users should ignore it.
+ * Adds the {@code values} to the {@code list}. This is a helper method used by generated code.
+ * Users should ignore it.
*
- * @throws NullPointerException if {@code values} or any of the elements of
- * {@code values} is null. When that happens, some elements of
- * {@code values} may have already been added to the result {@code list}.
+ * @throws NullPointerException if {@code values} or any of the elements of {@code values} is
+ * null.
*/
- protected static <T> void addAll(final Iterable<T> values,
- final Collection<? super T> list) {
- if (values == null) {
- throw new NullPointerException();
- }
+ protected static <T> void addAll(final Iterable<T> values, final List<? super T> list) {
+ checkNotNull(values);
if (values instanceof LazyStringList) {
// For StringOrByteStringLists, check the underlying elements to avoid
// forcing conversions of ByteStrings to Strings.
- checkForNullValues(((LazyStringList) values).getUnderlyingElements());
- list.addAll((Collection<T>) values);
- } else if (values instanceof Collection) {
- checkForNullValues(values);
- list.addAll((Collection<T>) values);
- } else {
- for (final T value : values) {
+ // TODO(dweis): Could we just prohibit nulls in all protobuf lists and get rid of this? Is
+ // if even possible to hit this condition as all protobuf methods check for null first,
+ // right?
+ List<?> lazyValues = ((LazyStringList) values).getUnderlyingElements();
+ LazyStringList lazyList = (LazyStringList) list;
+ int begin = list.size();
+ for (Object value : lazyValues) {
if (value == null) {
- throw new NullPointerException();
+ // encountered a null value so we must undo our modifications prior to throwing
+ String message = "Element at index " + (lazyList.size() - begin) + " is null.";
+ for (int i = lazyList.size() - 1; i >= begin; i--) {
+ lazyList.remove(i);
+ }
+ throw new NullPointerException(message);
+ }
+ if (value instanceof ByteString) {
+ lazyList.add((ByteString) value);
+ } else {
+ lazyList.add((String) value);
}
- list.add(value);
}
- }
- }
-
- private static void checkForNullValues(final Iterable<?> values) {
- for (final Object value : values) {
- if (value == null) {
- throw new NullPointerException();
+ } else {
+ if (values instanceof PrimitiveNonBoxingCollection) {
+ list.addAll((Collection<T>) values);
+ } else {
+ addAllCheckingNulls(values, list);
}
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractParser.java b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
index 1a4c6311..ba570e3d 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractParser.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractParser.java
@@ -31,9 +31,9 @@
package com.google.protobuf;
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
-
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
/**
* A partial implementation of the {@link Parser} interface which implements
@@ -78,26 +78,27 @@ public abstract class AbstractParser<MessageType extends MessageLite>
private static final ExtensionRegistryLite EMPTY_REGISTRY
= ExtensionRegistryLite.getEmptyRegistry();
+ @Override
public MessageType parsePartialFrom(CodedInputStream input)
throws InvalidProtocolBufferException {
return parsePartialFrom(input, EMPTY_REGISTRY);
}
- public MessageType parseFrom(CodedInputStream input,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(
parsePartialFrom(input, extensionRegistry));
}
- public MessageType parseFrom(CodedInputStream input)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parseFrom(CodedInputStream input) throws InvalidProtocolBufferException {
return parseFrom(input, EMPTY_REGISTRY);
}
- public MessageType parsePartialFrom(ByteString data,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parsePartialFrom(ByteString data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
MessageType message;
try {
CodedInputStream input = data.newCodedInput();
@@ -113,24 +114,49 @@ public abstract class AbstractParser<MessageType extends MessageLite>
}
}
- public MessageType parsePartialFrom(ByteString data)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parsePartialFrom(ByteString data) throws InvalidProtocolBufferException {
return parsePartialFrom(data, EMPTY_REGISTRY);
}
- public MessageType parseFrom(ByteString data,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseFrom(ByteString data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(parsePartialFrom(data, extensionRegistry));
}
- public MessageType parseFrom(ByteString data)
+ @Override
+ public MessageType parseFrom(ByteString data) throws InvalidProtocolBufferException {
+ return parseFrom(data, EMPTY_REGISTRY);
+ }
+
+ @Override
+ public MessageType parseFrom(ByteBuffer data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
+ MessageType message;
+ try {
+ CodedInputStream input = CodedInputStream.newInstance(data);
+ message = parsePartialFrom(input, extensionRegistry);
+ try {
+ input.checkLastTagWas(0);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(message);
+ }
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ }
+
+ return checkMessageInitialized(message);
+ }
+
+ @Override
+ public MessageType parseFrom(ByteBuffer data) throws InvalidProtocolBufferException {
return parseFrom(data, EMPTY_REGISTRY);
}
- public MessageType parsePartialFrom(byte[] data, int off, int len,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parsePartialFrom(
+ byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
CodedInputStream input = CodedInputStream.newInstance(data, off, len);
@@ -146,47 +172,50 @@ public abstract class AbstractParser<MessageType extends MessageLite>
}
}
+ @Override
public MessageType parsePartialFrom(byte[] data, int off, int len)
throws InvalidProtocolBufferException {
return parsePartialFrom(data, off, len, EMPTY_REGISTRY);
}
- public MessageType parsePartialFrom(byte[] data,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parsePartialFrom(byte[] data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return parsePartialFrom(data, 0, data.length, extensionRegistry);
}
- public MessageType parsePartialFrom(byte[] data)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parsePartialFrom(byte[] data) throws InvalidProtocolBufferException {
return parsePartialFrom(data, 0, data.length, EMPTY_REGISTRY);
}
- public MessageType parseFrom(byte[] data, int off, int len,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseFrom(
+ byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(
parsePartialFrom(data, off, len, extensionRegistry));
}
+ @Override
public MessageType parseFrom(byte[] data, int off, int len)
throws InvalidProtocolBufferException {
return parseFrom(data, off, len, EMPTY_REGISTRY);
}
- public MessageType parseFrom(byte[] data,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return parseFrom(data, 0, data.length, extensionRegistry);
}
- public MessageType parseFrom(byte[] data)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parseFrom(byte[] data) throws InvalidProtocolBufferException {
return parseFrom(data, EMPTY_REGISTRY);
}
- public MessageType parsePartialFrom(InputStream input,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parsePartialFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
CodedInputStream codedInput = CodedInputStream.newInstance(input);
MessageType message = parsePartialFrom(codedInput, extensionRegistry);
@@ -198,26 +227,26 @@ public abstract class AbstractParser<MessageType extends MessageLite>
return message;
}
- public MessageType parsePartialFrom(InputStream input)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parsePartialFrom(InputStream input) throws InvalidProtocolBufferException {
return parsePartialFrom(input, EMPTY_REGISTRY);
}
- public MessageType parseFrom(InputStream input,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(
parsePartialFrom(input, extensionRegistry));
}
- public MessageType parseFrom(InputStream input)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parseFrom(InputStream input) throws InvalidProtocolBufferException {
return parseFrom(input, EMPTY_REGISTRY);
}
+ @Override
public MessageType parsePartialDelimitedFrom(
- InputStream input,
- ExtensionRegistryLite extensionRegistry)
+ InputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
int size;
try {
@@ -227,27 +256,27 @@ public abstract class AbstractParser<MessageType extends MessageLite>
}
size = CodedInputStream.readRawVarint32(firstByte, input);
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage());
+ throw new InvalidProtocolBufferException(e);
}
InputStream limitedInput = new LimitedInputStream(input, size);
return parsePartialFrom(limitedInput, extensionRegistry);
}
+ @Override
public MessageType parsePartialDelimitedFrom(InputStream input)
throws InvalidProtocolBufferException {
return parsePartialDelimitedFrom(input, EMPTY_REGISTRY);
}
- public MessageType parseDelimitedFrom(
- InputStream input,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public MessageType parseDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return checkMessageInitialized(
parsePartialDelimitedFrom(input, extensionRegistry));
}
- public MessageType parseDelimitedFrom(InputStream input)
- throws InvalidProtocolBufferException {
+ @Override
+ public MessageType parseDelimitedFrom(InputStream input) throws InvalidProtocolBufferException {
return parseDelimitedFrom(input, EMPTY_REGISTRY);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
index bb6446b2..b17db6e0 100644
--- a/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
+++ b/java/core/src/main/java/com/google/protobuf/AbstractProtobufList.java
@@ -34,19 +34,25 @@ import com.google.protobuf.Internal.ProtobufList;
import java.util.AbstractList;
import java.util.Collection;
+import java.util.List;
+import java.util.RandomAccess;
/**
* An abstract implementation of {@link ProtobufList} which manages mutability semantics. All mutate
- * methods are check if the list is mutable before proceeding. Subclasses must invoke
+ * methods must check if the list is mutable before proceeding. Subclasses must invoke
* {@link #ensureIsMutable()} manually when overriding those methods.
+ * <p>
+ * This implementation assumes all subclasses are array based, supporting random access.
*/
abstract class AbstractProtobufList<E> extends AbstractList<E> implements ProtobufList<E> {
+ protected static final int DEFAULT_CAPACITY = 10;
+
/**
* Whether or not this list is modifiable.
*/
private boolean isMutable;
-
+
/**
* Constructs a mutable list by default.
*/
@@ -55,6 +61,44 @@ abstract class AbstractProtobufList<E> extends AbstractList<E> implements Protob
}
@Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof List)) {
+ return false;
+ }
+ // Handle lists that do not support RandomAccess as efficiently as possible by using an iterator
+ // based approach in our super class. Otherwise our index based approach will avoid those
+ // allocations.
+ if (!(o instanceof RandomAccess)) {
+ return super.equals(o);
+ }
+
+ List<?> other = (List<?>) o;
+ final int size = size();
+ if (size != other.size()) {
+ return false;
+ }
+ for (int i = 0; i < size; i++) {
+ if (!get(i).equals(other.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ final int size = size();
+ int hashCode = 1;
+ for (int i = 0; i < size; i++) {
+ hashCode = (31 * hashCode) + get(i).hashCode();
+ }
+ return hashCode;
+ }
+
+ @Override
public boolean add(E e) {
ensureIsMutable();
return super.add(e);
diff --git a/java/core/src/main/java/com/google/protobuf/Android.java b/java/core/src/main/java/com/google/protobuf/Android.java
new file mode 100644
index 00000000..cad54783
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/Android.java
@@ -0,0 +1,57 @@
+// 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;
+
+final class Android {
+
+ private static final Class<?> MEMORY_CLASS = getClassForName("libcore.io.Memory");
+ private static final boolean IS_ROBOLECTRIC =
+ getClassForName("org.robolectric.Robolectric") != null;
+
+ /** Returns {@code true} if running on an Android device. */
+ static boolean isOnAndroidDevice() {
+ return MEMORY_CLASS != null && !IS_ROBOLECTRIC;
+ }
+
+ /** Returns the memory class or {@code null} if not on Android device. */
+ static Class<?> getMemoryClass() {
+ return MEMORY_CLASS;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> Class<T> getClassForName(String name) {
+ try {
+ return (Class<T>) Class.forName(name);
+ } catch (Throwable e) {
+ return null;
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
index 70e042f5..4d7a9727 100644
--- a/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/BooleanArrayList.java
@@ -30,37 +30,35 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.BooleanList;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.BooleanList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.RandomAccess;
/**
* An implementation of {@link BooleanList} on top of a primitive array.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
-final class BooleanArrayList
- extends AbstractProtobufList<Boolean> implements BooleanList, RandomAccess {
-
- private static final int DEFAULT_CAPACITY = 10;
-
+final class BooleanArrayList extends AbstractProtobufList<Boolean>
+ implements BooleanList, RandomAccess, PrimitiveNonBoxingCollection {
+
private static final BooleanArrayList EMPTY_LIST = new BooleanArrayList();
static {
EMPTY_LIST.makeImmutable();
}
-
+
public static BooleanArrayList emptyList() {
return EMPTY_LIST;
}
-
+
/**
* The backing store for the list.
*/
private boolean[] array;
-
+
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@@ -71,35 +69,70 @@ final class BooleanArrayList
* Constructs a new mutable {@code BooleanArrayList} with default capacity.
*/
BooleanArrayList() {
- this(DEFAULT_CAPACITY);
+ this(new boolean[DEFAULT_CAPACITY], 0);
}
/**
- * Constructs a new mutable {@code BooleanArrayList} with the provided capacity.
+ * Constructs a new mutable {@code BooleanArrayList}
+ * containing the same elements as {@code other}.
*/
- BooleanArrayList(int capacity) {
- array = new boolean[capacity];
- size = 0;
+ private BooleanArrayList(boolean[] other, int size) {
+ array = other;
+ this.size = size;
}
- /**
- * Constructs a new mutable {@code BooleanArrayList} containing the same elements as
- * {@code other}.
- */
- BooleanArrayList(List<Boolean> other) {
- if (other instanceof BooleanArrayList) {
- BooleanArrayList list = (BooleanArrayList) other;
- array = list.array.clone();
- size = list.size;
- } else {
- size = other.size();
- array = new boolean[size];
- for (int i = 0; i < size; i++) {
- array[i] = other.get(i);
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ ensureIsMutable();
+ if (toIndex < fromIndex) {
+ throw new IndexOutOfBoundsException("toIndex < fromIndex");
+ }
+
+ System.arraycopy(array, toIndex, array, fromIndex, size - toIndex);
+ size -= (toIndex - fromIndex);
+ modCount++;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof BooleanArrayList)) {
+ return super.equals(o);
+ }
+ BooleanArrayList other = (BooleanArrayList) o;
+ if (size != other.size) {
+ return false;
+ }
+
+ final boolean[] arr = other.array;
+ for (int i = 0; i < size; i++) {
+ if (array[i] != arr[i]) {
+ return false;
}
}
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size; i++) {
+ result = (31 * result) + Internal.hashBoolean(array[i]);
+ }
+ return result;
}
-
+
+ @Override
+ public BooleanList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size) {
+ throw new IllegalArgumentException();
+ }
+ return new BooleanArrayList(Arrays.copyOf(array, capacity), size);
+ }
+
@Override
public Boolean get(int index) {
return getBoolean(index);
@@ -151,7 +184,7 @@ final class BooleanArrayList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
-
+
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@@ -159,10 +192,10 @@ final class BooleanArrayList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
boolean[] newArray = new boolean[length];
-
+
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
-
+
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@@ -176,38 +209,36 @@ final class BooleanArrayList
@Override
public boolean addAll(Collection<? extends Boolean> collection) {
ensureIsMutable();
-
- if (collection == null) {
- throw new NullPointerException();
- }
-
+
+ checkNotNull(collection);
+
// We specialize when adding another BooleanArrayList to avoid boxing elements.
if (!(collection instanceof BooleanArrayList)) {
return super.addAll(collection);
}
-
+
BooleanArrayList list = (BooleanArrayList) collection;
if (list.size == 0) {
return false;
}
-
+
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
-
+
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
-
+
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
-
+
@Override
public boolean remove(Object o) {
ensureIsMutable();
@@ -227,7 +258,9 @@ final class BooleanArrayList
ensureIsMutable();
ensureIndexInRange(index);
boolean value = array[index];
- System.arraycopy(array, index + 1, array, index, size - index);
+ if (index < size - 1) {
+ System.arraycopy(array, index + 1, array, index, size - index);
+ }
size--;
modCount++;
return value;
@@ -236,7 +269,7 @@ final class BooleanArrayList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
- *
+ *
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {
diff --git a/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
new file mode 100644
index 00000000..6157a52f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java
@@ -0,0 +1,185 @@
+// 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 java.lang.Math.max;
+import static java.lang.Math.min;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * Utility class to provide efficient writing of {@link ByteBuffer}s to {@link OutputStream}s.
+ */
+final class ByteBufferWriter {
+ private ByteBufferWriter() {}
+
+ /**
+ * Minimum size for a cached buffer. This prevents us from allocating buffers that are too
+ * small to be easily reused.
+ */
+ // TODO(nathanmittler): tune this property or allow configuration?
+ private static final int MIN_CACHED_BUFFER_SIZE = 1024;
+
+ /**
+ * Maximum size for a cached buffer. If a larger buffer is required, it will be allocated
+ * but not cached.
+ */
+ // TODO(nathanmittler): tune this property or allow configuration?
+ private static final int MAX_CACHED_BUFFER_SIZE = 16 * 1024;
+
+ /**
+ * The fraction of the requested buffer size under which the buffer will be reallocated.
+ */
+ // TODO(nathanmittler): tune this property or allow configuration?
+ private static final float BUFFER_REALLOCATION_THRESHOLD = 0.5f;
+
+ /**
+ * Keeping a soft reference to a thread-local buffer. This buffer is used for writing a
+ * {@link ByteBuffer} to an {@link OutputStream} when no zero-copy alternative was available.
+ * Using a "soft" reference since VMs may keep this reference around longer than "weak"
+ * (e.g. HotSpot will maintain soft references until memory pressure warrants collection).
+ */
+ private static final ThreadLocal<SoftReference<byte[]>> BUFFER =
+ new ThreadLocal<SoftReference<byte[]>>();
+
+ /**
+ * This is a hack for GAE, where {@code FileOutputStream} is unavailable.
+ */
+ private static final Class<?> FILE_OUTPUT_STREAM_CLASS = safeGetClass("java.io.FileOutputStream");
+ private static final long CHANNEL_FIELD_OFFSET = getChannelFieldOffset(FILE_OUTPUT_STREAM_CLASS);
+
+ /**
+ * For testing purposes only. Clears the cached buffer to force a new allocation on the next
+ * invocation.
+ */
+ static void clearCachedBuffer() {
+ BUFFER.set(null);
+ }
+
+ /**
+ * Writes the remaining content of the buffer to the given stream. The buffer {@code position}
+ * will remain unchanged by this method.
+ */
+ static void write(ByteBuffer buffer, OutputStream output) throws IOException {
+ final int initialPos = buffer.position();
+ try {
+ if (buffer.hasArray()) {
+ // Optimized write for array-backed buffers.
+ // Note that we're taking the risk that a malicious OutputStream could modify the array.
+ output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
+ } else if (!writeToChannel(buffer, output)){
+ // Read all of the data from the buffer to an array.
+ // TODO(nathanmittler): Consider performance improvements for other "known" stream types.
+ final byte[] array = getOrCreateBuffer(buffer.remaining());
+ while (buffer.hasRemaining()) {
+ int length = min(buffer.remaining(), array.length);
+ buffer.get(array, 0, length);
+ output.write(array, 0, length);
+ }
+ }
+ } finally {
+ // Restore the initial position.
+ buffer.position(initialPos);
+ }
+ }
+
+ private static byte[] getOrCreateBuffer(int requestedSize) {
+ requestedSize = max(requestedSize, MIN_CACHED_BUFFER_SIZE);
+
+ byte[] buffer = getBuffer();
+ // Only allocate if we need to.
+ if (buffer == null || needToReallocate(requestedSize, buffer.length)) {
+ buffer = new byte[requestedSize];
+
+ // Only cache the buffer if it's not too big.
+ if (requestedSize <= MAX_CACHED_BUFFER_SIZE) {
+ setBuffer(buffer);
+ }
+ }
+ return buffer;
+ }
+
+ private static boolean needToReallocate(int requestedSize, int bufferLength) {
+ // First check against just the requested length to avoid the multiply.
+ return bufferLength < requestedSize
+ && bufferLength < requestedSize * BUFFER_REALLOCATION_THRESHOLD;
+ }
+
+ private static byte[] getBuffer() {
+ SoftReference<byte[]> sr = BUFFER.get();
+ return sr == null ? null : sr.get();
+ }
+
+ private static void setBuffer(byte[] value) {
+ BUFFER.set(new SoftReference<byte[]>(value));
+ }
+
+ private static boolean writeToChannel(ByteBuffer buffer, OutputStream output) throws IOException {
+ if (CHANNEL_FIELD_OFFSET >= 0 && FILE_OUTPUT_STREAM_CLASS.isInstance(output)) {
+ // Use a channel to write out the ByteBuffer. This will automatically empty the buffer.
+ WritableByteChannel channel = null;
+ try {
+ channel = (WritableByteChannel) UnsafeUtil.getObject(output, CHANNEL_FIELD_OFFSET);
+ } catch (ClassCastException e) {
+ // Absorb.
+ }
+ if (channel != null) {
+ channel.write(buffer);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static Class<?> safeGetClass(String className) {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+ private static long getChannelFieldOffset(Class<?> clazz) {
+ try {
+ if (clazz != null && UnsafeUtil.hasUnsafeArrayOperations()) {
+ Field field = clazz.getDeclaredField("channel");
+ return UnsafeUtil.objectFieldOffset(field);
+ }
+ } catch (Throwable e) {
+ // Absorb
+ }
+ return -1;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ByteOutput.java b/java/core/src/main/java/com/google/protobuf/ByteOutput.java
new file mode 100644
index 00000000..ee588753
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ByteOutput.java
@@ -0,0 +1,116 @@
+// 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 java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * An output target for raw bytes. This interface provides semantics that support two types of
+ * writing:
+ *
+ * <p><b>Traditional write operations:</b>
+ * (as defined by {@link java.io.OutputStream}) where the target method is responsible for either
+ * copying the data or completing the write before returning from the method call.
+ *
+ * <p><b>Lazy write operations:</b> where the caller guarantees that it will never modify the
+ * provided buffer and it can therefore be considered immutable. The target method is free to
+ * maintain a reference to the buffer beyond the scope of the method call (e.g. until the write
+ * operation completes).
+ */
+@ExperimentalApi
+public abstract class ByteOutput {
+ /**
+ * Writes a single byte.
+ *
+ * @param value the byte to be written
+ * @throws IOException thrown if an error occurred while writing
+ */
+ public abstract void write(byte value) throws IOException;
+
+ /**
+ * Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will
+ * not be processed prior to the return of this method call, since {@code value} may be
+ * reused/altered by the caller.
+ *
+ * <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
+ * programming error and will lead to data corruption which will be difficult to debug.
+ *
+ * @param value the bytes to be written
+ * @param offset the offset of the start of the writable range
+ * @param length the number of bytes to write starting from {@code offset}
+ * @throws IOException thrown if an error occurred while writing
+ */
+ public abstract void write(byte[] value, int offset, int length) throws IOException;
+
+ /**
+ * Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value
+ * beyond the scope of this method call (e.g. write later) since it is considered immutable and is
+ * guaranteed not to change by the caller.
+ *
+ * <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
+ * programming error and will lead to data corruption which will be difficult to debug.
+ *
+ * @param value the bytes to be written
+ * @param offset the offset of the start of the writable range
+ * @param length the number of bytes to write starting from {@code offset}
+ * @throws IOException thrown if an error occurred while writing
+ */
+ public abstract void writeLazy(byte[] value, int offset, int length) throws IOException;
+
+ /**
+ * Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will
+ * not be processed prior to the return of this method call, since {@code value} may be
+ * reused/altered by the caller.
+ *
+ * <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
+ * programming error and will lead to data corruption which will be difficult to debug.
+ *
+ * @param value the bytes to be written. Upon returning from this call, the {@code position} of
+ * this buffer will be set to the {@code limit}
+ * @throws IOException thrown if an error occurred while writing
+ */
+ public abstract void write(ByteBuffer value) throws IOException;
+
+ /**
+ * Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value
+ * beyond the scope of this method call (e.g. write later) since it is considered immutable and is
+ * guaranteed not to change by the caller.
+ *
+ * <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
+ * programming error and will lead to data corruption which will be difficult to debug.
+ *
+ * @param value the bytes to be written. Upon returning from this call, the {@code position} of
+ * this buffer will be set to the {@code limit}
+ * @throws IOException thrown if an error occurred while writing
+ */
+ public abstract void writeLazy(ByteBuffer value) throws IOException;
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java
index 305236f3..d67bb54a 100644
--- a/java/core/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/ByteString.java
@@ -1,4 +1,32 @@
-// Copyright 2007 Google Inc. All rights reserved.
+// 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;
@@ -15,6 +43,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -22,14 +51,12 @@ import java.util.List;
import java.util.NoSuchElementException;
/**
- * Immutable sequence of bytes. Substring is supported by sharing the reference
- * to the immutable underlying bytes, as with {@link String}. Concatenation is
- * likewise supported without copying (long strings) by building a tree of
- * pieces in {@link RopeByteString}.
- * <p>
- * Like {@link String}, the contents of a {@link ByteString} can never be
- * observed to change, not even in the presence of a data race or incorrect
- * API usage in the client code.
+ * Immutable sequence of bytes. Substring is supported by sharing the reference to the immutable
+ * underlying bytes. Concatenation is likewise supported without copying (long strings) by building
+ * a tree of pieces in {@link RopeByteString}.
+ *
+ * <p>Like {@link String}, the contents of a {@link ByteString} can never be observed to change, not
+ * even in the presence of a data race or incorrect API usage in the client code.
*
* @author crazybob@google.com Bob Lee
* @author kenton@google.com Kenton Varda
@@ -60,6 +87,48 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
public static final ByteString EMPTY = new LiteralByteString(Internal.EMPTY_BYTE_ARRAY);
/**
+ * An interface to efficiently copy {@code byte[]}.
+ *
+ * <p>One of the noticeable costs of copying a byte[] into a new array using
+ * {@code System.arraycopy} is nullification of a new buffer before the copy. It has been shown
+ * the Hotspot VM is capable to intrisicfy {@code Arrays.copyOfRange} operation to avoid this
+ * expensive nullification and provide substantial performance gain. Unfortunately this does not
+ * hold on Android runtimes and could make the copy slightly slower due to additional code in
+ * the {@code Arrays.copyOfRange}. Thus we provide two different implementation for array copier
+ * for Hotspot and Android runtimes.
+ */
+ private interface ByteArrayCopier {
+ /**
+ * Copies the specified range of the specified array into a new array
+ */
+ byte[] copyFrom(byte[] bytes, int offset, int size);
+ }
+
+ /** Implementation of {@code ByteArrayCopier} which uses {@link System#arraycopy}. */
+ private static final class SystemByteArrayCopier implements ByteArrayCopier {
+ @Override
+ public byte[] copyFrom(byte[] bytes, int offset, int size) {
+ byte[] copy = new byte[size];
+ System.arraycopy(bytes, offset, copy, 0, size);
+ return copy;
+ }
+ }
+
+ /** Implementation of {@code ByteArrayCopier} which uses {@link Arrays#copyOfRange}. */
+ private static final class ArraysByteArrayCopier implements ByteArrayCopier {
+ @Override
+ public byte[] copyFrom(byte[] bytes, int offset, int size) {
+ return Arrays.copyOfRange(bytes, offset, offset + size);
+ }
+ }
+
+ private static final ByteArrayCopier byteArrayCopier;
+ static {
+ byteArrayCopier =
+ Android.isOnAndroidDevice() ? new SystemByteArrayCopier() : new ArraysByteArrayCopier();
+ }
+
+ /**
* Cached hash value. Intentionally accessed via a data race, which
* is safe because of the Java Memory Model's "no out-of-thin-air values"
* guarantees for ints. A value of 0 implies that the hash has not been set.
@@ -77,7 +146,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @param index index of byte
* @return the value
- * @throws ArrayIndexOutOfBoundsException {@code index < 0 or index >= size}
+ * @throws IndexOutOfBoundsException {@code index < 0 or index >= size}
*/
public abstract byte byteAt(int index);
@@ -109,7 +178,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
public byte nextByte() {
try {
return byteAt(position++);
- } catch (ArrayIndexOutOfBoundsException e) {
+ } catch (IndexOutOfBoundsException e) {
throw new NoSuchElementException(e.getMessage());
}
}
@@ -220,9 +289,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @return new {@code ByteString}
*/
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
- byte[] copy = new byte[size];
- System.arraycopy(bytes, offset, copy, 0, size);
- return new LiteralByteString(copy);
+ return new LiteralByteString(byteArrayCopier.copyFrom(bytes, offset, size));
}
/**
@@ -234,7 +301,19 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
public static ByteString copyFrom(byte[] bytes) {
return copyFrom(bytes, 0, bytes.length);
}
-
+
+ /**
+ * Wraps the given bytes into a {@code ByteString}. Intended for internal only usage.
+ */
+ static ByteString wrap(ByteBuffer buffer) {
+ if (buffer.hasArray()) {
+ final int offset = buffer.arrayOffset();
+ return ByteString.wrap(buffer.array(), offset + buffer.position(), buffer.remaining());
+ } else {
+ return new NioByteString(buffer);
+ }
+ }
+
/**
* Wraps the given bytes into a {@code ByteString}. Intended for internal only
* usage to force a classload of ByteString before LiteralByteString.
@@ -327,7 +406,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* immutable tree of byte arrays ("chunks") of the stream data. The
* first chunk is small, with subsequent chunks each being double
* the size, up to 8K.
- *
+ *
* <p>Each byte read from the input stream will be copied twice to ensure
* that the resulting ByteString is truly immutable.
*
@@ -478,7 +557,9 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
// Create a balanced concatenation of the next "length" elements from the
// iterable.
private static ByteString balancedConcat(Iterator<ByteString> iterator, int length) {
- assert length >= 1;
+ if (length < 1) {
+ throw new IllegalArgumentException(String.format("length (%s) must be >= 1", length));
+ }
ByteString result;
if (length == 1) {
result = iterator.next();
@@ -559,12 +640,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
}
/**
- * Writes the complete contents of this byte string to
- * the specified output stream argument.
- *
- * <p>It is assumed that the {@link OutputStream} will not modify the contents passed it
- * it. It may be possible for a malicious {@link OutputStream} to corrupt
- * the data underlying the {@link ByteString}.
+ * Writes a copy of the contents of this byte string to the specified output stream argument.
*
* @param out the output stream to which to write the data.
* @throws IOException if an I/O error occurs.
@@ -578,8 +654,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param sourceOffset offset within these bytes
* @param numberToWrite number of bytes to write
* @throws IOException if an I/O error occurs.
- * @throws IndexOutOfBoundsException if an offset or size is negative or too
- * large
+ * @throws IndexOutOfBoundsException if an offset or size is negative or too large
*/
final void writeTo(OutputStream out, int sourceOffset, int numberToWrite)
throws IOException {
@@ -597,6 +672,21 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
throws IOException;
/**
+ * Writes this {@link ByteString} to the provided {@link ByteOutput}. Calling
+ * this method may result in multiple operations on the target {@link ByteOutput}.
+ *
+ * <p>This method may expose internal backing buffers of the {@link ByteString} to the {@link
+ * ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious
+ * {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution!
+ *
+ * @param byteOutput the output target to receive the bytes
+ * @throws IOException if an I/O error occurs
+ * @see UnsafeByteOperations#unsafeWriteTo(ByteString, ByteOutput)
+ */
+ abstract void writeTo(ByteOutput byteOutput) throws IOException;
+
+
+ /**
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
* is equal to the contents of this byte string.
* The result uses the same backing array as the byte string, if possible.
@@ -737,6 +827,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
return true;
}
+
/**
* Check equality of the substring of given length of this object starting at
* zero with another {@code ByteString} substring starting at offset.
@@ -1102,7 +1193,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* @param index the index position to be tested
* @param size the length of the array
- * @throws ArrayIndexOutOfBoundsException if the index does not fall within the array.
+ * @throws IndexOutOfBoundsException if the index does not fall within the array.
*/
static void checkIndex(int index, int size) {
if ((index | (size - (index + 1))) < 0) {
@@ -1120,7 +1211,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* @param endIndex the end index of the range (exclusive)
* @param size the size of the array.
* @return the length of the range.
- * @throws ArrayIndexOutOfBoundsException some or all of the range falls outside of the array.
+ * @throws IndexOutOfBoundsException some or all of the range falls outside of the array.
*/
static int checkRange(int startIndex, int endIndex, int size) {
final int length = endIndex - startIndex;
@@ -1143,7 +1234,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
return String.format("<ByteString@%s size=%d>",
Integer.toHexString(System.identityHashCode(this)), size());
}
-
+
/**
* This class implements a {@link com.google.protobuf.ByteString} backed by a
* single array of bytes, contiguous in memory. It supports substring by
@@ -1236,6 +1327,11 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
}
@Override
+ final void writeTo(ByteOutput output) throws IOException {
+ output.writeLazy(bytes, getOffsetIntoBytes(), size());
+ }
+
+ @Override
protected final String toStringInternal(Charset charset) {
return new String(bytes, getOffsetIntoBytes(), size(), charset);
}
@@ -1362,7 +1458,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
return 0;
}
}
-
+
/**
* This class is used to represent the substring of a {@link ByteString} over a
* single byte array. In terms of the public API of {@link ByteString}, you end
diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
index b3118ee0..1297462e 100644
--- a/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedInputStream.java
@@ -30,55 +30,119 @@
package com.google.protobuf;
+import static com.google.protobuf.Internal.EMPTY_BYTE_ARRAY;
+import static com.google.protobuf.Internal.EMPTY_BYTE_BUFFER;
+import static com.google.protobuf.Internal.UTF_8;
+import static com.google.protobuf.Internal.checkNotNull;
+import static com.google.protobuf.WireFormat.FIXED32_SIZE;
+import static com.google.protobuf.WireFormat.FIXED64_SIZE;
+import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
/**
* Reads and decodes protocol message fields.
*
- * This class contains two kinds of methods: methods that read specific
- * protocol message constructs and field types (e.g. {@link #readTag()} and
- * {@link #readInt32()}) and methods that read low-level values (e.g.
- * {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
- * encoded protocol messages, you should use the former methods, but if you are
- * reading some other format of your own design, use the latter.
+ * <p>This class contains two kinds of methods: methods that read specific protocol message
+ * constructs and field types (e.g. {@link #readTag()} and {@link #readInt32()}) and methods that
+ * read low-level values (e.g. {@link #readRawVarint32()} and {@link #readRawBytes}). If you are
+ * reading encoded protocol messages, you should use the former methods, but if you are reading some
+ * other format of your own design, use the latter.
*
* @author kenton@google.com Kenton Varda
*/
-public final class CodedInputStream {
+public abstract class CodedInputStream {
+ private static final int DEFAULT_BUFFER_SIZE = 4096;
+ private static final int DEFAULT_RECURSION_LIMIT = 100;
+ // Integer.MAX_VALUE == 0x7FFFFFF == INT_MAX from limits.h
+ private static final int DEFAULT_SIZE_LIMIT = Integer.MAX_VALUE;
+
/**
- * Create a new CodedInputStream wrapping the given InputStream.
+ * Whether to enable our custom UTF-8 decode codepath which does not use {@link StringCoding}.
+ * Currently disabled.
*/
+ private static final boolean ENABLE_CUSTOM_UTF8_DECODE = false;
+
+ /** Visible for subclasses. See setRecursionLimit() */
+ int recursionDepth;
+
+ int recursionLimit = DEFAULT_RECURSION_LIMIT;
+
+ /** Visible for subclasses. See setSizeLimit() */
+ int sizeLimit = DEFAULT_SIZE_LIMIT;
+
+ /** Create a new CodedInputStream wrapping the given InputStream. */
public static CodedInputStream newInstance(final InputStream input) {
- return new CodedInputStream(input);
+ return newInstance(input, DEFAULT_BUFFER_SIZE);
}
- /**
- * Create a new CodedInputStream wrapping the given byte array.
- */
+ /** Create a new CodedInputStream wrapping the given InputStream. */
+ static CodedInputStream newInstance(final InputStream input, int bufferSize) {
+ if (input == null) {
+ // TODO(nathanmittler): Ideally we should throw here. This is done for backward compatibility.
+ return newInstance(EMPTY_BYTE_ARRAY);
+ }
+ return new StreamDecoder(input, bufferSize);
+ }
+
+ /** Create a new CodedInputStream wrapping the given {@code Iterable <ByteBuffer>}. */
+ public static CodedInputStream newInstance(final Iterable<ByteBuffer> input) {
+ if (!UnsafeDirectNioDecoder.isSupported()) {
+ return newInstance(new IterableByteBufferInputStream(input));
+ }
+ return newInstance(input, false);
+ }
+
+ /** Create a new CodedInputStream wrapping the given {@code Iterable <ByteBuffer>}. */
+ static CodedInputStream newInstance(
+ final Iterable<ByteBuffer> bufs, final boolean bufferIsImmutable) {
+ // flag is to check the type of input's ByteBuffers.
+ // flag equals 1: all ByteBuffers have array.
+ // flag equals 2: all ByteBuffers are direct ByteBuffers.
+ // flag equals 3: some ByteBuffers are direct and some have array.
+ // flag greater than 3: other cases.
+ int flag = 0;
+ // Total size of the input
+ int totalSize = 0;
+ for (ByteBuffer buf : bufs) {
+ totalSize += buf.remaining();
+ if (buf.hasArray()) {
+ flag |= 1;
+ } else if (buf.isDirect()) {
+ flag |= 2;
+ } else {
+ flag |= 4;
+ }
+ }
+ if (flag == 2) {
+ return new IterableDirectByteBufferDecoder(bufs, totalSize, bufferIsImmutable);
+ } else {
+ // TODO(yilunchong): add another decoders to deal case 1 and 3.
+ return newInstance(new IterableByteBufferInputStream(bufs));
+ }
+ }
+
+ /** Create a new CodedInputStream wrapping the given byte array. */
public static CodedInputStream newInstance(final byte[] buf) {
return newInstance(buf, 0, buf.length);
}
- /**
- * Create a new CodedInputStream wrapping the given byte array slice.
- */
- public static CodedInputStream newInstance(final byte[] buf, final int off,
- final int len) {
- return newInstance(buf, off, len, false);
+ /** Create a new CodedInputStream wrapping the given byte array slice. */
+ public static CodedInputStream newInstance(final byte[] buf, final int off, final int len) {
+ return newInstance(buf, off, len, false /* bufferIsImmutable */);
}
- /**
- * Create a new CodedInputStream wrapping the given byte array slice.
- */
- public static CodedInputStream newInstance(final byte[] buf, final int off,
- final int len, boolean bufferIsImmutable) {
- CodedInputStream result = new CodedInputStream(buf, off, len, bufferIsImmutable);
+ /** Create a new CodedInputStream wrapping the given byte array slice. */
+ static CodedInputStream newInstance(
+ final byte[] buf, final int off, final int len, final boolean bufferIsImmutable) {
+ ArrayDecoder result = new ArrayDecoder(buf, off, len, bufferIsImmutable);
try {
// Some uses of CodedInputStream can be more efficient if they know
// exactly how many bytes are available. By pushing the end point of the
@@ -100,571 +164,415 @@ public final class CodedInputStream {
}
/**
- * Create a new CodedInputStream wrapping the given ByteBuffer. The data
- * starting from the ByteBuffer's current position to its limit will be read.
- * The returned CodedInputStream may or may not share the underlying data
- * in the ByteBuffer, therefore the ByteBuffer cannot be changed while the
- * CodedInputStream is in use.
- * Note that the ByteBuffer's position won't be changed by this function.
- * Concurrent calls with the same ByteBuffer object are safe if no other
- * thread is trying to alter the ByteBuffer's status.
+ * Create a new CodedInputStream wrapping the given ByteBuffer. The data starting from the
+ * ByteBuffer's current position to its limit will be read. The returned CodedInputStream may or
+ * may not share the underlying data in the ByteBuffer, therefore the ByteBuffer cannot be changed
+ * while the CodedInputStream is in use. Note that the ByteBuffer's position won't be changed by
+ * this function. Concurrent calls with the same ByteBuffer object are safe if no other thread is
+ * trying to alter the ByteBuffer's status.
*/
public static CodedInputStream newInstance(ByteBuffer buf) {
+ return newInstance(buf, false /* bufferIsImmutable */);
+ }
+
+ /** Create a new CodedInputStream wrapping the given buffer. */
+ static CodedInputStream newInstance(ByteBuffer buf, boolean bufferIsImmutable) {
if (buf.hasArray()) {
- return newInstance(buf.array(), buf.arrayOffset() + buf.position(),
- buf.remaining());
- } else {
- ByteBuffer temp = buf.duplicate();
- byte[] buffer = new byte[temp.remaining()];
- temp.get(buffer);
- return newInstance(buffer);
+ return newInstance(
+ buf.array(), buf.arrayOffset() + buf.position(), buf.remaining(), bufferIsImmutable);
+ }
+
+ if (buf.isDirect() && UnsafeDirectNioDecoder.isSupported()) {
+ return new UnsafeDirectNioDecoder(buf, bufferIsImmutable);
}
+
+ // The buffer is non-direct and does not expose the underlying array. Using the ByteBuffer API
+ // to access individual bytes is very slow, so just copy the buffer to an array.
+ // TODO(nathanmittler): Re-evaluate with Java 9
+ byte[] buffer = new byte[buf.remaining()];
+ buf.duplicate().get(buffer);
+ return newInstance(buffer, 0, buffer.length, true);
}
+ /** Disable construction/inheritance outside of this class. */
+ private CodedInputStream() {}
+
// -----------------------------------------------------------------
/**
- * Attempt to read a field tag, returning zero if we have reached EOF.
- * Protocol message parsers use this to read tags, since a protocol message
- * may legally end wherever a tag occurs, and zero is not a valid tag number.
+ * Attempt to read a field tag, returning zero if we have reached EOF. Protocol message parsers
+ * use this to read tags, since a protocol message may legally end wherever a tag occurs, and zero
+ * is not a valid tag number.
*/
- public int readTag() throws IOException {
- if (isAtEnd()) {
- lastTag = 0;
- return 0;
- }
-
- lastTag = readRawVarint32();
- if (WireFormat.getTagFieldNumber(lastTag) == 0) {
- // If we actually read zero (or any tag number corresponding to field
- // number zero), that's not a valid tag.
- throw InvalidProtocolBufferException.invalidTag();
- }
- return lastTag;
- }
+ public abstract int readTag() throws IOException;
/**
- * Verifies that the last call to readTag() returned the given tag value.
- * This is used to verify that a nested group ended with the correct
- * end tag.
+ * Verifies that the last call to readTag() returned the given tag value. This is used to verify
+ * that a nested group ended with the correct end tag.
*
- * @throws InvalidProtocolBufferException {@code value} does not match the
- * last tag.
+ * @throws InvalidProtocolBufferException {@code value} does not match the last tag.
*/
- public void checkLastTagWas(final int value)
- throws InvalidProtocolBufferException {
- if (lastTag != value) {
- throw InvalidProtocolBufferException.invalidEndTag();
- }
- }
+ public abstract void checkLastTagWas(final int value) throws InvalidProtocolBufferException;
- public int getLastTag() {
- return lastTag;
- }
+ public abstract int getLastTag();
/**
* Reads and discards a single field, given its tag value.
*
- * @return {@code false} if the tag is an endgroup tag, in which case
- * nothing is skipped. Otherwise, returns {@code true}.
+ * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped.
+ * Otherwise, returns {@code true}.
*/
- public boolean skipField(final int tag) throws IOException {
- switch (WireFormat.getTagWireType(tag)) {
- case WireFormat.WIRETYPE_VARINT:
- skipRawVarint();
- return true;
- case WireFormat.WIRETYPE_FIXED64:
- skipRawBytes(8);
- return true;
- case WireFormat.WIRETYPE_LENGTH_DELIMITED:
- skipRawBytes(readRawVarint32());
- return true;
- case WireFormat.WIRETYPE_START_GROUP:
- skipMessage();
- checkLastTagWas(
- WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
- WireFormat.WIRETYPE_END_GROUP));
- return true;
- case WireFormat.WIRETYPE_END_GROUP:
- return false;
- case WireFormat.WIRETYPE_FIXED32:
- skipRawBytes(4);
- return true;
- default:
- throw InvalidProtocolBufferException.invalidWireType();
- }
- }
+ public abstract boolean skipField(final int tag) throws IOException;
/**
- * Reads a single field and writes it to output in wire format,
- * given its tag value.
+ * Reads a single field and writes it to output in wire format, given its tag value.
*
- * @return {@code false} if the tag is an endgroup tag, in which case
- * nothing is skipped. Otherwise, returns {@code true}.
- */
- public boolean skipField(final int tag, final CodedOutputStream output)
- throws IOException {
- switch (WireFormat.getTagWireType(tag)) {
- case WireFormat.WIRETYPE_VARINT: {
- long value = readInt64();
- output.writeRawVarint32(tag);
- output.writeUInt64NoTag(value);
- return true;
- }
- case WireFormat.WIRETYPE_FIXED64: {
- long value = readRawLittleEndian64();
- output.writeRawVarint32(tag);
- output.writeFixed64NoTag(value);
- return true;
- }
- case WireFormat.WIRETYPE_LENGTH_DELIMITED: {
- ByteString value = readBytes();
- output.writeRawVarint32(tag);
- output.writeBytesNoTag(value);
- return true;
- }
- case WireFormat.WIRETYPE_START_GROUP: {
- output.writeRawVarint32(tag);
- skipMessage(output);
- int endtag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
- WireFormat.WIRETYPE_END_GROUP);
- checkLastTagWas(endtag);
- output.writeRawVarint32(endtag);
- return true;
- }
- case WireFormat.WIRETYPE_END_GROUP: {
- return false;
- }
- case WireFormat.WIRETYPE_FIXED32: {
- int value = readRawLittleEndian32();
- output.writeRawVarint32(tag);
- output.writeFixed32NoTag(value);
- return true;
- }
- default:
- throw InvalidProtocolBufferException.invalidWireType();
- }
- }
-
- /**
- * Reads and discards an entire message. This will read either until EOF
- * or until an endgroup tag, whichever comes first.
+ * @return {@code false} if the tag is an endgroup tag, in which case nothing is skipped.
+ * Otherwise, returns {@code true}.
+ * @deprecated use {@code UnknownFieldSet} or {@code UnknownFieldSetLite} to skip to an output
+ * stream.
*/
- public void skipMessage() throws IOException {
- while (true) {
- final int tag = readTag();
- if (tag == 0 || !skipField(tag)) {
- return;
- }
- }
- }
+ @Deprecated
+ public abstract boolean skipField(final int tag, final CodedOutputStream output)
+ throws IOException;
/**
- * Reads an entire message and writes it to output in wire format.
- * This will read either until EOF or until an endgroup tag,
+ * Reads and discards an entire message. This will read either until EOF or until an endgroup tag,
* whichever comes first.
*/
- public void skipMessage(CodedOutputStream output) throws IOException {
- while (true) {
- final int tag = readTag();
- if (tag == 0 || !skipField(tag, output)) {
- return;
- }
- }
- }
+ public abstract void skipMessage() throws IOException;
/**
- * Collects the bytes skipped and returns the data in a ByteBuffer.
+ * Reads an entire message and writes it to output in wire format. This will read either until EOF
+ * or until an endgroup tag, whichever comes first.
*/
- private class SkippedDataSink implements RefillCallback {
- private int lastPos = bufferPos;
- private ByteArrayOutputStream byteArrayStream;
-
- @Override
- public void onRefill() {
- if (byteArrayStream == null) {
- byteArrayStream = new ByteArrayOutputStream();
- }
- byteArrayStream.write(buffer, lastPos, bufferPos - lastPos);
- lastPos = 0;
- }
-
- /**
- * Gets skipped data in a ByteBuffer. This method should only be
- * called once.
- */
- ByteBuffer getSkippedData() {
- if (byteArrayStream == null) {
- return ByteBuffer.wrap(buffer, lastPos, bufferPos - lastPos);
- } else {
- byteArrayStream.write(buffer, lastPos, bufferPos);
- return ByteBuffer.wrap(byteArrayStream.toByteArray());
- }
- }
- }
+ public abstract void skipMessage(CodedOutputStream output) throws IOException;
// -----------------------------------------------------------------
/** Read a {@code double} field value from the stream. */
- public double readDouble() throws IOException {
- return Double.longBitsToDouble(readRawLittleEndian64());
- }
+ public abstract double readDouble() throws IOException;
/** Read a {@code float} field value from the stream. */
- public float readFloat() throws IOException {
- return Float.intBitsToFloat(readRawLittleEndian32());
- }
+ public abstract float readFloat() throws IOException;
/** Read a {@code uint64} field value from the stream. */
- public long readUInt64() throws IOException {
- return readRawVarint64();
- }
+ public abstract long readUInt64() throws IOException;
/** Read an {@code int64} field value from the stream. */
- public long readInt64() throws IOException {
- return readRawVarint64();
- }
+ public abstract long readInt64() throws IOException;
/** Read an {@code int32} field value from the stream. */
- public int readInt32() throws IOException {
- return readRawVarint32();
- }
+ public abstract int readInt32() throws IOException;
/** Read a {@code fixed64} field value from the stream. */
- public long readFixed64() throws IOException {
- return readRawLittleEndian64();
- }
+ public abstract long readFixed64() throws IOException;
/** Read a {@code fixed32} field value from the stream. */
- public int readFixed32() throws IOException {
- return readRawLittleEndian32();
- }
+ public abstract int readFixed32() throws IOException;
/** Read a {@code bool} field value from the stream. */
- public boolean readBool() throws IOException {
- return readRawVarint64() != 0;
- }
+ public abstract boolean readBool() throws IOException;
/**
- * Read a {@code string} field value from the stream.
- * If the stream contains malformed UTF-8,
+ * Read a {@code string} field value from the stream. If the stream contains malformed UTF-8,
* replace the offending bytes with the standard UTF-8 replacement character.
*/
- public String readString() throws IOException {
- final int size = readRawVarint32();
- if (size <= (bufferSize - bufferPos) && size > 0) {
- // Fast path: We already have the bytes in a contiguous buffer, so
- // just copy directly from it.
- final String result = new String(buffer, bufferPos, size, Internal.UTF_8);
- bufferPos += size;
- return result;
- } else if (size == 0) {
- return "";
- } else {
- // Slow path: Build a byte array first then copy it.
- return new String(readRawBytesSlowPath(size), Internal.UTF_8);
- }
- }
+ public abstract String readString() throws IOException;
/**
- * Read a {@code string} field value from the stream.
- * If the stream contains malformed UTF-8,
+ * Read a {@code string} field value from the stream. If the stream contains malformed UTF-8,
* throw exception {@link InvalidProtocolBufferException}.
*/
- public String readStringRequireUtf8() throws IOException {
- final int size = readRawVarint32();
- final byte[] bytes;
- int pos = bufferPos;
- if (size <= (bufferSize - pos) && size > 0) {
- // Fast path: We already have the bytes in a contiguous buffer, so
- // just copy directly from it.
- bytes = buffer;
- bufferPos = pos + size;
- } else if (size == 0) {
- return "";
- } else {
- // Slow path: Build a byte array first then copy it.
- bytes = readRawBytesSlowPath(size);
- pos = 0;
- }
- // TODO(martinrb): We could save a pass by validating while decoding.
- if (!Utf8.isValidUtf8(bytes, pos, pos + size)) {
- throw InvalidProtocolBufferException.invalidUtf8();
- }
- return new String(bytes, pos, size, Internal.UTF_8);
- }
+ public abstract String readStringRequireUtf8() throws IOException;
/** Read a {@code group} field value from the stream. */
- public void readGroup(final int fieldNumber,
- final MessageLite.Builder builder,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
- if (recursionDepth >= recursionLimit) {
- throw InvalidProtocolBufferException.recursionLimitExceeded();
- }
- ++recursionDepth;
- builder.mergeFrom(this, extensionRegistry);
- checkLastTagWas(
- WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
- --recursionDepth;
- }
+ public abstract void readGroup(
+ final int fieldNumber,
+ final MessageLite.Builder builder,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException;
/** Read a {@code group} field value from the stream. */
- public <T extends MessageLite> T readGroup(
- final int fieldNumber,
- final Parser<T> parser,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
- if (recursionDepth >= recursionLimit) {
- throw InvalidProtocolBufferException.recursionLimitExceeded();
- }
- ++recursionDepth;
- T result = parser.parsePartialFrom(this, extensionRegistry);
- checkLastTagWas(
- WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
- --recursionDepth;
- return result;
- }
+ public abstract <T extends MessageLite> T readGroup(
+ final int fieldNumber, final Parser<T> parser, final ExtensionRegistryLite extensionRegistry)
+ throws IOException;
/**
- * Reads a {@code group} field value from the stream and merges it into the
- * given {@link UnknownFieldSet}.
+ * Reads a {@code group} field value from the stream and merges it into the given {@link
+ * UnknownFieldSet}.
*
- * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
- * you can just call {@link #readGroup}.
+ * @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so you can just call
+ * {@link #readGroup}.
*/
@Deprecated
- public void readUnknownGroup(final int fieldNumber,
- final MessageLite.Builder builder)
- throws IOException {
- // We know that UnknownFieldSet will ignore any ExtensionRegistry so it
- // is safe to pass null here. (We can't call
- // ExtensionRegistry.getEmptyRegistry() because that would make this
- // class depend on ExtensionRegistry, which is not part of the lite
- // library.)
- readGroup(fieldNumber, builder, null);
- }
+ public abstract void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder)
+ throws IOException;
/** Read an embedded message field value from the stream. */
- public void readMessage(final MessageLite.Builder builder,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
- final int length = readRawVarint32();
- if (recursionDepth >= recursionLimit) {
- throw InvalidProtocolBufferException.recursionLimitExceeded();
- }
- final int oldLimit = pushLimit(length);
- ++recursionDepth;
- builder.mergeFrom(this, extensionRegistry);
- checkLastTagWas(0);
- --recursionDepth;
- popLimit(oldLimit);
- }
+ public abstract void readMessage(
+ final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry)
+ throws IOException;
/** Read an embedded message field value from the stream. */
- public <T extends MessageLite> T readMessage(
- final Parser<T> parser,
- final ExtensionRegistryLite extensionRegistry)
- throws IOException {
- int length = readRawVarint32();
- if (recursionDepth >= recursionLimit) {
- throw InvalidProtocolBufferException.recursionLimitExceeded();
- }
- final int oldLimit = pushLimit(length);
- ++recursionDepth;
- T result = parser.parsePartialFrom(this, extensionRegistry);
- checkLastTagWas(0);
- --recursionDepth;
- popLimit(oldLimit);
- return result;
- }
+ public abstract <T extends MessageLite> T readMessage(
+ final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException;
/** Read a {@code bytes} field value from the stream. */
- public ByteString readBytes() throws IOException {
- final int size = readRawVarint32();
- if (size <= (bufferSize - bufferPos) && size > 0) {
- // Fast path: We already have the bytes in a contiguous buffer, so
- // just copy directly from it.
- final ByteString result = bufferIsImmutable && enableAliasing
- ? ByteString.wrap(buffer, bufferPos, size)
- : ByteString.copyFrom(buffer, bufferPos, size);
- bufferPos += size;
- return result;
- } else if (size == 0) {
- return ByteString.EMPTY;
- } else {
- // Slow path: Build a byte array first then copy it.
- return ByteString.wrap(readRawBytesSlowPath(size));
- }
- }
+ public abstract ByteString readBytes() throws IOException;
/** Read a {@code bytes} field value from the stream. */
- public byte[] readByteArray() throws IOException {
- final int size = readRawVarint32();
- if (size <= (bufferSize - bufferPos) && size > 0) {
- // Fast path: We already have the bytes in a contiguous buffer, so
- // just copy directly from it.
- final byte[] result =
- Arrays.copyOfRange(buffer, bufferPos, bufferPos + size);
- bufferPos += size;
- return result;
- } else {
- // Slow path: Build a byte array first then copy it.
- return readRawBytesSlowPath(size);
- }
- }
+ public abstract byte[] readByteArray() throws IOException;
/** Read a {@code bytes} field value from the stream. */
- public ByteBuffer readByteBuffer() throws IOException {
- final int size = readRawVarint32();
- if (size <= (bufferSize - bufferPos) && size > 0) {
- // Fast path: We already have the bytes in a contiguous buffer.
- // When aliasing is enabled, we can return a ByteBuffer pointing directly
- // into the underlying byte array without copy if the CodedInputStream is
- // constructed from a byte array. If aliasing is disabled or the input is
- // from an InputStream or ByteString, we have to make a copy of the bytes.
- ByteBuffer result = input == null && !bufferIsImmutable && enableAliasing
- ? ByteBuffer.wrap(buffer, bufferPos, size).slice()
- : ByteBuffer.wrap(Arrays.copyOfRange(
- buffer, bufferPos, bufferPos + size));
- bufferPos += size;
- return result;
- } else if (size == 0) {
- return Internal.EMPTY_BYTE_BUFFER;
- } else {
- // Slow path: Build a byte array first then copy it.
- return ByteBuffer.wrap(readRawBytesSlowPath(size));
- }
- }
+ public abstract ByteBuffer readByteBuffer() throws IOException;
/** Read a {@code uint32} field value from the stream. */
- public int readUInt32() throws IOException {
- return readRawVarint32();
- }
+ public abstract int readUInt32() throws IOException;
/**
- * Read an enum field value from the stream. Caller is responsible
- * for converting the numeric value to an actual enum.
+ * Read an enum field value from the stream. Caller is responsible for converting the numeric
+ * value to an actual enum.
*/
- public int readEnum() throws IOException {
- return readRawVarint32();
- }
+ public abstract int readEnum() throws IOException;
/** Read an {@code sfixed32} field value from the stream. */
- public int readSFixed32() throws IOException {
- return readRawLittleEndian32();
- }
+ public abstract int readSFixed32() throws IOException;
/** Read an {@code sfixed64} field value from the stream. */
- public long readSFixed64() throws IOException {
- return readRawLittleEndian64();
- }
+ public abstract long readSFixed64() throws IOException;
/** Read an {@code sint32} field value from the stream. */
- public int readSInt32() throws IOException {
- return decodeZigZag32(readRawVarint32());
- }
+ public abstract int readSInt32() throws IOException;
/** Read an {@code sint64} field value from the stream. */
- public long readSInt64() throws IOException {
- return decodeZigZag64(readRawVarint64());
- }
+ public abstract long readSInt64() throws IOException;
// =================================================================
+ /** Read a raw Varint from the stream. If larger than 32 bits, discard the upper bits. */
+ public abstract int readRawVarint32() throws IOException;
+
+ /** Read a raw Varint from the stream. */
+ public abstract long readRawVarint64() throws IOException;
+
+ /** Variant of readRawVarint64 for when uncomfortably close to the limit. */
+ /* Visible for testing */
+ abstract long readRawVarint64SlowPath() throws IOException;
+
+ /** Read a 32-bit little-endian integer from the stream. */
+ public abstract int readRawLittleEndian32() throws IOException;
+
+ /** Read a 64-bit little-endian integer from the stream. */
+ public abstract long readRawLittleEndian64() throws IOException;
+
+ // -----------------------------------------------------------------
+
/**
- * Read a raw Varint from the stream. If larger than 32 bits, discard the
- * upper bits.
+ * Enables {@link ByteString} aliasing of the underlying buffer, trading off on buffer pinning for
+ * data copies. Only valid for buffer-backed streams.
*/
- public int readRawVarint32() throws IOException {
- // See implementation notes for readRawVarint64
- fastpath: {
- int pos = bufferPos;
-
- if (bufferSize == pos) {
- break fastpath;
- }
+ public abstract void enableAliasing(boolean enabled);
- final byte[] buffer = this.buffer;
- int x;
- if ((x = buffer[pos++]) >= 0) {
- bufferPos = pos;
- return x;
- } else if (bufferSize - pos < 9) {
- break fastpath;
- } else if ((x ^= (buffer[pos++] << 7)) < 0) {
- x ^= (~0 << 7);
- } else if ((x ^= (buffer[pos++] << 14)) >= 0) {
- x ^= (~0 << 7) ^ (~0 << 14);
- } else if ((x ^= (buffer[pos++] << 21)) < 0) {
- x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
- } else {
- int y = buffer[pos++];
- x ^= y << 28;
- x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
- if (y < 0 &&
- buffer[pos++] < 0 &&
- buffer[pos++] < 0 &&
- buffer[pos++] < 0 &&
- buffer[pos++] < 0 &&
- buffer[pos++] < 0) {
- break fastpath; // Will throw malformedVarint()
- }
- }
- bufferPos = pos;
- return x;
+ /**
+ * Set the maximum message recursion depth. In order to prevent malicious messages from causing
+ * stack overflows, {@code CodedInputStream} limits how deeply messages may be nested. The default
+ * limit is 64.
+ *
+ * @return the old limit.
+ */
+ public final int setRecursionLimit(final int limit) {
+ if (limit < 0) {
+ throw new IllegalArgumentException("Recursion limit cannot be negative: " + limit);
}
- return (int) readRawVarint64SlowPath();
+ final int oldLimit = recursionLimit;
+ recursionLimit = limit;
+ return oldLimit;
}
- private void skipRawVarint() throws IOException {
- if (bufferSize - bufferPos >= 10) {
- final byte[] buffer = this.buffer;
- int pos = bufferPos;
- for (int i = 0; i < 10; i++) {
- if (buffer[pos++] >= 0) {
- bufferPos = pos;
- return;
- }
- }
+ /**
+ * Only valid for {@link InputStream}-backed streams.
+ *
+ * <p>Set the maximum message size. In order to prevent malicious messages from exhausting memory
+ * or causing integer overflows, {@code CodedInputStream} limits how large a message may be. The
+ * default limit is {@code Integer.MAX_INT}. You should set this limit as small as you can without
+ * harming your app's functionality. Note that size limits only apply when reading from an {@code
+ * InputStream}, not when constructed around a raw byte array.
+ *
+ * <p>If you want to read several messages from a single CodedInputStream, you could call {@link
+ * #resetSizeCounter()} after each one to avoid hitting the size limit.
+ *
+ * @return the old limit.
+ */
+ public final int setSizeLimit(final int limit) {
+ if (limit < 0) {
+ throw new IllegalArgumentException("Size limit cannot be negative: " + limit);
}
- skipRawVarintSlowPath();
+ final int oldLimit = sizeLimit;
+ sizeLimit = limit;
+ return oldLimit;
}
- private void skipRawVarintSlowPath() throws IOException {
- for (int i = 0; i < 10; i++) {
- if (readRawByte() >= 0) {
- return;
- }
- }
- throw InvalidProtocolBufferException.malformedVarint();
+
+ private boolean explicitDiscardUnknownFields = false;
+
+ private static volatile boolean proto3DiscardUnknownFieldsDefault = false;
+
+ static void setProto3DiscardUnknownsByDefaultForTest() {
+ proto3DiscardUnknownFieldsDefault = true;
+ }
+
+ static void setProto3KeepUnknownsByDefaultForTest() {
+ proto3DiscardUnknownFieldsDefault = false;
+ }
+
+ static boolean getProto3DiscardUnknownFieldsDefault() {
+ return proto3DiscardUnknownFieldsDefault;
}
/**
- * Reads a varint from the input one byte at a time, so that it does not
- * read any bytes after the end of the varint. If you simply wrapped the
- * stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
- * then you would probably end up reading past the end of the varint since
- * CodedInputStream buffers its input.
+ * Sets this {@code CodedInputStream} to discard unknown fields. Only applies to full runtime
+ * messages; lite messages will always preserve unknowns.
+ *
+ * <p>Note calling this function alone will have NO immediate effect on the underlying input data.
+ * The unknown fields will be discarded during parsing. This affects both Proto2 and Proto3 full
+ * runtime.
*/
- static int readRawVarint32(final InputStream input) throws IOException {
- final int firstByte = input.read();
- if (firstByte == -1) {
- throw InvalidProtocolBufferException.truncatedMessage();
- }
- return readRawVarint32(firstByte, input);
+ final void discardUnknownFields() {
+ explicitDiscardUnknownFields = true;
+ }
+
+ /**
+ * Reverts the unknown fields preservation behavior for Proto2 and Proto3 full runtime to their
+ * default.
+ */
+ final void unsetDiscardUnknownFields() {
+ explicitDiscardUnknownFields = false;
+ }
+
+ /**
+ * Whether unknown fields in this input stream should be discarded during parsing into full
+ * runtime messages.
+ */
+ final boolean shouldDiscardUnknownFields() {
+ return explicitDiscardUnknownFields;
}
/**
- * Like {@link #readRawVarint32(InputStream)}, but expects that the caller
- * has already read one byte. This allows the caller to determine if EOF
- * has been reached before attempting to read.
+ * Whether unknown fields in this input stream should be discarded during parsing for proto3 full
+ * runtime messages.
+ *
+ * <p>This function was temporarily introduced before proto3 unknown fields behavior is changed.
+ * TODO(liujisi): remove this and related code in GeneratedMessage after proto3 unknown
+ * fields migration is done.
+ */
+ final boolean shouldDiscardUnknownFieldsProto3() {
+ return explicitDiscardUnknownFields ? true : proto3DiscardUnknownFieldsDefault;
+ }
+
+ /**
+ * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). Only valid for {@link
+ * InputStream}-backed streams.
+ */
+ public abstract void resetSizeCounter();
+
+ /**
+ * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This is called when
+ * descending into a length-delimited embedded message.
+ *
+ * <p>Note that {@code pushLimit()} does NOT affect how many bytes the {@code CodedInputStream}
+ * reads from an underlying {@code InputStream} when refreshing its buffer. If you need to prevent
+ * reading past a certain point in the underlying {@code InputStream} (e.g. because you expect it
+ * to contain more data after the end of the message which you need to handle differently) then
+ * you must place a wrapper around your {@code InputStream} which limits the amount of data that
+ * can be read from it.
+ *
+ * @return the old limit.
*/
- public static int readRawVarint32(
- final int firstByte, final InputStream input) throws IOException {
+ public abstract int pushLimit(int byteLimit) throws InvalidProtocolBufferException;
+
+ /**
+ * Discards the current limit, returning to the previous limit.
+ *
+ * @param oldLimit The old limit, as returned by {@code pushLimit}.
+ */
+ public abstract void popLimit(final int oldLimit);
+
+ /**
+ * Returns the number of bytes to be read before the current limit. If no limit is set, returns
+ * -1.
+ */
+ public abstract int getBytesUntilLimit();
+
+ /**
+ * Returns true if the stream has reached the end of the input. This is the case if either the end
+ * of the underlying input source has been reached or if the stream has reached a limit created
+ * using {@link #pushLimit(int)}.
+ */
+ public abstract boolean isAtEnd() throws IOException;
+
+ /**
+ * The total bytes read up to the current position. Calling {@link #resetSizeCounter()} resets
+ * this value to zero.
+ */
+ public abstract int getTotalBytesRead();
+
+ /**
+ * Read one byte from the input.
+ *
+ * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached.
+ */
+ public abstract byte readRawByte() throws IOException;
+
+ /**
+ * Read a fixed size of bytes from the input.
+ *
+ * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached.
+ */
+ public abstract byte[] readRawBytes(final int size) throws IOException;
+
+ /**
+ * Reads and discards {@code size} bytes.
+ *
+ * @throws InvalidProtocolBufferException The end of the stream or the current limit was reached.
+ */
+ public abstract void skipRawBytes(final int size) throws IOException;
+
+ /**
+ * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n An unsigned 32-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ * @return A signed 32-bit integer.
+ */
+ public static int decodeZigZag32(final int n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers into values that can be
+ * efficiently encoded with varint. (Otherwise, negative values must be sign-extended to 64 bits
+ * to be varint encoded, thus always taking 10 bytes on the wire.)
+ *
+ * @param n An unsigned 64-bit integer, stored in a signed int because Java has no explicit
+ * unsigned support.
+ * @return A signed 64-bit integer.
+ */
+ public static long decodeZigZag64(final long n) {
+ return (n >>> 1) ^ -(n & 1);
+ }
+
+ /**
+ * Like {@link #readRawVarint32(InputStream)}, but expects that the caller has already read one
+ * byte. This allows the caller to determine if EOF has been reached before attempting to read.
+ */
+ public static int readRawVarint32(final int firstByte, final InputStream input)
+ throws IOException {
if ((firstByte & 0x80) == 0) {
return firstByte;
}
@@ -694,589 +602,3341 @@ public final class CodedInputStream {
throw InvalidProtocolBufferException.malformedVarint();
}
- /** Read a raw Varint from the stream. */
- public long readRawVarint64() throws IOException {
- // Implementation notes:
- //
- // Optimized for one-byte values, expected to be common.
- // The particular code below was selected from various candidates
- // empirically, by winning VarintBenchmark.
- //
- // Sign extension of (signed) Java bytes is usually a nuisance, but
- // we exploit it here to more easily obtain the sign of bytes read.
- // Instead of cleaning up the sign extension bits by masking eagerly,
- // we delay until we find the final (positive) byte, when we clear all
- // accumulated bits with one xor. We depend on javac to constant fold.
- fastpath: {
- int pos = bufferPos;
-
- if (bufferSize == pos) {
- break fastpath;
+ /**
+ * Reads a varint from the input one byte at a time, so that it does not read any bytes after the
+ * end of the varint. If you simply wrapped the stream in a CodedInputStream and used {@link
+ * #readRawVarint32(InputStream)} then you would probably end up reading past the end of the
+ * varint since CodedInputStream buffers its input.
+ */
+ static int readRawVarint32(final InputStream input) throws IOException {
+ final int firstByte = input.read();
+ if (firstByte == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ return readRawVarint32(firstByte, input);
+ }
+
+ /** A {@link CodedInputStream} implementation that uses a backing array as the input. */
+ private static final class ArrayDecoder extends CodedInputStream {
+ private final byte[] buffer;
+ private final boolean immutable;
+ private int limit;
+ private int bufferSizeAfterLimit;
+ private int pos;
+ private int startPos;
+ private int lastTag;
+ private boolean enableAliasing;
+
+ /** The absolute position of the end of the current message. */
+ private int currentLimit = Integer.MAX_VALUE;
+
+ private ArrayDecoder(final byte[] buffer, final int offset, final int len, boolean immutable) {
+ this.buffer = buffer;
+ limit = offset + len;
+ pos = offset;
+ startPos = pos;
+ this.immutable = immutable;
+ }
+
+ @Override
+ public int readTag() throws IOException {
+ if (isAtEnd()) {
+ lastTag = 0;
+ return 0;
}
- final byte[] buffer = this.buffer;
- long x;
- int y;
- if ((y = buffer[pos++]) >= 0) {
- bufferPos = pos;
- return y;
- } else if (bufferSize - pos < 9) {
- break fastpath;
- } else if ((y ^= (buffer[pos++] << 7)) < 0) {
- x = y ^ (~0 << 7);
- } else if ((y ^= (buffer[pos++] << 14)) >= 0) {
- x = y ^ ((~0 << 7) ^ (~0 << 14));
- } else if ((y ^= (buffer[pos++] << 21)) < 0) {
- x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
- } else if ((x = ((long) y) ^ ((long) buffer[pos++] << 28)) >= 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
- } else if ((x ^= ((long) buffer[pos++] << 35)) < 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
- } else if ((x ^= ((long) buffer[pos++] << 42)) >= 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
- } else if ((x ^= ((long) buffer[pos++] << 49)) < 0L) {
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42)
- ^ (~0L << 49);
- } else {
- x ^= ((long) buffer[pos++] << 56);
- x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42)
- ^ (~0L << 49) ^ (~0L << 56);
- if (x < 0L) {
- if (buffer[pos++] < 0L) {
- break fastpath; // Will throw malformedVarint()
+ lastTag = readRawVarint32();
+ if (WireFormat.getTagFieldNumber(lastTag) == 0) {
+ // If we actually read zero (or any tag number corresponding to field
+ // number zero), that's not a valid tag.
+ throw InvalidProtocolBufferException.invalidTag();
+ }
+ return lastTag;
+ }
+
+ @Override
+ public void checkLastTagWas(final int value) throws InvalidProtocolBufferException {
+ if (lastTag != value) {
+ throw InvalidProtocolBufferException.invalidEndTag();
+ }
+ }
+
+ @Override
+ public int getLastTag() {
+ return lastTag;
+ }
+
+ @Override
+ public boolean skipField(final int tag) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ skipRawVarint();
+ return true;
+ case WireFormat.WIRETYPE_FIXED64:
+ skipRawBytes(FIXED64_SIZE);
+ return true;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ skipRawBytes(readRawVarint32());
+ return true;
+ case WireFormat.WIRETYPE_START_GROUP:
+ skipMessage();
+ checkLastTagWas(
+ WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP));
+ return true;
+ case WireFormat.WIRETYPE_END_GROUP:
+ return false;
+ case WireFormat.WIRETYPE_FIXED32:
+ skipRawBytes(FIXED32_SIZE);
+ return true;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public boolean skipField(final int tag, final CodedOutputStream output) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ {
+ long value = readInt64();
+ output.writeRawVarint32(tag);
+ output.writeUInt64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_FIXED64:
+ {
+ long value = readRawLittleEndian64();
+ output.writeRawVarint32(tag);
+ output.writeFixed64NoTag(value);
+ return true;
}
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ {
+ ByteString value = readBytes();
+ output.writeRawVarint32(tag);
+ output.writeBytesNoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_START_GROUP:
+ {
+ output.writeRawVarint32(tag);
+ skipMessage(output);
+ int endtag =
+ WireFormat.makeTag(
+ WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+ checkLastTagWas(endtag);
+ output.writeRawVarint32(endtag);
+ return true;
+ }
+ case WireFormat.WIRETYPE_END_GROUP:
+ {
+ return false;
+ }
+ case WireFormat.WIRETYPE_FIXED32:
+ {
+ int value = readRawLittleEndian32();
+ output.writeRawVarint32(tag);
+ output.writeFixed32NoTag(value);
+ return true;
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public void skipMessage() throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag)) {
+ return;
}
}
- bufferPos = pos;
- return x;
}
- return readRawVarint64SlowPath();
- }
- /** Variant of readRawVarint64 for when uncomfortably close to the limit. */
- /* Visible for testing */
- long readRawVarint64SlowPath() throws IOException {
- long result = 0;
- for (int shift = 0; shift < 64; shift += 7) {
- final byte b = readRawByte();
- result |= (long) (b & 0x7F) << shift;
- if ((b & 0x80) == 0) {
+ @Override
+ public void skipMessage(CodedOutputStream output) throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag, output)) {
+ return;
+ }
+ }
+ }
+
+
+ // -----------------------------------------------------------------
+
+ @Override
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readRawLittleEndian64());
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readRawLittleEndian32());
+ }
+
+ @Override
+ public long readUInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public long readInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public int readInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public long readFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ return readRawVarint64() != 0;
+ }
+
+ @Override
+ public String readString() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= (limit - pos)) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ final String result = new String(buffer, pos, size, UTF_8);
+ pos += size;
return result;
}
+
+ if (size == 0) {
+ return "";
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
}
- throw InvalidProtocolBufferException.malformedVarint();
- }
- /** Read a 32-bit little-endian integer from the stream. */
- public int readRawLittleEndian32() throws IOException {
- int pos = bufferPos;
+ @Override
+ public String readStringRequireUtf8() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= (limit - pos)) {
+ if (ENABLE_CUSTOM_UTF8_DECODE) {
+ String result = Utf8.decodeUtf8(buffer, pos, size);
+ pos += size;
+ return result;
+ } else {
+ // TODO(martinrb): We could save a pass by validating while decoding.
+ if (!Utf8.isValidUtf8(buffer, pos, pos + size)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ final int tempPos = pos;
+ pos += size;
+ return new String(buffer, tempPos, size, UTF_8);
+ }
+ }
- // hand-inlined ensureAvailable(4);
- if (bufferSize - pos < 4) {
- refillBuffer(4);
- pos = bufferPos;
+ if (size == 0) {
+ return "";
+ }
+ if (size <= 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
}
- final byte[] buffer = this.buffer;
- bufferPos = pos + 4;
- return (((buffer[pos] & 0xff)) |
- ((buffer[pos + 1] & 0xff) << 8) |
- ((buffer[pos + 2] & 0xff) << 16) |
- ((buffer[pos + 3] & 0xff) << 24));
- }
+ @Override
+ public void readGroup(
+ final int fieldNumber,
+ final MessageLite.Builder builder,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ }
- /** Read a 64-bit little-endian integer from the stream. */
- public long readRawLittleEndian64() throws IOException {
- int pos = bufferPos;
-
- // hand-inlined ensureAvailable(8);
- if (bufferSize - pos < 8) {
- refillBuffer(8);
- pos = bufferPos;
- }
-
- final byte[] buffer = this.buffer;
- bufferPos = pos + 8;
- return ((((long) buffer[pos] & 0xffL)) |
- (((long) buffer[pos + 1] & 0xffL) << 8) |
- (((long) buffer[pos + 2] & 0xffL) << 16) |
- (((long) buffer[pos + 3] & 0xffL) << 24) |
- (((long) buffer[pos + 4] & 0xffL) << 32) |
- (((long) buffer[pos + 5] & 0xffL) << 40) |
- (((long) buffer[pos + 6] & 0xffL) << 48) |
- (((long) buffer[pos + 7] & 0xffL) << 56));
- }
- /**
- * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
- * into values that can be efficiently encoded with varint. (Otherwise,
- * negative values must be sign-extended to 64 bits to be varint encoded,
- * thus always taking 10 bytes on the wire.)
- *
- * @param n An unsigned 32-bit integer, stored in a signed int because
- * Java has no explicit unsigned support.
- * @return A signed 32-bit integer.
- */
- public static int decodeZigZag32(final int n) {
- return (n >>> 1) ^ -(n & 1);
- }
+ @Override
+ public <T extends MessageLite> T readGroup(
+ final int fieldNumber,
+ final Parser<T> parser,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ return result;
+ }
- /**
- * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
- * into values that can be efficiently encoded with varint. (Otherwise,
- * negative values must be sign-extended to 64 bits to be varint encoded,
- * thus always taking 10 bytes on the wire.)
- *
- * @param n An unsigned 64-bit integer, stored in a signed int because
- * Java has no explicit unsigned support.
- * @return A signed 64-bit integer.
- */
- public static long decodeZigZag64(final long n) {
- return (n >>> 1) ^ -(n & 1);
- }
+ @Deprecated
+ @Override
+ public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder)
+ throws IOException {
+ readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry());
+ }
- // -----------------------------------------------------------------
+ @Override
+ public void readMessage(
+ final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ final int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ }
- private final byte[] buffer;
- private final boolean bufferIsImmutable;
- private int bufferSize;
- private int bufferSizeAfterLimit;
- private int bufferPos;
- private final InputStream input;
- private int lastTag;
- private boolean enableAliasing = false;
- /**
- * The total number of bytes read before the current buffer. The total
- * bytes read up to the current position can be computed as
- * {@code totalBytesRetired + bufferPos}. This value may be negative if
- * reading started in the middle of the current buffer (e.g. if the
- * constructor that takes a byte array and an offset was used).
- */
- private int totalBytesRetired;
+ @Override
+ public <T extends MessageLite> T readMessage(
+ final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+ int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ return result;
+ }
- /** The absolute position of the end of the current message. */
- private int currentLimit = Integer.MAX_VALUE;
+ @Override
+ public ByteString readBytes() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= (limit - pos)) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ final ByteString result =
+ immutable && enableAliasing
+ ? ByteString.wrap(buffer, pos, size)
+ : ByteString.copyFrom(buffer, pos, size);
+ pos += size;
+ return result;
+ }
+ if (size == 0) {
+ return ByteString.EMPTY;
+ }
+ // Slow path: Build a byte array first then copy it.
+ return ByteString.wrap(readRawBytes(size));
+ }
- /** See setRecursionLimit() */
- private int recursionDepth;
- private int recursionLimit = DEFAULT_RECURSION_LIMIT;
+ @Override
+ public byte[] readByteArray() throws IOException {
+ final int size = readRawVarint32();
+ return readRawBytes(size);
+ }
- /** See setSizeLimit() */
- private int sizeLimit = DEFAULT_SIZE_LIMIT;
+ @Override
+ public ByteBuffer readByteBuffer() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= (limit - pos)) {
+ // Fast path: We already have the bytes in a contiguous buffer.
+ // When aliasing is enabled, we can return a ByteBuffer pointing directly
+ // into the underlying byte array without copy if the CodedInputStream is
+ // constructed from a byte array. If aliasing is disabled or the input is
+ // from an InputStream or ByteString, we have to make a copy of the bytes.
+ ByteBuffer result =
+ !immutable && enableAliasing
+ ? ByteBuffer.wrap(buffer, pos, size).slice()
+ : ByteBuffer.wrap(Arrays.copyOfRange(buffer, pos, pos + size));
+ pos += size;
+ // TODO(nathanmittler): Investigate making the ByteBuffer be made read-only
+ return result;
+ }
- private static final int DEFAULT_RECURSION_LIMIT = 100;
- private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
- private static final int BUFFER_SIZE = 4096;
-
- private CodedInputStream(final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) {
- this.buffer = buffer;
- bufferSize = off + len;
- bufferPos = off;
- totalBytesRetired = -off;
- input = null;
- this.bufferIsImmutable = bufferIsImmutable;
- }
+ if (size == 0) {
+ return EMPTY_BYTE_BUFFER;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
- private CodedInputStream(final InputStream input) {
- buffer = new byte[BUFFER_SIZE];
- bufferSize = 0;
- bufferPos = 0;
- totalBytesRetired = 0;
- this.input = input;
- bufferIsImmutable = false;
- }
+ @Override
+ public int readUInt32() throws IOException {
+ return readRawVarint32();
+ }
- public void enableAliasing(boolean enabled) {
- this.enableAliasing = enabled;
- }
+ @Override
+ public int readEnum() throws IOException {
+ return readRawVarint32();
+ }
- /**
- * Set the maximum message recursion depth. In order to prevent malicious
- * messages from causing stack overflows, {@code CodedInputStream} limits
- * how deeply messages may be nested. The default limit is 64.
- *
- * @return the old limit.
- */
- public int setRecursionLimit(final int limit) {
- if (limit < 0) {
- throw new IllegalArgumentException(
- "Recursion limit cannot be negative: " + limit);
+ @Override
+ public int readSFixed32() throws IOException {
+ return readRawLittleEndian32();
}
- final int oldLimit = recursionLimit;
- recursionLimit = limit;
- return oldLimit;
- }
- /**
- * Set the maximum message size. In order to prevent malicious
- * messages from exhausting memory or causing integer overflows,
- * {@code CodedInputStream} limits how large a message may be.
- * The default limit is 64MB. You should set this limit as small
- * as you can without harming your app's functionality. Note that
- * size limits only apply when reading from an {@code InputStream}, not
- * when constructed around a raw byte array (nor with
- * {@link ByteString#newCodedInput}).
- * <p>
- * If you want to read several messages from a single CodedInputStream, you
- * could call {@link #resetSizeCounter()} after each one to avoid hitting the
- * size limit.
- *
- * @return the old limit.
- */
- public int setSizeLimit(final int limit) {
- if (limit < 0) {
- throw new IllegalArgumentException(
- "Size limit cannot be negative: " + limit);
+ @Override
+ public long readSFixed64() throws IOException {
+ return readRawLittleEndian64();
}
- final int oldLimit = sizeLimit;
- sizeLimit = limit;
- return oldLimit;
- }
- /**
- * Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
- */
- public void resetSizeCounter() {
- totalBytesRetired = -bufferPos;
+ @Override
+ public int readSInt32() throws IOException {
+ return decodeZigZag32(readRawVarint32());
+ }
+
+ @Override
+ public long readSInt64() throws IOException {
+ return decodeZigZag64(readRawVarint64());
+ }
+
+ // =================================================================
+
+ @Override
+ public int readRawVarint32() throws IOException {
+ // See implementation notes for readRawVarint64
+ fastpath:
+ {
+ int tempPos = pos;
+
+ if (limit == tempPos) {
+ break fastpath;
+ }
+
+ final byte[] buffer = this.buffer;
+ int x;
+ if ((x = buffer[tempPos++]) >= 0) {
+ pos = tempPos;
+ return x;
+ } else if (limit - tempPos < 9) {
+ break fastpath;
+ } else if ((x ^= (buffer[tempPos++] << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (buffer[tempPos++] << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (buffer[tempPos++] << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
+ } else {
+ int y = buffer[tempPos++];
+ x ^= y << 28;
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
+ if (y < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return (int) readRawVarint64SlowPath();
+ }
+
+ private void skipRawVarint() throws IOException {
+ if (limit - pos >= MAX_VARINT_SIZE) {
+ skipRawVarintFastPath();
+ } else {
+ skipRawVarintSlowPath();
+ }
+ }
+
+ private void skipRawVarintFastPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (buffer[pos++] >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ private void skipRawVarintSlowPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (readRawByte() >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public long readRawVarint64() throws IOException {
+ // Implementation notes:
+ //
+ // Optimized for one-byte values, expected to be common.
+ // The particular code below was selected from various candidates
+ // empirically, by winning VarintBenchmark.
+ //
+ // Sign extension of (signed) Java bytes is usually a nuisance, but
+ // we exploit it here to more easily obtain the sign of bytes read.
+ // Instead of cleaning up the sign extension bits by masking eagerly,
+ // we delay until we find the final (positive) byte, when we clear all
+ // accumulated bits with one xor. We depend on javac to constant fold.
+ fastpath:
+ {
+ int tempPos = pos;
+
+ if (limit == tempPos) {
+ break fastpath;
+ }
+
+ final byte[] buffer = this.buffer;
+ long x;
+ int y;
+ if ((y = buffer[tempPos++]) >= 0) {
+ pos = tempPos;
+ return y;
+ } else if (limit - tempPos < 9) {
+ break fastpath;
+ } else if ((y ^= (buffer[tempPos++] << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (buffer[tempPos++] << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (buffer[tempPos++] << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = y ^ ((long) buffer[tempPos++] << 28)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ } else if ((x ^= ((long) buffer[tempPos++] << 35)) < 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
+ } else if ((x ^= ((long) buffer[tempPos++] << 42)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
+ } else if ((x ^= ((long) buffer[tempPos++] << 49)) < 0L) {
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49);
+ } else {
+ x ^= ((long) buffer[tempPos++] << 56);
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49)
+ ^ (~0L << 56);
+ if (x < 0L) {
+ if (buffer[tempPos++] < 0L) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return readRawVarint64SlowPath();
+ }
+
+ @Override
+ long readRawVarint64SlowPath() throws IOException {
+ long result = 0;
+ for (int shift = 0; shift < 64; shift += 7) {
+ final byte b = readRawByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public int readRawLittleEndian32() throws IOException {
+ int tempPos = pos;
+
+ if (limit - tempPos < FIXED32_SIZE) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ final byte[] buffer = this.buffer;
+ pos = tempPos + FIXED32_SIZE;
+ return (((buffer[tempPos] & 0xff))
+ | ((buffer[tempPos + 1] & 0xff) << 8)
+ | ((buffer[tempPos + 2] & 0xff) << 16)
+ | ((buffer[tempPos + 3] & 0xff) << 24));
+ }
+
+ @Override
+ public long readRawLittleEndian64() throws IOException {
+ int tempPos = pos;
+
+ if (limit - tempPos < FIXED64_SIZE) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ final byte[] buffer = this.buffer;
+ pos = tempPos + FIXED64_SIZE;
+ return (((buffer[tempPos] & 0xffL))
+ | ((buffer[tempPos + 1] & 0xffL) << 8)
+ | ((buffer[tempPos + 2] & 0xffL) << 16)
+ | ((buffer[tempPos + 3] & 0xffL) << 24)
+ | ((buffer[tempPos + 4] & 0xffL) << 32)
+ | ((buffer[tempPos + 5] & 0xffL) << 40)
+ | ((buffer[tempPos + 6] & 0xffL) << 48)
+ | ((buffer[tempPos + 7] & 0xffL) << 56));
+ }
+
+ @Override
+ public void enableAliasing(boolean enabled) {
+ this.enableAliasing = enabled;
+ }
+
+ @Override
+ public void resetSizeCounter() {
+ startPos = pos;
+ }
+
+ @Override
+ public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
+ if (byteLimit < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ byteLimit += getTotalBytesRead();
+ final int oldLimit = currentLimit;
+ if (byteLimit > oldLimit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ currentLimit = byteLimit;
+
+ recomputeBufferSizeAfterLimit();
+
+ return oldLimit;
+ }
+
+ private void recomputeBufferSizeAfterLimit() {
+ limit += bufferSizeAfterLimit;
+ final int bufferEnd = limit - startPos;
+ if (bufferEnd > currentLimit) {
+ // Limit is in current buffer.
+ bufferSizeAfterLimit = bufferEnd - currentLimit;
+ limit -= bufferSizeAfterLimit;
+ } else {
+ bufferSizeAfterLimit = 0;
+ }
+ }
+
+ @Override
+ public void popLimit(final int oldLimit) {
+ currentLimit = oldLimit;
+ recomputeBufferSizeAfterLimit();
+ }
+
+ @Override
+ public int getBytesUntilLimit() {
+ if (currentLimit == Integer.MAX_VALUE) {
+ return -1;
+ }
+
+ return currentLimit - getTotalBytesRead();
+ }
+
+ @Override
+ public boolean isAtEnd() throws IOException {
+ return pos == limit;
+ }
+
+ @Override
+ public int getTotalBytesRead() {
+ return pos - startPos;
+ }
+
+ @Override
+ public byte readRawByte() throws IOException {
+ if (pos == limit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ return buffer[pos++];
+ }
+
+ @Override
+ public byte[] readRawBytes(final int length) throws IOException {
+ if (length > 0 && length <= (limit - pos)) {
+ final int tempPos = pos;
+ pos += length;
+ return Arrays.copyOfRange(buffer, tempPos, pos);
+ }
+
+ if (length <= 0) {
+ if (length == 0) {
+ return Internal.EMPTY_BYTE_ARRAY;
+ } else {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ @Override
+ public void skipRawBytes(final int length) throws IOException {
+ if (length >= 0 && length <= (limit - pos)) {
+ // We have all the bytes we need already.
+ pos += length;
+ return;
+ }
+
+ if (length < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
}
/**
- * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
- * is called when descending into a length-delimited embedded message.
- *
- * <p>Note that {@code pushLimit()} does NOT affect how many bytes the
- * {@code CodedInputStream} reads from an underlying {@code InputStream} when
- * refreshing its buffer. If you need to prevent reading past a certain
- * point in the underlying {@code InputStream} (e.g. because you expect it to
- * contain more data after the end of the message which you need to handle
- * differently) then you must place a wrapper around your {@code InputStream}
- * which limits the amount of data that can be read from it.
- *
- * @return the old limit.
+ * A {@link CodedInputStream} implementation that uses a backing direct ByteBuffer as the input.
+ * Requires the use of {@code sun.misc.Unsafe} to perform fast reads on the buffer.
*/
- public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
- if (byteLimit < 0) {
- throw InvalidProtocolBufferException.negativeSize();
+ private static final class UnsafeDirectNioDecoder extends CodedInputStream {
+ /** The direct buffer that is backing this stream. */
+ private final ByteBuffer buffer;
+
+ /**
+ * If {@code true}, indicates that the buffer is backing a {@link ByteString} and is therefore
+ * considered to be an immutable input source.
+ */
+ private final boolean immutable;
+
+ /** The unsafe address of the content of {@link #buffer}. */
+ private final long address;
+
+ /** The unsafe address of the current read limit of the buffer. */
+ private long limit;
+
+ /** The unsafe address of the current read position of the buffer. */
+ private long pos;
+
+ /** The unsafe address of the starting read position. */
+ private long startPos;
+
+ /** The amount of available data in the buffer beyond {@link #limit}. */
+ private int bufferSizeAfterLimit;
+
+ /** The last tag that was read from this stream. */
+ private int lastTag;
+
+ /**
+ * If {@code true}, indicates that calls to read {@link ByteString} or {@code byte[]}
+ * <strong>may</strong> return slices of the underlying buffer, rather than copies.
+ */
+ private boolean enableAliasing;
+
+ /** The absolute position of the end of the current message. */
+ private int currentLimit = Integer.MAX_VALUE;
+
+ static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ private UnsafeDirectNioDecoder(ByteBuffer buffer, boolean immutable) {
+ this.buffer = buffer;
+ address = UnsafeUtil.addressOffset(buffer);
+ limit = address + buffer.limit();
+ pos = address + buffer.position();
+ startPos = pos;
+ this.immutable = immutable;
+ }
+
+ @Override
+ public int readTag() throws IOException {
+ if (isAtEnd()) {
+ lastTag = 0;
+ return 0;
+ }
+
+ lastTag = readRawVarint32();
+ if (WireFormat.getTagFieldNumber(lastTag) == 0) {
+ // If we actually read zero (or any tag number corresponding to field
+ // number zero), that's not a valid tag.
+ throw InvalidProtocolBufferException.invalidTag();
+ }
+ return lastTag;
}
- byteLimit += totalBytesRetired + bufferPos;
- final int oldLimit = currentLimit;
- if (byteLimit > oldLimit) {
+
+ @Override
+ public void checkLastTagWas(final int value) throws InvalidProtocolBufferException {
+ if (lastTag != value) {
+ throw InvalidProtocolBufferException.invalidEndTag();
+ }
+ }
+
+ @Override
+ public int getLastTag() {
+ return lastTag;
+ }
+
+ @Override
+ public boolean skipField(final int tag) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ skipRawVarint();
+ return true;
+ case WireFormat.WIRETYPE_FIXED64:
+ skipRawBytes(FIXED64_SIZE);
+ return true;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ skipRawBytes(readRawVarint32());
+ return true;
+ case WireFormat.WIRETYPE_START_GROUP:
+ skipMessage();
+ checkLastTagWas(
+ WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP));
+ return true;
+ case WireFormat.WIRETYPE_END_GROUP:
+ return false;
+ case WireFormat.WIRETYPE_FIXED32:
+ skipRawBytes(FIXED32_SIZE);
+ return true;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public boolean skipField(final int tag, final CodedOutputStream output) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ {
+ long value = readInt64();
+ output.writeRawVarint32(tag);
+ output.writeUInt64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_FIXED64:
+ {
+ long value = readRawLittleEndian64();
+ output.writeRawVarint32(tag);
+ output.writeFixed64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ {
+ ByteString value = readBytes();
+ output.writeRawVarint32(tag);
+ output.writeBytesNoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_START_GROUP:
+ {
+ output.writeRawVarint32(tag);
+ skipMessage(output);
+ int endtag =
+ WireFormat.makeTag(
+ WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+ checkLastTagWas(endtag);
+ output.writeRawVarint32(endtag);
+ return true;
+ }
+ case WireFormat.WIRETYPE_END_GROUP:
+ {
+ return false;
+ }
+ case WireFormat.WIRETYPE_FIXED32:
+ {
+ int value = readRawLittleEndian32();
+ output.writeRawVarint32(tag);
+ output.writeFixed32NoTag(value);
+ return true;
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public void skipMessage() throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag)) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void skipMessage(CodedOutputStream output) throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag, output)) {
+ return;
+ }
+ }
+ }
+
+
+ // -----------------------------------------------------------------
+
+ @Override
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readRawLittleEndian64());
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readRawLittleEndian32());
+ }
+
+ @Override
+ public long readUInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public long readInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public int readInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public long readFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ return readRawVarint64() != 0;
+ }
+
+ @Override
+ public String readString() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= remaining()) {
+ // TODO(nathanmittler): Is there a way to avoid this copy?
+ // TODO(anuraaga): It might be possible to share the optimized loop with
+ // readStringRequireUtf8 by implementing Java replacement logic there.
+ // The same as readBytes' logic
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(pos, bytes, 0, size);
+ String result = new String(bytes, UTF_8);
+ pos += size;
+ return result;
+ }
+
+ if (size == 0) {
+ return "";
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
throw InvalidProtocolBufferException.truncatedMessage();
}
- currentLimit = byteLimit;
- recomputeBufferSizeAfterLimit();
+ @Override
+ public String readStringRequireUtf8() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= remaining()) {
+ if (ENABLE_CUSTOM_UTF8_DECODE) {
+ final int bufferPos = bufferPos(pos);
+ String result = Utf8.decodeUtf8(buffer, bufferPos, size);
+ pos += size;
+ return result;
+ } else {
+ // TODO(nathanmittler): Is there a way to avoid this copy?
+ // The same as readBytes' logic
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(pos, bytes, 0, size);
+ // TODO(martinrb): We could save a pass by validating while decoding.
+ if (!Utf8.isValidUtf8(bytes)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
- return oldLimit;
- }
+ String result = new String(bytes, UTF_8);
+ pos += size;
+ return result;
+ }
+ }
- private void recomputeBufferSizeAfterLimit() {
- bufferSize += bufferSizeAfterLimit;
- final int bufferEnd = totalBytesRetired + bufferSize;
- if (bufferEnd > currentLimit) {
- // Limit is in current buffer.
- bufferSizeAfterLimit = bufferEnd - currentLimit;
- bufferSize -= bufferSizeAfterLimit;
- } else {
- bufferSizeAfterLimit = 0;
+ if (size == 0) {
+ return "";
+ }
+ if (size <= 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
}
- }
- /**
- * Discards the current limit, returning to the previous limit.
- *
- * @param oldLimit The old limit, as returned by {@code pushLimit}.
- */
- public void popLimit(final int oldLimit) {
- currentLimit = oldLimit;
- recomputeBufferSizeAfterLimit();
- }
+ @Override
+ public void readGroup(
+ final int fieldNumber,
+ final MessageLite.Builder builder,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ }
- /**
- * Returns the number of bytes to be read before the current limit.
- * If no limit is set, returns -1.
- */
- public int getBytesUntilLimit() {
- if (currentLimit == Integer.MAX_VALUE) {
- return -1;
+
+ @Override
+ public <T extends MessageLite> T readGroup(
+ final int fieldNumber,
+ final Parser<T> parser,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ return result;
}
- final int currentAbsolutePosition = totalBytesRetired + bufferPos;
- return currentLimit - currentAbsolutePosition;
- }
+ @Deprecated
+ @Override
+ public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder)
+ throws IOException {
+ readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry());
+ }
- /**
- * Returns true if the stream has reached the end of the input. This is the
- * case if either the end of the underlying input source has been reached or
- * if the stream has reached a limit created using {@link #pushLimit(int)}.
- */
- public boolean isAtEnd() throws IOException {
- return bufferPos == bufferSize && !tryRefillBuffer(1);
- }
+ @Override
+ public void readMessage(
+ final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ final int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ }
- /**
- * The total bytes read up to the current position. Calling
- * {@link #resetSizeCounter()} resets this value to zero.
- */
- public int getTotalBytesRead() {
- return totalBytesRetired + bufferPos;
- }
- private interface RefillCallback {
- void onRefill();
- }
+ @Override
+ public <T extends MessageLite> T readMessage(
+ final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+ int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ return result;
+ }
- private RefillCallback refillCallback = null;
+ @Override
+ public ByteString readBytes() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= remaining()) {
+ if (immutable && enableAliasing) {
+ final ByteBuffer result = slice(pos, pos + size);
+ pos += size;
+ return ByteString.wrap(result);
+ } else {
+ // Use UnsafeUtil to copy the memory to bytes instead of using ByteBuffer ways.
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(pos, bytes, 0, size);
+ pos += size;
+ return ByteString.wrap(bytes);
+ }
+ }
- /**
- * Reads more bytes from the input, making at least {@code n} bytes available
- * in the buffer. Caller must ensure that the requested space is not yet
- * available, and that the requested space is less than BUFFER_SIZE.
- *
- * @throws InvalidProtocolBufferException The end of the stream or the current
- * limit was reached.
- */
- private void refillBuffer(int n) throws IOException {
- if (!tryRefillBuffer(n)) {
+ if (size == 0) {
+ return ByteString.EMPTY;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
throw InvalidProtocolBufferException.truncatedMessage();
}
+
+ @Override
+ public byte[] readByteArray() throws IOException {
+ return readRawBytes(readRawVarint32());
+ }
+
+ @Override
+ public ByteBuffer readByteBuffer() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= remaining()) {
+ // "Immutable" implies that buffer is backing a ByteString.
+ // Disallow slicing in this case to prevent the caller from modifying the contents
+ // of the ByteString.
+ if (!immutable && enableAliasing) {
+ final ByteBuffer result = slice(pos, pos + size);
+ pos += size;
+ return result;
+ } else {
+ // The same as readBytes' logic
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(pos, bytes, 0, size);
+ pos += size;
+ return ByteBuffer.wrap(bytes);
+ }
+ // TODO(nathanmittler): Investigate making the ByteBuffer be made read-only
+ }
+
+ if (size == 0) {
+ return EMPTY_BYTE_BUFFER;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ @Override
+ public int readUInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public int readEnum() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public int readSFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public long readSFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readSInt32() throws IOException {
+ return decodeZigZag32(readRawVarint32());
+ }
+
+ @Override
+ public long readSInt64() throws IOException {
+ return decodeZigZag64(readRawVarint64());
+ }
+
+ // =================================================================
+
+ @Override
+ public int readRawVarint32() throws IOException {
+ // See implementation notes for readRawVarint64
+ fastpath:
+ {
+ long tempPos = pos;
+
+ if (limit == tempPos) {
+ break fastpath;
+ }
+
+ int x;
+ if ((x = UnsafeUtil.getByte(tempPos++)) >= 0) {
+ pos = tempPos;
+ return x;
+ } else if (limit - tempPos < 9) {
+ break fastpath;
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
+ } else {
+ int y = UnsafeUtil.getByte(tempPos++);
+ x ^= y << 28;
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
+ if (y < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return (int) readRawVarint64SlowPath();
+ }
+
+ private void skipRawVarint() throws IOException {
+ if (remaining() >= MAX_VARINT_SIZE) {
+ skipRawVarintFastPath();
+ } else {
+ skipRawVarintSlowPath();
+ }
+ }
+
+ private void skipRawVarintFastPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (UnsafeUtil.getByte(pos++) >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ private void skipRawVarintSlowPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (readRawByte() >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public long readRawVarint64() throws IOException {
+ // Implementation notes:
+ //
+ // Optimized for one-byte values, expected to be common.
+ // The particular code below was selected from various candidates
+ // empirically, by winning VarintBenchmark.
+ //
+ // Sign extension of (signed) Java bytes is usually a nuisance, but
+ // we exploit it here to more easily obtain the sign of bytes read.
+ // Instead of cleaning up the sign extension bits by masking eagerly,
+ // we delay until we find the final (positive) byte, when we clear all
+ // accumulated bits with one xor. We depend on javac to constant fold.
+ fastpath:
+ {
+ long tempPos = pos;
+
+ if (limit == tempPos) {
+ break fastpath;
+ }
+
+ long x;
+ int y;
+ if ((y = UnsafeUtil.getByte(tempPos++)) >= 0) {
+ pos = tempPos;
+ return y;
+ } else if (limit - tempPos < 9) {
+ break fastpath;
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = y ^ ((long) UnsafeUtil.getByte(tempPos++) << 28)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 35)) < 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 42)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 49)) < 0L) {
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49);
+ } else {
+ x ^= ((long) UnsafeUtil.getByte(tempPos++) << 56);
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49)
+ ^ (~0L << 56);
+ if (x < 0L) {
+ if (UnsafeUtil.getByte(tempPos++) < 0L) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return readRawVarint64SlowPath();
+ }
+
+ @Override
+ long readRawVarint64SlowPath() throws IOException {
+ long result = 0;
+ for (int shift = 0; shift < 64; shift += 7) {
+ final byte b = readRawByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public int readRawLittleEndian32() throws IOException {
+ long tempPos = pos;
+
+ if (limit - tempPos < FIXED32_SIZE) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ pos = tempPos + FIXED32_SIZE;
+ return (((UnsafeUtil.getByte(tempPos) & 0xff))
+ | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8)
+ | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16)
+ | ((UnsafeUtil.getByte(tempPos + 3) & 0xff) << 24));
+ }
+
+ @Override
+ public long readRawLittleEndian64() throws IOException {
+ long tempPos = pos;
+
+ if (limit - tempPos < FIXED64_SIZE) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ pos = tempPos + FIXED64_SIZE;
+ return (((UnsafeUtil.getByte(tempPos) & 0xffL))
+ | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8)
+ | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16)
+ | ((UnsafeUtil.getByte(tempPos + 3) & 0xffL) << 24)
+ | ((UnsafeUtil.getByte(tempPos + 4) & 0xffL) << 32)
+ | ((UnsafeUtil.getByte(tempPos + 5) & 0xffL) << 40)
+ | ((UnsafeUtil.getByte(tempPos + 6) & 0xffL) << 48)
+ | ((UnsafeUtil.getByte(tempPos + 7) & 0xffL) << 56));
+ }
+
+ @Override
+ public void enableAliasing(boolean enabled) {
+ this.enableAliasing = enabled;
+ }
+
+ @Override
+ public void resetSizeCounter() {
+ startPos = pos;
+ }
+
+ @Override
+ public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
+ if (byteLimit < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ byteLimit += getTotalBytesRead();
+ final int oldLimit = currentLimit;
+ if (byteLimit > oldLimit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ currentLimit = byteLimit;
+
+ recomputeBufferSizeAfterLimit();
+
+ return oldLimit;
+ }
+
+ @Override
+ public void popLimit(final int oldLimit) {
+ currentLimit = oldLimit;
+ recomputeBufferSizeAfterLimit();
+ }
+
+ @Override
+ public int getBytesUntilLimit() {
+ if (currentLimit == Integer.MAX_VALUE) {
+ return -1;
+ }
+
+ return currentLimit - getTotalBytesRead();
+ }
+
+ @Override
+ public boolean isAtEnd() throws IOException {
+ return pos == limit;
+ }
+
+ @Override
+ public int getTotalBytesRead() {
+ return (int) (pos - startPos);
+ }
+
+ @Override
+ public byte readRawByte() throws IOException {
+ if (pos == limit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ return UnsafeUtil.getByte(pos++);
+ }
+
+ @Override
+ public byte[] readRawBytes(final int length) throws IOException {
+ if (length >= 0 && length <= remaining()) {
+ byte[] bytes = new byte[length];
+ slice(pos, pos + length).get(bytes);
+ pos += length;
+ return bytes;
+ }
+
+ if (length <= 0) {
+ if (length == 0) {
+ return EMPTY_BYTE_ARRAY;
+ } else {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ }
+
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ @Override
+ public void skipRawBytes(final int length) throws IOException {
+ if (length >= 0 && length <= remaining()) {
+ // We have all the bytes we need already.
+ pos += length;
+ return;
+ }
+
+ if (length < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ private void recomputeBufferSizeAfterLimit() {
+ limit += bufferSizeAfterLimit;
+ final int bufferEnd = (int) (limit - startPos);
+ if (bufferEnd > currentLimit) {
+ // Limit is in current buffer.
+ bufferSizeAfterLimit = bufferEnd - currentLimit;
+ limit -= bufferSizeAfterLimit;
+ } else {
+ bufferSizeAfterLimit = 0;
+ }
+ }
+
+ private int remaining() {
+ return (int) (limit - pos);
+ }
+
+ private int bufferPos(long pos) {
+ return (int) (pos - address);
+ }
+
+ private ByteBuffer slice(long begin, long end) throws IOException {
+ int prevPos = buffer.position();
+ int prevLimit = buffer.limit();
+ try {
+ buffer.position(bufferPos(begin));
+ buffer.limit(bufferPos(end));
+ return buffer.slice();
+ } catch (IllegalArgumentException e) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ } finally {
+ buffer.position(prevPos);
+ buffer.limit(prevLimit);
+ }
+ }
}
/**
- * Tries to read more bytes from the input, making at least {@code n} bytes
- * available in the buffer. Caller must ensure that the requested space is
- * not yet available, and that the requested space is less than BUFFER_SIZE.
- *
- * @return {@code true} if the bytes could be made available; {@code false}
- * if the end of the stream or the current limit was reached.
+ * Implementation of {@link CodedInputStream} that uses an {@link InputStream} as the data source.
*/
- private boolean tryRefillBuffer(int n) throws IOException {
- if (bufferPos + n <= bufferSize) {
- throw new IllegalStateException(
- "refillBuffer() called when " + n +
- " bytes were already available in buffer");
+ private static final class StreamDecoder extends CodedInputStream {
+ private final InputStream input;
+ private final byte[] buffer;
+ /** bufferSize represents how many bytes are currently filled in the buffer */
+ private int bufferSize;
+
+ private int bufferSizeAfterLimit;
+ private int pos;
+ private int lastTag;
+
+ /**
+ * The total number of bytes read before the current buffer. The total bytes read up to the
+ * current position can be computed as {@code totalBytesRetired + pos}. This value may be
+ * negative if reading started in the middle of the current buffer (e.g. if the constructor that
+ * takes a byte array and an offset was used).
+ */
+ private int totalBytesRetired;
+
+ /** The absolute position of the end of the current message. */
+ private int currentLimit = Integer.MAX_VALUE;
+
+ private StreamDecoder(final InputStream input, int bufferSize) {
+ checkNotNull(input, "input");
+ this.input = input;
+ this.buffer = new byte[bufferSize];
+ this.bufferSize = 0;
+ pos = 0;
+ totalBytesRetired = 0;
}
- if (totalBytesRetired + bufferPos + n > currentLimit) {
- // Oops, we hit a limit.
- return false;
+ @Override
+ public int readTag() throws IOException {
+ if (isAtEnd()) {
+ lastTag = 0;
+ return 0;
+ }
+
+ lastTag = readRawVarint32();
+ if (WireFormat.getTagFieldNumber(lastTag) == 0) {
+ // If we actually read zero (or any tag number corresponding to field
+ // number zero), that's not a valid tag.
+ throw InvalidProtocolBufferException.invalidTag();
+ }
+ return lastTag;
+ }
+
+ @Override
+ public void checkLastTagWas(final int value) throws InvalidProtocolBufferException {
+ if (lastTag != value) {
+ throw InvalidProtocolBufferException.invalidEndTag();
+ }
+ }
+
+ @Override
+ public int getLastTag() {
+ return lastTag;
+ }
+
+ @Override
+ public boolean skipField(final int tag) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ skipRawVarint();
+ return true;
+ case WireFormat.WIRETYPE_FIXED64:
+ skipRawBytes(FIXED64_SIZE);
+ return true;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ skipRawBytes(readRawVarint32());
+ return true;
+ case WireFormat.WIRETYPE_START_GROUP:
+ skipMessage();
+ checkLastTagWas(
+ WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP));
+ return true;
+ case WireFormat.WIRETYPE_END_GROUP:
+ return false;
+ case WireFormat.WIRETYPE_FIXED32:
+ skipRawBytes(FIXED32_SIZE);
+ return true;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public boolean skipField(final int tag, final CodedOutputStream output) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ {
+ long value = readInt64();
+ output.writeRawVarint32(tag);
+ output.writeUInt64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_FIXED64:
+ {
+ long value = readRawLittleEndian64();
+ output.writeRawVarint32(tag);
+ output.writeFixed64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ {
+ ByteString value = readBytes();
+ output.writeRawVarint32(tag);
+ output.writeBytesNoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_START_GROUP:
+ {
+ output.writeRawVarint32(tag);
+ skipMessage(output);
+ int endtag =
+ WireFormat.makeTag(
+ WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+ checkLastTagWas(endtag);
+ output.writeRawVarint32(endtag);
+ return true;
+ }
+ case WireFormat.WIRETYPE_END_GROUP:
+ {
+ return false;
+ }
+ case WireFormat.WIRETYPE_FIXED32:
+ {
+ int value = readRawLittleEndian32();
+ output.writeRawVarint32(tag);
+ output.writeFixed32NoTag(value);
+ return true;
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public void skipMessage() throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag)) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void skipMessage(CodedOutputStream output) throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag, output)) {
+ return;
+ }
+ }
+ }
+
+ /** Collects the bytes skipped and returns the data in a ByteBuffer. */
+ private class SkippedDataSink implements RefillCallback {
+ private int lastPos = pos;
+ private ByteArrayOutputStream byteArrayStream;
+
+ @Override
+ public void onRefill() {
+ if (byteArrayStream == null) {
+ byteArrayStream = new ByteArrayOutputStream();
+ }
+ byteArrayStream.write(buffer, lastPos, pos - lastPos);
+ lastPos = 0;
+ }
+
+ /** Gets skipped data in a ByteBuffer. This method should only be called once. */
+ ByteBuffer getSkippedData() {
+ if (byteArrayStream == null) {
+ return ByteBuffer.wrap(buffer, lastPos, pos - lastPos);
+ } else {
+ byteArrayStream.write(buffer, lastPos, pos);
+ return ByteBuffer.wrap(byteArrayStream.toByteArray());
+ }
+ }
+ }
+
+
+ // -----------------------------------------------------------------
+
+ @Override
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readRawLittleEndian64());
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readRawLittleEndian32());
+ }
+
+ @Override
+ public long readUInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public long readInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public int readInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public long readFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ return readRawVarint64() != 0;
}
- if (refillCallback != null) {
- refillCallback.onRefill();
+ @Override
+ public String readString() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= (bufferSize - pos)) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ final String result = new String(buffer, pos, size, UTF_8);
+ pos += size;
+ return result;
+ }
+ if (size == 0) {
+ return "";
+ }
+ if (size <= bufferSize) {
+ refillBuffer(size);
+ String result = new String(buffer, pos, size, UTF_8);
+ pos += size;
+ return result;
+ }
+ // Slow path: Build a byte array first then copy it.
+ return new String(readRawBytesSlowPath(size), UTF_8);
+ }
+
+ @Override
+ public String readStringRequireUtf8() throws IOException {
+ final int size = readRawVarint32();
+ final byte[] bytes;
+ final int oldPos = pos;
+ final int tempPos;
+ if (size <= (bufferSize - oldPos) && size > 0) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ bytes = buffer;
+ pos = oldPos + size;
+ tempPos = oldPos;
+ } else if (size == 0) {
+ return "";
+ } else if (size <= bufferSize) {
+ refillBuffer(size);
+ bytes = buffer;
+ tempPos = 0;
+ pos = tempPos + size;
+ } else {
+ // Slow path: Build a byte array first then copy it.
+ bytes = readRawBytesSlowPath(size);
+ tempPos = 0;
+ }
+ if (ENABLE_CUSTOM_UTF8_DECODE) {
+ return Utf8.decodeUtf8(bytes, tempPos, size);
+ } else {
+ // TODO(martinrb): We could save a pass by validating while decoding.
+ if (!Utf8.isValidUtf8(bytes, tempPos, tempPos + size)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ return new String(bytes, tempPos, size, UTF_8);
+ }
+ }
+
+ @Override
+ public void readGroup(
+ final int fieldNumber,
+ final MessageLite.Builder builder,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ }
+
+
+ @Override
+ public <T extends MessageLite> T readGroup(
+ final int fieldNumber,
+ final Parser<T> parser,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ return result;
+ }
+
+ @Deprecated
+ @Override
+ public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder)
+ throws IOException {
+ readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry());
+ }
+
+ @Override
+ public void readMessage(
+ final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ final int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ }
+
+
+ @Override
+ public <T extends MessageLite> T readMessage(
+ final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+ int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ return result;
+ }
+
+ @Override
+ public ByteString readBytes() throws IOException {
+ final int size = readRawVarint32();
+ if (size <= (bufferSize - pos) && size > 0) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ final ByteString result = ByteString.copyFrom(buffer, pos, size);
+ pos += size;
+ return result;
+ }
+ if (size == 0) {
+ return ByteString.EMPTY;
+ }
+ return readBytesSlowPath(size);
+ }
+
+ @Override
+ public byte[] readByteArray() throws IOException {
+ final int size = readRawVarint32();
+ if (size <= (bufferSize - pos) && size > 0) {
+ // Fast path: We already have the bytes in a contiguous buffer, so
+ // just copy directly from it.
+ final byte[] result = Arrays.copyOfRange(buffer, pos, pos + size);
+ pos += size;
+ return result;
+ } else {
+ // Slow path: Build a byte array first then copy it.
+ return readRawBytesSlowPath(size);
+ }
+ }
+
+ @Override
+ public ByteBuffer readByteBuffer() throws IOException {
+ final int size = readRawVarint32();
+ if (size <= (bufferSize - pos) && size > 0) {
+ // Fast path: We already have the bytes in a contiguous buffer.
+ ByteBuffer result = ByteBuffer.wrap(Arrays.copyOfRange(buffer, pos, pos + size));
+ pos += size;
+ return result;
+ }
+ if (size == 0) {
+ return Internal.EMPTY_BYTE_BUFFER;
+ }
+ // Slow path: Build a byte array first then copy it.
+ return ByteBuffer.wrap(readRawBytesSlowPath(size));
+ }
+
+ @Override
+ public int readUInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public int readEnum() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public int readSFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public long readSFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readSInt32() throws IOException {
+ return decodeZigZag32(readRawVarint32());
+ }
+
+ @Override
+ public long readSInt64() throws IOException {
+ return decodeZigZag64(readRawVarint64());
+ }
+
+ // =================================================================
+
+ @Override
+ public int readRawVarint32() throws IOException {
+ // See implementation notes for readRawVarint64
+ fastpath:
+ {
+ int tempPos = pos;
+
+ if (bufferSize == tempPos) {
+ break fastpath;
+ }
+
+ final byte[] buffer = this.buffer;
+ int x;
+ if ((x = buffer[tempPos++]) >= 0) {
+ pos = tempPos;
+ return x;
+ } else if (bufferSize - tempPos < 9) {
+ break fastpath;
+ } else if ((x ^= (buffer[tempPos++] << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (buffer[tempPos++] << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (buffer[tempPos++] << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
+ } else {
+ int y = buffer[tempPos++];
+ x ^= y << 28;
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
+ if (y < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0
+ && buffer[tempPos++] < 0) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return (int) readRawVarint64SlowPath();
+ }
+
+ private void skipRawVarint() throws IOException {
+ if (bufferSize - pos >= MAX_VARINT_SIZE) {
+ skipRawVarintFastPath();
+ } else {
+ skipRawVarintSlowPath();
+ }
}
- if (input != null) {
- int pos = bufferPos;
- if (pos > 0) {
- if (bufferSize > pos) {
- System.arraycopy(buffer, pos, buffer, 0, bufferSize - pos);
+ private void skipRawVarintFastPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (buffer[pos++] >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ private void skipRawVarintSlowPath() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (readRawByte() >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public long readRawVarint64() throws IOException {
+ // Implementation notes:
+ //
+ // Optimized for one-byte values, expected to be common.
+ // The particular code below was selected from various candidates
+ // empirically, by winning VarintBenchmark.
+ //
+ // Sign extension of (signed) Java bytes is usually a nuisance, but
+ // we exploit it here to more easily obtain the sign of bytes read.
+ // Instead of cleaning up the sign extension bits by masking eagerly,
+ // we delay until we find the final (positive) byte, when we clear all
+ // accumulated bits with one xor. We depend on javac to constant fold.
+ fastpath:
+ {
+ int tempPos = pos;
+
+ if (bufferSize == tempPos) {
+ break fastpath;
+ }
+
+ final byte[] buffer = this.buffer;
+ long x;
+ int y;
+ if ((y = buffer[tempPos++]) >= 0) {
+ pos = tempPos;
+ return y;
+ } else if (bufferSize - tempPos < 9) {
+ break fastpath;
+ } else if ((y ^= (buffer[tempPos++] << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (buffer[tempPos++] << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (buffer[tempPos++] << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = y ^ ((long) buffer[tempPos++] << 28)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ } else if ((x ^= ((long) buffer[tempPos++] << 35)) < 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
+ } else if ((x ^= ((long) buffer[tempPos++] << 42)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
+ } else if ((x ^= ((long) buffer[tempPos++] << 49)) < 0L) {
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49);
+ } else {
+ x ^= ((long) buffer[tempPos++] << 56);
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49)
+ ^ (~0L << 56);
+ if (x < 0L) {
+ if (buffer[tempPos++] < 0L) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ }
+ pos = tempPos;
+ return x;
+ }
+ return readRawVarint64SlowPath();
+ }
+
+ @Override
+ long readRawVarint64SlowPath() throws IOException {
+ long result = 0;
+ for (int shift = 0; shift < 64; shift += 7) {
+ final byte b = readRawByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
+ }
+
+ @Override
+ public int readRawLittleEndian32() throws IOException {
+ int tempPos = pos;
+
+ if (bufferSize - tempPos < FIXED32_SIZE) {
+ refillBuffer(FIXED32_SIZE);
+ tempPos = pos;
+ }
+
+ final byte[] buffer = this.buffer;
+ pos = tempPos + FIXED32_SIZE;
+ return (((buffer[tempPos] & 0xff))
+ | ((buffer[tempPos + 1] & 0xff) << 8)
+ | ((buffer[tempPos + 2] & 0xff) << 16)
+ | ((buffer[tempPos + 3] & 0xff) << 24));
+ }
+
+ @Override
+ public long readRawLittleEndian64() throws IOException {
+ int tempPos = pos;
+
+ if (bufferSize - tempPos < FIXED64_SIZE) {
+ refillBuffer(FIXED64_SIZE);
+ tempPos = pos;
+ }
+
+ final byte[] buffer = this.buffer;
+ pos = tempPos + FIXED64_SIZE;
+ return (((buffer[tempPos] & 0xffL))
+ | ((buffer[tempPos + 1] & 0xffL) << 8)
+ | ((buffer[tempPos + 2] & 0xffL) << 16)
+ | ((buffer[tempPos + 3] & 0xffL) << 24)
+ | ((buffer[tempPos + 4] & 0xffL) << 32)
+ | ((buffer[tempPos + 5] & 0xffL) << 40)
+ | ((buffer[tempPos + 6] & 0xffL) << 48)
+ | ((buffer[tempPos + 7] & 0xffL) << 56));
+ }
+
+ // -----------------------------------------------------------------
+
+ @Override
+ public void enableAliasing(boolean enabled) {
+ // TODO(nathanmittler): Ideally we should throw here. Do nothing for backward compatibility.
+ }
+
+ @Override
+ public void resetSizeCounter() {
+ totalBytesRetired = -pos;
+ }
+
+ @Override
+ public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
+ if (byteLimit < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ byteLimit += totalBytesRetired + pos;
+ final int oldLimit = currentLimit;
+ if (byteLimit > oldLimit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ currentLimit = byteLimit;
+
+ recomputeBufferSizeAfterLimit();
+
+ return oldLimit;
+ }
+
+ private void recomputeBufferSizeAfterLimit() {
+ bufferSize += bufferSizeAfterLimit;
+ final int bufferEnd = totalBytesRetired + bufferSize;
+ if (bufferEnd > currentLimit) {
+ // Limit is in current buffer.
+ bufferSizeAfterLimit = bufferEnd - currentLimit;
+ bufferSize -= bufferSizeAfterLimit;
+ } else {
+ bufferSizeAfterLimit = 0;
+ }
+ }
+
+ @Override
+ public void popLimit(final int oldLimit) {
+ currentLimit = oldLimit;
+ recomputeBufferSizeAfterLimit();
+ }
+
+ @Override
+ public int getBytesUntilLimit() {
+ if (currentLimit == Integer.MAX_VALUE) {
+ return -1;
+ }
+
+ final int currentAbsolutePosition = totalBytesRetired + pos;
+ return currentLimit - currentAbsolutePosition;
+ }
+
+ @Override
+ public boolean isAtEnd() throws IOException {
+ return pos == bufferSize && !tryRefillBuffer(1);
+ }
+
+ @Override
+ public int getTotalBytesRead() {
+ return totalBytesRetired + pos;
+ }
+
+ private interface RefillCallback {
+ void onRefill();
+ }
+
+ private RefillCallback refillCallback = null;
+
+ /**
+ * Reads more bytes from the input, making at least {@code n} bytes available in the buffer.
+ * Caller must ensure that the requested space is not yet available, and that the requested
+ * space is less than BUFFER_SIZE.
+ *
+ * @throws InvalidProtocolBufferException The end of the stream or the current limit was
+ * reached.
+ */
+ private void refillBuffer(int n) throws IOException {
+ if (!tryRefillBuffer(n)) {
+ // We have to distinguish the exception between sizeLimitExceeded and truncatedMessage. So
+ // we just throw an sizeLimitExceeded exception here if it exceeds the sizeLimit
+ if (n > sizeLimit - totalBytesRetired - pos) {
+ throw InvalidProtocolBufferException.sizeLimitExceeded();
+ } else {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ }
+ }
+
+ /**
+ * Tries to read more bytes from the input, making at least {@code n} bytes available in the
+ * buffer. Caller must ensure that the requested space is not yet available, and that the
+ * requested space is less than BUFFER_SIZE.
+ *
+ * @return {@code true} If the bytes could be made available; {@code false} 1. Current at the
+ * end of the stream 2. The current limit was reached 3. The total size limit was reached
+ */
+ private boolean tryRefillBuffer(int n) throws IOException {
+ if (pos + n <= bufferSize) {
+ throw new IllegalStateException(
+ "refillBuffer() called when " + n + " bytes were already available in buffer");
+ }
+
+ // Check whether the size of total message needs to read is bigger than the size limit.
+ // We shouldn't throw an exception here as isAtEnd() function needs to get this function's
+ // return as the result.
+ if (n > sizeLimit - totalBytesRetired - pos) {
+ return false;
+ }
+
+ // Shouldn't throw the exception here either.
+ if (totalBytesRetired + pos + n > currentLimit) {
+ // Oops, we hit a limit.
+ return false;
+ }
+
+ if (refillCallback != null) {
+ refillCallback.onRefill();
+ }
+
+ int tempPos = pos;
+ if (tempPos > 0) {
+ if (bufferSize > tempPos) {
+ System.arraycopy(buffer, tempPos, buffer, 0, bufferSize - tempPos);
}
- totalBytesRetired += pos;
- bufferSize -= pos;
- bufferPos = 0;
+ totalBytesRetired += tempPos;
+ bufferSize -= tempPos;
+ pos = 0;
}
- int bytesRead = input.read(buffer, bufferSize, buffer.length - bufferSize);
+ // Here we should refill the buffer as many bytes as possible.
+ int bytesRead =
+ input.read(
+ buffer,
+ bufferSize,
+ Math.min(
+ // the size of allocated but unused bytes in the buffer
+ buffer.length - bufferSize,
+ // do not exceed the total bytes limit
+ sizeLimit - totalBytesRetired - bufferSize));
if (bytesRead == 0 || bytesRead < -1 || bytesRead > buffer.length) {
throw new IllegalStateException(
- "InputStream#read(byte[]) returned invalid result: " + bytesRead +
- "\nThe InputStream implementation is buggy.");
+ "InputStream#read(byte[]) returned invalid result: "
+ + bytesRead
+ + "\nThe InputStream implementation is buggy.");
}
if (bytesRead > 0) {
bufferSize += bytesRead;
- // Integer-overflow-conscious check against sizeLimit
- if (totalBytesRetired + n - sizeLimit > 0) {
- throw InvalidProtocolBufferException.sizeLimitExceeded();
- }
recomputeBufferSizeAfterLimit();
return (bufferSize >= n) ? true : tryRefillBuffer(n);
}
+
+ return false;
}
- return false;
- }
+ @Override
+ public byte readRawByte() throws IOException {
+ if (pos == bufferSize) {
+ refillBuffer(1);
+ }
+ return buffer[pos++];
+ }
- /**
- * Read one byte from the input.
- *
- * @throws InvalidProtocolBufferException The end of the stream or the current
- * limit was reached.
- */
- public byte readRawByte() throws IOException {
- if (bufferPos == bufferSize) {
- refillBuffer(1);
+ @Override
+ public byte[] readRawBytes(final int size) throws IOException {
+ final int tempPos = pos;
+ if (size <= (bufferSize - tempPos) && size > 0) {
+ pos = tempPos + size;
+ return Arrays.copyOfRange(buffer, tempPos, tempPos + size);
+ } else {
+ return readRawBytesSlowPath(size);
+ }
}
- return buffer[bufferPos++];
- }
- /**
- * Read a fixed size of bytes from the input.
- *
- * @throws InvalidProtocolBufferException The end of the stream or the current
- * limit was reached.
- */
- public byte[] readRawBytes(final int size) throws IOException {
- final int pos = bufferPos;
- if (size <= (bufferSize - pos) && size > 0) {
- bufferPos = pos + size;
- return Arrays.copyOfRange(buffer, pos, pos + size);
- } else {
- return readRawBytesSlowPath(size);
+ /**
+ * Exactly like readRawBytes, but caller must have already checked the fast path: (size <=
+ * (bufferSize - pos) && size > 0)
+ */
+ private byte[] readRawBytesSlowPath(final int size) throws IOException {
+ // Attempt to read the data in one byte array when it's safe to do.
+ byte[] result = readRawBytesSlowPathOneChunk(size);
+ if (result != null) {
+ return result;
+ }
+
+ final int originalBufferPos = pos;
+ final int bufferedBytes = bufferSize - pos;
+
+ // Mark the current buffer consumed.
+ totalBytesRetired += bufferSize;
+ pos = 0;
+ bufferSize = 0;
+
+ // Determine the number of bytes we need to read from the input stream.
+ int sizeLeft = size - bufferedBytes;
+
+ // The size is very large. For security reasons we read them in small
+ // chunks.
+ List<byte[]> chunks = readRawBytesSlowPathRemainingChunks(sizeLeft);
+
+ // OK, got everything. Now concatenate it all into one buffer.
+ final byte[] bytes = new byte[size];
+
+ // Start by copying the leftover bytes from this.buffer.
+ System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+
+ // And now all the chunks.
+ int tempPos = bufferedBytes;
+ for (final byte[] chunk : chunks) {
+ System.arraycopy(chunk, 0, bytes, tempPos, chunk.length);
+ tempPos += chunk.length;
+ }
+
+ // Done.
+ return bytes;
+ }
+
+ /**
+ * Attempts to read the data in one byte array when it's safe to do. Returns null if the size to
+ * read is too large and needs to be allocated in smaller chunks for security reasons.
+ */
+ private byte[] readRawBytesSlowPathOneChunk(final int size) throws IOException {
+ if (size == 0) {
+ return Internal.EMPTY_BYTE_ARRAY;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+
+ // Integer-overflow-conscious check that the message size so far has not exceeded sizeLimit.
+ int currentMessageSize = totalBytesRetired + pos + size;
+ if (currentMessageSize - sizeLimit > 0) {
+ throw InvalidProtocolBufferException.sizeLimitExceeded();
+ }
+
+ // Verify that the message size so far has not exceeded currentLimit.
+ if (currentMessageSize > currentLimit) {
+ // Read to the end of the stream anyway.
+ skipRawBytes(currentLimit - totalBytesRetired - pos);
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ final int bufferedBytes = bufferSize - pos;
+ // Determine the number of bytes we need to read from the input stream.
+ int sizeLeft = size - bufferedBytes;
+ // TODO(nathanmittler): Consider using a value larger than DEFAULT_BUFFER_SIZE.
+ if (sizeLeft < DEFAULT_BUFFER_SIZE || sizeLeft <= input.available()) {
+ // Either the bytes we need are known to be available, or the required buffer is
+ // within an allowed threshold - go ahead and allocate the buffer now.
+ final byte[] bytes = new byte[size];
+
+ // Copy all of the buffered bytes to the result buffer.
+ System.arraycopy(buffer, pos, bytes, 0, bufferedBytes);
+ totalBytesRetired += bufferSize;
+ pos = 0;
+ bufferSize = 0;
+
+ // Fill the remaining bytes from the input stream.
+ int tempPos = bufferedBytes;
+ while (tempPos < bytes.length) {
+ int n = input.read(bytes, tempPos, size - tempPos);
+ if (n == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ totalBytesRetired += n;
+ tempPos += n;
+ }
+
+ return bytes;
+ }
+
+ return null;
+ }
+
+ /** Reads the remaining data in small chunks from the input stream. */
+ private List<byte[]> readRawBytesSlowPathRemainingChunks(int sizeLeft) throws IOException {
+ // The size is very large. For security reasons, we can't allocate the
+ // entire byte array yet. The size comes directly from the input, so a
+ // maliciously-crafted message could provide a bogus very large size in
+ // order to trick the app into allocating a lot of memory. We avoid this
+ // by allocating and reading only a small chunk at a time, so that the
+ // malicious message must actually *be* extremely large to cause
+ // problems. Meanwhile, we limit the allowed size of a message elsewhere.
+ final List<byte[]> chunks = new ArrayList<byte[]>();
+
+ while (sizeLeft > 0) {
+ // TODO(nathanmittler): Consider using a value larger than DEFAULT_BUFFER_SIZE.
+ final byte[] chunk = new byte[Math.min(sizeLeft, DEFAULT_BUFFER_SIZE)];
+ int tempPos = 0;
+ while (tempPos < chunk.length) {
+ final int n = input.read(chunk, tempPos, chunk.length - tempPos);
+ if (n == -1) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ totalBytesRetired += n;
+ tempPos += n;
+ }
+ sizeLeft -= chunk.length;
+ chunks.add(chunk);
+ }
+
+ return chunks;
+ }
+
+ /**
+ * Like readBytes, but caller must have already checked the fast path: (size <= (bufferSize -
+ * pos) && size > 0 || size == 0)
+ */
+ private ByteString readBytesSlowPath(final int size) throws IOException {
+ final byte[] result = readRawBytesSlowPathOneChunk(size);
+ if (result != null) {
+ return ByteString.wrap(result);
+ }
+
+ final int originalBufferPos = pos;
+ final int bufferedBytes = bufferSize - pos;
+
+ // Mark the current buffer consumed.
+ totalBytesRetired += bufferSize;
+ pos = 0;
+ bufferSize = 0;
+
+ // Determine the number of bytes we need to read from the input stream.
+ int sizeLeft = size - bufferedBytes;
+
+ // The size is very large. For security reasons we read them in small
+ // chunks.
+ List<byte[]> chunks = readRawBytesSlowPathRemainingChunks(sizeLeft);
+
+ // Wrap the byte arrays into a single ByteString.
+ List<ByteString> byteStrings = new ArrayList<ByteString>(1 + chunks.size());
+ byteStrings.add(ByteString.copyFrom(buffer, originalBufferPos, bufferedBytes));
+ for (byte[] chunk : chunks) {
+ byteStrings.add(ByteString.wrap(chunk));
+ }
+ return ByteString.copyFrom(byteStrings);
+ }
+
+ @Override
+ public void skipRawBytes(final int size) throws IOException {
+ if (size <= (bufferSize - pos) && size >= 0) {
+ // We have all the bytes we need already.
+ pos += size;
+ } else {
+ skipRawBytesSlowPath(size);
+ }
+ }
+
+ /**
+ * Exactly like skipRawBytes, but caller must have already checked the fast path: (size <=
+ * (bufferSize - pos) && size >= 0)
+ */
+ private void skipRawBytesSlowPath(final int size) throws IOException {
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+
+ if (totalBytesRetired + pos + size > currentLimit) {
+ // Read to the end of the stream anyway.
+ skipRawBytes(currentLimit - totalBytesRetired - pos);
+ // Then fail.
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ // Skipping more bytes than are in the buffer. First skip what we have.
+ int tempPos = bufferSize - pos;
+ pos = bufferSize;
+
+ // Keep refilling the buffer until we get to the point we wanted to skip to.
+ // This has the side effect of ensuring the limits are updated correctly.
+ refillBuffer(1);
+ while (size - tempPos > bufferSize) {
+ tempPos += bufferSize;
+ pos = bufferSize;
+ refillBuffer(1);
+ }
+
+ pos = size - tempPos;
}
}
/**
- * Exactly like readRawBytes, but caller must have already checked the fast
- * path: (size <= (bufferSize - pos) && size > 0)
+ * Implementation of {@link CodedInputStream} that uses an {@link Iterable <ByteBuffer>} as the
+ * data source. Requires the use of {@code sun.misc.Unsafe} to perform fast reads on the buffer.
*/
- private byte[] readRawBytesSlowPath(final int size) throws IOException {
- if (size <= 0) {
+ private static final class IterableDirectByteBufferDecoder extends CodedInputStream {
+ /** The object that need to decode. */
+ private Iterable<ByteBuffer> input;
+ /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */
+ private Iterator<ByteBuffer> iterator;
+ /** The current ByteBuffer; */
+ private ByteBuffer currentByteBuffer;
+ /**
+ * If {@code true}, indicates that all the buffer are backing a {@link ByteString} and are
+ * therefore considered to be an immutable input source.
+ */
+ private boolean immutable;
+ /**
+ * If {@code true}, indicates that calls to read {@link ByteString} or {@code byte[]}
+ * <strong>may</strong> return slices of the underlying buffer, rather than copies.
+ */
+ private boolean enableAliasing;
+ /** The global total message length limit */
+ private int totalBufferSize;
+ /** The amount of available data in the input beyond {@link #currentLimit}. */
+ private int bufferSizeAfterCurrentLimit;
+ /** The absolute position of the end of the current message. */
+ private int currentLimit = Integer.MAX_VALUE;
+ /** The last tag that was read from this stream. */
+ private int lastTag;
+ /** Total Bytes have been Read from the {@link Iterable} {@link ByteBuffer} */
+ private int totalBytesRead;
+ /** The start position offset of the whole message, used as to reset the totalBytesRead */
+ private int startOffset;
+ /** The current position for current ByteBuffer */
+ private long currentByteBufferPos;
+
+ private long currentByteBufferStartPos;
+ /**
+ * If the current ByteBuffer is unsafe-direct based, currentAddress is the start address of this
+ * ByteBuffer; otherwise should be zero.
+ */
+ private long currentAddress;
+ /** The limit position for current ByteBuffer */
+ private long currentByteBufferLimit;
+
+ /**
+ * The constructor of {@code Iterable<ByteBuffer>} decoder.
+ *
+ * @param inputBufs The input data.
+ * @param size The total size of the input data.
+ * @param immutableFlag whether the input data is immutable.
+ */
+ private IterableDirectByteBufferDecoder(
+ Iterable<ByteBuffer> inputBufs, int size, boolean immutableFlag) {
+ totalBufferSize = size;
+ input = inputBufs;
+ iterator = input.iterator();
+ immutable = immutableFlag;
+ startOffset = totalBytesRead = 0;
if (size == 0) {
- return Internal.EMPTY_BYTE_ARRAY;
+ currentByteBuffer = EMPTY_BYTE_BUFFER;
+ currentByteBufferPos = 0;
+ currentByteBufferStartPos = 0;
+ currentByteBufferLimit = 0;
+ currentAddress = 0;
} else {
+ tryGetNextByteBuffer();
+ }
+ }
+
+ /** To get the next ByteBuffer from {@code input}, and then update the parameters */
+ private void getNextByteBuffer() throws InvalidProtocolBufferException {
+ if (!iterator.hasNext()) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ tryGetNextByteBuffer();
+ }
+
+ private void tryGetNextByteBuffer() {
+ currentByteBuffer = iterator.next();
+ totalBytesRead += (int) (currentByteBufferPos - currentByteBufferStartPos);
+ currentByteBufferPos = currentByteBuffer.position();
+ currentByteBufferStartPos = currentByteBufferPos;
+ currentByteBufferLimit = currentByteBuffer.limit();
+ currentAddress = UnsafeUtil.addressOffset(currentByteBuffer);
+ currentByteBufferPos += currentAddress;
+ currentByteBufferStartPos += currentAddress;
+ currentByteBufferLimit += currentAddress;
+ }
+
+ @Override
+ public int readTag() throws IOException {
+ if (isAtEnd()) {
+ lastTag = 0;
+ return 0;
+ }
+
+ lastTag = readRawVarint32();
+ if (WireFormat.getTagFieldNumber(lastTag) == 0) {
+ // If we actually read zero (or any tag number corresponding to field
+ // number zero), that's not a valid tag.
+ throw InvalidProtocolBufferException.invalidTag();
+ }
+ return lastTag;
+ }
+
+ @Override
+ public void checkLastTagWas(final int value) throws InvalidProtocolBufferException {
+ if (lastTag != value) {
+ throw InvalidProtocolBufferException.invalidEndTag();
+ }
+ }
+
+ @Override
+ public int getLastTag() {
+ return lastTag;
+ }
+
+ @Override
+ public boolean skipField(final int tag) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ skipRawVarint();
+ return true;
+ case WireFormat.WIRETYPE_FIXED64:
+ skipRawBytes(FIXED64_SIZE);
+ return true;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ skipRawBytes(readRawVarint32());
+ return true;
+ case WireFormat.WIRETYPE_START_GROUP:
+ skipMessage();
+ checkLastTagWas(
+ WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP));
+ return true;
+ case WireFormat.WIRETYPE_END_GROUP:
+ return false;
+ case WireFormat.WIRETYPE_FIXED32:
+ skipRawBytes(FIXED32_SIZE);
+ return true;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public boolean skipField(final int tag, final CodedOutputStream output) throws IOException {
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ {
+ long value = readInt64();
+ output.writeRawVarint32(tag);
+ output.writeUInt64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_FIXED64:
+ {
+ long value = readRawLittleEndian64();
+ output.writeRawVarint32(tag);
+ output.writeFixed64NoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ {
+ ByteString value = readBytes();
+ output.writeRawVarint32(tag);
+ output.writeBytesNoTag(value);
+ return true;
+ }
+ case WireFormat.WIRETYPE_START_GROUP:
+ {
+ output.writeRawVarint32(tag);
+ skipMessage(output);
+ int endtag =
+ WireFormat.makeTag(
+ WireFormat.getTagFieldNumber(tag), WireFormat.WIRETYPE_END_GROUP);
+ checkLastTagWas(endtag);
+ output.writeRawVarint32(endtag);
+ return true;
+ }
+ case WireFormat.WIRETYPE_END_GROUP:
+ {
+ return false;
+ }
+ case WireFormat.WIRETYPE_FIXED32:
+ {
+ int value = readRawLittleEndian32();
+ output.writeRawVarint32(tag);
+ output.writeFixed32NoTag(value);
+ return true;
+ }
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
+
+ @Override
+ public void skipMessage() throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag)) {
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void skipMessage(CodedOutputStream output) throws IOException {
+ while (true) {
+ final int tag = readTag();
+ if (tag == 0 || !skipField(tag, output)) {
+ return;
+ }
+ }
+ }
+
+ // -----------------------------------------------------------------
+
+ @Override
+ public double readDouble() throws IOException {
+ return Double.longBitsToDouble(readRawLittleEndian64());
+ }
+
+ @Override
+ public float readFloat() throws IOException {
+ return Float.intBitsToFloat(readRawLittleEndian32());
+ }
+
+ @Override
+ public long readUInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public long readInt64() throws IOException {
+ return readRawVarint64();
+ }
+
+ @Override
+ public int readInt32() throws IOException {
+ return readRawVarint32();
+ }
+
+ @Override
+ public long readFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
+
+ @Override
+ public int readFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
+
+ @Override
+ public boolean readBool() throws IOException {
+ return readRawVarint64() != 0;
+ }
+
+ @Override
+ public String readString() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) {
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size);
+ String result = new String(bytes, UTF_8);
+ currentByteBufferPos += size;
+ return result;
+ } else if (size > 0 && size <= remaining()) {
+ // TODO(yilunchong): To use an underlying bytes[] instead of allocating a new bytes[]
+ byte[] bytes = new byte[size];
+ readRawBytesTo(bytes, 0, size);
+ String result = new String(bytes, UTF_8);
+ return result;
+ }
+
+ if (size == 0) {
+ return "";
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ @Override
+ public String readStringRequireUtf8() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) {
+ if (ENABLE_CUSTOM_UTF8_DECODE) {
+ final int bufferPos = (int) (currentByteBufferPos - currentByteBufferStartPos);
+ String result = Utf8.decodeUtf8(currentByteBuffer, bufferPos, size);
+ currentByteBufferPos += size;
+ return result;
+ } else {
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size);
+ if (!Utf8.isValidUtf8(bytes)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ String result = new String(bytes, UTF_8);
+ currentByteBufferPos += size;
+ return result;
+ }
+ }
+ if (size >= 0 && size <= remaining()) {
+ byte[] bytes = new byte[size];
+ readRawBytesTo(bytes, 0, size);
+ if (ENABLE_CUSTOM_UTF8_DECODE) {
+ return Utf8.decodeUtf8(bytes, 0, size);
+ } else {
+ if (!Utf8.isValidUtf8(bytes)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ String result = new String(bytes, UTF_8);
+ return result;
+ }
+ }
+
+ if (size == 0) {
+ return "";
+ }
+ if (size <= 0) {
throw InvalidProtocolBufferException.negativeSize();
}
+ throw InvalidProtocolBufferException.truncatedMessage();
}
- // Verify that the message size so far has not exceeded sizeLimit.
- int currentMessageSize = totalBytesRetired + bufferPos + size;
- if (currentMessageSize > sizeLimit) {
- throw InvalidProtocolBufferException.sizeLimitExceeded();
+ @Override
+ public void readGroup(
+ final int fieldNumber,
+ final MessageLite.Builder builder,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
}
- // Verify that the message size so far has not exceeded currentLimit.
- if (currentMessageSize > currentLimit) {
- // Read to the end of the stream anyway.
- skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
+
+ @Override
+ public <T extends MessageLite> T readGroup(
+ final int fieldNumber,
+ final Parser<T> parser,
+ final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
+ --recursionDepth;
+ return result;
+ }
+
+ @Deprecated
+ @Override
+ public void readUnknownGroup(final int fieldNumber, final MessageLite.Builder builder)
+ throws IOException {
+ readGroup(fieldNumber, builder, ExtensionRegistryLite.getEmptyRegistry());
+ }
+
+ @Override
+ public void readMessage(
+ final MessageLite.Builder builder, final ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ final int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ builder.mergeFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ }
+
+
+ @Override
+ public <T extends MessageLite> T readMessage(
+ final Parser<T> parser, final ExtensionRegistryLite extensionRegistry) throws IOException {
+ int length = readRawVarint32();
+ if (recursionDepth >= recursionLimit) {
+ throw InvalidProtocolBufferException.recursionLimitExceeded();
+ }
+ final int oldLimit = pushLimit(length);
+ ++recursionDepth;
+ T result = parser.parsePartialFrom(this, extensionRegistry);
+ checkLastTagWas(0);
+ --recursionDepth;
+ popLimit(oldLimit);
+ return result;
+ }
+
+ @Override
+ public ByteString readBytes() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= currentByteBufferLimit - currentByteBufferPos) {
+ if (immutable && enableAliasing) {
+ final int idx = (int) (currentByteBufferPos - currentAddress);
+ final ByteString result = ByteString.wrap(slice(idx, idx + size));
+ currentByteBufferPos += size;
+ return result;
+ } else {
+ byte[] bytes;
+ bytes = new byte[size];
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size);
+ currentByteBufferPos += size;
+ return ByteString.wrap(bytes);
+ }
+ } else if (size > 0 && size <= remaining()) {
+ byte[] temp = new byte[size];
+ readRawBytesTo(temp, 0, size);
+ return ByteString.wrap(temp);
+ }
+
+ if (size == 0) {
+ return ByteString.EMPTY;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
throw InvalidProtocolBufferException.truncatedMessage();
}
- // We need the input stream to proceed.
- if (input == null) {
+ @Override
+ public byte[] readByteArray() throws IOException {
+ return readRawBytes(readRawVarint32());
+ }
+
+ @Override
+ public ByteBuffer readByteBuffer() throws IOException {
+ final int size = readRawVarint32();
+ if (size > 0 && size <= currentRemaining()) {
+ if (!immutable && enableAliasing) {
+ currentByteBufferPos += size;
+ return slice(
+ (int) (currentByteBufferPos - currentAddress - size),
+ (int) (currentByteBufferPos - currentAddress));
+ } else {
+ byte[] bytes = new byte[size];
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, size);
+ currentByteBufferPos += size;
+ return ByteBuffer.wrap(bytes);
+ }
+ } else if (size > 0 && size <= remaining()) {
+ byte[] temp = new byte[size];
+ readRawBytesTo(temp, 0, size);
+ return ByteBuffer.wrap(temp);
+ }
+
+ if (size == 0) {
+ return EMPTY_BYTE_BUFFER;
+ }
+ if (size < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
throw InvalidProtocolBufferException.truncatedMessage();
}
- final int originalBufferPos = bufferPos;
- final int bufferedBytes = bufferSize - bufferPos;
+ @Override
+ public int readUInt32() throws IOException {
+ return readRawVarint32();
+ }
- // Mark the current buffer consumed.
- totalBytesRetired += bufferSize;
- bufferPos = 0;
- bufferSize = 0;
+ @Override
+ public int readEnum() throws IOException {
+ return readRawVarint32();
+ }
- // Determine the number of bytes we need to read from the input stream.
- int sizeLeft = size - bufferedBytes;
- // TODO(nathanmittler): Consider using a value larger than BUFFER_SIZE.
- if (sizeLeft < BUFFER_SIZE || sizeLeft <= input.available()) {
- // Either the bytes we need are known to be available, or the required buffer is
- // within an allowed threshold - go ahead and allocate the buffer now.
- final byte[] bytes = new byte[size];
+ @Override
+ public int readSFixed32() throws IOException {
+ return readRawLittleEndian32();
+ }
- // Copy all of the buffered bytes to the result buffer.
- System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+ @Override
+ public long readSFixed64() throws IOException {
+ return readRawLittleEndian64();
+ }
- // Fill the remaining bytes from the input stream.
- int pos = bufferedBytes;
- while (pos < bytes.length) {
- int n = input.read(bytes, pos, size - pos);
- if (n == -1) {
- throw InvalidProtocolBufferException.truncatedMessage();
+ @Override
+ public int readSInt32() throws IOException {
+ return decodeZigZag32(readRawVarint32());
+ }
+
+ @Override
+ public long readSInt64() throws IOException {
+ return decodeZigZag64(readRawVarint64());
+ }
+
+ @Override
+ public int readRawVarint32() throws IOException {
+ fastpath:
+ {
+ long tempPos = currentByteBufferPos;
+
+ if (currentByteBufferLimit == currentByteBufferPos) {
+ break fastpath;
}
- totalBytesRetired += n;
- pos += n;
+
+ int x;
+ if ((x = UnsafeUtil.getByte(tempPos++)) >= 0) {
+ currentByteBufferPos++;
+ return x;
+ } else if (currentByteBufferLimit - currentByteBufferPos < 10) {
+ break fastpath;
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) {
+ x ^= (~0 << 7);
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) {
+ x ^= (~0 << 7) ^ (~0 << 14);
+ } else if ((x ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) {
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21);
+ } else {
+ int y = UnsafeUtil.getByte(tempPos++);
+ x ^= y << 28;
+ x ^= (~0 << 7) ^ (~0 << 14) ^ (~0 << 21) ^ (~0 << 28);
+ if (y < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0
+ && UnsafeUtil.getByte(tempPos++) < 0) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ currentByteBufferPos = tempPos;
+ return x;
}
+ return (int) readRawVarint64SlowPath();
+ }
- return bytes;
+ @Override
+ public long readRawVarint64() throws IOException {
+ fastpath:
+ {
+ long tempPos = currentByteBufferPos;
+
+ if (currentByteBufferLimit == currentByteBufferPos) {
+ break fastpath;
+ }
+
+ long x;
+ int y;
+ if ((y = UnsafeUtil.getByte(tempPos++)) >= 0) {
+ currentByteBufferPos++;
+ return y;
+ } else if (currentByteBufferLimit - currentByteBufferPos < 10) {
+ break fastpath;
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 7)) < 0) {
+ x = y ^ (~0 << 7);
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 14)) >= 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14));
+ } else if ((y ^= (UnsafeUtil.getByte(tempPos++) << 21)) < 0) {
+ x = y ^ ((~0 << 7) ^ (~0 << 14) ^ (~0 << 21));
+ } else if ((x = y ^ ((long) UnsafeUtil.getByte(tempPos++) << 28)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 35)) < 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 42)) >= 0L) {
+ x ^= (~0L << 7) ^ (~0L << 14) ^ (~0L << 21) ^ (~0L << 28) ^ (~0L << 35) ^ (~0L << 42);
+ } else if ((x ^= ((long) UnsafeUtil.getByte(tempPos++) << 49)) < 0L) {
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49);
+ } else {
+ x ^= ((long) UnsafeUtil.getByte(tempPos++) << 56);
+ x ^=
+ (~0L << 7)
+ ^ (~0L << 14)
+ ^ (~0L << 21)
+ ^ (~0L << 28)
+ ^ (~0L << 35)
+ ^ (~0L << 42)
+ ^ (~0L << 49)
+ ^ (~0L << 56);
+ if (x < 0L) {
+ if (UnsafeUtil.getByte(tempPos++) < 0L) {
+ break fastpath; // Will throw malformedVarint()
+ }
+ }
+ }
+ currentByteBufferPos = tempPos;
+ return x;
+ }
+ return readRawVarint64SlowPath();
}
- // The size is very large. For security reasons, we can't allocate the
- // entire byte array yet. The size comes directly from the input, so a
- // maliciously-crafted message could provide a bogus very large size in
- // order to trick the app into allocating a lot of memory. We avoid this
- // by allocating and reading only a small chunk at a time, so that the
- // malicious message must actually *be* extremely large to cause
- // problems. Meanwhile, we limit the allowed size of a message elsewhere.
- final List<byte[]> chunks = new ArrayList<byte[]>();
-
- while (sizeLeft > 0) {
- // TODO(nathanmittler): Consider using a value larger than BUFFER_SIZE.
- final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
- int pos = 0;
- while (pos < chunk.length) {
- final int n = input.read(chunk, pos, chunk.length - pos);
- if (n == -1) {
- throw InvalidProtocolBufferException.truncatedMessage();
+ @Override
+ long readRawVarint64SlowPath() throws IOException {
+ long result = 0;
+ for (int shift = 0; shift < 64; shift += 7) {
+ final byte b = readRawByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0) {
+ return result;
}
- totalBytesRetired += n;
- pos += n;
}
- sizeLeft -= chunk.length;
- chunks.add(chunk);
+ throw InvalidProtocolBufferException.malformedVarint();
}
- // OK, got everything. Now concatenate it all into one buffer.
- final byte[] bytes = new byte[size];
+ @Override
+ public int readRawLittleEndian32() throws IOException {
+ if (currentRemaining() >= FIXED32_SIZE) {
+ long tempPos = currentByteBufferPos;
+ currentByteBufferPos += FIXED32_SIZE;
+ return (((UnsafeUtil.getByte(tempPos) & 0xff))
+ | ((UnsafeUtil.getByte(tempPos + 1) & 0xff) << 8)
+ | ((UnsafeUtil.getByte(tempPos + 2) & 0xff) << 16)
+ | ((UnsafeUtil.getByte(tempPos + 3) & 0xff) << 24));
+ }
+ return ((readRawByte() & 0xff)
+ | ((readRawByte() & 0xff) << 8)
+ | ((readRawByte() & 0xff) << 16)
+ | ((readRawByte() & 0xff) << 24));
+ }
- // Start by copying the leftover bytes from this.buffer.
- System.arraycopy(buffer, originalBufferPos, bytes, 0, bufferedBytes);
+ @Override
+ public long readRawLittleEndian64() throws IOException {
+ if (currentRemaining() >= FIXED64_SIZE) {
+ long tempPos = currentByteBufferPos;
+ currentByteBufferPos += FIXED64_SIZE;
+ return (((UnsafeUtil.getByte(tempPos) & 0xffL))
+ | ((UnsafeUtil.getByte(tempPos + 1) & 0xffL) << 8)
+ | ((UnsafeUtil.getByte(tempPos + 2) & 0xffL) << 16)
+ | ((UnsafeUtil.getByte(tempPos + 3) & 0xffL) << 24)
+ | ((UnsafeUtil.getByte(tempPos + 4) & 0xffL) << 32)
+ | ((UnsafeUtil.getByte(tempPos + 5) & 0xffL) << 40)
+ | ((UnsafeUtil.getByte(tempPos + 6) & 0xffL) << 48)
+ | ((UnsafeUtil.getByte(tempPos + 7) & 0xffL) << 56));
+ }
+ return ((readRawByte() & 0xffL)
+ | ((readRawByte() & 0xffL) << 8)
+ | ((readRawByte() & 0xffL) << 16)
+ | ((readRawByte() & 0xffL) << 24)
+ | ((readRawByte() & 0xffL) << 32)
+ | ((readRawByte() & 0xffL) << 40)
+ | ((readRawByte() & 0xffL) << 48)
+ | ((readRawByte() & 0xffL) << 56));
+ }
- // And now all the chunks.
- int pos = bufferedBytes;
- for (final byte[] chunk : chunks) {
- System.arraycopy(chunk, 0, bytes, pos, chunk.length);
- pos += chunk.length;
+ @Override
+ public void enableAliasing(boolean enabled) {
+ this.enableAliasing = enabled;
}
- // Done.
- return bytes;
- }
+ @Override
+ public void resetSizeCounter() {
+ startOffset = (int) (totalBytesRead + currentByteBufferPos - currentByteBufferStartPos);
+ }
- /**
- * Reads and discards {@code size} bytes.
- *
- * @throws InvalidProtocolBufferException The end of the stream or the current
- * limit was reached.
- */
- public void skipRawBytes(final int size) throws IOException {
- if (size <= (bufferSize - bufferPos) && size >= 0) {
- // We have all the bytes we need already.
- bufferPos += size;
- } else {
- skipRawBytesSlowPath(size);
+ @Override
+ public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
+ if (byteLimit < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ byteLimit += getTotalBytesRead();
+ final int oldLimit = currentLimit;
+ if (byteLimit > oldLimit) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+ currentLimit = byteLimit;
+
+ recomputeBufferSizeAfterLimit();
+
+ return oldLimit;
}
- }
- /**
- * Exactly like skipRawBytes, but caller must have already checked the fast
- * path: (size <= (bufferSize - pos) && size >= 0)
- */
- private void skipRawBytesSlowPath(final int size) throws IOException {
- if (size < 0) {
- throw InvalidProtocolBufferException.negativeSize();
+ private void recomputeBufferSizeAfterLimit() {
+ totalBufferSize += bufferSizeAfterCurrentLimit;
+ final int bufferEnd = totalBufferSize - startOffset;
+ if (bufferEnd > currentLimit) {
+ // Limit is in current buffer.
+ bufferSizeAfterCurrentLimit = bufferEnd - currentLimit;
+ totalBufferSize -= bufferSizeAfterCurrentLimit;
+ } else {
+ bufferSizeAfterCurrentLimit = 0;
+ }
}
- if (totalBytesRetired + bufferPos + size > currentLimit) {
- // Read to the end of the stream anyway.
- skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
- // Then fail.
+ @Override
+ public void popLimit(final int oldLimit) {
+ currentLimit = oldLimit;
+ recomputeBufferSizeAfterLimit();
+ }
+
+ @Override
+ public int getBytesUntilLimit() {
+ if (currentLimit == Integer.MAX_VALUE) {
+ return -1;
+ }
+
+ return currentLimit - getTotalBytesRead();
+ }
+
+ @Override
+ public boolean isAtEnd() throws IOException {
+ return totalBytesRead + currentByteBufferPos - currentByteBufferStartPos == totalBufferSize;
+ }
+
+ @Override
+ public int getTotalBytesRead() {
+ return (int)
+ (totalBytesRead - startOffset + currentByteBufferPos - currentByteBufferStartPos);
+ }
+
+ @Override
+ public byte readRawByte() throws IOException {
+ if (currentRemaining() == 0) {
+ getNextByteBuffer();
+ }
+ return UnsafeUtil.getByte(currentByteBufferPos++);
+ }
+
+ @Override
+ public byte[] readRawBytes(final int length) throws IOException {
+ if (length >= 0 && length <= currentRemaining()) {
+ byte[] bytes = new byte[length];
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, 0, length);
+ currentByteBufferPos += length;
+ return bytes;
+ }
+ if (length >= 0 && length <= remaining()) {
+ byte[] bytes = new byte[length];
+ readRawBytesTo(bytes, 0, length);
+ return bytes;
+ }
+
+ if (length <= 0) {
+ if (length == 0) {
+ return EMPTY_BYTE_ARRAY;
+ } else {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ }
+
throw InvalidProtocolBufferException.truncatedMessage();
}
- // Skipping more bytes than are in the buffer. First skip what we have.
- int pos = bufferSize - bufferPos;
- bufferPos = bufferSize;
+ /**
+ * Try to get raw bytes from {@code input} with the size of {@code length} and copy to {@code
+ * bytes} array. If the size is bigger than the number of remaining bytes in the input, then
+ * throw {@code truncatedMessage} exception.
+ *
+ * @param bytes
+ * @param offset
+ * @param length
+ * @throws IOException
+ */
+ private void readRawBytesTo(byte[] bytes, int offset, final int length) throws IOException {
+ if (length >= 0 && length <= remaining()) {
+ int l = length;
+ while (l > 0) {
+ if (currentRemaining() == 0) {
+ getNextByteBuffer();
+ }
+ int bytesToCopy = Math.min(l, (int) currentRemaining());
+ UnsafeUtil.copyMemory(currentByteBufferPos, bytes, length - l + offset, bytesToCopy);
+ l -= bytesToCopy;
+ currentByteBufferPos += bytesToCopy;
+ }
+ return;
+ }
- // Keep refilling the buffer until we get to the point we wanted to skip to.
- // This has the side effect of ensuring the limits are updated correctly.
- refillBuffer(1);
- while (size - pos > bufferSize) {
- pos += bufferSize;
- bufferPos = bufferSize;
- refillBuffer(1);
+ if (length <= 0) {
+ if (length == 0) {
+ return;
+ } else {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ @Override
+ public void skipRawBytes(final int length) throws IOException {
+ if (length >= 0
+ && length
+ <= (totalBufferSize
+ - totalBytesRead
+ - currentByteBufferPos
+ + currentByteBufferStartPos)) {
+ // We have all the bytes we need already.
+ int l = length;
+ while (l > 0) {
+ if (currentRemaining() == 0) {
+ getNextByteBuffer();
+ }
+ int rl = Math.min(l, (int) currentRemaining());
+ l -= rl;
+ currentByteBufferPos += rl;
+ }
+ return;
+ }
+
+ if (length < 0) {
+ throw InvalidProtocolBufferException.negativeSize();
+ }
+ throw InvalidProtocolBufferException.truncatedMessage();
+ }
+
+ // TODO: optimize to fastpath
+ private void skipRawVarint() throws IOException {
+ for (int i = 0; i < MAX_VARINT_SIZE; i++) {
+ if (readRawByte() >= 0) {
+ return;
+ }
+ }
+ throw InvalidProtocolBufferException.malformedVarint();
}
- bufferPos = size - pos;
+ /**
+ * Try to get the number of remaining bytes in {@code input}.
+ *
+ * @return the number of remaining bytes in {@code input}.
+ */
+ private int remaining() {
+ return (int)
+ (totalBufferSize - totalBytesRead - currentByteBufferPos + currentByteBufferStartPos);
+ }
+
+ /**
+ * Try to get the number of remaining bytes in {@code currentByteBuffer}.
+ *
+ * @return the number of remaining bytes in {@code currentByteBuffer}
+ */
+ private long currentRemaining() {
+ return (currentByteBufferLimit - currentByteBufferPos);
+ }
+
+ private ByteBuffer slice(int begin, int end) throws IOException {
+ int prevPos = currentByteBuffer.position();
+ int prevLimit = currentByteBuffer.limit();
+ try {
+ currentByteBuffer.position(begin);
+ currentByteBuffer.limit(end);
+ return currentByteBuffer.slice();
+ } catch (IllegalArgumentException e) {
+ throw InvalidProtocolBufferException.truncatedMessage();
+ } finally {
+ currentByteBuffer.position(prevPos);
+ currentByteBuffer.limit(prevLimit);
+ }
+ }
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
index d8ebad21..7b1ac651 100644
--- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -30,11 +30,17 @@
package com.google.protobuf;
-import com.google.protobuf.Utf8.UnpairedSurrogateException;
+import static com.google.protobuf.WireFormat.FIXED32_SIZE;
+import static com.google.protobuf.WireFormat.FIXED64_SIZE;
+import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE;
+import static java.lang.Math.max;
+import com.google.protobuf.Utf8.UnpairedSurrogateException;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -49,20 +55,16 @@ import java.util.logging.Logger;
* you are writing some other format of your own design, use the latter.
*
* <p>This class is totally unsynchronized.
- *
- * @author kneton@google.com Kenton Varda
*/
-public final class CodedOutputStream {
-
+public abstract class CodedOutputStream extends ByteOutput {
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
+ private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations();
- // TODO(dweis): Consider migrating to a ByteBuffer.
- private final byte[] buffer;
- private final int limit;
- private int position;
- private int totalBytesWritten = 0;
-
- private final OutputStream output;
+ /**
+ * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead.
+ */
+ @Deprecated
+ public static final int LITTLE_ENDIAN_32_SIZE = FIXED32_SIZE;
/**
* The buffer size used in {@link #newInstance(OutputStream)}.
@@ -77,40 +79,33 @@ public final class CodedOutputStream {
* CodedOutputStream.
*/
static int computePreferredBufferSize(int dataLength) {
- if (dataLength > DEFAULT_BUFFER_SIZE) return DEFAULT_BUFFER_SIZE;
+ if (dataLength > DEFAULT_BUFFER_SIZE) {
+ return DEFAULT_BUFFER_SIZE;
+ }
return dataLength;
}
- private CodedOutputStream(final byte[] buffer, final int offset,
- final int length) {
- output = null;
- this.buffer = buffer;
- position = offset;
- limit = offset + length;
- }
-
- private CodedOutputStream(final OutputStream output, final byte[] buffer) {
- this.output = output;
- this.buffer = buffer;
- position = 0;
- limit = buffer.length;
- }
-
/**
- * Create a new {@code CodedOutputStream} wrapping the given
- * {@code OutputStream}.
+ * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream}.
+ *
+ * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or
+ * modify the provided byte arrays. Doing so may result in corrupted data, which would be
+ * difficult to debug.
*/
public static CodedOutputStream newInstance(final OutputStream output) {
return newInstance(output, DEFAULT_BUFFER_SIZE);
}
/**
- * Create a new {@code CodedOutputStream} wrapping the given
- * {@code OutputStream} with a given buffer size.
+ * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream} with a given
+ * buffer size.
+ *
+ * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or
+ * modify the provided byte arrays. Doing so may result in corrupted data, which would be
+ * difficult to debug.
*/
- public static CodedOutputStream newInstance(final OutputStream output,
- final int bufferSize) {
- return new CodedOutputStream(output, new byte[bufferSize]);
+ public static CodedOutputStream newInstance(final OutputStream output, final int bufferSize) {
+ return new OutputStreamEncoder(output, bufferSize);
}
/**
@@ -131,149 +126,197 @@ public final class CodedOutputStream {
* array is faster than writing to an {@code OutputStream}. See also
* {@link ByteString#newCodedBuilder}.
*/
- public static CodedOutputStream newInstance(final byte[] flatArray,
- final int offset,
- final int length) {
- return new CodedOutputStream(flatArray, offset, length);
+ public static CodedOutputStream newInstance(
+ final byte[] flatArray, final int offset, final int length) {
+ return new ArrayEncoder(flatArray, offset, length);
+ }
+
+ /** Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. */
+ public static CodedOutputStream newInstance(ByteBuffer buffer) {
+ if (buffer.hasArray()) {
+ return new HeapNioEncoder(buffer);
+ }
+ if (buffer.isDirect() && !buffer.isReadOnly()) {
+ return UnsafeDirectNioEncoder.isSupported()
+ ? newUnsafeInstance(buffer)
+ : newSafeInstance(buffer);
+ }
+ throw new IllegalArgumentException("ByteBuffer is read-only");
+ }
+
+ /** For testing purposes only. */
+ static CodedOutputStream newUnsafeInstance(ByteBuffer buffer) {
+ return new UnsafeDirectNioEncoder(buffer);
+ }
+
+ /** For testing purposes only. */
+ static CodedOutputStream newSafeInstance(ByteBuffer buffer) {
+ return new SafeDirectNioEncoder(buffer);
}
/**
- * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
+ * Configures serialization to be deterministic.
+ *
+ * <p>The deterministic serialization guarantees that for a given binary, equal (defined by the
+ * {@code equals()} methods in protos) messages will always be serialized to the same bytes. This
+ * implies:
+ *
+ * <ul>
+ * <li>repeated serialization of a message will return the same bytes
+ * <li>different processes of the same binary (which may be executing on different machines) will
+ * serialize equal messages to the same bytes.
+ * </ul>
+ *
+ * <p>Note the deterministic serialization is NOT canonical across languages; it is also unstable
+ * across different builds with schema changes due to unknown fields. Users who need canonical
+ * serialization, e.g. persistent storage in a canonical form, fingerprinting, etc, should define
+ * their own canonicalization specification and implement the serializer using reflection APIs
+ * rather than relying on this API.
+ *
+ * <p> Once set, the serializer will: (Note this is an implementation detail and may subject to
+ * change in the future)
+ *
+ * <ul>
+ * <li> sort map entries by keys in lexicographical order or numerical order. Note: For string
+ * keys, the order is based on comparing the Unicode value of each character in the strings.
+ * The order may be different from the deterministic serialization in other languages where
+ * maps are sorted on the lexicographical order of the UTF8 encoded keys.
+ * </ul>
*/
- public static CodedOutputStream newInstance(ByteBuffer byteBuffer) {
- return newInstance(byteBuffer, DEFAULT_BUFFER_SIZE);
+ public void useDeterministicSerialization() {
+ serializationDeterministic = true;
}
+ boolean isSerializationDeterministic() {
+ return serializationDeterministic;
+ }
+ private boolean serializationDeterministic;
+
/**
- * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
+ * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}.
+ *
+ * @deprecated the size parameter is no longer used since use of an internal buffer is useless
+ * (and wasteful) when writing to a {@link ByteBuffer}. Use {@link #newInstance(ByteBuffer)}
+ * instead.
*/
+ @Deprecated
public static CodedOutputStream newInstance(ByteBuffer byteBuffer,
- int bufferSize) {
- return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize);
+ @SuppressWarnings("unused") int unused) {
+ return newInstance(byteBuffer);
}
- private static class ByteBufferOutputStream extends OutputStream {
- private final ByteBuffer byteBuffer;
- public ByteBufferOutputStream(ByteBuffer byteBuffer) {
- this.byteBuffer = byteBuffer;
+ /**
+ * Create a new {@code CodedOutputStream} that writes to the provided {@link ByteOutput}.
+ *
+ * <p> NOTE: The {@link ByteOutput} <strong>MUST NOT</strong> modify the provided buffers. Doing
+ * so may result in corrupted data, which would be difficult to debug.
+ *
+ * @param byteOutput the output target for encoded bytes.
+ * @param bufferSize the size of the internal scratch buffer to be used for string encoding.
+ * Setting this to {@code 0} will disable buffering, requiring an allocation for each encoded
+ * string.
+ */
+ static CodedOutputStream newInstance(ByteOutput byteOutput, int bufferSize) {
+ if (bufferSize < 0) {
+ throw new IllegalArgumentException("bufferSize must be positive");
}
- @Override
- public void write(int b) throws IOException {
- byteBuffer.put((byte) b);
- }
+ return new ByteOutputEncoder(byteOutput, bufferSize);
+ }
- @Override
- public void write(byte[] data, int offset, int length) throws IOException {
- byteBuffer.put(data, offset, length);
- }
+ // Disallow construction outside of this class.
+ private CodedOutputStream() {
}
// -----------------------------------------------------------------
- /** Write a {@code double} field, including tag, to the stream. */
- public void writeDouble(final int fieldNumber, final double value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
- writeDoubleNoTag(value);
- }
+ /** Encode and write a tag. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeTag(int fieldNumber, int wireType) throws IOException;
- /** Write a {@code float} field, including tag, to the stream. */
- public void writeFloat(final int fieldNumber, final float value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
- writeFloatNoTag(value);
+ /** Write an {@code int32} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeInt32(int fieldNumber, int value) throws IOException;
+
+ /** Write a {@code uint32} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeUInt32(int fieldNumber, int value) throws IOException;
+
+ /** Write a {@code sint32} field, including tag, to the stream. */
+ public final void writeSInt32(final int fieldNumber, final int value) throws IOException {
+ writeUInt32(fieldNumber, encodeZigZag32(value));
}
- /** Write a {@code uint64} field, including tag, to the stream. */
- public void writeUInt64(final int fieldNumber, final long value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeUInt64NoTag(value);
+ /** Write a {@code fixed32} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeFixed32(int fieldNumber, int value) throws IOException;
+
+ /** Write an {@code sfixed32} field, including tag, to the stream. */
+ public final void writeSFixed32(final int fieldNumber, final int value) throws IOException {
+ writeFixed32(fieldNumber, value);
}
/** Write an {@code int64} field, including tag, to the stream. */
- public void writeInt64(final int fieldNumber, final long value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeInt64NoTag(value);
+ public final void writeInt64(final int fieldNumber, final long value) throws IOException {
+ writeUInt64(fieldNumber, value);
}
- /** Write an {@code int32} field, including tag, to the stream. */
- public void writeInt32(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeInt32NoTag(value);
- }
+ /** Write a {@code uint64} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeUInt64(int fieldNumber, long value) throws IOException;
- /** Write a {@code fixed64} field, including tag, to the stream. */
- public void writeFixed64(final int fieldNumber, final long value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
- writeFixed64NoTag(value);
+ /** Write an {@code sint64} field, including tag, to the stream. */
+ public final void writeSInt64(final int fieldNumber, final long value) throws IOException {
+ writeUInt64(fieldNumber, encodeZigZag64(value));
}
- /** Write a {@code fixed32} field, including tag, to the stream. */
- public void writeFixed32(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
- writeFixed32NoTag(value);
- }
+ /** Write a {@code fixed64} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeFixed64(int fieldNumber, long value) throws IOException;
- /** Write a {@code bool} field, including tag, to the stream. */
- public void writeBool(final int fieldNumber, final boolean value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeBoolNoTag(value);
+ /** Write an {@code sfixed64} field, including tag, to the stream. */
+ public final void writeSFixed64(final int fieldNumber, final long value) throws IOException {
+ writeFixed64(fieldNumber, value);
}
- /** Write a {@code string} field, including tag, to the stream. */
- public void writeString(final int fieldNumber, final String value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeStringNoTag(value);
+ /** Write a {@code float} field, including tag, to the stream. */
+ public final void writeFloat(final int fieldNumber, final float value) throws IOException {
+ writeFixed32(fieldNumber, Float.floatToRawIntBits(value));
}
- /** Write a {@code group} field, including tag, to the stream. */
- public void writeGroup(final int fieldNumber, final MessageLite value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
- writeGroupNoTag(value);
- writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
+ /** Write a {@code double} field, including tag, to the stream. */
+ public final void writeDouble(final int fieldNumber, final double value) throws IOException {
+ writeFixed64(fieldNumber, Double.doubleToRawLongBits(value));
}
+ /** Write a {@code bool} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeBool(int fieldNumber, boolean value) throws IOException;
- /** Write an embedded message field, including tag, to the stream. */
- public void writeMessage(final int fieldNumber, final MessageLite value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeMessageNoTag(value);
+ /**
+ * Write an enum field, including tag, to the stream. The provided value is the numeric
+ * value used to represent the enum value on the wire (not the enum ordinal value).
+ */
+ public final void writeEnum(final int fieldNumber, final int value) throws IOException {
+ writeInt32(fieldNumber, value);
}
+ /** Write a {@code string} field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeString(int fieldNumber, String value) throws IOException;
/** Write a {@code bytes} field, including tag, to the stream. */
- public void writeBytes(final int fieldNumber, final ByteString value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeBytesNoTag(value);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeBytes(int fieldNumber, ByteString value) throws IOException;
/** Write a {@code bytes} field, including tag, to the stream. */
- public void writeByteArray(final int fieldNumber, final byte[] value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeByteArrayNoTag(value);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeByteArray(int fieldNumber, byte[] value) throws IOException;
/** Write a {@code bytes} field, including tag, to the stream. */
- public void writeByteArray(final int fieldNumber,
- final byte[] value,
- final int offset,
- final int length)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeByteArrayNoTag(value, offset, length);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeByteArray(int fieldNumber, byte[] value, int offset, int length)
+ throws IOException;
/**
* Write a {@code bytes} field, including tag, to the stream.
@@ -285,317 +328,223 @@ public final class CodedOutputStream {
* of a ByteBuffer, you can call
* {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}.
*/
- public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
- writeByteBufferNoTag(value);
- }
-
- /** Write a {@code uint32} field, including tag, to the stream. */
- public void writeUInt32(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeUInt32NoTag(value);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException;
/**
- * Write an enum field, including tag, to the stream. Caller is responsible
- * for converting the enum value to its numeric value.
+ * Write a single byte.
*/
- public void writeEnum(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeEnumNoTag(value);
+ public final void writeRawByte(final byte value) throws IOException {
+ write(value);
}
- /** Write an {@code sfixed32} field, including tag, to the stream. */
- public void writeSFixed32(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
- writeSFixed32NoTag(value);
+ /** Write a single byte, represented by an integer value. */
+ public final void writeRawByte(final int value) throws IOException {
+ write((byte) value);
}
- /** Write an {@code sfixed64} field, including tag, to the stream. */
- public void writeSFixed64(final int fieldNumber, final long value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
- writeSFixed64NoTag(value);
+ /** Write an array of bytes. */
+ public final void writeRawBytes(final byte[] value) throws IOException {
+ write(value, 0, value.length);
}
- /** Write an {@code sint32} field, including tag, to the stream. */
- public void writeSInt32(final int fieldNumber, final int value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeSInt32NoTag(value);
+ /**
+ * Write part of an array of bytes.
+ */
+ public final void writeRawBytes(final byte[] value, int offset, int length) throws IOException {
+ write(value, offset, length);
}
- /** Write an {@code sint64} field, including tag, to the stream. */
- public void writeSInt64(final int fieldNumber, final long value)
- throws IOException {
- writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
- writeSInt64NoTag(value);
+ /** Write a byte string. */
+ public final void writeRawBytes(final ByteString value) throws IOException {
+ value.writeTo(this);
}
/**
+ * Write a ByteBuffer. This method will write all content of the ByteBuffer
+ * regardless of the current position and limit (i.e., the number of bytes
+ * to be written is value.capacity(), not value.remaining()). Furthermore,
+ * this method doesn't alter the state of the passed-in ByteBuffer. Its
+ * position, limit, mark, etc. will remain unchanged. If you only want to
+ * write the remaining bytes of a ByteBuffer, you can call
+ * {@code writeRawBytes(byteBuffer.slice())}.
+ */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeRawBytes(final ByteBuffer value) throws IOException;
+
+ /** Write an embedded message field, including tag, to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeMessage(final int fieldNumber, final MessageLite value)
+ throws IOException;
+
+
+ /**
* Write a MessageSet extension field to the stream. For historical reasons,
* the wire format differs from normal fields.
*/
- public void writeMessageSetExtension(final int fieldNumber,
- final MessageLite value)
- throws IOException {
- writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
- writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
- writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
- writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
+ throws IOException;
/**
* Write an unparsed MessageSet extension field to the stream. For
* historical reasons, the wire format differs from normal fields.
*/
- public void writeRawMessageSetExtension(final int fieldNumber,
- final ByteString value)
- throws IOException {
- writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
- writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
- writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
- writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
+ throws IOException;
// -----------------------------------------------------------------
- /** Write a {@code double} field to the stream. */
- public void writeDoubleNoTag(final double value) throws IOException {
- writeRawLittleEndian64(Double.doubleToRawLongBits(value));
- }
+ /** Write an {@code int32} field to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeInt32NoTag(final int value) throws IOException;
- /** Write a {@code float} field to the stream. */
- public void writeFloatNoTag(final float value) throws IOException {
- writeRawLittleEndian32(Float.floatToRawIntBits(value));
+ /** Write a {@code uint32} field to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeUInt32NoTag(int value) throws IOException;
+
+ /** Write a {@code sint32} field to the stream. */
+ public final void writeSInt32NoTag(final int value) throws IOException {
+ writeUInt32NoTag(encodeZigZag32(value));
}
- /** Write a {@code uint64} field to the stream. */
- public void writeUInt64NoTag(final long value) throws IOException {
- writeRawVarint64(value);
+ /** Write a {@code fixed32} field to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeFixed32NoTag(int value) throws IOException;
+
+ /** Write a {@code sfixed32} field to the stream. */
+ public final void writeSFixed32NoTag(final int value) throws IOException {
+ writeFixed32NoTag(value);
}
/** Write an {@code int64} field to the stream. */
- public void writeInt64NoTag(final long value) throws IOException {
- writeRawVarint64(value);
+ public final void writeInt64NoTag(final long value) throws IOException {
+ writeUInt64NoTag(value);
}
- /** Write an {@code int32} field to the stream. */
- public void writeInt32NoTag(final int value) throws IOException {
- if (value >= 0) {
- writeRawVarint32(value);
- } else {
- // Must sign-extend.
- writeRawVarint64(value);
- }
+ /** Write a {@code uint64} field to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeUInt64NoTag(long value) throws IOException;
+
+ /** Write a {@code sint64} field to the stream. */
+ public final void writeSInt64NoTag(final long value) throws IOException {
+ writeUInt64NoTag(encodeZigZag64(value));
}
/** Write a {@code fixed64} field to the stream. */
- public void writeFixed64NoTag(final long value) throws IOException {
- writeRawLittleEndian64(value);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeFixed64NoTag(long value) throws IOException;
- /** Write a {@code fixed32} field to the stream. */
- public void writeFixed32NoTag(final int value) throws IOException {
- writeRawLittleEndian32(value);
+ /** Write a {@code sfixed64} field to the stream. */
+ public final void writeSFixed64NoTag(final long value) throws IOException {
+ writeFixed64NoTag(value);
}
- /** Write a {@code bool} field to the stream. */
- public void writeBoolNoTag(final boolean value) throws IOException {
- writeRawByte(value ? 1 : 0);
+ /** Write a {@code float} field to the stream. */
+ public final void writeFloatNoTag(final float value) throws IOException {
+ writeFixed32NoTag(Float.floatToRawIntBits(value));
}
- /** Write a {@code string} field to the stream. */
- // TODO(dweis): Document behavior on ill-formed UTF-16 input.
- public void writeStringNoTag(final String value) throws IOException {
- try {
- efficientWriteStringNoTag(value);
- } catch (UnpairedSurrogateException e) {
- logger.log(Level.WARNING,
- "Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", e);
- inefficientWriteStringNoTag(value);
- }
+ /** Write a {@code double} field to the stream. */
+ public final void writeDoubleNoTag(final double value) throws IOException {
+ writeFixed64NoTag(Double.doubleToRawLongBits(value));
}
- /** Write a {@code string} field to the stream. */
- private void inefficientWriteStringNoTag(final String value) throws IOException {
- // Unfortunately there does not appear to be any way to tell Java to encode
- // UTF-8 directly into our buffer, so we have to let it create its own byte
- // array and then copy.
- // TODO(dweis): Consider using nio Charset methods instead.
- final byte[] bytes = value.getBytes(Internal.UTF_8);
- writeRawVarint32(bytes.length);
- writeRawBytes(bytes);
+ /** Write a {@code bool} field to the stream. */
+ public final void writeBoolNoTag(final boolean value) throws IOException {
+ write((byte) (value ? 1 : 0));
}
/**
- * Write a {@code string} field to the stream efficiently. If the {@code string} is malformed,
- * this method rolls back its changes and throws an {@link UnpairedSurrogateException} with the
- * intent that the caller will catch and retry with {@link #inefficientWriteStringNoTag(String)}.
- *
- * @param value the string to write to the stream
- *
- * @throws UnpairedSurrogateException when {@code value} is ill-formed UTF-16.
- */
- private void efficientWriteStringNoTag(final String value) throws IOException {
- // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
- // and at most 3 times of it. We take advantage of this in both branches below.
- final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR;
- final int maxLengthVarIntSize = computeRawVarint32Size(maxLength);
-
- // If we are streaming and the potential length is too big to fit in our buffer, we take the
- // slower path. Otherwise, we're good to try the fast path.
- if (output != null && maxLengthVarIntSize + maxLength > limit - position) {
- // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes()
- // does the same internally and then does *another copy* to return a byte[] of exactly the
- // right size. We can skip that copy and just writeRawBytes up to the actualLength of the
- // UTF-8 encoded bytes.
- final byte[] encodedBytes = new byte[maxLength];
- int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength);
- writeRawVarint32(actualLength);
- writeRawBytes(encodedBytes, 0, actualLength);
- } else {
- // Optimize for the case where we know this length results in a constant varint length as this
- // saves a pass for measuring the length of the string.
- final int minLengthVarIntSize = computeRawVarint32Size(value.length());
- int oldPosition = position;
- final int length;
- try {
- if (minLengthVarIntSize == maxLengthVarIntSize) {
- position = oldPosition + minLengthVarIntSize;
- int newPosition = Utf8.encode(value, buffer, position, limit - position);
- // Since this class is stateful and tracks the position, we rewind and store the state,
- // prepend the length, then reset it back to the end of the string.
- position = oldPosition;
- length = newPosition - oldPosition - minLengthVarIntSize;
- writeRawVarint32(length);
- position = newPosition;
- } else {
- length = Utf8.encodedLength(value);
- writeRawVarint32(length);
- position = Utf8.encode(value, buffer, position, limit - position);
- }
- } catch (UnpairedSurrogateException e) {
- // Be extra careful and restore the original position for retrying the write with the less
- // efficient path.
- position = oldPosition;
- throw e;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new OutOfSpaceException(e);
- }
- totalBytesWritten += length;
- }
- }
-
- /** Write a {@code group} field to the stream. */
- public void writeGroupNoTag(final MessageLite value) throws IOException {
- value.writeTo(this);
- }
-
-
- /** Write an embedded message field to the stream. */
- public void writeMessageNoTag(final MessageLite value) throws IOException {
- writeRawVarint32(value.getSerializedSize());
- value.writeTo(this);
+ * Write an enum field to the stream. The provided value is the numeric
+ * value used to represent the enum value on the wire (not the enum ordinal value).
+ */
+ public final void writeEnumNoTag(final int value) throws IOException {
+ writeInt32NoTag(value);
}
+ /** Write a {@code string} field to the stream. */
+ // TODO(dweis): Document behavior on ill-formed UTF-16 input.
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeStringNoTag(String value) throws IOException;
/** Write a {@code bytes} field to the stream. */
- public void writeBytesNoTag(final ByteString value) throws IOException {
- writeRawVarint32(value.size());
- writeRawBytes(value);
- }
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeBytesNoTag(final ByteString value) throws IOException;
/** Write a {@code bytes} field to the stream. */
- public void writeByteArrayNoTag(final byte[] value) throws IOException {
- writeRawVarint32(value.length);
- writeRawBytes(value);
+ public final void writeByteArrayNoTag(final byte[] value) throws IOException {
+ writeByteArrayNoTag(value, 0, value.length);
}
- /** Write a {@code bytes} field to the stream. */
- public void writeByteArrayNoTag(final byte[] value,
- final int offset,
- final int length) throws IOException {
- writeRawVarint32(length);
- writeRawBytes(value, offset, length);
- }
+ /** Write an embedded message field to the stream. */
+ // Abstract to avoid overhead of additional virtual method calls.
+ public abstract void writeMessageNoTag(final MessageLite value) throws IOException;
- /**
- * Write a {@code bytes} field to the stream. This method will write all
- * content of the ByteBuffer regardless of the current position and limit
- * (i.e., the number of bytes to be written is value.capacity(), not
- * value.remaining()). Furthermore, this method doesn't alter the state of
- * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain
- * unchanged. If you only want to write the remaining bytes of a ByteBuffer,
- * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}.
- */
- public void writeByteBufferNoTag(final ByteBuffer value) throws IOException {
- writeRawVarint32(value.capacity());
- writeRawBytes(value);
- }
- /** Write a {@code uint32} field to the stream. */
- public void writeUInt32NoTag(final int value) throws IOException {
- writeRawVarint32(value);
- }
+ //=================================================================
- /**
- * Write an enum field to the stream. Caller is responsible
- * for converting the enum value to its numeric value.
- */
- public void writeEnumNoTag(final int value) throws IOException {
- writeInt32NoTag(value);
- }
+ @ExperimentalApi
+ @Override
+ public abstract void write(byte value) throws IOException;
- /** Write an {@code sfixed32} field to the stream. */
- public void writeSFixed32NoTag(final int value) throws IOException {
- writeRawLittleEndian32(value);
- }
+ @ExperimentalApi
+ @Override
+ public abstract void write(byte[] value, int offset, int length) throws IOException;
- /** Write an {@code sfixed64} field to the stream. */
- public void writeSFixed64NoTag(final long value) throws IOException {
- writeRawLittleEndian64(value);
- }
+ @ExperimentalApi
+ @Override
+ public abstract void writeLazy(byte[] value, int offset, int length) throws IOException;
- /** Write an {@code sint32} field to the stream. */
- public void writeSInt32NoTag(final int value) throws IOException {
- writeRawVarint32(encodeZigZag32(value));
- }
+ @Override
+ public abstract void write(ByteBuffer value) throws IOException;
- /** Write an {@code sint64} field to the stream. */
- public void writeSInt64NoTag(final long value) throws IOException {
- writeRawVarint64(encodeZigZag64(value));
- }
+ @ExperimentalApi
+ @Override
+ public abstract void writeLazy(ByteBuffer value) throws IOException;
// =================================================================
+ // =================================================================
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code double} field, including tag.
+ * Compute the number of bytes that would be needed to encode an
+ * {@code int32} field, including tag.
*/
- public static int computeDoubleSize(final int fieldNumber,
- final double value) {
- return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value);
+ public static int computeInt32Size(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code float} field, including tag.
+ * {@code uint32} field, including tag.
*/
- public static int computeFloatSize(final int fieldNumber, final float value) {
- return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value);
+ public static int computeUInt32Size(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value);
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode an
+ * {@code sint32} field, including tag.
+ */
+ public static int computeSInt32Size(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code uint64} field, including tag.
+ * {@code fixed32} field, including tag.
*/
- public static int computeUInt64Size(final int fieldNumber, final long value) {
- return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value);
+ public static int computeFixed32Size(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value);
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode an
+ * {@code sfixed32} field, including tag.
+ */
+ public static int computeSFixed32Size(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value);
}
/**
@@ -607,73 +556,83 @@ public final class CodedOutputStream {
}
/**
+ * Compute the number of bytes that would be needed to encode a
+ * {@code uint64} field, including tag.
+ */
+ public static int computeUInt64Size(final int fieldNumber, final long value) {
+ return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value);
+ }
+
+ /**
* Compute the number of bytes that would be needed to encode an
- * {@code int32} field, including tag.
+ * {@code sint64} field, including tag.
*/
- public static int computeInt32Size(final int fieldNumber, final int value) {
- return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value);
+ public static int computeSInt64Size(final int fieldNumber, final long value) {
+ return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code fixed64} field, including tag.
*/
- public static int computeFixed64Size(final int fieldNumber,
- final long value) {
+ public static int computeFixed64Size(final int fieldNumber, final long value) {
return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value);
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code fixed32} field, including tag.
+ * Compute the number of bytes that would be needed to encode an
+ * {@code sfixed64} field, including tag.
*/
- public static int computeFixed32Size(final int fieldNumber,
- final int value) {
- return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value);
+ public static int computeSFixed64Size(final int fieldNumber, final long value) {
+ return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code bool} field, including tag.
+ * {@code float} field, including tag.
*/
- public static int computeBoolSize(final int fieldNumber,
- final boolean value) {
- return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value);
+ public static int computeFloatSize(final int fieldNumber, final float value) {
+ return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code string} field, including tag.
+ * {@code double} field, including tag.
*/
- public static int computeStringSize(final int fieldNumber,
- final String value) {
- return computeTagSize(fieldNumber) + computeStringSizeNoTag(value);
+ public static int computeDoubleSize(final int fieldNumber, final double value) {
+ return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code group} field, including tag.
+ * {@code bool} field, including tag.
*/
- public static int computeGroupSize(final int fieldNumber,
- final MessageLite value) {
- return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value);
+ public static int computeBoolSize(final int fieldNumber, final boolean value) {
+ return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode an
- * embedded message field, including tag.
+ * enum field, including tag. The provided value is the numeric
+ * value used to represent the enum value on the wire (not the enum ordinal value).
*/
- public static int computeMessageSize(final int fieldNumber,
- final MessageLite value) {
- return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value);
+ public static int computeEnumSize(final int fieldNumber, final int value) {
+ return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value);
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode a
+ * {@code string} field, including tag.
+ */
+ public static int computeStringSize(final int fieldNumber, final String value) {
+ return computeTagSize(fieldNumber) + computeStringSizeNoTag(value);
}
/**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field, including tag.
*/
- public static int computeBytesSize(final int fieldNumber,
- final ByteString value) {
+ public static int computeBytesSize(final int fieldNumber, final ByteString value) {
return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value);
}
@@ -681,8 +640,7 @@ public final class CodedOutputStream {
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field, including tag.
*/
- public static int computeByteArraySize(final int fieldNumber,
- final byte[] value) {
+ public static int computeByteArraySize(final int fieldNumber, final byte[] value) {
return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value);
}
@@ -690,8 +648,7 @@ public final class CodedOutputStream {
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field, including tag.
*/
- public static int computeByteBufferSize(final int fieldNumber,
- final ByteBuffer value) {
+ public static int computeByteBufferSize(final int fieldNumber, final ByteBuffer value) {
return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value);
}
@@ -699,167 +656,207 @@ public final class CodedOutputStream {
* Compute the number of bytes that would be needed to encode an
* embedded message in lazy field, including tag.
*/
- public static int computeLazyFieldSize(final int fieldNumber,
- final LazyFieldLite value) {
+ public static int computeLazyFieldSize(final int fieldNumber, final LazyFieldLite value) {
return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value);
}
/**
+ * Compute the number of bytes that would be needed to encode an
+ * embedded message field, including tag.
+ */
+ public static int computeMessageSize(final int fieldNumber, final MessageLite value) {
+ return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value);
+ }
+
+
+ /**
* Compute the number of bytes that would be needed to encode a
- * {@code uint32} field, including tag.
+ * MessageSet extension to the stream. For historical reasons,
+ * the wire format differs from normal fields.
*/
- public static int computeUInt32Size(final int fieldNumber, final int value) {
- return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value);
+ public static int computeMessageSetExtensionSize(final int fieldNumber, final MessageLite value) {
+ return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
+ + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
+ + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value);
}
/**
* Compute the number of bytes that would be needed to encode an
- * enum field, including tag. Caller is responsible for converting the
- * enum value to its numeric value.
+ * unparsed MessageSet extension field to the stream. For
+ * historical reasons, the wire format differs from normal fields.
*/
- public static int computeEnumSize(final int fieldNumber, final int value) {
- return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value);
+ public static int computeRawMessageSetExtensionSize(
+ final int fieldNumber, final ByteString value) {
+ return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
+ + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
+ + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value);
}
/**
* Compute the number of bytes that would be needed to encode an
- * {@code sfixed32} field, including tag.
+ * lazily parsed MessageSet extension field to the stream. For
+ * historical reasons, the wire format differs from normal fields.
*/
- public static int computeSFixed32Size(final int fieldNumber,
- final int value) {
- return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value);
+ public static int computeLazyFieldMessageSetExtensionSize(
+ final int fieldNumber, final LazyFieldLite value) {
+ return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
+ + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
+ + computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value);
+ }
+
+ // -----------------------------------------------------------------
+
+ /** Compute the number of bytes that would be needed to encode a tag. */
+ public static int computeTagSize(final int fieldNumber) {
+ return computeUInt32SizeNoTag(WireFormat.makeTag(fieldNumber, 0));
}
/**
* Compute the number of bytes that would be needed to encode an
- * {@code sfixed64} field, including tag.
+ * {@code int32} field, including tag.
*/
- public static int computeSFixed64Size(final int fieldNumber,
- final long value) {
- return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value);
+ public static int computeInt32SizeNoTag(final int value) {
+ if (value >= 0) {
+ return computeUInt32SizeNoTag(value);
+ } else {
+ // Must sign-extend.
+ return MAX_VARINT_SIZE;
+ }
}
/**
- * Compute the number of bytes that would be needed to encode an
- * {@code sint32} field, including tag.
+ * Compute the number of bytes that would be needed to encode a
+ * {@code uint32} field.
*/
- public static int computeSInt32Size(final int fieldNumber, final int value) {
- return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value);
+ public static int computeUInt32SizeNoTag(final int value) {
+ if ((value & (~0 << 7)) == 0) {
+ return 1;
+ }
+ if ((value & (~0 << 14)) == 0) {
+ return 2;
+ }
+ if ((value & (~0 << 21)) == 0) {
+ return 3;
+ }
+ if ((value & (~0 << 28)) == 0) {
+ return 4;
+ }
+ return 5;
}
/**
* Compute the number of bytes that would be needed to encode an
- * {@code sint64} field, including tag.
+ * {@code sint32} field.
*/
- public static int computeSInt64Size(final int fieldNumber, final long value) {
- return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value);
+ public static int computeSInt32SizeNoTag(final int value) {
+ return computeUInt32SizeNoTag(encodeZigZag32(value));
}
/**
* Compute the number of bytes that would be needed to encode a
- * MessageSet extension to the stream. For historical reasons,
- * the wire format differs from normal fields.
+ * {@code fixed32} field.
*/
- public static int computeMessageSetExtensionSize(
- final int fieldNumber, final MessageLite value) {
- return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 +
- computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) +
- computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value);
+ public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
+ return FIXED32_SIZE;
}
/**
* Compute the number of bytes that would be needed to encode an
- * unparsed MessageSet extension field to the stream. For
- * historical reasons, the wire format differs from normal fields.
+ * {@code sfixed32} field.
*/
- public static int computeRawMessageSetExtensionSize(
- final int fieldNumber, final ByteString value) {
- return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 +
- computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) +
- computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value);
+ public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
+ return FIXED32_SIZE;
}
/**
* Compute the number of bytes that would be needed to encode an
- * lazily parsed MessageSet extension field to the stream. For
- * historical reasons, the wire format differs from normal fields.
+ * {@code int64} field, including tag.
*/
- public static int computeLazyFieldMessageSetExtensionSize(
- final int fieldNumber, final LazyFieldLite value) {
- return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 +
- computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) +
- computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value);
+ public static int computeInt64SizeNoTag(final long value) {
+ return computeUInt64SizeNoTag(value);
}
- // -----------------------------------------------------------------
-
/**
* Compute the number of bytes that would be needed to encode a
- * {@code double} field, including tag.
+ * {@code uint64} field, including tag.
*/
- public static int computeDoubleSizeNoTag(final double value) {
- return LITTLE_ENDIAN_64_SIZE;
+ public static int computeUInt64SizeNoTag(long value) {
+ // handle two popular special cases up front ...
+ if ((value & (~0L << 7)) == 0L) {
+ return 1;
+ }
+ if (value < 0L) {
+ return 10;
+ }
+ // ... leaving us with 8 remaining, which we can divide and conquer
+ int n = 2;
+ if ((value & (~0L << 35)) != 0L) {
+ n += 4; value >>>= 28;
+ }
+ if ((value & (~0L << 21)) != 0L) {
+ n += 2; value >>>= 14;
+ }
+ if ((value & (~0L << 14)) != 0L) {
+ n += 1;
+ }
+ return n;
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code float} field, including tag.
+ * Compute the number of bytes that would be needed to encode an
+ * {@code sint64} field.
*/
- public static int computeFloatSizeNoTag(final float value) {
- return LITTLE_ENDIAN_32_SIZE;
+ public static int computeSInt64SizeNoTag(final long value) {
+ return computeUInt64SizeNoTag(encodeZigZag64(value));
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code uint64} field, including tag.
+ * {@code fixed64} field.
*/
- public static int computeUInt64SizeNoTag(final long value) {
- return computeRawVarint64Size(value);
+ public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
+ return FIXED64_SIZE;
}
/**
* Compute the number of bytes that would be needed to encode an
- * {@code int64} field, including tag.
+ * {@code sfixed64} field.
*/
- public static int computeInt64SizeNoTag(final long value) {
- return computeRawVarint64Size(value);
+ public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
+ return FIXED64_SIZE;
}
/**
- * Compute the number of bytes that would be needed to encode an
- * {@code int32} field, including tag.
+ * Compute the number of bytes that would be needed to encode a
+ * {@code float} field, including tag.
*/
- public static int computeInt32SizeNoTag(final int value) {
- if (value >= 0) {
- return computeRawVarint32Size(value);
- } else {
- // Must sign-extend.
- return 10;
- }
+ public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) {
+ return FIXED32_SIZE;
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code fixed64} field.
+ * {@code double} field, including tag.
*/
- public static int computeFixed64SizeNoTag(final long value) {
- return LITTLE_ENDIAN_64_SIZE;
+ public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) {
+ return FIXED64_SIZE;
}
/**
* Compute the number of bytes that would be needed to encode a
- * {@code fixed32} field.
+ * {@code bool} field.
*/
- public static int computeFixed32SizeNoTag(final int value) {
- return LITTLE_ENDIAN_32_SIZE;
+ public static int computeBoolSizeNoTag(@SuppressWarnings("unused") final boolean unused) {
+ return 1;
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code bool} field.
+ * Compute the number of bytes that would be needed to encode an enum field.
+ * The provided value is the numeric value used to represent the enum value on the wire
+ * (not the enum ordinal value).
*/
- public static int computeBoolSizeNoTag(final boolean value) {
- return 1;
+ public static int computeEnumSizeNoTag(final int value) {
+ return computeInt32SizeNoTag(value);
}
/**
@@ -876,24 +873,7 @@ public final class CodedOutputStream {
length = bytes.length;
}
- return computeRawVarint32Size(length) + length;
- }
-
- /**
- * Compute the number of bytes that would be needed to encode a
- * {@code group} field.
- */
- public static int computeGroupSizeNoTag(final MessageLite value) {
- return value.getSerializedSize();
- }
-
- /**
- * Compute the number of bytes that would be needed to encode an embedded
- * message field.
- */
- public static int computeMessageSizeNoTag(final MessageLite value) {
- final int size = value.getSerializedSize();
- return computeRawVarint32Size(size) + size;
+ return computeLengthDelimitedFieldSize(length);
}
/**
@@ -901,8 +881,7 @@ public final class CodedOutputStream {
* message stored in lazy field.
*/
public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) {
- final int size = value.getSerializedSize();
- return computeRawVarint32Size(size) + size;
+ return computeLengthDelimitedFieldSize(value.getSerializedSize());
}
/**
@@ -910,8 +889,7 @@ public final class CodedOutputStream {
* {@code bytes} field.
*/
public static int computeBytesSizeNoTag(final ByteString value) {
- return computeRawVarint32Size(value.size()) +
- value.size();
+ return computeLengthDelimitedFieldSize(value.size());
}
/**
@@ -919,7 +897,7 @@ public final class CodedOutputStream {
* {@code bytes} field.
*/
public static int computeByteArraySizeNoTag(final byte[] value) {
- return computeRawVarint32Size(value.length) + value.length;
+ return computeLengthDelimitedFieldSize(value.length);
}
/**
@@ -927,98 +905,65 @@ public final class CodedOutputStream {
* {@code bytes} field.
*/
public static int computeByteBufferSizeNoTag(final ByteBuffer value) {
- return computeRawVarint32Size(value.capacity()) + value.capacity();
+ return computeLengthDelimitedFieldSize(value.capacity());
}
/**
- * Compute the number of bytes that would be needed to encode a
- * {@code uint32} field.
+ * Compute the number of bytes that would be needed to encode an embedded
+ * message field.
*/
- public static int computeUInt32SizeNoTag(final int value) {
- return computeRawVarint32Size(value);
+ public static int computeMessageSizeNoTag(final MessageLite value) {
+ return computeLengthDelimitedFieldSize(value.getSerializedSize());
}
- /**
- * Compute the number of bytes that would be needed to encode an enum field.
- * Caller is responsible for converting the enum value to its numeric value.
- */
- public static int computeEnumSizeNoTag(final int value) {
- return computeInt32SizeNoTag(value);
- }
- /**
- * Compute the number of bytes that would be needed to encode an
- * {@code sfixed32} field.
- */
- public static int computeSFixed32SizeNoTag(final int value) {
- return LITTLE_ENDIAN_32_SIZE;
+ static int computeLengthDelimitedFieldSize(int fieldLength) {
+ return computeUInt32SizeNoTag(fieldLength) + fieldLength;
}
/**
- * Compute the number of bytes that would be needed to encode an
- * {@code sfixed64} field.
- */
- public static int computeSFixed64SizeNoTag(final long value) {
- return LITTLE_ENDIAN_64_SIZE;
- }
-
- /**
- * Compute the number of bytes that would be needed to encode an
- * {@code sint32} field.
+ * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
+ * into values that can be efficiently encoded with varint. (Otherwise,
+ * negative values must be sign-extended to 64 bits to be varint encoded,
+ * thus always taking 10 bytes on the wire.)
+ *
+ * @param n A signed 32-bit integer.
+ * @return An unsigned 32-bit integer, stored in a signed int because
+ * Java has no explicit unsigned support.
*/
- public static int computeSInt32SizeNoTag(final int value) {
- return computeRawVarint32Size(encodeZigZag32(value));
+ public static int encodeZigZag32(final int n) {
+ // Note: the right-shift must be arithmetic
+ return (n << 1) ^ (n >> 31);
}
/**
- * Compute the number of bytes that would be needed to encode an
- * {@code sint64} field.
+ * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
+ * into values that can be efficiently encoded with varint. (Otherwise,
+ * negative values must be sign-extended to 64 bits to be varint encoded,
+ * thus always taking 10 bytes on the wire.)
+ *
+ * @param n A signed 64-bit integer.
+ * @return An unsigned 64-bit integer, stored in a signed int because
+ * Java has no explicit unsigned support.
*/
- public static int computeSInt64SizeNoTag(final long value) {
- return computeRawVarint64Size(encodeZigZag64(value));
+ public static long encodeZigZag64(final long n) {
+ // Note: the right-shift must be arithmetic
+ return (n << 1) ^ (n >> 63);
}
// =================================================================
/**
- * Internal helper that writes the current buffer to the output. The
- * buffer position is reset to its initial value when this returns.
- */
- private void refreshBuffer() throws IOException {
- if (output == null) {
- // We're writing to a single buffer.
- throw new OutOfSpaceException();
- }
-
- // Since we have an output stream, this is our buffer
- // and buffer offset == 0
- output.write(buffer, 0, position);
- position = 0;
- }
-
- /**
* Flushes the stream and forces any buffered bytes to be written. This
* does not flush the underlying OutputStream.
*/
- public void flush() throws IOException {
- if (output != null) {
- refreshBuffer();
- }
- }
+ public abstract void flush() throws IOException;
/**
* If writing to a flat array, return the space left in the array.
* Otherwise, throws {@code UnsupportedOperationException}.
*/
- public int spaceLeft() {
- if (output == null) {
- return limit - position;
- } else {
- throw new UnsupportedOperationException(
- "spaceLeft() can only be called on CodedOutputStreams that are " +
- "writing to a flat array.");
- }
- }
+ public abstract int spaceLeft();
/**
* Verifies that {@link #spaceLeft()} returns zero. It's common to create
@@ -1027,10 +972,9 @@ public final class CodedOutputStream {
* after writing verifies that the message was actually as big as expected,
* which can help catch bugs.
*/
- public void checkNoSpaceLeft() {
+ public final void checkNoSpaceLeft() {
if (spaceLeft() != 0) {
- throw new IllegalStateException(
- "Did not write as much data as expected.");
+ throw new IllegalStateException("Did not write as much data as expected.");
}
}
@@ -1049,9 +993,17 @@ public final class CodedOutputStream {
super(MESSAGE);
}
+ OutOfSpaceException(String explanationMessage) {
+ super(MESSAGE + ": " + explanationMessage);
+ }
+
OutOfSpaceException(Throwable cause) {
super(MESSAGE, cause);
}
+
+ OutOfSpaceException(String explanationMessage, Throwable cause) {
+ super(MESSAGE + ": " + explanationMessage, cause);
+ }
}
/**
@@ -1059,274 +1011,1997 @@ public final class CodedOutputStream {
* returned value is not guaranteed to be accurate if exceptions have been
* found in the middle of writing.
*/
- public int getTotalBytesWritten() {
- return totalBytesWritten;
- }
+ public abstract int getTotalBytesWritten();
+
+ // =================================================================
- /** Write a single byte. */
- public void writeRawByte(final byte value) throws IOException {
- if (position == limit) {
- refreshBuffer();
+ /** Write a {@code bytes} field to the stream. Visible for testing. */
+ abstract void writeByteArrayNoTag(final byte[] value, final int offset, final int length)
+ throws IOException;
+
+ final void inefficientWriteStringNoTag(String value, UnpairedSurrogateException cause)
+ throws IOException {
+ logger.log(Level.WARNING,
+ "Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", cause);
+
+ // Unfortunately there does not appear to be any way to tell Java to encode
+ // UTF-8 directly into our buffer, so we have to let it create its own byte
+ // array and then copy.
+ // TODO(dweis): Consider using nio Charset methods instead.
+ final byte[] bytes = value.getBytes(Internal.UTF_8);
+ try {
+ writeUInt32NoTag(bytes.length);
+ writeLazy(bytes, 0, bytes.length);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ } catch (OutOfSpaceException e) {
+ throw e;
}
+ }
- buffer[position++] = value;
- ++totalBytesWritten;
+ // =================================================================
+
+ /**
+ * Write a {@code group} field, including tag, to the stream.
+ *
+ * @deprecated groups are deprecated.
+ */
+ @Deprecated
+ public final void writeGroup(final int fieldNumber, final MessageLite value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
+ writeGroupNoTag(value);
+ writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
}
- /** Write a single byte, represented by an integer value. */
- public void writeRawByte(final int value) throws IOException {
- writeRawByte((byte) value);
+
+ /**
+ * Write a {@code group} field to the stream.
+ *
+ * @deprecated groups are deprecated.
+ */
+ @Deprecated
+ public final void writeGroupNoTag(final MessageLite value) throws IOException {
+ value.writeTo(this);
}
- /** Write a byte string. */
- public void writeRawBytes(final ByteString value) throws IOException {
- writeRawBytes(value, 0, value.size());
+
+ /**
+ * Compute the number of bytes that would be needed to encode a
+ * {@code group} field, including tag.
+ *
+ * @deprecated groups are deprecated.
+ */
+ @Deprecated
+ public static int computeGroupSize(final int fieldNumber, final MessageLite value) {
+ return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value);
}
- /** Write an array of bytes. */
- public void writeRawBytes(final byte[] value) throws IOException {
- writeRawBytes(value, 0, value.length);
+
+ /**
+ * Compute the number of bytes that would be needed to encode a
+ * {@code group} field.
+ */
+ @Deprecated
+ public static int computeGroupSizeNoTag(final MessageLite value) {
+ return value.getSerializedSize();
}
+
/**
- * Write a ByteBuffer. This method will write all content of the ByteBuffer
- * regardless of the current position and limit (i.e., the number of bytes
- * to be written is value.capacity(), not value.remaining()). Furthermore,
- * this method doesn't alter the state of the passed-in ByteBuffer. Its
- * position, limit, mark, etc. will remain unchanged. If you only want to
- * write the remaining bytes of a ByteBuffer, you can call
- * {@code writeRawBytes(byteBuffer.slice())}.
+ * Encode and write a varint. {@code value} is treated as
+ * unsigned, so it won't be sign-extended if negative.
+ *
+ * @deprecated use {@link #writeUInt32NoTag} instead.
*/
- public void writeRawBytes(final ByteBuffer value) throws IOException {
- if (value.hasArray()) {
- writeRawBytes(value.array(), value.arrayOffset(), value.capacity());
- } else {
- ByteBuffer duplicated = value.duplicate();
- duplicated.clear();
- writeRawBytesInternal(duplicated);
- }
+ @Deprecated
+ public final void writeRawVarint32(int value) throws IOException {
+ writeUInt32NoTag(value);
}
- /** Write a ByteBuffer that isn't backed by an array. */
- private void writeRawBytesInternal(final ByteBuffer value)
- throws IOException {
- int length = value.remaining();
- if (limit - position >= length) {
- // We have room in the current buffer.
- value.get(buffer, position, length);
- position += length;
- totalBytesWritten += length;
- } else {
- // Write extends past current buffer. Fill the rest of this buffer and
- // flush.
- final int bytesWritten = limit - position;
- value.get(buffer, position, bytesWritten);
- length -= bytesWritten;
- position = limit;
- totalBytesWritten += bytesWritten;
- refreshBuffer();
-
- // Now deal with the rest.
- // Since we have an output stream, this is our buffer
- // and buffer offset == 0
- while (length > limit) {
- // Copy data into the buffer before writing it to OutputStream.
- // TODO(xiaofeng): Introduce ZeroCopyOutputStream to avoid this copy.
- value.get(buffer, 0, limit);
- output.write(buffer, 0, limit);
- length -= limit;
- totalBytesWritten += limit;
+ /**
+ * Encode and write a varint.
+ *
+ * @deprecated use {@link #writeUInt64NoTag} instead.
+ */
+ @Deprecated
+ public final void writeRawVarint64(long value) throws IOException {
+ writeUInt64NoTag(value);
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode a varint.
+ * {@code value} is treated as unsigned, so it won't be sign-extended if
+ * negative.
+ *
+ * @deprecated use {@link #computeUInt32SizeNoTag(int)} instead.
+ */
+ @Deprecated
+ public static int computeRawVarint32Size(final int value) {
+ return computeUInt32SizeNoTag(value);
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode a varint.
+ *
+ * @deprecated use {@link #computeUInt64SizeNoTag(long)} instead.
+ */
+ @Deprecated
+ public static int computeRawVarint64Size(long value) {
+ return computeUInt64SizeNoTag(value);
+ }
+
+ /**
+ * Write a little-endian 32-bit integer.
+ *
+ * @deprecated Use {@link #writeFixed32NoTag} instead.
+ */
+ @Deprecated
+ public final void writeRawLittleEndian32(final int value) throws IOException {
+ writeFixed32NoTag(value);
+ }
+
+ /**
+ * Write a little-endian 64-bit integer.
+ *
+ * @deprecated Use {@link #writeFixed64NoTag} instead.
+ */
+ @Deprecated
+ public final void writeRawLittleEndian64(final long value) throws IOException {
+ writeFixed64NoTag(value);
+ }
+
+ // =================================================================
+
+ /**
+ * A {@link CodedOutputStream} that writes directly to a byte array.
+ */
+ private static class ArrayEncoder extends CodedOutputStream {
+ private final byte[] buffer;
+ private final int offset;
+ private final int limit;
+ private int position;
+
+ ArrayEncoder(byte[] buffer, int offset, int length) {
+ if (buffer == null) {
+ throw new NullPointerException("buffer");
}
- value.get(buffer, 0, length);
- position = length;
- totalBytesWritten += length;
+ if ((offset | length | (buffer.length - (offset + length))) < 0) {
+ throw new IllegalArgumentException(String.format(
+ "Array range is invalid. Buffer.length=%d, offset=%d, length=%d",
+ buffer.length, offset, length));
+ }
+ this.buffer = buffer;
+ this.offset = offset;
+ position = offset;
+ limit = offset + length;
}
- }
- /** Write part of an array of bytes. */
- public void writeRawBytes(final byte[] value, int offset, int length)
- throws IOException {
- if (limit - position >= length) {
- // We have room in the current buffer.
- System.arraycopy(value, offset, buffer, position, length);
- position += length;
- totalBytesWritten += length;
- } else {
- // Write extends past current buffer. Fill the rest of this buffer and
- // flush.
- final int bytesWritten = limit - position;
- System.arraycopy(value, offset, buffer, position, bytesWritten);
- offset += bytesWritten;
- length -= bytesWritten;
- position = limit;
- totalBytesWritten += bytesWritten;
- refreshBuffer();
-
- // Now deal with the rest.
- // Since we have an output stream, this is our buffer
- // and buffer offset == 0
- if (length <= limit) {
- // Fits in new buffer.
- System.arraycopy(value, offset, buffer, 0, length);
- position = length;
+ @Override
+ public final void writeTag(final int fieldNumber, final int wireType) throws IOException {
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ @Override
+ public final void writeInt32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeInt32NoTag(value);
+ }
+
+ @Override
+ public final void writeUInt32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt32NoTag(value);
+ }
+
+ @Override
+ public final void writeFixed32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
+ writeFixed32NoTag(value);
+ }
+
+ @Override
+ public final void writeUInt64(final int fieldNumber, final long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt64NoTag(value);
+ }
+
+ @Override
+ public final void writeFixed64(final int fieldNumber, final long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
+ writeFixed64NoTag(value);
+ }
+
+ @Override
+ public final void writeBool(final int fieldNumber, final boolean value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ public final void writeString(final int fieldNumber, final String value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeStringNoTag(value);
+ }
+
+ @Override
+ public final void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeBytesNoTag(value);
+ }
+
+ @Override
+ public final void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
+ writeByteArray(fieldNumber, value, 0, value.length);
+ }
+
+ @Override
+ public final void writeByteArray(
+ final int fieldNumber, final byte[] value, final int offset, final int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ @Override
+ public final void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeUInt32NoTag(value.capacity());
+ writeRawBytes(value);
+ }
+
+ @Override
+ public final void writeBytesNoTag(final ByteString value) throws IOException {
+ writeUInt32NoTag(value.size());
+ value.writeTo(this);
+ }
+
+ @Override
+ public final void writeByteArrayNoTag(final byte[] value, int offset, int length)
+ throws IOException {
+ writeUInt32NoTag(length);
+ write(value, offset, length);
+ }
+
+ @Override
+ public final void writeRawBytes(final ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ write(value.array(), value.arrayOffset(), value.capacity());
} else {
- // Write is very big. Let's do it all at once.
- output.write(value, offset, length);
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ write(duplicated);
}
- totalBytesWritten += length;
}
- }
- /** Write part of a byte string. */
- public void writeRawBytes(final ByteString value, int offset, int length)
- throws IOException {
- if (limit - position >= length) {
- // We have room in the current buffer.
- value.copyTo(buffer, offset, position, length);
- position += length;
- totalBytesWritten += length;
- } else {
- // Write extends past current buffer. Fill the rest of this buffer and
- // flush.
- final int bytesWritten = limit - position;
- value.copyTo(buffer, offset, position, bytesWritten);
- offset += bytesWritten;
- length -= bytesWritten;
- position = limit;
- totalBytesWritten += bytesWritten;
- refreshBuffer();
-
- // Now deal with the rest.
- // Since we have an output stream, this is our buffer
- // and buffer offset == 0
- if (length <= limit) {
- // Fits in new buffer.
- value.copyTo(buffer, offset, 0, length);
- position = length;
+ @Override
+ public final void writeMessage(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeMessageNoTag(value);
+ }
+
+
+ @Override
+ public final void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public final void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public final void writeMessageNoTag(final MessageLite value) throws IOException {
+ writeUInt32NoTag(value.getSerializedSize());
+ value.writeTo(this);
+ }
+
+
+ @Override
+ public final void write(byte value) throws IOException {
+ try {
+ buffer[position++] = value;
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
+ }
+ }
+
+ @Override
+ public final void writeInt32NoTag(int value) throws IOException {
+ if (value >= 0) {
+ writeUInt32NoTag(value);
} else {
- value.writeTo(output, offset, length);
+ // Must sign-extend.
+ writeUInt64NoTag(value);
}
- totalBytesWritten += length;
}
- }
- /** Encode and write a tag. */
- public void writeTag(final int fieldNumber, final int wireType)
- throws IOException {
- writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType));
+ @Override
+ public final void writeUInt32NoTag(int value) throws IOException {
+ if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ UnsafeUtil.putByte(buffer, position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } else {
+ try {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ buffer[position++] = (byte) value;
+ return;
+ } else {
+ buffer[position++] = (byte) ((value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
+ }
+ }
+ }
+
+ @Override
+ public final void writeFixed32NoTag(int value) throws IOException {
+ try {
+ buffer[position++] = (byte) (value & 0xFF);
+ buffer[position++] = (byte) ((value >> 8) & 0xFF);
+ buffer[position++] = (byte) ((value >> 16) & 0xFF);
+ buffer[position++] = (byte) ((value >> 24) & 0xFF);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
+ }
+ }
+
+ @Override
+ public final void writeUInt64NoTag(long value) throws IOException {
+ if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ UnsafeUtil.putByte(buffer, position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } else {
+ try {
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ buffer[position++] = (byte) value;
+ return;
+ } else {
+ buffer[position++] = (byte) (((int) value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
+ }
+ }
+ }
+
+ @Override
+ public final void writeFixed64NoTag(long value) throws IOException {
+ try {
+ buffer[position++] = (byte) ((int) (value) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 8) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 16) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 24) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 32) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 40) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 48) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e);
+ }
+ }
+
+ @Override
+ public final void write(byte[] value, int offset, int length) throws IOException {
+ try {
+ System.arraycopy(value, offset, buffer, position, length);
+ position += length;
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
+ }
+ }
+
+ @Override
+ public final void writeLazy(byte[] value, int offset, int length) throws IOException {
+ write(value, offset, length);
+ }
+
+ @Override
+ public final void write(ByteBuffer value) throws IOException {
+ final int length = value.remaining();
+ try {
+ value.get(buffer, position, length);
+ position += length;
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e);
+ }
+ }
+
+ @Override
+ public final void writeLazy(ByteBuffer value) throws IOException {
+ write(value);
+ }
+
+ @Override
+ public final void writeStringNoTag(String value) throws IOException {
+ final int oldPosition = position;
+ try {
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+ // and at most 3 times of it. We take advantage of this in both branches below.
+ final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR;
+ final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength);
+ final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length());
+ if (minLengthVarIntSize == maxLengthVarIntSize) {
+ position = oldPosition + minLengthVarIntSize;
+ int newPosition = Utf8.encode(value, buffer, position, spaceLeft());
+ // Since this class is stateful and tracks the position, we rewind and store the state,
+ // prepend the length, then reset it back to the end of the string.
+ position = oldPosition;
+ int length = newPosition - oldPosition - minLengthVarIntSize;
+ writeUInt32NoTag(length);
+ position = newPosition;
+ } else {
+ int length = Utf8.encodedLength(value);
+ writeUInt32NoTag(length);
+ position = Utf8.encode(value, buffer, position, spaceLeft());
+ }
+ } catch (UnpairedSurrogateException e) {
+ // Roll back the change - we fall back to inefficient path.
+ position = oldPosition;
+
+ // TODO(nathanmittler): We should throw an IOException here instead.
+ inefficientWriteStringNoTag(value, e);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void flush() {
+ // Do nothing.
+ }
+
+ @Override
+ public final int spaceLeft() {
+ return limit - position;
+ }
+
+ @Override
+ public final int getTotalBytesWritten() {
+ return position - offset;
+ }
}
- /** Compute the number of bytes that would be needed to encode a tag. */
- public static int computeTagSize(final int fieldNumber) {
- return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0));
+ /**
+ * A {@link CodedOutputStream} that writes directly to a heap {@link ByteBuffer}. Writes are
+ * done directly to the underlying array. The buffer position is only updated after a flush.
+ */
+ private static final class HeapNioEncoder extends ArrayEncoder {
+ private final ByteBuffer byteBuffer;
+ private int initialPosition;
+
+ HeapNioEncoder(ByteBuffer byteBuffer) {
+ super(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(),
+ byteBuffer.remaining());
+ this.byteBuffer = byteBuffer;
+ this.initialPosition = byteBuffer.position();
+ }
+
+ @Override
+ public void flush() {
+ // Update the position on the buffer.
+ byteBuffer.position(initialPosition + getTotalBytesWritten());
+ }
}
/**
- * Encode and write a varint. {@code value} is treated as
- * unsigned, so it won't be sign-extended if negative.
+ * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer}, using only
+ * safe operations..
*/
- public void writeRawVarint32(int value) throws IOException {
- while (true) {
- if ((value & ~0x7F) == 0) {
- writeRawByte(value);
- return;
+ private static final class SafeDirectNioEncoder extends CodedOutputStream {
+ private final ByteBuffer originalBuffer;
+ private final ByteBuffer buffer;
+ private final int initialPosition;
+
+ SafeDirectNioEncoder(ByteBuffer buffer) {
+ this.originalBuffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ initialPosition = buffer.position();
+ }
+
+ @Override
+ public void writeTag(final int fieldNumber, final int wireType) throws IOException {
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ @Override
+ public void writeInt32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeInt32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32(final int fieldNumber, final int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
+ writeFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64(final int fieldNumber, final long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64(final int fieldNumber, final long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
+ writeFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeBool(final int fieldNumber, final boolean value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ public void writeString(final int fieldNumber, final String value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeStringNoTag(value);
+ }
+
+ @Override
+ public void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeBytesNoTag(value);
+ }
+
+ @Override
+ public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
+ writeByteArray(fieldNumber, value, 0, value.length);
+ }
+
+ @Override
+ public void writeByteArray(
+ final int fieldNumber, final byte[] value, final int offset, final int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ @Override
+ public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeUInt32NoTag(value.capacity());
+ writeRawBytes(value);
+ }
+
+ @Override
+ public void writeMessage(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeMessageNoTag(value);
+ }
+
+
+ @Override
+ public void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeMessageNoTag(final MessageLite value) throws IOException {
+ writeUInt32NoTag(value.getSerializedSize());
+ value.writeTo(this);
+ }
+
+
+ @Override
+ public void write(byte value) throws IOException {
+ try {
+ buffer.put(value);
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeBytesNoTag(final ByteString value) throws IOException {
+ writeUInt32NoTag(value.size());
+ value.writeTo(this);
+ }
+
+ @Override
+ public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException {
+ writeUInt32NoTag(length);
+ write(value, offset, length);
+ }
+
+ @Override
+ public void writeRawBytes(final ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ write(value.array(), value.arrayOffset(), value.capacity());
} else {
- writeRawByte((value & 0x7F) | 0x80);
- value >>>= 7;
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ write(duplicated);
+ }
+ }
+
+ @Override
+ public void writeInt32NoTag(int value) throws IOException {
+ if (value >= 0) {
+ writeUInt32NoTag(value);
+ } else {
+ // Must sign-extend.
+ writeUInt64NoTag(value);
+ }
+ }
+
+ @Override
+ public void writeUInt32NoTag(int value) throws IOException {
+ try {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ buffer.put((byte) value);
+ return;
+ } else {
+ buffer.put((byte) ((value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeFixed32NoTag(int value) throws IOException {
+ try {
+ buffer.putInt(value);
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeUInt64NoTag(long value) throws IOException {
+ try {
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ buffer.put((byte) value);
+ return;
+ } else {
+ buffer.put((byte) (((int) value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeFixed64NoTag(long value) throws IOException {
+ try {
+ buffer.putLong(value);
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) throws IOException {
+ try {
+ buffer.put(value, offset, length);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ write(value, offset, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ try {
+ buffer.put(value);
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ write(value);
+ }
+
+ @Override
+ public void writeStringNoTag(String value) throws IOException {
+ final int startPos = buffer.position();
+ try {
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+ // and at most 3 times of it. We take advantage of this in both branches below.
+ final int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR;
+ final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize);
+ final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length());
+ if (minLengthVarIntSize == maxLengthVarIntSize) {
+ // Save the current position and increment past the length field. We'll come back
+ // and write the length field after the encoding is complete.
+ final int startOfBytes = buffer.position() + minLengthVarIntSize;
+ buffer.position(startOfBytes);
+
+ // Encode the string.
+ encode(value);
+
+ // Now go back to the beginning and write the length.
+ int endOfBytes = buffer.position();
+ buffer.position(startPos);
+ writeUInt32NoTag(endOfBytes - startOfBytes);
+
+ // Reposition the buffer past the written data.
+ buffer.position(endOfBytes);
+ } else {
+ final int length = Utf8.encodedLength(value);
+ writeUInt32NoTag(length);
+ encode(value);
+ }
+ } catch (UnpairedSurrogateException e) {
+ // Roll back the change and convert to an IOException.
+ buffer.position(startPos);
+
+ // TODO(nathanmittler): We should throw an IOException here instead.
+ inefficientWriteStringNoTag(value, e);
+ } catch (IllegalArgumentException e) {
+ // Thrown by buffer.position() if out of range.
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void flush() {
+ // Update the position of the original buffer.
+ originalBuffer.position(buffer.position());
+ }
+
+ @Override
+ public int spaceLeft() {
+ return buffer.remaining();
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return buffer.position() - initialPosition;
+ }
+
+ private void encode(String value) throws IOException {
+ try {
+ Utf8.encodeUtf8(value, buffer);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
}
}
}
/**
- * Compute the number of bytes that would be needed to encode a varint.
- * {@code value} is treated as unsigned, so it won't be sign-extended if
- * negative.
+ * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer} using {@code
+ * sun.misc.Unsafe}.
*/
- public static int computeRawVarint32Size(final int value) {
- if ((value & (~0 << 7)) == 0) return 1;
- if ((value & (~0 << 14)) == 0) return 2;
- if ((value & (~0 << 21)) == 0) return 3;
- if ((value & (~0 << 28)) == 0) return 4;
- return 5;
- }
+ private static final class UnsafeDirectNioEncoder extends CodedOutputStream {
+ private final ByteBuffer originalBuffer;
+ private final ByteBuffer buffer;
+ private final long address;
+ private final long initialPosition;
+ private final long limit;
+ private final long oneVarintLimit;
+ private long position;
- /** Encode and write a varint. */
- public void writeRawVarint64(long value) throws IOException {
- while (true) {
- if ((value & ~0x7FL) == 0) {
- writeRawByte((int)value);
- return;
+ UnsafeDirectNioEncoder(ByteBuffer buffer) {
+ this.originalBuffer = buffer;
+ this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+ address = UnsafeUtil.addressOffset(buffer);
+ initialPosition = address + buffer.position();
+ limit = address + buffer.limit();
+ oneVarintLimit = limit - MAX_VARINT_SIZE;
+ position = initialPosition;
+ }
+
+ static boolean isSupported() {
+ return UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ @Override
+ public void writeTag(int fieldNumber, int wireType) throws IOException {
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ @Override
+ public void writeInt32(int fieldNumber, int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeInt32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt32(int fieldNumber, int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32(int fieldNumber, int value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
+ writeFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64(int fieldNumber, long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ writeUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64(int fieldNumber, long value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
+ writeFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeBool(int fieldNumber, boolean value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ write((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ public void writeString(int fieldNumber, String value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeStringNoTag(value);
+ }
+
+ @Override
+ public void writeBytes(int fieldNumber, ByteString value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeBytesNoTag(value);
+ }
+
+ @Override
+ public void writeByteArray(int fieldNumber, byte[] value) throws IOException {
+ writeByteArray(fieldNumber, value, 0, value.length);
+ }
+
+ @Override
+ public void writeByteArray(int fieldNumber, byte[] value, int offset, int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ @Override
+ public void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeUInt32NoTag(value.capacity());
+ writeRawBytes(value);
+ }
+
+ @Override
+ public void writeMessage(int fieldNumber, MessageLite value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeMessageNoTag(value);
+ }
+
+
+ @Override
+ public void writeMessageSetExtension(int fieldNumber, MessageLite value) throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeRawMessageSetExtension(int fieldNumber, ByteString value) throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeMessageNoTag(MessageLite value) throws IOException {
+ writeUInt32NoTag(value.getSerializedSize());
+ value.writeTo(this);
+ }
+
+
+ @Override
+ public void write(byte value) throws IOException {
+ if (position >= limit) {
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1));
+ }
+ UnsafeUtil.putByte(position++, value);
+ }
+
+ @Override
+ public void writeBytesNoTag(ByteString value) throws IOException {
+ writeUInt32NoTag(value.size());
+ value.writeTo(this);
+ }
+
+ @Override
+ public void writeByteArrayNoTag(byte[] value, int offset, int length) throws IOException {
+ writeUInt32NoTag(length);
+ write(value, offset, length);
+ }
+
+ @Override
+ public void writeRawBytes(ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ write(value.array(), value.arrayOffset(), value.capacity());
} else {
- writeRawByte(((int)value & 0x7F) | 0x80);
- value >>>= 7;
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ write(duplicated);
}
}
- }
- /** Compute the number of bytes that would be needed to encode a varint. */
- public static int computeRawVarint64Size(long value) {
- // handle two popular special cases up front ...
- if ((value & (~0L << 7)) == 0L) return 1;
- if (value < 0L) return 10;
- // ... leaving us with 8 remaining, which we can divide and conquer
- int n = 2;
- if ((value & (~0L << 35)) != 0L) { n += 4; value >>>= 28; }
- if ((value & (~0L << 21)) != 0L) { n += 2; value >>>= 14; }
- if ((value & (~0L << 14)) != 0L) { n += 1; }
- return n;
- }
+ @Override
+ public void writeInt32NoTag(int value) throws IOException {
+ if (value >= 0) {
+ writeUInt32NoTag(value);
+ } else {
+ // Must sign-extend.
+ writeUInt64NoTag(value);
+ }
+ }
- /** Write a little-endian 32-bit integer. */
- public void writeRawLittleEndian32(final int value) throws IOException {
- writeRawByte((value ) & 0xFF);
- writeRawByte((value >> 8) & 0xFF);
- writeRawByte((value >> 16) & 0xFF);
- writeRawByte((value >> 24) & 0xFF);
- }
+ @Override
+ public void writeUInt32NoTag(int value) throws IOException {
+ if (position <= oneVarintLimit) {
+ // Optimization to avoid bounds checks on each iteration.
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ UnsafeUtil.putByte(position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } else {
+ while (position < limit) {
+ if ((value & ~0x7F) == 0) {
+ UnsafeUtil.putByte(position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1));
+ }
+ }
+
+ @Override
+ public void writeFixed32NoTag(int value) throws IOException {
+ buffer.putInt(bufferPos(position), value);
+ position += FIXED32_SIZE;
+ }
+
+ @Override
+ public void writeUInt64NoTag(long value) throws IOException {
+ if (position <= oneVarintLimit) {
+ // Optimization to avoid bounds checks on each iteration.
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ UnsafeUtil.putByte(position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ } else {
+ while (position < limit) {
+ if ((value & ~0x7FL) == 0) {
+ UnsafeUtil.putByte(position++, (byte) value);
+ return;
+ } else {
+ UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1));
+ }
+ }
+
+ @Override
+ public void writeFixed64NoTag(long value) throws IOException {
+ buffer.putLong(bufferPos(position), value);
+ position += FIXED64_SIZE;
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) throws IOException {
+ if (value == null
+ || offset < 0
+ || length < 0
+ || (value.length - length) < offset
+ || (limit - length) < position) {
+ if (value == null) {
+ throw new NullPointerException("value");
+ }
+ throw new OutOfSpaceException(
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length));
+ }
+
+ UnsafeUtil.copyMemory(value, offset, position, length);
+ position += length;
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ write(value, offset, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ try {
+ int length = value.remaining();
+ repositionBuffer(position);
+ buffer.put(value);
+ position += length;
+ } catch (BufferOverflowException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ write(value);
+ }
+
+ @Override
+ public void writeStringNoTag(String value) throws IOException {
+ long prevPos = position;
+ try {
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+ // and at most 3 times of it. We take advantage of this in both branches below.
+ int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR;
+ int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize);
+ int minLengthVarIntSize = computeUInt32SizeNoTag(value.length());
+ if (minLengthVarIntSize == maxLengthVarIntSize) {
+ // Save the current position and increment past the length field. We'll come back
+ // and write the length field after the encoding is complete.
+ int stringStart = bufferPos(position) + minLengthVarIntSize;
+ buffer.position(stringStart);
+
+ // Encode the string.
+ Utf8.encodeUtf8(value, buffer);
+
+ // Write the length and advance the position.
+ int length = buffer.position() - stringStart;
+ writeUInt32NoTag(length);
+ position += length;
+ } else {
+ // Calculate and write the encoded length.
+ int length = Utf8.encodedLength(value);
+ writeUInt32NoTag(length);
+
+ // Write the string and advance the position.
+ repositionBuffer(position);
+ Utf8.encodeUtf8(value, buffer);
+ position += length;
+ }
+ } catch (UnpairedSurrogateException e) {
+ // Roll back the change and convert to an IOException.
+ position = prevPos;
+ repositionBuffer(position);
+
+ // TODO(nathanmittler): We should throw an IOException here instead.
+ inefficientWriteStringNoTag(value, e);
+ } catch (IllegalArgumentException e) {
+ // Thrown by buffer.position() if out of range.
+ throw new OutOfSpaceException(e);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
- public static final int LITTLE_ENDIAN_32_SIZE = 4;
+ @Override
+ public void flush() {
+ // Update the position of the original buffer.
+ originalBuffer.position(bufferPos(position));
+ }
- /** Write a little-endian 64-bit integer. */
- public void writeRawLittleEndian64(final long value) throws IOException {
- writeRawByte((int)(value ) & 0xFF);
- writeRawByte((int)(value >> 8) & 0xFF);
- writeRawByte((int)(value >> 16) & 0xFF);
- writeRawByte((int)(value >> 24) & 0xFF);
- writeRawByte((int)(value >> 32) & 0xFF);
- writeRawByte((int)(value >> 40) & 0xFF);
- writeRawByte((int)(value >> 48) & 0xFF);
- writeRawByte((int)(value >> 56) & 0xFF);
+ @Override
+ public int spaceLeft() {
+ return (int) (limit - position);
+ }
+
+ @Override
+ public int getTotalBytesWritten() {
+ return (int) (position - initialPosition);
+ }
+
+ private void repositionBuffer(long pos) {
+ buffer.position(bufferPos(pos));
+ }
+
+ private int bufferPos(long pos) {
+ return (int) (pos - address);
+ }
}
- public static final int LITTLE_ENDIAN_64_SIZE = 8;
+ /**
+ * Abstract base class for buffered encoders.
+ */
+ private abstract static class AbstractBufferedEncoder extends CodedOutputStream {
+ final byte[] buffer;
+ final int limit;
+ int position;
+ int totalBytesWritten;
+
+ AbstractBufferedEncoder(int bufferSize) {
+ if (bufferSize < 0) {
+ throw new IllegalArgumentException("bufferSize must be >= 0");
+ }
+ // As an optimization, we require that the buffer be able to store at least 2
+ // varints so that we can buffer any integer write (tag + value). This reduces the
+ // number of range checks for a single write to 1 (i.e. if there is not enough space
+ // to buffer the tag+value, flush and then buffer it).
+ this.buffer = new byte[max(bufferSize, MAX_VARINT_SIZE * 2)];
+ this.limit = buffer.length;
+ }
+
+ @Override
+ public final int spaceLeft() {
+ throw new UnsupportedOperationException(
+ "spaceLeft() can only be called on CodedOutputStreams that are "
+ + "writing to a flat array or ByteBuffer.");
+ }
+
+ @Override
+ public final int getTotalBytesWritten() {
+ return totalBytesWritten;
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void buffer(byte value) {
+ buffer[position++] = value;
+ totalBytesWritten++;
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferTag(final int fieldNumber, final int wireType) {
+ bufferUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferInt32NoTag(final int value) {
+ if (value >= 0) {
+ bufferUInt32NoTag(value);
+ } else {
+ // Must sign-extend.
+ bufferUInt64NoTag(value);
+ }
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferUInt32NoTag(int value) {
+ if (HAS_UNSAFE_ARRAY_OPERATIONS) {
+ final long originalPos = position;
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ UnsafeUtil.putByte(buffer, position++, (byte) value);
+ break;
+ } else {
+ UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ int delta = (int) (position - originalPos);
+ totalBytesWritten += delta;
+ } else {
+ while (true) {
+ if ((value & ~0x7F) == 0) {
+ buffer[position++] = (byte) value;
+ totalBytesWritten++;
+ return;
+ } else {
+ buffer[position++] = (byte) ((value & 0x7F) | 0x80);
+ totalBytesWritten++;
+ value >>>= 7;
+ }
+ }
+ }
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferUInt64NoTag(long value) {
+ if (HAS_UNSAFE_ARRAY_OPERATIONS) {
+ final long originalPos = position;
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ UnsafeUtil.putByte(buffer, position++, (byte) value);
+ break;
+ } else {
+ UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80));
+ value >>>= 7;
+ }
+ }
+ int delta = (int) (position - originalPos);
+ totalBytesWritten += delta;
+ } else {
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ buffer[position++] = (byte) value;
+ totalBytesWritten++;
+ return;
+ } else {
+ buffer[position++] = (byte) (((int) value & 0x7F) | 0x80);
+ totalBytesWritten++;
+ value >>>= 7;
+ }
+ }
+ }
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferFixed32NoTag(int value) {
+ buffer[position++] = (byte) (value & 0xFF);
+ buffer[position++] = (byte) ((value >> 8) & 0xFF);
+ buffer[position++] = (byte) ((value >> 16) & 0xFF);
+ buffer[position++] = (byte) ((value >> 24) & 0xFF);
+ totalBytesWritten += FIXED32_SIZE;
+ }
+
+ /**
+ * This method does not perform bounds checking on the array. Checking array bounds is the
+ * responsibility of the caller.
+ */
+ final void bufferFixed64NoTag(long value) {
+ buffer[position++] = (byte) (value & 0xFF);
+ buffer[position++] = (byte) ((value >> 8) & 0xFF);
+ buffer[position++] = (byte) ((value >> 16) & 0xFF);
+ buffer[position++] = (byte) ((value >> 24) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 32) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 40) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 48) & 0xFF);
+ buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
+ totalBytesWritten += FIXED64_SIZE;
+ }
+ }
/**
- * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
- * into values that can be efficiently encoded with varint. (Otherwise,
- * negative values must be sign-extended to 64 bits to be varint encoded,
- * thus always taking 10 bytes on the wire.)
- *
- * @param n A signed 32-bit integer.
- * @return An unsigned 32-bit integer, stored in a signed int because
- * Java has no explicit unsigned support.
+ * A {@link CodedOutputStream} that decorates a {@link ByteOutput}. It internal buffer only to
+ * support string encoding operations. All other writes are just passed through to the
+ * {@link ByteOutput}.
*/
- public static int encodeZigZag32(final int n) {
- // Note: the right-shift must be arithmetic
- return (n << 1) ^ (n >> 31);
+ private static final class ByteOutputEncoder extends AbstractBufferedEncoder {
+ private final ByteOutput out;
+
+ ByteOutputEncoder(ByteOutput out, int bufferSize) {
+ super(bufferSize);
+ if (out == null) {
+ throw new NullPointerException("out");
+ }
+ this.out = out;
+ }
+
+ @Override
+ public void writeTag(final int fieldNumber, final int wireType) throws IOException {
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ @Override
+ public void writeInt32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferInt32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + FIXED32_SIZE);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
+ bufferFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64(final int fieldNumber, final long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64(final int fieldNumber, final long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + FIXED64_SIZE);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
+ bufferFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeBool(final int fieldNumber, final boolean value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + 1);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ buffer((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ public void writeString(final int fieldNumber, final String value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeStringNoTag(value);
+ }
+
+ @Override
+ public void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeBytesNoTag(value);
+ }
+
+ @Override
+ public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
+ writeByteArray(fieldNumber, value, 0, value.length);
+ }
+
+ @Override
+ public void writeByteArray(
+ final int fieldNumber, final byte[] value, final int offset, final int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ @Override
+ public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeUInt32NoTag(value.capacity());
+ writeRawBytes(value);
+ }
+
+ @Override
+ public void writeBytesNoTag(final ByteString value) throws IOException {
+ writeUInt32NoTag(value.size());
+ value.writeTo(this);
+ }
+
+ @Override
+ public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException {
+ writeUInt32NoTag(length);
+ write(value, offset, length);
+ }
+
+ @Override
+ public void writeRawBytes(final ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ write(value.array(), value.arrayOffset(), value.capacity());
+ } else {
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ write(duplicated);
+ }
+ }
+
+ @Override
+ public void writeMessage(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeMessageNoTag(value);
+ }
+
+
+ @Override
+ public void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeMessageNoTag(final MessageLite value) throws IOException {
+ writeUInt32NoTag(value.getSerializedSize());
+ value.writeTo(this);
+ }
+
+
+ @Override
+ public void write(byte value) throws IOException {
+ if (position == limit) {
+ doFlush();
+ }
+
+ buffer(value);
+ }
+
+ @Override
+ public void writeInt32NoTag(int value) throws IOException {
+ if (value >= 0) {
+ writeUInt32NoTag(value);
+ } else {
+ // Must sign-extend.
+ writeUInt64NoTag(value);
+ }
+ }
+
+ @Override
+ public void writeUInt32NoTag(int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE);
+ bufferUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32NoTag(final int value) throws IOException {
+ flushIfNotAvailable(FIXED32_SIZE);
+ bufferFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64NoTag(long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE);
+ bufferUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64NoTag(final long value) throws IOException {
+ flushIfNotAvailable(FIXED64_SIZE);
+ bufferFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeStringNoTag(String value) throws IOException {
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+ // and at most 3 times of it. We take advantage of this in both branches below.
+ final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR;
+ final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength);
+
+ // If we are streaming and the potential length is too big to fit in our buffer, we take the
+ // slower path.
+ if (maxLengthVarIntSize + maxLength > limit) {
+ // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes()
+ // does the same internally and then does *another copy* to return a byte[] of exactly the
+ // right size. We can skip that copy and just writeRawBytes up to the actualLength of the
+ // UTF-8 encoded bytes.
+ final byte[] encodedBytes = new byte[maxLength];
+ int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength);
+ writeUInt32NoTag(actualLength);
+ writeLazy(encodedBytes, 0, actualLength);
+ return;
+ }
+
+ // Fast path: we have enough space available in our buffer for the string...
+ if (maxLengthVarIntSize + maxLength > limit - position) {
+ // Flush to free up space.
+ doFlush();
+ }
+
+ final int oldPosition = position;
+ try {
+ // Optimize for the case where we know this length results in a constant varint length as
+ // this saves a pass for measuring the length of the string.
+ final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length());
+
+ if (minLengthVarIntSize == maxLengthVarIntSize) {
+ position = oldPosition + minLengthVarIntSize;
+ int newPosition = Utf8.encode(value, buffer, position, limit - position);
+ // Since this class is stateful and tracks the position, we rewind and store the state,
+ // prepend the length, then reset it back to the end of the string.
+ position = oldPosition;
+ int length = newPosition - oldPosition - minLengthVarIntSize;
+ bufferUInt32NoTag(length);
+ position = newPosition;
+ totalBytesWritten += length;
+ } else {
+ int length = Utf8.encodedLength(value);
+ bufferUInt32NoTag(length);
+ position = Utf8.encode(value, buffer, position, length);
+ totalBytesWritten += length;
+ }
+ } catch (UnpairedSurrogateException e) {
+ // Roll back the change and convert to an IOException.
+ totalBytesWritten -= position - oldPosition;
+ position = oldPosition;
+
+ // TODO(nathanmittler): We should throw an IOException here instead.
+ inefficientWriteStringNoTag(value, e);
+ } catch (IndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (position > 0) {
+ // Flush the buffer.
+ doFlush();
+ }
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) throws IOException {
+ flush();
+ out.write(value, offset, length);
+ totalBytesWritten += length;
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ flush();
+ out.writeLazy(value, offset, length);
+ totalBytesWritten += length;
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ flush();
+ int length = value.remaining();
+ out.write(value);
+ totalBytesWritten += length;
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ flush();
+ int length = value.remaining();
+ out.writeLazy(value);
+ totalBytesWritten += length;
+ }
+
+ private void flushIfNotAvailable(int requiredSize) throws IOException {
+ if (limit - position < requiredSize) {
+ doFlush();
+ }
+ }
+
+ private void doFlush() throws IOException {
+ out.write(buffer, 0, position);
+ position = 0;
+ }
}
/**
- * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
- * into values that can be efficiently encoded with varint. (Otherwise,
- * negative values must be sign-extended to 64 bits to be varint encoded,
- * thus always taking 10 bytes on the wire.)
- *
- * @param n A signed 64-bit integer.
- * @return An unsigned 64-bit integer, stored in a signed int because
- * Java has no explicit unsigned support.
+ * An {@link CodedOutputStream} that decorates an {@link OutputStream}. It performs internal
+ * buffering to optimize writes to the {@link OutputStream}.
*/
- public static long encodeZigZag64(final long n) {
- // Note: the right-shift must be arithmetic
- return (n << 1) ^ (n >> 63);
+ private static final class OutputStreamEncoder extends AbstractBufferedEncoder {
+ private final OutputStream out;
+
+ OutputStreamEncoder(OutputStream out, int bufferSize) {
+ super(bufferSize);
+ if (out == null) {
+ throw new NullPointerException("out");
+ }
+ this.out = out;
+ }
+
+ @Override
+ public void writeTag(final int fieldNumber, final int wireType) throws IOException {
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
+ }
+
+ @Override
+ public void writeInt32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferInt32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32(final int fieldNumber, final int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + FIXED32_SIZE);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
+ bufferFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64(final int fieldNumber, final long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE * 2);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ bufferUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64(final int fieldNumber, final long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + FIXED64_SIZE);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
+ bufferFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeBool(final int fieldNumber, final boolean value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE + 1);
+ bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
+ buffer((byte) (value ? 1 : 0));
+ }
+
+ @Override
+ public void writeString(final int fieldNumber, final String value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeStringNoTag(value);
+ }
+
+ @Override
+ public void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeBytesNoTag(value);
+ }
+
+ @Override
+ public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
+ writeByteArray(fieldNumber, value, 0, value.length);
+ }
+
+ @Override
+ public void writeByteArray(
+ final int fieldNumber, final byte[] value, final int offset, final int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ @Override
+ public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeUInt32NoTag(value.capacity());
+ writeRawBytes(value);
+ }
+
+ @Override
+ public void writeBytesNoTag(final ByteString value) throws IOException {
+ writeUInt32NoTag(value.size());
+ value.writeTo(this);
+ }
+
+ @Override
+ public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException {
+ writeUInt32NoTag(length);
+ write(value, offset, length);
+ }
+
+ @Override
+ public void writeRawBytes(final ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ write(value.array(), value.arrayOffset(), value.capacity());
+ } else {
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ write(duplicated);
+ }
+ }
+
+ @Override
+ public void writeMessage(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeMessageNoTag(value);
+ }
+
+
+ @Override
+ public void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
+ throws IOException {
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP);
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber);
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value);
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP);
+ }
+
+ @Override
+ public void writeMessageNoTag(final MessageLite value) throws IOException {
+ writeUInt32NoTag(value.getSerializedSize());
+ value.writeTo(this);
+ }
+
+
+ @Override
+ public void write(byte value) throws IOException {
+ if (position == limit) {
+ doFlush();
+ }
+
+ buffer(value);
+ }
+
+ @Override
+ public void writeInt32NoTag(int value) throws IOException {
+ if (value >= 0) {
+ writeUInt32NoTag(value);
+ } else {
+ // Must sign-extend.
+ writeUInt64NoTag(value);
+ }
+ }
+
+ @Override
+ public void writeUInt32NoTag(int value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE);
+ bufferUInt32NoTag(value);
+ }
+
+ @Override
+ public void writeFixed32NoTag(final int value) throws IOException {
+ flushIfNotAvailable(FIXED32_SIZE);
+ bufferFixed32NoTag(value);
+ }
+
+ @Override
+ public void writeUInt64NoTag(long value) throws IOException {
+ flushIfNotAvailable(MAX_VARINT_SIZE);
+ bufferUInt64NoTag(value);
+ }
+
+ @Override
+ public void writeFixed64NoTag(final long value) throws IOException {
+ flushIfNotAvailable(FIXED64_SIZE);
+ bufferFixed64NoTag(value);
+ }
+
+ @Override
+ public void writeStringNoTag(String value) throws IOException {
+ try {
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
+ // and at most 3 times of it. We take advantage of this in both branches below.
+ final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR;
+ final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength);
+
+ // If we are streaming and the potential length is too big to fit in our buffer, we take the
+ // slower path.
+ if (maxLengthVarIntSize + maxLength > limit) {
+ // Allocate a byte[] that we know can fit the string and encode into it. String.getBytes()
+ // does the same internally and then does *another copy* to return a byte[] of exactly the
+ // right size. We can skip that copy and just writeRawBytes up to the actualLength of the
+ // UTF-8 encoded bytes.
+ final byte[] encodedBytes = new byte[maxLength];
+ int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength);
+ writeUInt32NoTag(actualLength);
+ writeLazy(encodedBytes, 0, actualLength);
+ return;
+ }
+
+ // Fast path: we have enough space available in our buffer for the string...
+ if (maxLengthVarIntSize + maxLength > limit - position) {
+ // Flush to free up space.
+ doFlush();
+ }
+
+ // Optimize for the case where we know this length results in a constant varint length as
+ // this saves a pass for measuring the length of the string.
+ final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length());
+ int oldPosition = position;
+ final int length;
+ try {
+ if (minLengthVarIntSize == maxLengthVarIntSize) {
+ position = oldPosition + minLengthVarIntSize;
+ int newPosition = Utf8.encode(value, buffer, position, limit - position);
+ // Since this class is stateful and tracks the position, we rewind and store the
+ // state, prepend the length, then reset it back to the end of the string.
+ position = oldPosition;
+ length = newPosition - oldPosition - minLengthVarIntSize;
+ bufferUInt32NoTag(length);
+ position = newPosition;
+ } else {
+ length = Utf8.encodedLength(value);
+ bufferUInt32NoTag(length);
+ position = Utf8.encode(value, buffer, position, length);
+ }
+ totalBytesWritten += length;
+ } catch (UnpairedSurrogateException e) {
+ // Be extra careful and restore the original position for retrying the write with the
+ // less efficient path.
+ totalBytesWritten -= position - oldPosition;
+ position = oldPosition;
+ throw e;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new OutOfSpaceException(e);
+ }
+ } catch (UnpairedSurrogateException e) {
+ inefficientWriteStringNoTag(value, e);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ if (position > 0) {
+ // Flush the buffer.
+ doFlush();
+ }
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length)
+ throws IOException {
+ if (limit - position >= length) {
+ // We have room in the current buffer.
+ System.arraycopy(value, offset, buffer, position, length);
+ position += length;
+ totalBytesWritten += length;
+ } else {
+ // Write extends past current buffer. Fill the rest of this buffer and
+ // flush.
+ final int bytesWritten = limit - position;
+ System.arraycopy(value, offset, buffer, position, bytesWritten);
+ offset += bytesWritten;
+ length -= bytesWritten;
+ position = limit;
+ totalBytesWritten += bytesWritten;
+ doFlush();
+
+ // Now deal with the rest.
+ // Since we have an output stream, this is our buffer
+ // and buffer offset == 0
+ if (length <= limit) {
+ // Fits in new buffer.
+ System.arraycopy(value, offset, buffer, 0, length);
+ position = length;
+ } else {
+ // Write is very big. Let's do it all at once.
+ out.write(value, offset, length);
+ }
+ totalBytesWritten += length;
+ }
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ write(value, offset, length);
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ int length = value.remaining();
+ if (limit - position >= length) {
+ // We have room in the current buffer.
+ value.get(buffer, position, length);
+ position += length;
+ totalBytesWritten += length;
+ } else {
+ // Write extends past current buffer. Fill the rest of this buffer and
+ // flush.
+ final int bytesWritten = limit - position;
+ value.get(buffer, position, bytesWritten);
+ length -= bytesWritten;
+ position = limit;
+ totalBytesWritten += bytesWritten;
+ doFlush();
+
+ // Now deal with the rest.
+ // Since we have an output stream, this is our buffer
+ // and buffer offset == 0
+ while (length > limit) {
+ // Copy data into the buffer before writing it to OutputStream.
+ value.get(buffer, 0, limit);
+ out.write(buffer, 0, limit);
+ length -= limit;
+ totalBytesWritten += limit;
+ }
+ value.get(buffer, 0, length);
+ position = length;
+ totalBytesWritten += length;
+ }
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ write(value);
+ }
+
+ private void flushIfNotAvailable(int requiredSize) throws IOException {
+ if (limit - position < requiredSize) {
+ doFlush();
+ }
+ }
+
+ private void doFlush() throws IOException {
+ out.write(buffer, 0, position);
+ position = 0;
+ }
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java
index 5e15cfbe..75b16fe3 100644
--- a/java/core/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java
@@ -30,9 +30,10 @@
package com.google.protobuf;
+import static com.google.protobuf.Internal.checkNotNull;
+
import com.google.protobuf.DescriptorProtos.*;
import com.google.protobuf.Descriptors.FileDescriptor.Syntax;
-
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -74,16 +75,28 @@ public final class Descriptors {
*/
public static final class FileDescriptor extends GenericDescriptor {
/** Convert the descriptor to its protocol message representation. */
- public FileDescriptorProto toProto() { return proto; }
+ @Override
+ public FileDescriptorProto toProto() {
+ return proto;
+ }
/** Get the file name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/** Returns this object. */
- public FileDescriptor getFile() { return this; }
+ @Override
+ public FileDescriptor getFile() {
+ return this;
+ }
/** Returns the same as getName(). */
- public String getFullName() { return proto.getName(); }
+ @Override
+ public String getFullName() {
+ return proto.getName();
+ }
/**
* Get the proto package name. This is the package name given by the
@@ -272,7 +285,7 @@ public final class Descriptors {
* because a field has an undefined type or because two messages
* were defined with the same name.
*/
- private static FileDescriptor buildFrom(
+ public static FileDescriptor buildFrom(
final FileDescriptorProto proto, final FileDescriptor[] dependencies,
final boolean allowUnknownDependencies)
throws DescriptorValidationException {
@@ -582,10 +595,16 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public DescriptorProto toProto() { return proto; }
+ @Override
+ public DescriptorProto toProto() {
+ return proto;
+ }
/** Get the type's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/**
* Get the type's fully-qualified name, within the proto language's
@@ -598,10 +617,16 @@ public final class Descriptors {
* </pre>
* {@code Baz}'s full name is "foo.bar.Baz".
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the {@link FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** If this is a nested type, get the outer descriptor, otherwise null. */
public Descriptor getContainingType() { return containingType; }
@@ -658,9 +683,7 @@ public final class Descriptors {
/** Determines if the given field name is reserved. */
public boolean isReservedName(final String name) {
- if (name == null) {
- throw new NullPointerException();
- }
+ checkNotNull(name);
for (final String reservedName : proto.getReservedNameList()) {
if (reservedName.equals(name)) {
return true;
@@ -847,6 +870,10 @@ public final class Descriptors {
nestedTypes[i].setProto(proto.getNestedType(i));
}
+ for (int i = 0; i < oneofs.length; i++) {
+ oneofs[i].setProto(proto.getOneofDecl(i));
+ }
+
for (int i = 0; i < enumTypes.length; i++) {
enumTypes[i].setProto(proto.getEnumType(i));
}
@@ -875,19 +902,31 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public FieldDescriptorProto toProto() { return proto; }
+ @Override
+ public FieldDescriptorProto toProto() {
+ return proto;
+ }
/** Get the field's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/** Get the field's number. */
- public int getNumber() { return proto.getNumber(); }
+ @Override
+ public int getNumber() {
+ return proto.getNumber();
+ }
/**
* Get the field's fully-qualified name.
* @see Descriptors.Descriptor#getFullName()
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the JSON name of this field. */
public String getJsonName() {
@@ -901,17 +940,22 @@ public final class Descriptors {
public JavaType getJavaType() { return type.getJavaType(); }
/** For internal use only. */
+ @Override
public WireFormat.JavaType getLiteJavaType() {
return getLiteType().getJavaType();
}
/** Get the {@code FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** Get the field's declared type. */
public Type getType() { return type; }
/** For internal use only. */
+ @Override
public WireFormat.FieldType getLiteType() {
return table[type.ordinal()];
}
@@ -953,6 +997,7 @@ public final class Descriptors {
}
/** Is this field declared repeated? */
+ @Override
public boolean isRepeated() {
return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED;
}
@@ -960,6 +1005,7 @@ public final class Descriptors {
/** Does this field have the {@code [packed = true]} option or is this field
* packable in proto3 and not explicitly setted to unpacked?
*/
+ @Override
public boolean isPacked() {
if (!isPackable()) {
return false;
@@ -1048,6 +1094,7 @@ public final class Descriptors {
}
/** For enum fields, gets the field's type. */
+ @Override
public EnumDescriptor getEnumType() {
if (getJavaType() != JavaType.ENUM) {
throw new UnsupportedOperationException(
@@ -1066,6 +1113,7 @@ public final class Descriptors {
* @return negative, zero, or positive if {@code this} is less than,
* equal to, or greater than {@code other}, respectively.
*/
+ @Override
public int compareTo(final FieldDescriptor other) {
if (other.containingType != containingType) {
throw new IllegalArgumentException(
@@ -1123,7 +1171,7 @@ public final class Descriptors {
private JavaType javaType;
public FieldDescriptorProto.Type toProto() {
- return FieldDescriptorProto.Type.valueOf(ordinal() + 1);
+ return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
}
public JavaType getJavaType() { return javaType; }
@@ -1163,33 +1211,20 @@ public final class Descriptors {
private final Object defaultDefault;
}
- // TODO(xiaofeng): Implement it consistently across different languages. See b/24751348.
- private static String fieldNameToLowerCamelCase(String name) {
+ // This method should match exactly with the ToJsonName() function in C++
+ // descriptor.cc.
+ private static String fieldNameToJsonName(String name) {
StringBuilder result = new StringBuilder(name.length());
boolean isNextUpperCase = false;
for (int i = 0; i < name.length(); i++) {
Character ch = name.charAt(i);
- if (Character.isLowerCase(ch)) {
- if (isNextUpperCase) {
- result.append(Character.toUpperCase(ch));
- } else {
- result.append(ch);
- }
- isNextUpperCase = false;
- } else if (Character.isUpperCase(ch)) {
- if (i == 0) {
- // Force first letter to lower-case.
- result.append(Character.toLowerCase(ch));
- } else {
- // Capital letters after the first are left as-is.
- result.append(ch);
- }
- isNextUpperCase = false;
- } else if (Character.isDigit(ch)) {
- result.append(ch);
+ if (ch == '_') {
+ isNextUpperCase = true;
+ } else if (isNextUpperCase) {
+ result.append(Character.toUpperCase(ch));
isNextUpperCase = false;
} else {
- isNextUpperCase = true;
+ result.append(ch);
}
}
return result.toString();
@@ -1208,7 +1243,7 @@ public final class Descriptors {
if (proto.hasJsonName()) {
jsonName = proto.getJsonName();
} else {
- jsonName = fieldNameToLowerCamelCase(proto.getName());
+ jsonName = fieldNameToJsonName(proto.getName());
}
if (proto.hasType()) {
@@ -1466,8 +1501,8 @@ public final class Descriptors {
* For internal use only. This is to satisfy the FieldDescriptorLite
* interface.
*/
- public MessageLite.Builder internalMergeFrom(
- MessageLite.Builder to, MessageLite from) {
+ @Override
+ public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
// FieldDescriptors are only used with non-lite messages so we can just
// down-cast and call mergeFrom directly.
return ((Message.Builder) to).mergeFrom((Message) from);
@@ -1487,19 +1522,31 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public EnumDescriptorProto toProto() { return proto; }
+ @Override
+ public EnumDescriptorProto toProto() {
+ return proto;
+ }
/** Get the type's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/**
* Get the type's fully-qualified name.
* @see Descriptors.Descriptor#getFullName()
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the {@link FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** If this is a nested type, get the outer descriptor, otherwise null. */
public Descriptor getContainingType() { return containingType; }
@@ -1533,6 +1580,7 @@ public final class Descriptors {
* @param number The value's number.
* @return the value's descriptor, or {@code null} if not found.
*/
+ @Override
public EnumValueDescriptor findValueByNumber(final int number) {
return file.pool.enumValuesByNumber.get(
new DescriptorPool.DescriptorIntPair(this, number));
@@ -1659,13 +1707,22 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public EnumValueDescriptorProto toProto() { return proto; }
+ @Override
+ public EnumValueDescriptorProto toProto() {
+ return proto;
+ }
/** Get the value's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/** Get the value's number. */
- public int getNumber() { return proto.getNumber(); }
+ @Override
+ public int getNumber() {
+ return proto.getNumber();
+ }
@Override
public String toString() { return proto.getName(); }
@@ -1674,10 +1731,16 @@ public final class Descriptors {
* Get the value's fully-qualified name.
* @see Descriptors.Descriptor#getFullName()
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the {@link FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** Get the value's enum type. */
public EnumDescriptor getType() { return type; }
@@ -1745,19 +1808,31 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public ServiceDescriptorProto toProto() { return proto; }
+ @Override
+ public ServiceDescriptorProto toProto() {
+ return proto;
+ }
/** Get the type's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/**
* Get the type's fully-qualified name.
* @see Descriptors.Descriptor#getFullName()
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the {@link FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */
public ServiceOptions getOptions() { return proto.getOptions(); }
@@ -1835,19 +1910,31 @@ public final class Descriptors {
public int getIndex() { return index; }
/** Convert the descriptor to its protocol message representation. */
- public MethodDescriptorProto toProto() { return proto; }
+ @Override
+ public MethodDescriptorProto toProto() {
+ return proto;
+ }
/** Get the method's unqualified name. */
- public String getName() { return proto.getName(); }
+ @Override
+ public String getName() {
+ return proto.getName();
+ }
/**
* Get the method's fully-qualified name.
* @see Descriptors.Descriptor#getFullName()
*/
- public String getFullName() { return fullName; }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
/** Get the {@link FileDescriptor} containing this descriptor. */
- public FileDescriptor getFile() { return file; }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
/** Get the method's service type. */
public ServiceDescriptor getService() { return service; }
@@ -2035,7 +2122,7 @@ public final class Descriptors {
// Can't happen, because addPackage() only fails when the name
// conflicts with a non-package, but we have not yet added any
// non-packages at this point.
- assert false;
+ throw new AssertionError(e);
}
}
}
@@ -2248,10 +2335,22 @@ public final class Descriptors {
* that has the same name as an existing package.
*/
private static final class PackageDescriptor extends GenericDescriptor {
- public Message toProto() { return file.toProto(); }
- public String getName() { return name; }
- public String getFullName() { return fullName; }
- public FileDescriptor getFile() { return file; }
+ @Override
+ public Message toProto() {
+ return file.toProto();
+ }
+ @Override
+ public String getName() {
+ return name;
+ }
+ @Override
+ public String getFullName() {
+ return fullName;
+ }
+ @Override
+ public FileDescriptor getFile() {
+ return file;
+ }
PackageDescriptor(final String name, final String fullName,
final FileDescriptor file) {
@@ -2404,6 +2503,10 @@ public final class Descriptors {
public int getFieldCount() { return fieldCount; }
+ public OneofOptions getOptions() {
+ return proto.getOptions();
+ }
+
/** Get a list of this message type's fields. */
public List<FieldDescriptor> getFields() {
return Collections.unmodifiableList(Arrays.asList(fields));
@@ -2413,6 +2516,10 @@ public final class Descriptors {
return fields[index];
}
+ private void setProto(final OneofDescriptorProto proto) {
+ this.proto = proto;
+ }
+
private OneofDescriptor(final OneofDescriptorProto proto,
final FileDescriptor file,
final Descriptor parent,
diff --git a/java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java b/java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java
new file mode 100644
index 00000000..7ae94349
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/DiscardUnknownFieldsParser.java
@@ -0,0 +1,71 @@
+// 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;
+
+/**
+ * Parsers to discard unknown fields during parsing.
+ */
+public final class DiscardUnknownFieldsParser {
+
+ /**
+ * Warps a given {@link Parser} into a new {@link Parser} that discards unknown fields during
+ * parsing.
+ *
+ * <p>Usage example:
+ * <pre>{@code
+ * private final static Parser<Foo> FOO_PARSER = DiscardUnknownFieldsParser.wrap(Foo.parser());
+ * Foo parseFooDiscardUnknown(ByteBuffer input) throws IOException {
+ * return FOO_PARSER.parseFrom(input);
+ * }
+ * }</pre>
+ *
+ * <p>Like all other implementations of {@code Parser}, this parser is stateless and thread-safe.
+ *
+ * @param parser The delegated parser that parses messages.
+ * @return a {@link Parser} that will discard unknown fields during parsing.
+ */
+ public static final <T extends Message> Parser<T> wrap(final Parser<T> parser) {
+ return new AbstractParser<T>() {
+ @Override
+ public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ try {
+ input.discardUnknownFields();
+ return parser.parsePartialFrom(input, extensionRegistry);
+ } finally {
+ input.unsetDiscardUnknownFields();
+ }
+ }
+ };
+ }
+
+ private DiscardUnknownFieldsParser() {}
+}
diff --git a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
index bcc9d6ee..5b28b4a8 100644
--- a/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/DoubleArrayList.java
@@ -30,37 +30,35 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.DoubleList;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.DoubleList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.RandomAccess;
/**
* An implementation of {@link DoubleList} on top of a primitive array.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
-final class DoubleArrayList
- extends AbstractProtobufList<Double> implements DoubleList, RandomAccess {
-
- private static final int DEFAULT_CAPACITY = 10;
-
+final class DoubleArrayList extends AbstractProtobufList<Double>
+ implements DoubleList, RandomAccess, PrimitiveNonBoxingCollection {
+
private static final DoubleArrayList EMPTY_LIST = new DoubleArrayList();
static {
EMPTY_LIST.makeImmutable();
}
-
+
public static DoubleArrayList emptyList() {
return EMPTY_LIST;
}
-
+
/**
* The backing store for the list.
*/
private double[] array;
-
+
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@@ -71,34 +69,71 @@ final class DoubleArrayList
* Constructs a new mutable {@code DoubleArrayList} with default capacity.
*/
DoubleArrayList() {
- this(DEFAULT_CAPACITY);
+ this(new double[DEFAULT_CAPACITY], 0);
}
/**
- * Constructs a new mutable {@code DoubleArrayList} with the provided capacity.
+ * Constructs a new mutable {@code DoubleArrayList}
+ * containing the same elements as {@code other}.
*/
- DoubleArrayList(int capacity) {
- array = new double[capacity];
- size = 0;
+ private DoubleArrayList(double[] other, int size) {
+ array = other;
+ this.size = size;
}
- /**
- * Constructs a new mutable {@code DoubleArrayList} containing the same elements as {@code other}.
- */
- DoubleArrayList(List<Double> other) {
- if (other instanceof DoubleArrayList) {
- DoubleArrayList list = (DoubleArrayList) other;
- array = list.array.clone();
- size = list.size;
- } else {
- size = other.size();
- array = new double[size];
- for (int i = 0; i < size; i++) {
- array[i] = other.get(i);
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ ensureIsMutable();
+ if (toIndex < fromIndex) {
+ throw new IndexOutOfBoundsException("toIndex < fromIndex");
+ }
+
+ System.arraycopy(array, toIndex, array, fromIndex, size - toIndex);
+ size -= (toIndex - fromIndex);
+ modCount++;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DoubleArrayList)) {
+ return super.equals(o);
+ }
+ DoubleArrayList other = (DoubleArrayList) o;
+ if (size != other.size) {
+ return false;
+ }
+
+ final double[] arr = other.array;
+ for (int i = 0; i < size; i++) {
+ if (array[i] != arr[i]) {
+ return false;
}
}
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size; i++) {
+ long bits = Double.doubleToLongBits(array[i]);
+ result = (31 * result) + Internal.hashLong(bits);
+ }
+ return result;
}
-
+
+ @Override
+ public DoubleList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size) {
+ throw new IllegalArgumentException();
+ }
+ return new DoubleArrayList(Arrays.copyOf(array, capacity), size);
+ }
+
@Override
public Double get(int index) {
return getDouble(index);
@@ -150,7 +185,7 @@ final class DoubleArrayList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
-
+
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@@ -158,10 +193,10 @@ final class DoubleArrayList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
double[] newArray = new double[length];
-
+
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
-
+
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@@ -175,38 +210,36 @@ final class DoubleArrayList
@Override
public boolean addAll(Collection<? extends Double> collection) {
ensureIsMutable();
-
- if (collection == null) {
- throw new NullPointerException();
- }
-
+
+ checkNotNull(collection);
+
// We specialize when adding another DoubleArrayList to avoid boxing elements.
if (!(collection instanceof DoubleArrayList)) {
return super.addAll(collection);
}
-
+
DoubleArrayList list = (DoubleArrayList) collection;
if (list.size == 0) {
return false;
}
-
+
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
-
+
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
-
+
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
-
+
@Override
public boolean remove(Object o) {
ensureIsMutable();
@@ -226,7 +259,9 @@ final class DoubleArrayList
ensureIsMutable();
ensureIndexInRange(index);
double value = array[index];
- System.arraycopy(array, index + 1, array, index, size - index);
+ if (index < size - 1) {
+ System.arraycopy(array, index + 1, array, index, size - index);
+ }
size--;
modCount++;
return value;
@@ -235,7 +270,7 @@ final class DoubleArrayList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
- *
+ *
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {
diff --git a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
index 3ea1b688..a6a774b7 100644
--- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java
@@ -30,11 +30,12 @@
package com.google.protobuf;
+import static com.google.protobuf.Internal.checkNotNull;
+
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
-
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
@@ -156,18 +157,22 @@ public final class DynamicMessage extends AbstractMessage {
// -----------------------------------------------------------------
// Implementation of Message interface.
+ @Override
public Descriptor getDescriptorForType() {
return type;
}
+ @Override
public DynamicMessage getDefaultInstanceForType() {
return getDefaultInstance(type);
}
+ @Override
public Map<FieldDescriptor, Object> getAllFields() {
return fields.getAllFields();
}
+ @Override
public boolean hasOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
@@ -177,16 +182,19 @@ public final class DynamicMessage extends AbstractMessage {
return true;
}
+ @Override
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
return oneofCases[oneof.getIndex()];
}
+ @Override
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
}
+ @Override
public Object getField(FieldDescriptor field) {
verifyContainingType(field);
Object result = fields.getField(field);
@@ -202,16 +210,19 @@ public final class DynamicMessage extends AbstractMessage {
return result;
}
+ @Override
public int getRepeatedFieldCount(FieldDescriptor field) {
verifyContainingType(field);
return fields.getRepeatedFieldCount(field);
}
+ @Override
public Object getRepeatedField(FieldDescriptor field, int index) {
verifyContainingType(field);
return fields.getRepeatedField(field, index);
}
+ @Override
public UnknownFieldSet getUnknownFields() {
return unknownFields;
}
@@ -264,19 +275,22 @@ public final class DynamicMessage extends AbstractMessage {
return size;
}
+ @Override
public Builder newBuilderForType() {
return new Builder(type);
}
+ @Override
public Builder toBuilder() {
return newBuilderForType().mergeFrom(this);
}
+ @Override
public Parser<DynamicMessage> getParserForType() {
return new AbstractParser<DynamicMessage>() {
+ @Override
public DynamicMessage parsePartialFrom(
- CodedInputStream input,
- ExtensionRegistryLite extensionRegistry)
+ CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
Builder builder = newBuilder(type);
try {
@@ -284,7 +298,7 @@ public final class DynamicMessage extends AbstractMessage {
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
+ throw new InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
@@ -325,6 +339,20 @@ public final class DynamicMessage extends AbstractMessage {
this.fields = FieldSet.newFieldSet();
this.unknownFields = UnknownFieldSet.getDefaultInstance();
this.oneofCases = new FieldDescriptor[type.toProto().getOneofDeclCount()];
+ // A MapEntry has all of its fields present at all times.
+ if (type.getOptions().getMapEntry()) {
+ populateMapEntry();
+ }
+ }
+
+ private void populateMapEntry() {
+ for (FieldDescriptor field : type.getFields()) {
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ fields.setField(field, getDefaultInstance(field.getMessageType()));
+ } else {
+ fields.setField(field, field.getDefaultValue());
+ }
+ }
}
// ---------------------------------------------------------------
@@ -337,6 +365,10 @@ public final class DynamicMessage extends AbstractMessage {
} else {
fields.clear();
}
+ // A MapEntry has all of its fields present at all times.
+ if (type.getOptions().getMapEntry()) {
+ populateMapEntry();
+ }
unknownFields = UnknownFieldSet.getDefaultInstance();
return this;
}
@@ -370,6 +402,7 @@ public final class DynamicMessage extends AbstractMessage {
}
}
+ @Override
public DynamicMessage build() {
if (!isInitialized()) {
throw newUninitializedMessageException(
@@ -394,6 +427,7 @@ public final class DynamicMessage extends AbstractMessage {
return buildPartial();
}
+ @Override
public DynamicMessage buildPartial() {
fields.makeImmutable();
DynamicMessage result =
@@ -411,22 +445,27 @@ public final class DynamicMessage extends AbstractMessage {
return result;
}
+ @Override
public boolean isInitialized() {
return DynamicMessage.isInitialized(type, fields);
}
+ @Override
public Descriptor getDescriptorForType() {
return type;
}
+ @Override
public DynamicMessage getDefaultInstanceForType() {
return getDefaultInstance(type);
}
+ @Override
public Map<FieldDescriptor, Object> getAllFields() {
return fields.getAllFields();
}
+ @Override
public Builder newBuilderForField(FieldDescriptor field) {
verifyContainingType(field);
@@ -438,6 +477,7 @@ public final class DynamicMessage extends AbstractMessage {
return new Builder(field.getMessageType());
}
+ @Override
public boolean hasOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
@@ -447,11 +487,13 @@ public final class DynamicMessage extends AbstractMessage {
return true;
}
+ @Override
public FieldDescriptor getOneofFieldDescriptor(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
return oneofCases[oneof.getIndex()];
}
+ @Override
public Builder clearOneof(OneofDescriptor oneof) {
verifyOneofContainingType(oneof);
FieldDescriptor field = oneofCases[oneof.getIndex()];
@@ -461,11 +503,13 @@ public final class DynamicMessage extends AbstractMessage {
return this;
}
+ @Override
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
}
+ @Override
public Object getField(FieldDescriptor field) {
verifyContainingType(field);
Object result = fields.getField(field);
@@ -481,6 +525,7 @@ public final class DynamicMessage extends AbstractMessage {
return result;
}
+ @Override
public Builder setField(FieldDescriptor field, Object value) {
verifyContainingType(field);
ensureIsMutable();
@@ -500,11 +545,20 @@ public final class DynamicMessage extends AbstractMessage {
fields.clearField(oldField);
}
oneofCases[index] = field;
+ } else if (field.getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3) {
+ if (!field.isRepeated()
+ && field.getJavaType() != FieldDescriptor.JavaType.MESSAGE
+ && value.equals(field.getDefaultValue())) {
+ // In proto3, setting a field to its default value is equivalent to clearing the field.
+ fields.clearField(field);
+ return this;
+ }
}
fields.setField(field, value);
return this;
}
+ @Override
public Builder clearField(FieldDescriptor field) {
verifyContainingType(field);
ensureIsMutable();
@@ -519,24 +573,27 @@ public final class DynamicMessage extends AbstractMessage {
return this;
}
+ @Override
public int getRepeatedFieldCount(FieldDescriptor field) {
verifyContainingType(field);
return fields.getRepeatedFieldCount(field);
}
+ @Override
public Object getRepeatedField(FieldDescriptor field, int index) {
verifyContainingType(field);
return fields.getRepeatedField(field, index);
}
- public Builder setRepeatedField(FieldDescriptor field,
- int index, Object value) {
+ @Override
+ public Builder setRepeatedField(FieldDescriptor field, int index, Object value) {
verifyContainingType(field);
ensureIsMutable();
fields.setRepeatedField(field, index, value);
return this;
}
+ @Override
public Builder addRepeatedField(FieldDescriptor field, Object value) {
verifyContainingType(field);
ensureIsMutable();
@@ -544,14 +601,15 @@ public final class DynamicMessage extends AbstractMessage {
return this;
}
+ @Override
public UnknownFieldSet getUnknownFields() {
return unknownFields;
}
+ @Override
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
- if (getDescriptorForType().getFile().getSyntax()
- == Descriptors.FileDescriptor.Syntax.PROTO3) {
- // Proto3 discards unknown fields.
+ if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3
+ && CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
return this;
}
this.unknownFields = unknownFields;
@@ -560,9 +618,8 @@ public final class DynamicMessage extends AbstractMessage {
@Override
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
- if (getDescriptorForType().getFile().getSyntax()
- == Descriptors.FileDescriptor.Syntax.PROTO3) {
- // Proto3 discards unknown fields.
+ if (getDescriptorForType().getFile().getSyntax() == Descriptors.FileDescriptor.Syntax.PROTO3
+ && CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
return this;
}
this.unknownFields =
@@ -591,9 +648,7 @@ public final class DynamicMessage extends AbstractMessage {
/** Verifies that the value is EnumValueDescriptor and matches Enum Type. */
private void ensureSingularEnumValueDescriptor(
FieldDescriptor field, Object value) {
- if (value == null) {
- throw new NullPointerException();
- }
+ checkNotNull(value);
if (!(value instanceof EnumValueDescriptor)) {
throw new IllegalArgumentException(
"DynamicMessage should use EnumValueDescriptor to set Enum Value.");
diff --git a/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java
index 6f41fb81..3cd4c884 100644
--- a/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java
+++ b/java/core/src/main/java/com/google/protobuf/ExperimentalApi.java
@@ -1,3 +1,33 @@
+// 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 java.lang.annotation.Documented;
diff --git a/java/core/src/main/java/com/google/protobuf/Extension.java b/java/core/src/main/java/com/google/protobuf/Extension.java
index 68d29f33..5df12e64 100644
--- a/java/core/src/main/java/com/google/protobuf/Extension.java
+++ b/java/core/src/main/java/com/google/protobuf/Extension.java
@@ -42,6 +42,7 @@ public abstract class Extension<ContainingType extends MessageLite, Type>
public abstract Descriptors.FieldDescriptor getDescriptor();
/** Returns whether or not this extension is a Lite Extension. */
+ @Override
final boolean isLite() {
return false;
}
@@ -57,10 +58,7 @@ public abstract class Extension<ContainingType extends MessageLite, Type>
PROTO1,
}
- protected ExtensionType getExtensionType() {
- // TODO(liujisi): make this abstract after we fix proto1.
- return ExtensionType.IMMUTABLE;
- }
+ protected abstract ExtensionType getExtensionType();
/**
* Type of a message extension.
@@ -69,7 +67,7 @@ public abstract class Extension<ContainingType extends MessageLite, Type>
PROTO1,
PROTO2,
}
-
+
/**
* If the extension is a message extension (i.e., getLiteType() == MESSAGE),
* returns the type of the message, otherwise undefined.
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
index 0067392f..a22a74a0 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistry.java
@@ -32,7 +32,6 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -101,7 +100,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistry getEmptyRegistry() {
- return EMPTY;
+ return EMPTY_REGISTRY;
}
@@ -243,6 +242,11 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
add(newExtensionInfo(extension), extension.getExtensionType());
}
+ /** Add an extension from a generated file to the registry. */
+ public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
+ add((Extension<?, ?>) extension);
+ }
+
static ExtensionInfo newExtensionInfo(final Extension<?, ?> extension) {
if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
@@ -311,7 +315,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
private final Map<DescriptorIntPair, ExtensionInfo> mutableExtensionsByNumber;
ExtensionRegistry(boolean empty) {
- super(ExtensionRegistryLite.getEmptyRegistry());
+ super(EMPTY_REGISTRY_LITE);
this.immutableExtensionsByName =
Collections.<String, ExtensionInfo>emptyMap();
this.mutableExtensionsByName =
@@ -321,7 +325,7 @@ public class ExtensionRegistry extends ExtensionRegistryLite {
this.mutableExtensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
}
- private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
+ static final ExtensionRegistry EMPTY_REGISTRY = new ExtensionRegistry(true);
private void add(
final ExtensionInfo extension,
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java
new file mode 100644
index 00000000..89f7ab9b
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryFactory.java
@@ -0,0 +1,96 @@
+// 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.protobuf.ExtensionRegistryLite.EMPTY_REGISTRY_LITE;
+
+/**
+ * A factory object to create instances of {@link ExtensionRegistryLite}.
+ *
+ * <p>
+ * This factory detects (via reflection) if the full (non-Lite) protocol buffer libraries
+ * are available, and if so, the instances returned are actually {@link ExtensionRegistry}.
+ */
+final class ExtensionRegistryFactory {
+
+ static final String FULL_REGISTRY_CLASS_NAME = "com.google.protobuf.ExtensionRegistry";
+
+ /* Visible for Testing
+ @Nullable */
+ static final Class<?> EXTENSION_REGISTRY_CLASS = reflectExtensionRegistry();
+
+ /* @Nullable */
+ static Class<?> reflectExtensionRegistry() {
+ try {
+ return Class.forName(FULL_REGISTRY_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ // The exception allocation is potentially expensive on Android (where it can be triggered
+ // many times at start up). Is there a way to ameliorate this?
+ return null;
+ }
+ }
+
+ /** Construct a new, empty instance. */
+ public static ExtensionRegistryLite create() {
+ if (EXTENSION_REGISTRY_CLASS != null) {
+ try {
+ return invokeSubclassFactory("newInstance");
+ } catch (Exception e) {
+ // return a Lite registry.
+ }
+ }
+ return new ExtensionRegistryLite();
+ }
+
+ /** Get the unmodifiable singleton empty instance. */
+ public static ExtensionRegistryLite createEmpty() {
+ if (EXTENSION_REGISTRY_CLASS != null) {
+ try {
+ return invokeSubclassFactory("getEmptyRegistry");
+ } catch (Exception e) {
+ // return a Lite registry.
+ }
+ }
+ return EMPTY_REGISTRY_LITE;
+ }
+
+
+ static boolean isFullRegistry(ExtensionRegistryLite registry) {
+ return EXTENSION_REGISTRY_CLASS != null
+ && EXTENSION_REGISTRY_CLASS.isAssignableFrom(registry.getClass());
+ }
+
+ private static final ExtensionRegistryLite invokeSubclassFactory(String methodName)
+ throws Exception {
+ return (ExtensionRegistryLite) EXTENSION_REGISTRY_CLASS
+ .getDeclaredMethod(methodName).invoke(null);
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
index 65cf7385..f3d48d3a 100644
--- a/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/ExtensionRegistryLite.java
@@ -79,6 +79,22 @@ public class ExtensionRegistryLite {
// applications. Need to support this feature on smaller granularity.
private static volatile boolean eagerlyParseMessageSets = false;
+ // Visible for testing.
+ static final String EXTENSION_CLASS_NAME = "com.google.protobuf.Extension";
+
+ /* @Nullable */
+ static Class<?> resolveExtensionClass() {
+ try {
+ return Class.forName(EXTENSION_CLASS_NAME);
+ } catch (ClassNotFoundException e) {
+ // See comment in ExtensionRegistryFactory on the potential expense of this.
+ return null;
+ }
+ }
+
+ /* @Nullable */
+ private static final Class<?> extensionClass = resolveExtensionClass();
+
public static boolean isEagerlyParseMessageSets() {
return eagerlyParseMessageSets;
}
@@ -87,16 +103,25 @@ public class ExtensionRegistryLite {
eagerlyParseMessageSets = isEagerlyParse;
}
- /** Construct a new, empty instance. */
+ /**
+ * Construct a new, empty instance.
+ *
+ * <p>This may be an {@code ExtensionRegistry} if the full (non-Lite) proto libraries are
+ * available.
+ */
public static ExtensionRegistryLite newInstance() {
- return new ExtensionRegistryLite();
+ return ExtensionRegistryFactory.create();
}
- /** Get the unmodifiable singleton empty instance. */
+ /**
+ * Get the unmodifiable singleton empty instance of either ExtensionRegistryLite or
+ * {@code ExtensionRegistry} (if the full (non-Lite) proto libraries are available).
+ */
public static ExtensionRegistryLite getEmptyRegistry() {
- return EMPTY;
+ return ExtensionRegistryFactory.createEmpty();
}
+
/** Returns an unmodifiable view of the registry. */
public ExtensionRegistryLite getUnmodifiable() {
return new ExtensionRegistryLite(this);
@@ -128,6 +153,23 @@ public class ExtensionRegistryLite {
extension);
}
+ /**
+ * Add an extension from a lite generated file to the registry only if it is
+ * a non-lite extension i.e. {@link GeneratedMessageLite.GeneratedExtension}. */
+ public final void add(ExtensionLite<?, ?> extension) {
+ if (GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(extension.getClass())) {
+ add((GeneratedMessageLite.GeneratedExtension<?, ?>) extension);
+ }
+ if (ExtensionRegistryFactory.isFullRegistry(this)) {
+ try {
+ this.getClass().getMethod("add", extensionClass).invoke(this, extension);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(
+ String.format("Could not invoke ExtensionRegistry#add for %s", extension), e);
+ }
+ }
+ }
+
// =================================================================
// Private stuff.
@@ -139,9 +181,11 @@ public class ExtensionRegistryLite {
new HashMap<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>();
}
+ static final ExtensionRegistryLite EMPTY_REGISTRY_LITE =
+ new ExtensionRegistryLite(true);
ExtensionRegistryLite(ExtensionRegistryLite other) {
- if (other == EMPTY) {
+ if (other == EMPTY_REGISTRY_LITE) {
this.extensionsByNumber = Collections.emptyMap();
} else {
this.extensionsByNumber =
@@ -153,11 +197,9 @@ public class ExtensionRegistryLite {
GeneratedMessageLite.GeneratedExtension<?, ?>>
extensionsByNumber;
- private ExtensionRegistryLite(boolean empty) {
+ ExtensionRegistryLite(boolean empty) {
this.extensionsByNumber = Collections.emptyMap();
}
- private static final ExtensionRegistryLite EMPTY =
- new ExtensionRegistryLite(true);
/** A (Object, int) pair, used as a map key. */
private static final class ObjectIntPair {
diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java
index 47924b65..c09daa32 100644
--- a/java/core/src/main/java/com/google/protobuf/FieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java
@@ -30,8 +30,9 @@
package com.google.protobuf;
-import com.google.protobuf.LazyField.LazyIterator;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.LazyField.LazyIterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -101,6 +102,11 @@ final class FieldSet<FieldDescriptorType extends
@SuppressWarnings("rawtypes")
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
+ /** Returns {@code true} if empty, {@code false} otherwise. */
+ boolean isEmpty() {
+ return fields.isEmpty();
+ }
+
/** Make this FieldSet immutable from this point forward. */
@SuppressWarnings("unchecked")
public void makeImmutable() {
@@ -121,6 +127,25 @@ final class FieldSet<FieldDescriptorType extends
return isImmutable;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof FieldSet)) {
+ return false;
+ }
+
+ FieldSet<?> other = (FieldSet<?>) o;
+ return fields.equals(other.fields);
+ }
+
+ @Override
+ public int hashCode() {
+ return fields.hashCode();
+ }
+
/**
* Clones the FieldSet. The returned FieldSet will be mutable even if the
* original FieldSet was immutable.
@@ -201,6 +226,7 @@ final class FieldSet<FieldDescriptorType extends
return fields.entrySet().iterator();
}
+
/**
* Useful for implementing
* {@link Message#hasField(Descriptors.FieldDescriptor)}.
@@ -365,9 +391,7 @@ final class FieldSet<FieldDescriptorType extends
*/
private static void verifyType(final WireFormat.FieldType type,
final Object value) {
- if (value == null) {
- throw new NullPointerException();
- }
+ checkNotNull(value);
boolean isValid = false;
switch (type.getJavaType()) {
@@ -474,7 +498,7 @@ final class FieldSet<FieldDescriptorType extends
}
/**
- * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
+ * Like {@link Message.Builder#mergeFrom(Message)}, but merges from another
* {@link FieldSet}.
*/
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
@@ -619,10 +643,11 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static void writeElement(final CodedOutputStream output,
- final WireFormat.FieldType type,
- final int number,
- final Object value) throws IOException {
+ static void writeElement(
+ final CodedOutputStream output,
+ final WireFormat.FieldType type,
+ final int number,
+ final Object value) throws IOException {
// Special case for groups, which need a start and end tag; other fields
// can just use writeTag() and writeFieldNoTag().
if (type == WireFormat.FieldType.GROUP) {
@@ -785,9 +810,8 @@ final class FieldSet<FieldDescriptorType extends
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
- private static int computeElementSize(
- final WireFormat.FieldType type,
- final int number, final Object value) {
+ static int computeElementSize(
+ final WireFormat.FieldType type, final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) {
// Only count the end group tag for proto2 messages as for proto1 the end
diff --git a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
index 033b5eed..7c080af3 100644
--- a/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/FloatArrayList.java
@@ -30,36 +30,35 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.FloatList;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.FloatList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.RandomAccess;
/**
* An implementation of {@link FloatList} on top of a primitive array.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
-final class FloatArrayList extends AbstractProtobufList<Float> implements FloatList, RandomAccess {
-
- private static final int DEFAULT_CAPACITY = 10;
-
+final class FloatArrayList extends AbstractProtobufList<Float>
+ implements FloatList, RandomAccess, PrimitiveNonBoxingCollection {
+
private static final FloatArrayList EMPTY_LIST = new FloatArrayList();
static {
EMPTY_LIST.makeImmutable();
}
-
+
public static FloatArrayList emptyList() {
return EMPTY_LIST;
}
-
+
/**
* The backing store for the list.
*/
private float[] array;
-
+
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@@ -70,34 +69,70 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
* Constructs a new mutable {@code FloatArrayList} with default capacity.
*/
FloatArrayList() {
- this(DEFAULT_CAPACITY);
+ this(new float[DEFAULT_CAPACITY], 0);
}
/**
- * Constructs a new mutable {@code FloatArrayList} with the provided capacity.
+ * Constructs a new mutable {@code FloatArrayList}
+ * containing the same elements as {@code other}.
*/
- FloatArrayList(int capacity) {
- array = new float[capacity];
- size = 0;
+ private FloatArrayList(float[] other, int size) {
+ array = other;
+ this.size = size;
}
- /**
- * Constructs a new mutable {@code FloatArrayList} containing the same elements as {@code other}.
- */
- FloatArrayList(List<Float> other) {
- if (other instanceof FloatArrayList) {
- FloatArrayList list = (FloatArrayList) other;
- array = list.array.clone();
- size = list.size;
- } else {
- size = other.size();
- array = new float[size];
- for (int i = 0; i < size; i++) {
- array[i] = other.get(i);
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ ensureIsMutable();
+ if (toIndex < fromIndex) {
+ throw new IndexOutOfBoundsException("toIndex < fromIndex");
+ }
+
+ System.arraycopy(array, toIndex, array, fromIndex, size - toIndex);
+ size -= (toIndex - fromIndex);
+ modCount++;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof FloatArrayList)) {
+ return super.equals(o);
+ }
+ FloatArrayList other = (FloatArrayList) o;
+ if (size != other.size) {
+ return false;
+ }
+
+ final float[] arr = other.array;
+ for (int i = 0; i < size; i++) {
+ if (array[i] != arr[i]) {
+ return false;
}
}
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size; i++) {
+ result = (31 * result) + Float.floatToIntBits(array[i]);
+ }
+ return result;
}
-
+
+ @Override
+ public FloatList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size) {
+ throw new IllegalArgumentException();
+ }
+ return new FloatArrayList(Arrays.copyOf(array, capacity), size);
+ }
+
@Override
public Float get(int index) {
return getFloat(index);
@@ -149,7 +184,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
-
+
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@@ -157,10 +192,10 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
float[] newArray = new float[length];
-
+
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
-
+
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@@ -174,38 +209,36 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
@Override
public boolean addAll(Collection<? extends Float> collection) {
ensureIsMutable();
-
- if (collection == null) {
- throw new NullPointerException();
- }
-
+
+ checkNotNull(collection);
+
// We specialize when adding another FloatArrayList to avoid boxing elements.
if (!(collection instanceof FloatArrayList)) {
return super.addAll(collection);
}
-
+
FloatArrayList list = (FloatArrayList) collection;
if (list.size == 0) {
return false;
}
-
+
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
-
+
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
-
+
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
-
+
@Override
public boolean remove(Object o) {
ensureIsMutable();
@@ -225,7 +258,9 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
ensureIsMutable();
ensureIndexInRange(index);
float value = array[index];
- System.arraycopy(array, index + 1, array, index, size - index);
+ if (index < size - 1) {
+ System.arraycopy(array, index + 1, array, index, size - index);
+ }
size--;
modCount++;
return value;
@@ -234,7 +269,7 @@ final class FloatArrayList extends AbstractProtobufList<Float> implements FloatL
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
- *
+ *
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
index ceb97a4e..60179e37 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -80,6 +80,7 @@ public abstract class GeneratedMessage extends AbstractMessage
unknownFields = builder.getUnknownFields();
}
+ @Override
public Parser<? extends GeneratedMessage> getParserForType() {
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
@@ -102,7 +103,7 @@ public abstract class GeneratedMessage extends AbstractMessage
*/
protected abstract FieldAccessorTable internalGetFieldAccessorTable();
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Descriptor getDescriptorForType() {
return internalGetFieldAccessorTable().descriptor;
}
@@ -191,7 +192,7 @@ public abstract class GeneratedMessage extends AbstractMessage
return true;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Map<FieldDescriptor, Object> getAllFields() {
return Collections.unmodifiableMap(
getAllFieldsMutable(/* getBytesForString = */ false));
@@ -212,22 +213,22 @@ public abstract class GeneratedMessage extends AbstractMessage
getAllFieldsMutable(/* getBytesForString = */ true));
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasOneof(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).has(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).get(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).has(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Object getField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).get(this);
}
@@ -244,19 +245,19 @@ public abstract class GeneratedMessage extends AbstractMessage
return internalGetFieldAccessorTable().getField(field).getRaw(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public int getRepeatedFieldCount(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field)
.getRepeatedCount(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Object getRepeatedField(final FieldDescriptor field, final int index) {
return internalGetFieldAccessorTable().getField(field)
.getRepeated(this, index);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public UnknownFieldSet getUnknownFields() {
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
@@ -354,33 +355,32 @@ public abstract class GeneratedMessage extends AbstractMessage
// Noop for messages without extensions.
}
- protected abstract Message.Builder newBuilderForType(BuilderParent parent);
+ /**
+ * TODO(xiaofeng): remove this after b/29368482 is fixed. We need to move this
+ * interface to AbstractMessage in order to versioning GeneratedMessage but
+ * this move breaks binary compatibility for AppEngine. After AppEngine is
+ * fixed we can exlude this from google3.
+ */
+ protected interface BuilderParent extends AbstractMessage.BuilderParent {}
/**
- * Interface for the parent of a Builder that allows the builder to
- * communicate invalidations back to the parent for use when using nested
- * builders.
+ * TODO(xiaofeng): remove this together with GeneratedMessage.BuilderParent.
*/
- protected interface BuilderParent {
+ protected abstract Message.Builder newBuilderForType(BuilderParent parent);
- /**
- * A builder becomes dirty whenever a field is modified -- including fields
- * in nested builders -- and becomes clean when build() is called. Thus,
- * when a builder becomes dirty, all its parents become dirty as well, and
- * when it becomes clean, all its children become clean. The dirtiness
- * state is used to invalidate certain cached values.
- * <br>
- * To this end, a builder calls markAsDirty() on its parent whenever it
- * transitions from clean to dirty. The parent must propagate this call to
- * its own parent, unless it was already dirty, in which case the
- * grandparent must necessarily already be dirty as well. The parent can
- * only transition back to "clean" after calling build() on all children.
- */
- void markDirty();
+ @Override
+ protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) {
+ return newBuilderForType(new BuilderParent() {
+ @Override
+ public void markDirty() {
+ parent.markDirty();
+ }
+ });
}
+
@SuppressWarnings("unchecked")
- public abstract static class Builder <BuilderType extends Builder>
+ public abstract static class Builder <BuilderType extends Builder<BuilderType>>
extends AbstractMessage.Builder<BuilderType> {
private BuilderParent builderParent;
@@ -402,6 +402,7 @@ public abstract class GeneratedMessage extends AbstractMessage
this.builderParent = builderParent;
}
+ @Override
void dispose() {
builderParent = null;
}
@@ -419,6 +420,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Called by the subclass or a builder to notify us that a message was
* built and may be cached and therefore invalidations are needed.
*/
+ @Override
protected void markClean() {
this.isClean = true;
}
@@ -444,6 +446,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Called by the initialization and clear code paths to allow subclasses to
* reset any of their builtin fields back to the initial values.
*/
+ @Override
public BuilderType clear() {
unknownFields = UnknownFieldSet.getDefaultInstance();
onChanged();
@@ -457,12 +460,12 @@ public abstract class GeneratedMessage extends AbstractMessage
*/
protected abstract FieldAccessorTable internalGetFieldAccessorTable();
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Descriptor getDescriptorForType() {
return internalGetFieldAccessorTable().descriptor;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Map<FieldDescriptor, Object> getAllFields() {
return Collections.unmodifiableMap(getAllFieldsMutable());
}
@@ -510,39 +513,38 @@ public abstract class GeneratedMessage extends AbstractMessage
return result;
}
- public Message.Builder newBuilderForField(
- final FieldDescriptor field) {
+ @Override
+ public Message.Builder newBuilderForField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).newBuilder();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Message.Builder getFieldBuilder(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).getBuilder(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field,
- int index) {
+ @Override
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) {
return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder(
this, index);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasOneof(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).has(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
return internalGetFieldAccessorTable().getOneof(oneof).get(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasField(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field).has(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Object getField(final FieldDescriptor field) {
Object object = internalGetFieldAccessorTable().getField(field).get(this);
if (field.isRepeated()) {
@@ -554,52 +556,52 @@ public abstract class GeneratedMessage extends AbstractMessage
}
}
- public BuilderType setField(final FieldDescriptor field,
- final Object value) {
+ @Override
+ public BuilderType setField(final FieldDescriptor field, final Object value) {
internalGetFieldAccessorTable().getField(field).set(this, value);
return (BuilderType) this;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public BuilderType clearField(final FieldDescriptor field) {
internalGetFieldAccessorTable().getField(field).clear(this);
return (BuilderType) this;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public BuilderType clearOneof(final OneofDescriptor oneof) {
internalGetFieldAccessorTable().getOneof(oneof).clear(this);
return (BuilderType) this;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public int getRepeatedFieldCount(final FieldDescriptor field) {
return internalGetFieldAccessorTable().getField(field)
.getRepeatedCount(this);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public Object getRepeatedField(final FieldDescriptor field,
- final int index) {
+ @Override
+ public Object getRepeatedField(final FieldDescriptor field, final int index) {
return internalGetFieldAccessorTable().getField(field)
.getRepeated(this, index);
}
- public BuilderType setRepeatedField(final FieldDescriptor field,
- final int index, final Object value) {
+ @Override
+ public BuilderType setRepeatedField(
+ final FieldDescriptor field, final int index, final Object value) {
internalGetFieldAccessorTable().getField(field)
.setRepeated(this, index, value);
return (BuilderType) this;
}
- public BuilderType addRepeatedField(final FieldDescriptor field,
- final Object value) {
+ @Override
+ public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) {
internalGetFieldAccessorTable().getField(field).addRepeated(this, value);
return (BuilderType) this;
}
- public BuilderType setUnknownFields(
- final UnknownFieldSet unknownFields) {
+ @Override
+ public BuilderType setUnknownFields(final UnknownFieldSet unknownFields) {
this.unknownFields = unknownFields;
onChanged();
return (BuilderType) this;
@@ -616,7 +618,7 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean isInitialized() {
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
// Check that all required fields are present.
@@ -646,7 +648,7 @@ public abstract class GeneratedMessage extends AbstractMessage
return true;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final UnknownFieldSet getUnknownFields() {
return unknownFields;
}
@@ -670,7 +672,7 @@ public abstract class GeneratedMessage extends AbstractMessage
*/
private class BuilderParentImpl implements BuilderParent {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void markDirty() {
onChanged();
}
@@ -735,6 +737,7 @@ public abstract class GeneratedMessage extends AbstractMessage
public interface ExtendableMessageOrBuilder<
MessageType extends ExtendableMessage> extends MessageOrBuilder {
// Re-define for return type covariance.
+ @Override
Message getDefaultInstanceForType();
/** Check if a singular extension is present. */
@@ -753,6 +756,33 @@ public abstract class GeneratedMessage extends AbstractMessage
<Type> Type getExtension(
ExtensionLite<MessageType, List<Type>> extension,
int index);
+
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ Extension<MessageType, Type> extension);
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ Extension<MessageType, List<Type>> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ GeneratedExtension<MessageType, List<Type>> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, Type> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, List<Type>> extension,
+ int index);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, List<Type>> extension,
+ int index);
}
/**
@@ -795,6 +825,8 @@ public abstract class GeneratedMessage extends AbstractMessage
extends GeneratedMessage
implements ExtendableMessageOrBuilder<MessageType> {
+ private static final long serialVersionUID = 1L;
+
private final FieldSet<FieldDescriptor> extensions;
protected ExtendableMessage() {
@@ -821,9 +853,9 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Check if a singular extension is present. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public final <Type> boolean hasExtension(
- final ExtensionLite<MessageType, Type> extensionLite) {
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extensionLite) {
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -831,7 +863,8 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get the number of elements in a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
+ @SuppressWarnings("unchecked")
public final <Type> int getExtensionCount(
final ExtensionLite<MessageType, List<Type>> extensionLite) {
Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
@@ -842,10 +875,9 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get the value of an extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
@SuppressWarnings("unchecked")
- public final <Type> Type getExtension(
- final ExtensionLite<MessageType, Type> extensionLite) {
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extensionLite) {
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -867,11 +899,10 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get one element of a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final ExtensionLite<MessageType, List<Type>> extensionLite,
- final int index) {
+ final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) {
Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -880,6 +911,53 @@ public abstract class GeneratedMessage extends AbstractMessage
extensions.getRepeatedField(descriptor, index));
}
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
@@ -1019,7 +1097,9 @@ public abstract class GeneratedMessage extends AbstractMessage
verifyContainingType(field);
final Object value = extensions.getField(field);
if (value == null) {
- if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isRepeated()) {
+ return Collections.emptyList();
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
// Lacking an ExtensionRegistry, we have no way to determine the
// extension's real type, so we return a DynamicMessage.
return DynamicMessage.getDefaultInstance(field.getMessageType());
@@ -1103,7 +1183,7 @@ public abstract class GeneratedMessage extends AbstractMessage
@SuppressWarnings("unchecked")
public abstract static class ExtendableBuilder<
MessageType extends ExtendableMessage,
- BuilderType extends ExtendableBuilder>
+ BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
extends Builder<BuilderType>
implements ExtendableMessageOrBuilder<MessageType> {
@@ -1155,9 +1235,8 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Check if a singular extension is present. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public final <Type> boolean hasExtension(
- final ExtensionLite<MessageType, Type> extensionLite) {
+ @Override
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extensionLite) {
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -1165,7 +1244,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get the number of elements in a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final <Type> int getExtensionCount(
final ExtensionLite<MessageType, List<Type>> extensionLite) {
Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
@@ -1176,9 +1255,8 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get the value of an extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public final <Type> Type getExtension(
- final ExtensionLite<MessageType, Type> extensionLite) {
+ @Override
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extensionLite) {
Extension<MessageType, Type> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -1200,10 +1278,9 @@ public abstract class GeneratedMessage extends AbstractMessage
}
/** Get one element of a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final <Type> Type getExtension(
- final ExtensionLite<MessageType, List<Type>> extensionLite,
- final int index) {
+ final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) {
Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
verifyExtensionContainingType(extension);
@@ -1269,6 +1346,95 @@ public abstract class GeneratedMessage extends AbstractMessage
return (BuilderType) this;
}
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Set the value of an extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of an extension. */
+ public <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Append a value to a repeated extension. */
+ public final <Type> BuilderType addExtension(
+ final Extension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Append a value to a repeated extension. */
+ public <Type> BuilderType addExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Clear an extension. */
+ public final <Type> BuilderType clearExtension(
+ final Extension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+ /** Clear an extension. */
+ public <Type> BuilderType clearExtension(
+ final GeneratedExtension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
@@ -1456,10 +1622,9 @@ public abstract class GeneratedMessage extends AbstractMessage
// obtained.
return new GeneratedExtension<ContainingType, Type>(
new CachedDescriptorRetriever() {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public FieldDescriptor loadDescriptor() {
- return scope.getDescriptorForType().getExtensions()
- .get(descriptorIndex);
+ return scope.getDescriptorForType().getExtensions().get(descriptorIndex);
}
},
singularType,
@@ -1487,6 +1652,7 @@ public abstract class GeneratedMessage extends AbstractMessage
private volatile FieldDescriptor descriptor;
protected abstract FieldDescriptor loadDescriptor();
+ @Override
public FieldDescriptor getDescriptor() {
if (descriptor == null) {
synchronized (this) {
@@ -1516,6 +1682,7 @@ public abstract class GeneratedMessage extends AbstractMessage
// obtained.
return new GeneratedExtension<ContainingType, Type>(
new CachedDescriptorRetriever() {
+ @Override
protected FieldDescriptor loadDescriptor() {
return scope.getDescriptorForType().findFieldByName(name);
}
@@ -1542,17 +1709,18 @@ public abstract class GeneratedMessage extends AbstractMessage
// used to obtain the extension's FieldDescriptor.
return new GeneratedExtension<ContainingType, Type>(
new CachedDescriptorRetriever() {
+ @Override
protected FieldDescriptor loadDescriptor() {
try {
- Class clazz =
- singularType.getClassLoader().loadClass(descriptorOuterClass);
- FileDescriptor file =
- (FileDescriptor) clazz.getField("descriptor").get(null);
+ Class clazz = singularType.getClassLoader().loadClass(descriptorOuterClass);
+ FileDescriptor file = (FileDescriptor) clazz.getField("descriptor").get(null);
return file.findExtensionByName(extensionName);
} catch (Exception e) {
throw new RuntimeException(
- "Cannot load descriptors: " + descriptorOuterClass +
- " is not a valid descriptor class name", e);
+ "Cannot load descriptors: "
+ + descriptorOuterClass
+ + " is not a valid descriptor class name",
+ e);
}
}
},
@@ -1634,12 +1802,13 @@ public abstract class GeneratedMessage extends AbstractMessage
if (descriptorRetriever != null) {
throw new IllegalStateException("Already initialized.");
}
- descriptorRetriever = new ExtensionDescriptorRetriever() {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public FieldDescriptor getDescriptor() {
- return descriptor;
- }
- };
+ descriptorRetriever =
+ new ExtensionDescriptorRetriever() {
+ @Override
+ public FieldDescriptor getDescriptor() {
+ return descriptor;
+ }
+ };
}
private ExtensionDescriptorRetriever descriptorRetriever;
@@ -1649,6 +1818,7 @@ public abstract class GeneratedMessage extends AbstractMessage
private final Method enumGetValueDescriptor;
private final ExtensionType extensionType;
+ @Override
public FieldDescriptor getDescriptor() {
if (descriptorRetriever == null) {
throw new IllegalStateException(
@@ -1661,10 +1831,12 @@ public abstract class GeneratedMessage extends AbstractMessage
* If the extension is an embedded message or group, returns the default
* instance of the message.
*/
+ @Override
public Message getMessageDefaultInstance() {
return messageDefaultInstance;
}
+ @Override
protected ExtensionType getExtensionType() {
return extensionType;
}
@@ -1675,7 +1847,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* EnumValueDescriptors but the native accessors use the generated enum
* type.
*/
- // @Override
+ @Override
@SuppressWarnings("unchecked")
protected Object fromReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
@@ -1700,7 +1872,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Like {@link #fromReflectionType(Object)}, but if the type is a repeated
* type, this converts a single element.
*/
- // @Override
+ @Override
protected Object singularFromReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
switch (descriptor.getJavaType()) {
@@ -1724,7 +1896,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* EnumValueDescriptors but the native accessors use the generated enum
* type.
*/
- // @Override
+ @Override
@SuppressWarnings("unchecked")
protected Object toReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
@@ -1748,7 +1920,7 @@ public abstract class GeneratedMessage extends AbstractMessage
* Like {@link #toReflectionType(Object)}, but if the type is a repeated
* type, this converts a single element.
*/
- // @Override
+ @Override
protected Object singularToReflectionType(final Object value) {
FieldDescriptor descriptor = getDescriptor();
switch (descriptor.getJavaType()) {
@@ -1759,22 +1931,22 @@ public abstract class GeneratedMessage extends AbstractMessage
}
}
- // @Override
+ @Override
public int getNumber() {
return getDescriptor().getNumber();
}
- // @Override
+ @Override
public WireFormat.FieldType getLiteType() {
return getDescriptor().getLiteType();
}
- // @Override
+ @Override
public boolean isRepeated() {
return getDescriptor().isRepeated();
}
- // @Override
+ @Override
@SuppressWarnings("unchecked")
public Type getDefaultValue() {
if (isRepeated()) {
@@ -2124,49 +2296,57 @@ public abstract class GeneratedMessage extends AbstractMessage
return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
}
+ @Override
public Object get(final GeneratedMessage message) {
return invokeOrDie(getMethod, message);
}
+ @Override
public Object get(GeneratedMessage.Builder builder) {
return invokeOrDie(getMethodBuilder, builder);
}
+ @Override
public Object getRaw(final GeneratedMessage message) {
return get(message);
}
+ @Override
public Object getRaw(GeneratedMessage.Builder builder) {
return get(builder);
}
+ @Override
public void set(final Builder builder, final Object value) {
invokeOrDie(setMethod, builder, value);
}
- public Object getRepeated(final GeneratedMessage message,
- final int index) {
+ @Override
+ public Object getRepeated(final GeneratedMessage message, final int index) {
throw new UnsupportedOperationException(
"getRepeatedField() called on a singular field.");
}
- public Object getRepeatedRaw(final GeneratedMessage message,
- final int index) {
+ @Override
+ public Object getRepeatedRaw(final GeneratedMessage message, final int index) {
throw new UnsupportedOperationException(
"getRepeatedFieldRaw() called on a singular field.");
}
+ @Override
public Object getRepeated(GeneratedMessage.Builder builder, int index) {
throw new UnsupportedOperationException(
"getRepeatedField() called on a singular field.");
}
- public Object getRepeatedRaw(GeneratedMessage.Builder builder,
- int index) {
+ @Override
+ public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) {
throw new UnsupportedOperationException(
"getRepeatedFieldRaw() called on a singular field.");
}
- public void setRepeated(final Builder builder, final int index,
- final Object value) {
+ @Override
+ public void setRepeated(final Builder builder, final int index, final Object value) {
throw new UnsupportedOperationException(
"setRepeatedField() called on a singular field.");
}
+ @Override
public void addRepeated(final Builder builder, final Object value) {
throw new UnsupportedOperationException(
"addRepeatedField() called on a singular field.");
}
+ @Override
public boolean has(final GeneratedMessage message) {
if (!hasHasMethod) {
if (isOneofField) {
@@ -2176,6 +2356,7 @@ public abstract class GeneratedMessage extends AbstractMessage
}
return (Boolean) invokeOrDie(hasMethod, message);
}
+ @Override
public boolean has(GeneratedMessage.Builder builder) {
if (!hasHasMethod) {
if (isOneofField) {
@@ -2185,27 +2366,32 @@ public abstract class GeneratedMessage extends AbstractMessage
}
return (Boolean) invokeOrDie(hasMethodBuilder, builder);
}
+ @Override
public int getRepeatedCount(final GeneratedMessage message) {
throw new UnsupportedOperationException(
"getRepeatedFieldSize() called on a singular field.");
}
+ @Override
public int getRepeatedCount(GeneratedMessage.Builder builder) {
throw new UnsupportedOperationException(
"getRepeatedFieldSize() called on a singular field.");
}
+ @Override
public void clear(final Builder builder) {
invokeOrDie(clearMethod, builder);
}
+ @Override
public Message.Builder newBuilder() {
throw new UnsupportedOperationException(
"newBuilderForField() called on a non-Message type.");
}
+ @Override
public Message.Builder getBuilder(GeneratedMessage.Builder builder) {
throw new UnsupportedOperationException(
"getFieldBuilder() called on a non-Message type.");
}
- public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder,
- int index) {
+ @Override
+ public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) {
throw new UnsupportedOperationException(
"getRepeatedFieldBuilder() called on a non-Message type.");
}
@@ -2249,18 +2435,23 @@ public abstract class GeneratedMessage extends AbstractMessage
clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
}
+ @Override
public Object get(final GeneratedMessage message) {
return invokeOrDie(getMethod, message);
}
+ @Override
public Object get(GeneratedMessage.Builder builder) {
return invokeOrDie(getMethodBuilder, builder);
}
+ @Override
public Object getRaw(final GeneratedMessage message) {
return get(message);
}
+ @Override
public Object getRaw(GeneratedMessage.Builder builder) {
return get(builder);
}
+ @Override
public void set(final Builder builder, final Object value) {
// Add all the elements individually. This serves two purposes:
// 1) Verifies that each element has the correct type.
@@ -2271,54 +2462,64 @@ public abstract class GeneratedMessage extends AbstractMessage
addRepeated(builder, element);
}
}
- public Object getRepeated(final GeneratedMessage message,
- final int index) {
+ @Override
+ public Object getRepeated(final GeneratedMessage message, final int index) {
return invokeOrDie(getRepeatedMethod, message, index);
}
+ @Override
public Object getRepeated(GeneratedMessage.Builder builder, int index) {
return invokeOrDie(getRepeatedMethodBuilder, builder, index);
}
+ @Override
public Object getRepeatedRaw(GeneratedMessage message, int index) {
return getRepeated(message, index);
}
- public Object getRepeatedRaw(GeneratedMessage.Builder builder,
- int index) {
+ @Override
+ public Object getRepeatedRaw(GeneratedMessage.Builder builder, int index) {
return getRepeated(builder, index);
}
- public void setRepeated(final Builder builder,
- final int index, final Object value) {
+ @Override
+ public void setRepeated(final Builder builder, final int index, final Object value) {
invokeOrDie(setRepeatedMethod, builder, index, value);
}
+ @Override
public void addRepeated(final Builder builder, final Object value) {
invokeOrDie(addRepeatedMethod, builder, value);
}
+ @Override
public boolean has(final GeneratedMessage message) {
throw new UnsupportedOperationException(
"hasField() called on a repeated field.");
}
+ @Override
public boolean has(GeneratedMessage.Builder builder) {
throw new UnsupportedOperationException(
"hasField() called on a repeated field.");
}
+ @Override
public int getRepeatedCount(final GeneratedMessage message) {
return (Integer) invokeOrDie(getCountMethod, message);
}
+ @Override
public int getRepeatedCount(GeneratedMessage.Builder builder) {
return (Integer) invokeOrDie(getCountMethodBuilder, builder);
}
+ @Override
public void clear(final Builder builder) {
invokeOrDie(clearMethod, builder);
}
+ @Override
public Message.Builder newBuilder() {
throw new UnsupportedOperationException(
"newBuilderForField() called on a non-Message type.");
}
+ @Override
public Message.Builder getBuilder(GeneratedMessage.Builder builder) {
throw new UnsupportedOperationException(
"getFieldBuilder() called on a non-Message type.");
}
- public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder,
- int index) {
+ @Override
+ public Message.Builder getRepeatedBuilder(GeneratedMessage.Builder builder, int index) {
throw new UnsupportedOperationException(
"getRepeatedFieldBuilder() called on a non-Message type.");
}
@@ -2355,6 +2556,8 @@ public abstract class GeneratedMessage extends AbstractMessage
field.getNumber());
}
+ @Override
+ @SuppressWarnings("unchecked")
public Object get(GeneratedMessage message) {
List result = new ArrayList();
for (int i = 0; i < getRepeatedCount(message); i++) {
@@ -2363,6 +2566,8 @@ public abstract class GeneratedMessage extends AbstractMessage
return Collections.unmodifiableList(result);
}
+ @Override
+ @SuppressWarnings("unchecked")
public Object get(Builder builder) {
List result = new ArrayList();
for (int i = 0; i < getRepeatedCount(builder); i++) {
@@ -2371,14 +2576,17 @@ public abstract class GeneratedMessage extends AbstractMessage
return Collections.unmodifiableList(result);
}
+ @Override
public Object getRaw(GeneratedMessage message) {
return get(message);
}
+ @Override
public Object getRaw(GeneratedMessage.Builder builder) {
return get(builder);
}
+ @Override
public void set(Builder builder, Object value) {
clear(builder);
for (Object entry : (List) value) {
@@ -2386,63 +2594,76 @@ public abstract class GeneratedMessage extends AbstractMessage
}
}
+ @Override
public Object getRepeated(GeneratedMessage message, int index) {
return getMapField(message).getList().get(index);
}
+ @Override
public Object getRepeated(Builder builder, int index) {
return getMapField(builder).getList().get(index);
}
+ @Override
public Object getRepeatedRaw(GeneratedMessage message, int index) {
return getRepeated(message, index);
}
+ @Override
public Object getRepeatedRaw(Builder builder, int index) {
return getRepeated(builder, index);
}
+ @Override
public void setRepeated(Builder builder, int index, Object value) {
getMutableMapField(builder).getMutableList().set(index, (Message) value);
}
+ @Override
public void addRepeated(Builder builder, Object value) {
getMutableMapField(builder).getMutableList().add((Message) value);
}
+ @Override
public boolean has(GeneratedMessage message) {
throw new UnsupportedOperationException(
"hasField() is not supported for repeated fields.");
}
+ @Override
public boolean has(Builder builder) {
throw new UnsupportedOperationException(
"hasField() is not supported for repeated fields.");
}
+ @Override
public int getRepeatedCount(GeneratedMessage message) {
return getMapField(message).getList().size();
}
+ @Override
public int getRepeatedCount(Builder builder) {
return getMapField(builder).getList().size();
}
+ @Override
public void clear(Builder builder) {
getMutableMapField(builder).getMutableList().clear();
}
+ @Override
public com.google.protobuf.Message.Builder newBuilder() {
return mapEntryMessageDefaultInstance.newBuilderForType();
}
+ @Override
public com.google.protobuf.Message.Builder getBuilder(Builder builder) {
throw new UnsupportedOperationException(
"Nested builder not supported for map fields.");
}
- public com.google.protobuf.Message.Builder getRepeatedBuilder(
- Builder builder, int index) {
+ @Override
+ public com.google.protobuf.Message.Builder getRepeatedBuilder(Builder builder, int index) {
throw new UnsupportedOperationException(
"Nested builder not supported for map fields.");
}
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index 81e1862c..df01547e 100644
--- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -30,24 +30,28 @@
package com.google.protobuf;
+import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
import com.google.protobuf.Internal.BooleanList;
import com.google.protobuf.Internal.DoubleList;
+import com.google.protobuf.Internal.EnumLiteMap;
import com.google.protobuf.Internal.FloatList;
import com.google.protobuf.Internal.IntList;
import com.google.protobuf.Internal.LongList;
import com.google.protobuf.Internal.ProtobufList;
import com.google.protobuf.WireFormat.FieldType;
-
import java.io.IOException;
+import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Lite version of {@link GeneratedMessage}.
@@ -56,49 +60,144 @@ import java.util.Map;
*/
public abstract class GeneratedMessageLite<
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
- BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
- extends AbstractMessageLite
- implements Serializable {
-
- private static final long serialVersionUID = 1L;
+ BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
+ extends AbstractMessageLite<MessageType, BuilderType> {
+ // BEGIN REGULAR
+ static final boolean ENABLE_EXPERIMENTAL_RUNTIME_AT_BUILD_TIME = false;
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // static final boolean ENABLE_EXPERIMENTAL_RUNTIME_AT_BUILD_TIME = true;
+ // END EXPERIMENTAL
/** For use by generated code only. Lazily initialized to reduce allocations. */
- protected UnknownFieldSetLite unknownFields = null;
-
+ protected UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
+
/** For use by generated code only. */
protected int memoizedSerializedSize = -1;
-
+
+ @Override
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final Parser<MessageType> getParserForType() {
return (Parser<MessageType>) dynamicMethod(MethodToInvoke.GET_PARSER);
}
+ @Override
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final MessageType getDefaultInstanceForType() {
return (MessageType) dynamicMethod(MethodToInvoke.GET_DEFAULT_INSTANCE);
}
+ @Override
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final BuilderType newBuilderForType() {
return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
}
+ /**
+ * A reflective toString function. This is primarily intended as a developer aid, while keeping
+ * binary size down. The first line of the {@code toString()} representation includes a commented
+ * version of {@code super.toString()} to act as an indicator that this should not be relied on
+ * for comparisons.
+ * <p>
+ * NOTE: This method relies on the field getter methods not being stripped or renamed by proguard.
+ * If they are, the fields will not be included in the returned string representation.
+ * <p>
+ * NOTE: This implementation is liable to change in the future, and should not be relied on in
+ * code.
+ */
+ @Override
+ public String toString() {
+ return MessageLiteToString.toString(this, super.toString());
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime
+ @Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ // BEGIN EXPERIMENTAL
+ // memoizedHashCode = Protobuf.getInstance().schemaFor(this).hashCode(this);
+ // return memoizedHashCode;
+ // END EXPERIMENTAL
+ // BEGIN REGULAR
+ HashCodeVisitor visitor = new HashCodeVisitor();
+ visit(visitor, (MessageType) this);
+ memoizedHashCode = visitor.hashCode;
+ return memoizedHashCode;
+ // END REGULAR
+ }
+
+ // BEGIN REGULAR
+ @SuppressWarnings("unchecked") // Guaranteed by runtime
+ int hashCode(HashCodeVisitor visitor) {
+ if (memoizedHashCode == 0) {
+ int inProgressHashCode = visitor.hashCode;
+ visitor.hashCode = 0;
+ visit(visitor, (MessageType) this);
+ memoizedHashCode = visitor.hashCode;
+ visitor.hashCode = inProgressHashCode;
+ }
+ return memoizedHashCode;
+ }
+ // END REGULAR
+
+ @SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!getDefaultInstanceForType().getClass().isInstance(other)) {
+ return false;
+ }
+
+ // BEGIN EXPERIMENTAL
+ // return Protobuf.getInstance().schemaFor(this).equals(this, (MessageType) other);
+ // END EXPERIMENTAL
+ // BEGIN REGULAR
+
+ try {
+ visit(EqualsVisitor.INSTANCE, (MessageType) other);
+ } catch (EqualsVisitor.NotEqualsException e) {
+ return false;
+ }
+ return true;
+ // END REGULAR
+ }
+
+ // BEGIN REGULAR
+ /** Same as {@link #equals(Object)} but throws {@code NotEqualsException}. */
+ @SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime
+ boolean equals(EqualsVisitor visitor, MessageLite other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!getDefaultInstanceForType().getClass().isInstance(other)) {
+ return false;
+ }
+
+ visit(visitor, (MessageType) other);
+ return true;
+ }
+ // END REGULAR
+
// 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.
- /**
- * Lazily initializes unknown fields.
- */
+ /** Lazily initializes unknown fields. */
private final void ensureUnknownFieldsInitialized() {
- if (unknownFields == null) {
+ if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) {
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 boolean parseUnknownField(int tag, CodedInputStream input) throws IOException {
@@ -106,7 +205,7 @@ public abstract class GeneratedMessageLite<
if (WireFormat.getTagWireType(tag) == WireFormat.WIRETYPE_END_GROUP) {
return false;
}
-
+
ensureUnknownFieldsInitialized();
return unknownFields.mergeFieldFrom(tag, input);
}
@@ -118,7 +217,7 @@ public abstract class GeneratedMessageLite<
ensureUnknownFieldsInitialized();
unknownFields.mergeVarintField(tag, value);
}
-
+
/**
* Called by subclasses to parse an unknown field. For use by generated code only.
*/
@@ -126,22 +225,41 @@ public abstract class GeneratedMessageLite<
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();
- }
+ protected void makeImmutable() {
+ // BEGIN REGULAR
+ dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
+ unknownFields.makeImmutable();
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // Protobuf.getInstance().schemaFor(this).makeImmutable(this);
+ // END EXPERIMENTAL
}
+ protected final <
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
+ BuilderType createBuilder() {
+ return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
+ }
+
+ protected final <
+ MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
+ BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
+ BuilderType createBuilder(MessageType prototype) {
+ return ((BuilderType) createBuilder()).mergeFrom(prototype);
+ }
+
+ @Override
public final boolean isInitialized() {
- return dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.TRUE) != null;
+ return isInitialized((MessageType) this, Boolean.TRUE);
}
+ @Override
+ @SuppressWarnings("unchecked")
public final BuilderType toBuilder() {
BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
builder.mergeFrom((MessageType) this);
@@ -155,11 +273,18 @@ public abstract class GeneratedMessageLite<
* For use by generated code only.
*/
public static enum MethodToInvoke {
+ // BEGIN REGULAR
IS_INITIALIZED,
- PARSE_PARTIAL_FROM,
- MERGE_FROM,
+ VISIT,
+ MERGE_FROM_STREAM,
MAKE_IMMUTABLE,
- NEW_INSTANCE,
+ // END REGULAR
+ // Rely on/modify instance state
+ GET_MEMOIZED_IS_INITIALIZED,
+ SET_MEMOIZED_IS_INITIALIZED,
+
+ // Rely on static state
+ NEW_MUTABLE_INSTANCE,
NEW_BUILDER,
GET_DEFAULT_INSTANCE,
GET_PARSER;
@@ -170,24 +295,30 @@ public abstract class GeneratedMessageLite<
* Theses different kinds of operations are required to implement message-level operations for
* builders in the runtime. This method bundles those operations to reduce the generated methods
* count.
+ *
* <ul>
- * <li>{@code PARSE_PARTIAL_FROM} is parameterized with an {@link CodedInputStream} and
- * {@link ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the
- * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException}, the
- * implementation wraps it in a RuntimeException
- * <li>{@code NEW_INSTANCE} returns a new instance of the protocol buffer
- * <li>{@code IS_INITIALIZED} is parameterized with a {@code Boolean} detailing whether to
- * memoize. It returns {@code null} for false and the default instance for true. We optionally
- * memoize to support the Builder case, where memoization is not desired.
- * <li>{@code NEW_BUILDER} returns a {@code BuilderType} instance.
- * <li>{@code MERGE_FROM} is parameterized with a {@code MessageType} and merges the fields from
- * that instance into this instance.
- * <li>{@code MAKE_IMMUTABLE} sets all internal fields to an immutable state.
+ * <li>{@code MERGE_FROM_STREAM} is parameterized with an {@link CodedInputStream} and {@link
+ * ExtensionRegistryLite}. It consumes the input stream, parsing the contents into the
+ * returned protocol buffer. If parsing throws an {@link InvalidProtocolBufferException},
+ * the implementation wraps it in a RuntimeException.
+ * <li>{@code NEW_INSTANCE} returns a new instance of the protocol buffer that has not yet been
+ * made immutable. See {@code MAKE_IMMUTABLE}.
+ * <li>{@code IS_INITIALIZED} returns {@code null} for false and the default instance for true.
+ * It doesn't use or modify any memoized value.
+ * <li>{@code GET_MEMOIZED_IS_INITIALIZED} returns the memoized {@code isInitialized} byte
+ * value.
+ * <li>{@code SET_MEMOIZED_IS_INITIALIZED} sets the memoized {@code isInitilaized} byte value to
+ * 1 if the first parameter is not null, or to 0 if the first parameter is null.
+ * <li>{@code NEW_BUILDER} returns a {@code BuilderType} instance.
+ * <li>{@code VISIT} is parameterized with a {@code Visitor} and a {@code MessageType} and
+ * recursively iterates through the fields side by side between this and the instance.
+ * <li>{@code MAKE_IMMUTABLE} sets all internal fields to an immutable state.
* </ul>
+ *
* This method, plus the implementation of the Builder, enables the Builder class to be proguarded
* away entirely on Android.
- * <p>
- * For use by generated code only.
+ *
+ * <p>For use by generated code only.
*/
protected abstract Object dynamicMethod(MethodToInvoke method, Object arg0, Object arg1);
@@ -205,9 +336,27 @@ public abstract class GeneratedMessageLite<
return dynamicMethod(method, null, null);
}
+ // BEGIN REGULAR
+ void visit(Visitor visitor, MessageType other) {
+ dynamicMethod(MethodToInvoke.VISIT, visitor, other);
+ unknownFields = visitor.visitUnknownFields(unknownFields, other.unknownFields);
+ }
+ // END REGULAR
+
+ @Override
+ int getMemoizedSerializedSize() {
+ return memoizedSerializedSize;
+ }
+
+ @Override
+ void setMemoizedSerializedSize(int size) {
+ memoizedSerializedSize = size;
+ }
+
+
+
/**
- * Merge some unknown fields into the {@link UnknownFieldSetLite} for this
- * message.
+ * Merge some unknown fields into the {@link UnknownFieldSetLite} for this message.
*
* <p>For use by generated code only.
*/
@@ -219,7 +368,7 @@ public abstract class GeneratedMessageLite<
public abstract static class Builder<
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
BuilderType extends Builder<MessageType, BuilderType>>
- extends AbstractMessageLite.Builder<BuilderType> {
+ extends AbstractMessageLite.Builder<MessageType, BuilderType> {
private final MessageType defaultInstance;
protected MessageType instance;
@@ -227,7 +376,8 @@ public abstract class GeneratedMessageLite<
protected Builder(MessageType defaultInstance) {
this.defaultInstance = defaultInstance;
- this.instance = (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
+ this.instance =
+ (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
isBuilt = false;
}
@@ -237,26 +387,27 @@ public abstract class GeneratedMessageLite<
*/
protected void copyOnWrite() {
if (isBuilt) {
- MessageType newInstance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
- newInstance.dynamicMethod(MethodToInvoke.MERGE_FROM, instance);
+ MessageType newInstance =
+ (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ mergeFromInstance(newInstance, instance);
instance = newInstance;
isBuilt = false;
}
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final boolean isInitialized() {
return GeneratedMessageLite.isInitialized(instance, false /* shouldMemoize */);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final BuilderType clear() {
// No need to copy on write since we're dropping the instance anyways.
- instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_INSTANCE);
+ instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
return (BuilderType) this;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public BuilderType clone() {
BuilderType builder =
(BuilderType) getDefaultInstanceForType().newBuilderForType();
@@ -264,20 +415,19 @@ public abstract class GeneratedMessageLite<
return builder;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public MessageType buildPartial() {
if (isBuilt) {
return instance;
}
-
- instance.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
- instance.unknownFields.makeImmutable();
-
+
+ instance.makeImmutable();
+
isBuilt = true;
return instance;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final MessageType build() {
MessageType result = buildPartial();
if (!result.isInitialized()) {
@@ -285,34 +435,74 @@ public abstract class GeneratedMessageLite<
}
return result;
}
-
+
+ @Override
+ protected BuilderType internalMergeFrom(MessageType message) {
+ return mergeFrom(message);
+ }
+
/** All subclasses implement this. */
public BuilderType mergeFrom(MessageType message) {
copyOnWrite();
- instance.dynamicMethod(MethodToInvoke.MERGE_FROM, message);
+ mergeFromInstance(instance, message);
return (BuilderType) this;
}
-
+
+ private void mergeFromInstance(MessageType dest, MessageType src) {
+ // BEGIN EXPERIMENTAL
+ // Protobuf.getInstance().schemaFor(dest).mergeFrom(dest, src);
+ // END EXPERIMENTAL
+ // BEGIN REGULAR
+ dest.visit(MergeFromVisitor.INSTANCE, src);
+ // END REGULAR
+ }
+
+ @Override
public MessageType getDefaultInstanceForType() {
return defaultInstance;
}
-
+
+ @Override
+ public BuilderType mergeFrom(byte[] input, int offset, int length)
+ throws InvalidProtocolBufferException {
+ // BEGIN REGULAR
+ return super.mergeFrom(input, offset, length);
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // copyOnWrite();
+ // try {
+ // Protobuf.getInstance().schemaFor(instance).mergeFrom(
+ // instance, input, offset, offset + length, new ArrayDecoders.Registers());
+ // } catch (InvalidProtocolBufferException e) {
+ // throw e;
+ // } catch (IndexOutOfBoundsException e) {
+ // throw InvalidProtocolBufferException.truncatedMessage();
+ // } catch (IOException e) {
+ // throw new RuntimeException("Reading from byte array should not throw IOException.", e);
+ // }
+ // return (BuilderType) this;
+ // END EXPERIMENTAL
+ }
+
+ @Override
public BuilderType mergeFrom(
com.google.protobuf.CodedInputStream input,
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
- throws java.io.IOException {
- MessageType parsedMessage = null;
+ throws IOException {
+ copyOnWrite();
try {
- parsedMessage =
- (MessageType) getDefaultInstanceForType().getParserForType().parsePartialFrom(
- input, extensionRegistry);
- } catch (com.google.protobuf.InvalidProtocolBufferException e) {
- parsedMessage = (MessageType) e.getUnfinishedMessage();
- throw e;
- } finally {
- if (parsedMessage != null) {
- mergeFrom(parsedMessage);
+ // BEGIN REGULAR
+ instance.dynamicMethod(MethodToInvoke.MERGE_FROM_STREAM, input, extensionRegistry);
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // Protobuf.getInstance().schemaFor(instance).mergeFrom(
+ // instance, CodedInputStreamReader.forCodedInput(input), extensionRegistry);
+ // END EXPERIMENTAL
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
}
+ throw e;
}
return (BuilderType) this;
}
@@ -356,32 +546,38 @@ public abstract class GeneratedMessageLite<
extends GeneratedMessageLite<MessageType, BuilderType>
implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
- /**
- * Represents the set of extensions on this message. For use by generated
- * code only.
- */
- protected FieldSet<ExtensionDescriptor> extensions = FieldSet.newFieldSet();
+ /** Represents the set of extensions on this message. For use by generated code only. */
+ protected FieldSet<ExtensionDescriptor> extensions = FieldSet.emptySet();
+ @SuppressWarnings("unchecked")
protected final void mergeExtensionFields(final MessageType other) {
if (extensions.isImmutable()) {
extensions = extensions.clone();
}
extensions.mergeFrom(((ExtendableMessage) other).extensions);
}
-
+
+ // BEGIN REGULAR
+ @Override
+ final void visit(Visitor visitor, MessageType other) {
+ super.visit(visitor, other);
+ extensions = visitor.visitExtensions(extensions, other.extensions);
+ }
+ // END REGULAR
+
/**
* 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 tag)
+ throws IOException {
int fieldNumber = WireFormat.getTagFieldNumber(tag);
// TODO(dweis): How much bytecode would be saved by not requiring the generated code to
@@ -389,6 +585,17 @@ public abstract class GeneratedMessageLite<
GeneratedExtension<MessageType, ?> extension = extensionRegistry.findLiteExtensionByNumber(
defaultInstance, fieldNumber);
+ return parseExtension(input, extensionRegistry, extension, tag, fieldNumber);
+ }
+
+ private boolean parseExtension(
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry,
+ GeneratedExtension<?, ?> extension,
+ int tag,
+ int fieldNumber)
+ throws IOException {
+ int wireType = WireFormat.getTagWireType(tag);
boolean unknown = false;
boolean packed = false;
if (extension == null) {
@@ -411,6 +618,8 @@ public abstract class GeneratedMessageLite<
return parseUnknownField(tag, input);
}
+ ensureExtensionsAreMutable();
+
if (packed) {
int length = input.readRawVarint32();
int limit = input.pushLimit(length);
@@ -489,10 +698,156 @@ public abstract class GeneratedMessageLite<
extension.singularToFieldSetType(value));
}
}
-
return true;
}
+ /**
+ * 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 parseUnknownFieldAsMessageSet(
+ MessageType defaultInstance,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry,
+ int tag)
+ throws IOException {
+
+ if (tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
+ mergeMessageSetExtensionFromCodedStream(defaultInstance, input, extensionRegistry);
+ return true;
+ }
+
+ // TODO(dweis): Do we really want to support non message set wire format in message sets?
+ // Full runtime does... So we do for now.
+ int wireType = WireFormat.getTagWireType(tag);
+ if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) {
+ return parseUnknownField(defaultInstance, input, extensionRegistry, tag);
+ } else {
+ // TODO(dweis): Should we throw on invalid input? Full runtime does not...
+ return input.skipField(tag);
+ }
+ }
+
+ /**
+ * Merges the message set from the input stream; requires message set wire format.
+ *
+ * @param defaultInstance the default instance of the containing message we are parsing in
+ * @param input the stream to parse from
+ * @param extensionRegistry the registry to use when parsing
+ */
+ private <MessageType extends MessageLite> void mergeMessageSetExtensionFromCodedStream(
+ MessageType defaultInstance,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ // The wire format for MessageSet is:
+ // message MessageSet {
+ // repeated group Item = 1 {
+ // required int32 typeId = 2;
+ // required bytes message = 3;
+ // }
+ // }
+ // "typeId" is the extension's field number. The extension can only be
+ // a message type, where "message" contains the encoded bytes of that
+ // message.
+ //
+ // In practice, we will probably never see a MessageSet item in which
+ // the message appears before the type ID, or where either field does not
+ // appear exactly once. However, in theory such cases are valid, so we
+ // should be prepared to accept them.
+
+ int typeId = 0;
+ ByteString rawBytes = null; // If we encounter "message" before "typeId"
+ GeneratedExtension<?, ?> extension = null;
+
+ // Read bytes from input, if we get it's type first then parse it eagerly,
+ // otherwise we store the raw bytes in a local variable.
+ while (true) {
+ final int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+
+ if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
+ typeId = input.readUInt32();
+ if (typeId != 0) {
+ extension = extensionRegistry.findLiteExtensionByNumber(defaultInstance, typeId);
+ }
+
+ } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
+ if (typeId != 0) {
+ if (extension != null) {
+ // We already know the type, so we can parse directly from the
+ // input with no copying. Hooray!
+ eagerlyMergeMessageSetExtension(input, extension, extensionRegistry, typeId);
+ rawBytes = null;
+ continue;
+ }
+ }
+ // We haven't seen a type ID yet or we want parse message lazily.
+ rawBytes = input.readBytes();
+
+ } else { // Unknown tag. Skip it.
+ if (!input.skipField(tag)) {
+ break; // End of group
+ }
+ }
+ }
+ input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
+
+ // Process the raw bytes.
+ if (rawBytes != null && typeId != 0) { // Zero is not a valid type ID.
+ if (extension != null) { // We known the type
+ mergeMessageSetExtensionFromBytes(rawBytes, extensionRegistry, extension);
+ } else { // We don't know how to parse this. Ignore it.
+ if (rawBytes != null) {
+ mergeLengthDelimitedField(typeId, rawBytes);
+ }
+ }
+ }
+ }
+
+ private void eagerlyMergeMessageSetExtension(
+ CodedInputStream input,
+ GeneratedExtension<?, ?> extension,
+ ExtensionRegistryLite extensionRegistry,
+ int typeId)
+ throws IOException {
+ int fieldNumber = typeId;
+ int tag = WireFormat.makeTag(typeId, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ parseExtension(input, extensionRegistry, extension, tag, fieldNumber);
+ }
+
+ private void mergeMessageSetExtensionFromBytes(
+ ByteString rawBytes,
+ ExtensionRegistryLite extensionRegistry,
+ GeneratedExtension<?, ?> extension)
+ throws IOException {
+ MessageLite.Builder subBuilder = null;
+ MessageLite existingValue = (MessageLite) extensions.getField(extension.descriptor);
+ if (existingValue != null) {
+ subBuilder = existingValue.toBuilder();
+ }
+ if (subBuilder == null) {
+ subBuilder = extension.getMessageDefaultInstance().newBuilderForType();
+ }
+ subBuilder.mergeFrom(rawBytes, extensionRegistry);
+ MessageLite value = subBuilder.build();
+
+ ensureExtensionsAreMutable().setField(
+ extension.descriptor, extension.singularToFieldSetType(value));
+ }
+
+ private FieldSet<ExtensionDescriptor> ensureExtensionsAreMutable() {
+ if (extensions.isImmutable()) {
+ extensions = extensions.clone();
+ }
+ return extensions;
+ }
+
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
if (extension.getContainingTypeDefaultInstance() !=
@@ -505,35 +860,33 @@ public abstract class GeneratedMessageLite<
}
/** Check if a singular extension is present. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public final <Type> boolean hasExtension(
- final ExtensionLite<MessageType, Type> extension) {
+ @Override
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extension) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
return extensions.hasField(extensionLite.descriptor);
}
/** Get the number of elements in a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final <Type> int getExtensionCount(
final ExtensionLite<MessageType, List<Type>> extension) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
return extensions.getRepeatedFieldCount(extensionLite.descriptor);
}
/** Get the value of an extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
@SuppressWarnings("unchecked")
- public final <Type> Type getExtension(
- final ExtensionLite<MessageType, Type> extension) {
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extension) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
final Object value = extensions.getField(extensionLite.descriptor);
if (value == null) {
@@ -544,11 +897,10 @@ public abstract class GeneratedMessageLite<
}
/** Get one element of a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
- final ExtensionLite<MessageType, List<Type>> extension,
- final int index) {
+ final ExtensionLite<MessageType, List<Type>> extension, final int index) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
@@ -562,14 +914,15 @@ public abstract class GeneratedMessageLite<
return extensions.isInitialized();
}
-
@Override
- protected final void doneParsing() {
- super.doneParsing();
-
+ protected final void makeImmutable() {
+ super.makeImmutable();
+ // BEGIN REGULAR
extensions.makeImmutable();
+ // END REGULAR
}
+
/**
* Used by subclasses to serialize extensions. Extension ranges may be
* interleaved with field numbers, but we must write them in canonical
@@ -640,12 +993,6 @@ public abstract class GeneratedMessageLite<
implements ExtendableMessageOrBuilder<MessageType, BuilderType> {
protected ExtendableBuilder(MessageType defaultInstance) {
super(defaultInstance);
-
- // TODO(dweis): This is kind of an unnecessary clone since we construct a
- // new instance in the parent constructor which makes the extensions
- // immutable. This extra allocation shouldn't matter in practice
- // though.
- instance.extensions = instance.extensions.clone();
}
// For immutable message conversion.
@@ -654,17 +1001,26 @@ public abstract class GeneratedMessageLite<
instance.extensions = extensions;
}
- // @Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
protected void copyOnWrite() {
if (!isBuilt) {
return;
}
-
+
super.copyOnWrite();
instance.extensions = instance.extensions.clone();
}
- // @Override (Java 1.6 override semantics, but we must support 1.5)
+ private FieldSet<ExtensionDescriptor> ensureExtensionsAreMutable() {
+ FieldSet<ExtensionDescriptor> extensions = instance.extensions;
+ if (extensions.isImmutable()) {
+ extensions = extensions.clone();
+ instance.extensions = extensions;
+ }
+ return extensions;
+ }
+
+ @Override
public final MessageType buildPartial() {
if (isBuilt) {
return instance;
@@ -686,54 +1042,44 @@ public abstract class GeneratedMessageLite<
}
/** Check if a singular extension is present. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public final <Type> boolean hasExtension(
- final ExtensionLite<MessageType, Type> extension) {
+ @Override
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extension) {
return instance.hasExtension(extension);
}
/** Get the number of elements in a repeated extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public final <Type> int getExtensionCount(
final ExtensionLite<MessageType, List<Type>> extension) {
return instance.getExtensionCount(extension);
}
/** Get the value of an extension. */
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
@SuppressWarnings("unchecked")
- public final <Type> Type getExtension(
- final ExtensionLite<MessageType, Type> extension) {
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extension) {
return instance.getExtension(extension);
}
/** Get one element of a repeated extension. */
+ @Override
@SuppressWarnings("unchecked")
- //@Override (Java 1.6 override semantics, but we must support 1.5)
public final <Type> Type getExtension(
- final ExtensionLite<MessageType, List<Type>> extension,
- final int index) {
+ final ExtensionLite<MessageType, List<Type>> extension, final int index) {
return instance.getExtension(extension, index);
}
- // This is implemented here only to work around an apparent bug in the
- // Java compiler and/or build system. See bug #1898463. The mere presence
- // of this dummy clone() implementation makes it go away.
- @Override
- public BuilderType clone() {
- return super.clone();
- }
-
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final ExtensionLite<MessageType, Type> extension,
final Type value) {
GeneratedExtension<MessageType, Type> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
- instance.extensions.setField(extensionLite.descriptor, extensionLite.toFieldSetType(value));
+ ensureExtensionsAreMutable()
+ .setField(extensionLite.descriptor, extensionLite.toFieldSetType(value));
return (BuilderType) this;
}
@@ -743,11 +1089,12 @@ public abstract class GeneratedMessageLite<
final int index, final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
- instance.extensions.setRepeatedField(
- extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value));
+ ensureExtensionsAreMutable()
+ .setRepeatedField(
+ extensionLite.descriptor, index, extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
@@ -757,11 +1104,11 @@ public abstract class GeneratedMessageLite<
final Type value) {
GeneratedExtension<MessageType, List<Type>> extensionLite =
checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
- instance.extensions.addRepeatedField(
- extensionLite.descriptor, extensionLite.singularToFieldSetType(value));
+ ensureExtensionsAreMutable()
+ .addRepeatedField(extensionLite.descriptor, extensionLite.singularToFieldSetType(value));
return (BuilderType) this;
}
@@ -769,10 +1116,10 @@ public abstract class GeneratedMessageLite<
public final <Type> BuilderType clearExtension(
final ExtensionLite<MessageType, ?> extension) {
GeneratedExtension<MessageType, ?> extensionLite = checkIsLite(extension);
-
+
verifyExtensionContainingType(extensionLite);
copyOnWrite();
- instance.extensions.clearField(extensionLite.descriptor);
+ ensureExtensionsAreMutable().clearField(extensionLite.descriptor);
return (BuilderType) this;
}
}
@@ -844,37 +1191,44 @@ public abstract class GeneratedMessageLite<
final boolean isRepeated;
final boolean isPacked;
+ @Override
public int getNumber() {
return number;
}
+ @Override
public WireFormat.FieldType getLiteType() {
return type;
}
+ @Override
public WireFormat.JavaType getLiteJavaType() {
return type.getJavaType();
}
+ @Override
public boolean isRepeated() {
return isRepeated;
}
+ @Override
public boolean isPacked() {
return isPacked;
}
+ @Override
public Internal.EnumLiteMap<?> getEnumType() {
return enumTypeMap;
}
+ @Override
@SuppressWarnings("unchecked")
- public MessageLite.Builder internalMergeFrom(
- MessageLite.Builder to, MessageLite from) {
+ public MessageLite.Builder internalMergeFrom(MessageLite.Builder to, MessageLite from) {
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
}
+ @Override
public int compareTo(ExtensionDescriptor other) {
return number - other.number;
}
@@ -915,6 +1269,7 @@ public abstract class GeneratedMessageLite<
}
}
+
/**
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
*
@@ -969,6 +1324,7 @@ public abstract class GeneratedMessageLite<
}
/** Get the field number. */
+ @Override
public int getNumber() {
return descriptor.getNumber();
}
@@ -978,6 +1334,7 @@ public abstract class GeneratedMessageLite<
* If the extension is an embedded message or group, returns the default
* instance of the message.
*/
+ @Override
public MessageLite getMessageDefaultInstance() {
return messageDefaultInstance;
}
@@ -1032,14 +1389,17 @@ public abstract class GeneratedMessageLite<
}
}
+ @Override
public FieldType getLiteType() {
return descriptor.getLiteType();
}
+ @Override
public boolean isRepeated() {
return descriptor.isRepeated;
}
+ @Override
public Type getDefaultValue() {
return defaultValue;
}
@@ -1049,7 +1409,12 @@ public abstract class GeneratedMessageLite<
* A serialized (serializable) form of the generated message. Stores the
* message as a class name and a byte array.
*/
- static final class SerializedForm implements Serializable {
+ protected static final class SerializedForm implements Serializable {
+
+ public static SerializedForm of(MessageLite message) {
+ return new SerializedForm(message);
+ }
+
private static final long serialVersionUID = 0L;
private final String messageClassName;
@@ -1083,7 +1448,7 @@ public abstract class GeneratedMessageLite<
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
- throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e);
+ return readResolveFallback();
} catch (SecurityException e) {
throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) {
@@ -1092,18 +1457,35 @@ public abstract class GeneratedMessageLite<
throw new RuntimeException("Unable to understand proto buffer", e);
}
}
- }
- /**
- * Replaces this object in the output stream with a serialized form.
- * Part of Java's serialization magic. Generated sub-classes must override
- * this method by calling {@code return super.writeReplace();}
- * @return a SerializedForm of this message
- */
- protected Object writeReplace() throws ObjectStreamException {
- return new SerializedForm(this);
+ /**
+ * @deprecated from v3.0.0-beta-3+, for compatibility with v2.5.0 and v2.6.1 generated code.
+ */
+ @Deprecated
+ private Object readResolveFallback() throws ObjectStreamException {
+ try {
+ Class<?> messageClass = Class.forName(messageClassName);
+ java.lang.reflect.Field defaultInstanceField =
+ messageClass.getDeclaredField("defaultInstance");
+ defaultInstanceField.setAccessible(true);
+ MessageLite defaultInstance = (MessageLite) defaultInstanceField.get(null);
+ return defaultInstance.newBuilderForType()
+ .mergeFrom(asBytes)
+ .buildPartial();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Unable to find defaultInstance in " + messageClassName, e);
+ } catch (SecurityException e) {
+ throw new RuntimeException("Unable to call defaultInstance in " + messageClassName, e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Unable to call parsePartialFrom", e);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException("Unable to understand proto buffer", e);
+ }
+ }
}
-
+
/**
* Checks that the {@link Extension} is Lite and returns it as a
* {@link GeneratedExtension}.
@@ -1117,7 +1499,7 @@ public abstract class GeneratedMessageLite<
if (!extension.isLite()) {
throw new IllegalArgumentException("Expected a lite extension.");
}
-
+
return (GeneratedExtension<MessageType, T>) extension;
}
@@ -1128,31 +1510,88 @@ public abstract class GeneratedMessageLite<
*/
protected static final <T extends GeneratedMessageLite<T, ?>> boolean isInitialized(
T message, boolean shouldMemoize) {
- return message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, shouldMemoize) != null;
+ byte memoizedIsInitialized =
+ (Byte) message.dynamicMethod(MethodToInvoke.GET_MEMOIZED_IS_INITIALIZED);
+ if (memoizedIsInitialized == 1) {
+ return true;
+ }
+ if (memoizedIsInitialized == 0) {
+ return false;
+ }
+ // BEGIN EXPERIMENTAL
+ // boolean isInitialized = Protobuf.getInstance().schemaFor(message).isInitialized(message);
+ // END EXPERIMENTAL
+ // BEGIN REGULAR
+ boolean isInitialized =
+ message.dynamicMethod(MethodToInvoke.IS_INITIALIZED, Boolean.FALSE) != null;
+ // END REGULAR
+ if (shouldMemoize) {
+ message.dynamicMethod(
+ MethodToInvoke.SET_MEMOIZED_IS_INITIALIZED, isInitialized ? message : null);
+ }
+ return isInitialized;
}
-
- protected static final <T extends GeneratedMessageLite<T, ?>> void makeImmutable(T message) {
- message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
+
+ protected static IntList emptyIntList() {
+ return IntArrayList.emptyList();
}
-
- /**
- * A static helper method for parsing a partial from input using the extension registry and the
- * instance.
- */
- static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
- T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
- try {
- return (T) instance.dynamicMethod(
- MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry);
- } catch (RuntimeException e) {
- if (e.getCause() instanceof InvalidProtocolBufferException) {
- throw (InvalidProtocolBufferException) e.getCause();
- }
- throw e;
- }
+
+ protected static IntList mutableCopy(IntList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ protected static LongList emptyLongList() {
+ return LongArrayList.emptyList();
}
-
+
+ protected static LongList mutableCopy(LongList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ protected static FloatList emptyFloatList() {
+ return FloatArrayList.emptyList();
+ }
+
+ protected static FloatList mutableCopy(FloatList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ protected static DoubleList emptyDoubleList() {
+ return DoubleArrayList.emptyList();
+ }
+
+ protected static DoubleList mutableCopy(DoubleList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ protected static BooleanList emptyBooleanList() {
+ return BooleanArrayList.emptyList();
+ }
+
+ protected static BooleanList mutableCopy(BooleanList list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
+ protected static <E> ProtobufList<E> emptyProtobufList() {
+ return ProtobufArrayList.emptyList();
+ }
+
+ protected static <E> ProtobufList<E> mutableCopy(ProtobufList<E> list) {
+ int size = list.size();
+ return list.mutableCopyWithCapacity(
+ size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2);
+ }
+
/**
* A {@link Parser} implementation that delegates to the default instance.
* <p>
@@ -1160,117 +1599,954 @@ public abstract class GeneratedMessageLite<
*/
protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
extends AbstractParser<T> {
-
+
private T defaultInstance;
-
+
public DefaultInstanceBasedParser(T defaultInstance) {
this.defaultInstance = defaultInstance;
}
-
+
@Override
public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry);
}
+
+ @Override
+ public T parsePartialFrom(byte[] input) throws InvalidProtocolBufferException {
+ return GeneratedMessageLite.parsePartialFrom(defaultInstance, input);
+ }
}
-
- protected static IntList newIntList() {
- return new IntArrayList();
- }
-
- protected static IntList newIntListWithCapacity(int capacity) {
- return new IntArrayList(capacity);
- }
-
- protected static IntList newIntList(List<Integer> toCopy) {
- return new IntArrayList(toCopy);
+
+ /**
+ * A static helper method for parsing a partial from input using the extension registry and the
+ * instance.
+ */
+ // TODO(dweis): Should this verify that the last tag was 0?
+ static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
+ T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ @SuppressWarnings("unchecked") // Guaranteed by protoc
+ T result = (T) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ try {
+ // BEGIN REGULAR
+ result.dynamicMethod(MethodToInvoke.MERGE_FROM_STREAM, input, extensionRegistry);
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // Protobuf.getInstance().schemaFor(result).mergeFrom(
+ // result, CodedInputStreamReader.forCodedInput(input), extensionRegistry);
+ // END EXPERIMENTAL
+ result.makeImmutable();
+ // BEGIN EXPERIMENTAL
+ // } catch (IOException e) {
+ // if (e.getCause() instanceof InvalidProtocolBufferException) {
+ // throw (InvalidProtocolBufferException) e.getCause();
+ // }
+ // throw new InvalidProtocolBufferException(e.getMessage()).setUnfinishedMessage(result);
+ // END EXPERIMENTAL
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof InvalidProtocolBufferException) {
+ throw (InvalidProtocolBufferException) e.getCause();
+ }
+ throw e;
+ }
+ return result;
}
-
- protected static IntList emptyIntList() {
- return IntArrayList.emptyList();
+
+ /** A static helper method for parsing a partial from byte array. */
+ static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(T instance, byte[] input)
+ throws InvalidProtocolBufferException {
+ // BEGIN REGULAR
+ return parsePartialFrom(instance, input, ExtensionRegistryLite.getEmptyRegistry());
+ // END REGULAR
+ // BEGIN EXPERIMENTAL
+ // @SuppressWarnings("unchecked") // Guaranteed by protoc
+ // T result = (T) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE);
+ // try {
+ // Protobuf.getInstance().schemaFor(result).mergeFrom(
+ // result, input, 0, input.length, new ArrayDecoders.Registers());
+ // result.makeImmutable();
+ // if (result.memoizedHashCode != 0) {
+ // throw new RuntimeException();
+ // }
+ // } catch (IOException e) {
+ // if (e.getCause() instanceof InvalidProtocolBufferException) {
+ // throw (InvalidProtocolBufferException) e.getCause();
+ // }
+ // throw new InvalidProtocolBufferException(e.getMessage()).setUnfinishedMessage(result);
+ // } catch (IndexOutOfBoundsException e) {
+ // throw InvalidProtocolBufferException.truncatedMessage().setUnfinishedMessage(result);
+ // }
+ // return result;
+ // END EXPERIMENTAL
}
- protected static LongList newLongList() {
- return new LongArrayList();
+ protected static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
+ T defaultInstance,
+ CodedInputStream input)
+ throws InvalidProtocolBufferException {
+ return parsePartialFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
}
- protected static LongList newLongListWithCapacity(int capacity) {
- return new LongArrayList(capacity);
+ /**
+ * Helper method to check if message is initialized.
+ *
+ * @throws InvalidProtocolBufferException if it is not initialized.
+ * @return The message to check.
+ */
+ private static <T extends GeneratedMessageLite<T, ?>> T checkMessageInitialized(T message)
+ throws InvalidProtocolBufferException {
+ if (message != null && !message.isInitialized()) {
+ throw message.newUninitializedMessageException()
+ .asInvalidProtocolBufferException()
+ .setUnfinishedMessage(message);
+ }
+ return message;
}
-
- protected static LongList newLongList(List<Long> toCopy) {
- return new LongArrayList(toCopy);
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, ByteBuffer data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parseFrom(defaultInstance, CodedInputStream.newInstance(data), extensionRegistry));
}
-
- protected static LongList emptyLongList() {
- return LongArrayList.emptyList();
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, ByteBuffer data) throws InvalidProtocolBufferException {
+ return parseFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry());
}
-
- protected static FloatList newFloatList() {
- return new FloatArrayList();
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, ByteString data)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parseFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry()));
}
-
- protected static FloatList newFloatListWithCapacity(int capacity) {
- return new FloatArrayList(capacity);
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
}
-
- protected static FloatList newFloatList(List<Float> toCopy) {
- return new FloatArrayList(toCopy);
+
+ // This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
+ // ByteString.
+ private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
+ T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ T message;
+ try {
+ CodedInputStream input = data.newCodedInput();
+ message = parsePartialFrom(defaultInstance, input, extensionRegistry);
+ try {
+ input.checkLastTagWas(0);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(message);
+ }
+ return message;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ }
}
-
- protected static FloatList emptyFloatList() {
- return FloatArrayList.emptyList();
+
+ // This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
+ // ByteString.
+ private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
+ T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ T message;
+ try {
+ CodedInputStream input = CodedInputStream.newInstance(data);
+ message = parsePartialFrom(defaultInstance, input, extensionRegistry);
+ try {
+ input.checkLastTagWas(0);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(message);
+ }
+ return message;
+ } catch (InvalidProtocolBufferException e) {
+ throw e;
+ }
}
-
- protected static DoubleList newDoubleList() {
- return new DoubleArrayList();
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, byte[] data)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(parsePartialFrom(defaultInstance, data));
}
-
- protected static DoubleList newDoubleListWithCapacity(int capacity) {
- return new DoubleArrayList(capacity);
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
}
-
- protected static DoubleList newDoubleList(List<Double> toCopy) {
- return new DoubleArrayList(toCopy);
+
+ // Does not validate last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, InputStream input)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input),
+ ExtensionRegistryLite.getEmptyRegistry()));
}
-
- protected static DoubleList emptyDoubleList() {
- return DoubleArrayList.emptyList();
+
+ // Does not validate last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input), extensionRegistry));
}
-
- protected static BooleanList newBooleanList() {
- return new BooleanArrayList();
+
+ // Does not validate last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, CodedInputStream input)
+ throws InvalidProtocolBufferException {
+ return parseFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
}
-
- protected static BooleanList newBooleanListWithCapacity(int capacity) {
- return new BooleanArrayList(capacity);
+
+ // Does not validate last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
+ T defaultInstance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parsePartialFrom(defaultInstance, input, extensionRegistry));
}
-
- protected static BooleanList newBooleanList(List<Boolean> toCopy) {
- return new BooleanArrayList(toCopy);
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseDelimitedFrom(
+ T defaultInstance, InputStream input)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parsePartialDelimitedFrom(defaultInstance, input,
+ ExtensionRegistryLite.getEmptyRegistry()));
}
-
- protected static BooleanList emptyBooleanList() {
- return BooleanArrayList.emptyList();
+
+ // Validates last tag.
+ protected static <T extends GeneratedMessageLite<T, ?>> T parseDelimitedFrom(
+ T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ return checkMessageInitialized(
+ parsePartialDelimitedFrom(defaultInstance, input, extensionRegistry));
}
-
- protected static <E> ProtobufList<E> newProtobufList() {
- return new ProtobufArrayList<E>();
+
+ private static <T extends GeneratedMessageLite<T, ?>> T parsePartialDelimitedFrom(
+ T defaultInstance,
+ InputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ int size;
+ try {
+ int firstByte = input.read();
+ if (firstByte == -1) {
+ return null;
+ }
+ size = CodedInputStream.readRawVarint32(firstByte, input);
+ } catch (IOException e) {
+ throw new InvalidProtocolBufferException(e.getMessage());
+ }
+ InputStream limitedInput = new LimitedInputStream(input, size);
+ CodedInputStream codedInput = CodedInputStream.newInstance(limitedInput);
+ T message = parsePartialFrom(defaultInstance, codedInput, extensionRegistry);
+ try {
+ codedInput.checkLastTagWas(0);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(message);
+ }
+ return message;
}
-
- protected static <E> ProtobufList<E> newProtobufList(List<E> toCopy) {
- return new ProtobufArrayList<E>(toCopy);
+
+ // BEGIN REGULAR
+ /**
+ * An abstract visitor that the generated code calls into that we use to implement various
+ * features. Fields that are not members of oneofs are always visited. Members of a oneof are only
+ * visited when they are the set oneof case value on the "other" proto. The visitOneofNotSet
+ * method is invoked if other's oneof case is not set.
+ */
+ protected interface Visitor {
+ boolean visitBoolean(boolean minePresent, boolean mine, boolean otherPresent, boolean other);
+ int visitInt(boolean minePresent, int mine, boolean otherPresent, int other);
+ double visitDouble(boolean minePresent, double mine, boolean otherPresent, double other);
+ float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other);
+ long visitLong(boolean minePresent, long mine, boolean otherPresent, long other);
+ String visitString(boolean minePresent, String mine, boolean otherPresent, String other);
+ ByteString visitByteString(
+ boolean minePresent, ByteString mine, boolean otherPresent, ByteString other);
+
+ Object visitOneofBoolean(boolean minePresent, Object mine, Object other);
+ Object visitOneofInt(boolean minePresent, Object mine, Object other);
+ Object visitOneofDouble(boolean minePresent, Object mine, Object other);
+ Object visitOneofFloat(boolean minePresent, Object mine, Object other);
+ Object visitOneofLong(boolean minePresent, Object mine, Object other);
+ Object visitOneofString(boolean minePresent, Object mine, Object other);
+ Object visitOneofByteString(boolean minePresent, Object mine, Object other);
+ Object visitOneofMessage(boolean minePresent, Object mine, Object other);
+ void visitOneofNotSet(boolean minePresent);
+
+ /**
+ * Message fields use null sentinals.
+ */
+ <T extends MessageLite> T visitMessage(T mine, T other);
+
+ <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other);
+ BooleanList visitBooleanList(BooleanList mine, BooleanList other);
+ IntList visitIntList(IntList mine, IntList other);
+ DoubleList visitDoubleList(DoubleList mine, DoubleList other);
+ FloatList visitFloatList(FloatList mine, FloatList other);
+ LongList visitLongList(LongList mine, LongList other);
+ FieldSet<ExtensionDescriptor> visitExtensions(
+ FieldSet<ExtensionDescriptor> mine, FieldSet<ExtensionDescriptor> other);
+ UnknownFieldSetLite visitUnknownFields(UnknownFieldSetLite mine, UnknownFieldSetLite other);
+ <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other);
}
-
- protected static <E> ProtobufList<E> newProtobufListWithCapacity(int capacity) {
- return new ProtobufArrayList<E>(capacity);
+
+ /**
+ * Implements equals. Throws a {@link NotEqualsException} when not equal.
+ */
+ static class EqualsVisitor implements Visitor {
+
+ static final class NotEqualsException extends RuntimeException {}
+
+ static final EqualsVisitor INSTANCE = new EqualsVisitor();
+
+ static final NotEqualsException NOT_EQUALS = new NotEqualsException();
+
+ private EqualsVisitor() {}
+
+ @Override
+ public boolean visitBoolean(
+ boolean minePresent, boolean mine, boolean otherPresent, boolean other) {
+ if (minePresent != otherPresent || mine != other) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) {
+ if (minePresent != otherPresent || mine != other) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public double visitDouble(
+ boolean minePresent, double mine, boolean otherPresent, double other) {
+ if (minePresent != otherPresent || mine != other) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) {
+ if (minePresent != otherPresent || mine != other) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) {
+ if (minePresent != otherPresent || mine != other) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public String visitString(
+ boolean minePresent, String mine, boolean otherPresent, String other) {
+ if (minePresent != otherPresent || !mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public ByteString visitByteString(
+ boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) {
+ if (minePresent != otherPresent || !mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofInt(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofDouble(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofFloat(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofLong(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofString(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofByteString(boolean minePresent, Object mine, Object other) {
+ if (minePresent && mine.equals(other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
+ if (minePresent && ((GeneratedMessageLite<?, ?>) mine).equals(this, (MessageLite) other)) {
+ return mine;
+ }
+ throw NOT_EQUALS;
+ }
+
+ @Override
+ public void visitOneofNotSet(boolean minePresent) {
+ if (minePresent) {
+ throw NOT_EQUALS;
+ }
+ }
+
+ @Override
+ public <T extends MessageLite> T visitMessage(T mine, T other) {
+ if (mine == null && other == null) {
+ return null;
+ }
+
+ if (mine == null || other == null) {
+ throw NOT_EQUALS;
+ }
+
+ ((GeneratedMessageLite<?, ?>) mine).equals(this, other);
+
+ return mine;
+ }
+
+ @Override
+ public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public BooleanList visitBooleanList(BooleanList mine, BooleanList other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public IntList visitIntList(IntList mine, IntList other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public DoubleList visitDoubleList(DoubleList mine, DoubleList other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public FloatList visitFloatList(FloatList mine, FloatList other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public LongList visitLongList(LongList mine, LongList other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public FieldSet<ExtensionDescriptor> visitExtensions(
+ FieldSet<ExtensionDescriptor> mine,
+ FieldSet<ExtensionDescriptor> other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public UnknownFieldSetLite visitUnknownFields(
+ UnknownFieldSetLite mine,
+ UnknownFieldSetLite other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
+
+ @Override
+ public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
+ if (!mine.equals(other)) {
+ throw NOT_EQUALS;
+ }
+ return mine;
+ }
}
-
- protected static <E> ProtobufList<E> emptyProtobufList() {
- return ProtobufArrayList.emptyList();
+
+ /**
+ * Implements hashCode by accumulating state.
+ */
+ static class HashCodeVisitor implements Visitor {
+
+ // The caller must ensure that the visitor is invoked parameterized with this and this such that
+ // other is this. This is required due to how oneof cases are handled. See the class comment
+ // on Visitor for more information.
+
+ int hashCode = 0;
+
+ @Override
+ public boolean visitBoolean(
+ boolean minePresent, boolean mine, boolean otherPresent, boolean other) {
+ hashCode = (53 * hashCode) + Internal.hashBoolean(mine);
+ return mine;
+ }
+
+ @Override
+ public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) {
+ hashCode = (53 * hashCode) + mine;
+ return mine;
+ }
+
+ @Override
+ public double visitDouble(
+ boolean minePresent, double mine, boolean otherPresent, double other) {
+ hashCode = (53 * hashCode) + Internal.hashLong(Double.doubleToLongBits(mine));
+ return mine;
+ }
+
+ @Override
+ public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) {
+ hashCode = (53 * hashCode) + Float.floatToIntBits(mine);
+ return mine;
+ }
+
+ @Override
+ public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) {
+ hashCode = (53 * hashCode) + Internal.hashLong(mine);
+ return mine;
+ }
+
+ @Override
+ public String visitString(
+ boolean minePresent, String mine, boolean otherPresent, String other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public ByteString visitByteString(
+ boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + Internal.hashBoolean(((Boolean) mine));
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofInt(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + (Integer) mine;
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofDouble(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + Internal.hashLong(Double.doubleToLongBits((Double) mine));
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofFloat(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + Float.floatToIntBits((Float) mine);
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofLong(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + Internal.hashLong((Long) mine);
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofString(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofByteString(boolean minePresent, Object mine, Object other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
+ return visitMessage((MessageLite) mine, (MessageLite) other);
+ }
+
+ @Override
+ public void visitOneofNotSet(boolean minePresent) {
+ if (minePresent) {
+ throw new IllegalStateException(); // Can't happen if other == this.
+ }
+ }
+
+ @Override
+ public <T extends MessageLite> T visitMessage(T mine, T other) {
+ final int protoHash;
+ if (mine != null) {
+ if (mine instanceof GeneratedMessageLite) {
+ protoHash = ((GeneratedMessageLite) mine).hashCode(this);
+ } else {
+ protoHash = mine.hashCode();
+ }
+ } else {
+ protoHash = 37;
+ }
+ hashCode = (53 * hashCode) + protoHash;
+ return mine;
+ }
+
+ @Override
+ public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public BooleanList visitBooleanList(BooleanList mine, BooleanList other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public IntList visitIntList(IntList mine, IntList other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public DoubleList visitDoubleList(DoubleList mine, DoubleList other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public FloatList visitFloatList(FloatList mine, FloatList other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public LongList visitLongList(LongList mine, LongList other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public FieldSet<ExtensionDescriptor> visitExtensions(
+ FieldSet<ExtensionDescriptor> mine,
+ FieldSet<ExtensionDescriptor> other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public UnknownFieldSetLite visitUnknownFields(
+ UnknownFieldSetLite mine,
+ UnknownFieldSetLite other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
+
+ @Override
+ public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
+ hashCode = (53 * hashCode) + mine.hashCode();
+ return mine;
+ }
}
-
- protected static LazyStringArrayList emptyLazyStringArrayList() {
- return LazyStringArrayList.emptyList();
+
+ /**
+ * Implements field merging semantics over the visitor interface.
+ */
+ protected static class MergeFromVisitor implements Visitor {
+
+ public static final MergeFromVisitor INSTANCE = new MergeFromVisitor();
+
+ private MergeFromVisitor() {}
+
+ @Override
+ public boolean visitBoolean(
+ boolean minePresent, boolean mine, boolean otherPresent, boolean other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public int visitInt(boolean minePresent, int mine, boolean otherPresent, int other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public double visitDouble(
+ boolean minePresent, double mine, boolean otherPresent, double other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public float visitFloat(boolean minePresent, float mine, boolean otherPresent, float other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public long visitLong(boolean minePresent, long mine, boolean otherPresent, long other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public String visitString(
+ boolean minePresent, String mine, boolean otherPresent, String other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public ByteString visitByteString(
+ boolean minePresent, ByteString mine, boolean otherPresent, ByteString other) {
+ return otherPresent ? other : mine;
+ }
+
+ @Override
+ public Object visitOneofBoolean(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofInt(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofDouble(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofFloat(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofLong(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofString(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofByteString(boolean minePresent, Object mine, Object other) {
+ return other;
+ }
+
+ @Override
+ public Object visitOneofMessage(boolean minePresent, Object mine, Object other) {
+ if (minePresent) {
+ return visitMessage((MessageLite) mine, (MessageLite) other);
+ }
+ return other;
+ }
+
+ @Override
+ public void visitOneofNotSet(boolean minePresent) {
+ return;
+ }
+
+ @SuppressWarnings("unchecked") // Guaranteed by runtime.
+ @Override
+ public <T extends MessageLite> T visitMessage(T mine, T other) {
+ if (mine != null && other != null) {
+ return (T) mine.toBuilder().mergeFrom(other).build();
+ }
+
+ return mine != null ? mine : other;
+ }
+
+ @Override
+ public <T> ProtobufList<T> visitList(ProtobufList<T> mine, ProtobufList<T> other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public BooleanList visitBooleanList(BooleanList mine, BooleanList other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public IntList visitIntList(IntList mine, IntList other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public DoubleList visitDoubleList(DoubleList mine, DoubleList other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public FloatList visitFloatList(FloatList mine, FloatList other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public LongList visitLongList(LongList mine, LongList other) {
+ int size = mine.size();
+ int otherSize = other.size();
+ if (size > 0 && otherSize > 0) {
+ if (!mine.isModifiable()) {
+ mine = mine.mutableCopyWithCapacity(size + otherSize);
+ }
+ mine.addAll(other);
+ }
+
+ return size > 0 ? mine : other;
+ }
+
+ @Override
+ public FieldSet<ExtensionDescriptor> visitExtensions(
+ FieldSet<ExtensionDescriptor> mine,
+ FieldSet<ExtensionDescriptor> other) {
+ if (mine.isImmutable()) {
+ mine = mine.clone();
+ }
+ mine.mergeFrom(other);
+ return mine;
+ }
+
+ @Override
+ public UnknownFieldSetLite visitUnknownFields(
+ UnknownFieldSetLite mine,
+ UnknownFieldSetLite other) {
+ return other == UnknownFieldSetLite.getDefaultInstance()
+ ? mine : UnknownFieldSetLite.mutableCopyOf(mine, other);
+ }
+
+ @Override
+ public <K, V> MapFieldLite<K, V> visitMap(MapFieldLite<K, V> mine, MapFieldLite<K, V> other) {
+ if (!other.isEmpty()) {
+ if (!mine.isMutable()) {
+ mine = mine.mutableCopy();
+ }
+ mine.mergeFrom(other);
+ }
+ return mine;
+ }
}
+ // END REGULAR
}
diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
new file mode 100644
index 00000000..4acd8f2f
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java
@@ -0,0 +1,2861 @@
+// 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.protobuf.Internal.checkNotNull;
+
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import com.google.protobuf.Descriptors.OneofDescriptor;
+// In opensource protobuf, we have versioned this GeneratedMessageV3 class to GeneratedMessageV3V3 and
+// in the future may have GeneratedMessageV3V4 etc. This allows us to change some aspects of this
+// class without breaking binary compatibility with old generated code that still subclasses
+// the old GeneratedMessageV3 class. To allow these different GeneratedMessageV3V? classes to
+// interoperate (e.g., a GeneratedMessageV3V3 object has a message extension field whose class
+// type is GeneratedMessageV3V4), these classes still share a common parent class AbstractMessage
+// and are using the same GeneratedMessage.GeneratedExtension class for extension definitions.
+// Since this class becomes GeneratedMessageV3V? in opensource, we have to add an import here
+// to be able to use GeneratedMessage.GeneratedExtension. The GeneratedExtension definition in
+// this file is also excluded from opensource to avoid conflict.
+import com.google.protobuf.GeneratedMessage.GeneratedExtension;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * All generated protocol message classes extend this class. This class
+ * implements most of the Message and Builder interfaces using Java reflection.
+ * Users can ignore this class and pretend that generated messages implement
+ * the Message interface directly.
+ *
+ * @author kenton@google.com Kenton Varda
+ */
+public abstract class GeneratedMessageV3 extends AbstractMessage
+ implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * For testing. Allows a test to disable the optimization that avoids using
+ * field builders for nested messages until they are requested. By disabling
+ * this optimization, existing tests can be reused to test the field builders.
+ */
+ protected static boolean alwaysUseFieldBuilders = false;
+
+ /** For use by generated code only. */
+ protected UnknownFieldSet unknownFields;
+
+ protected GeneratedMessageV3() {
+ unknownFields = UnknownFieldSet.getDefaultInstance();
+ }
+
+ protected GeneratedMessageV3(Builder<?> builder) {
+ unknownFields = builder.getUnknownFields();
+ }
+
+ @Override
+ public Parser<? extends GeneratedMessageV3> getParserForType() {
+ throw new UnsupportedOperationException(
+ "This is supposed to be overridden by subclasses.");
+ }
+
+ /**
+ * For testing. Allows a test to disable the optimization that avoids using
+ * field builders for nested messages until they are requested. By disabling
+ * this optimization, existing tests can be reused to test the field builders.
+ * See {@link RepeatedFieldBuilder} and {@link SingleFieldBuilder}.
+ */
+ static void enableAlwaysUseFieldBuildersForTesting() {
+ alwaysUseFieldBuilders = true;
+ }
+
+ /**
+ * Get the FieldAccessorTable for this type. We can't have the message
+ * class pass this in to the constructor because of bootstrapping trouble
+ * with DescriptorProtos.
+ */
+ protected abstract FieldAccessorTable internalGetFieldAccessorTable();
+
+ @Override
+ public Descriptor getDescriptorForType() {
+ return internalGetFieldAccessorTable().descriptor;
+ }
+
+ /**
+ * Internal helper to return a modifiable map containing all the fields.
+ * The returned Map is modifialbe so that the caller can add additional
+ * extension fields to implement {@link #getAllFields()}.
+ *
+ * @param getBytesForString whether to generate ByteString for string fields
+ */
+ private Map<FieldDescriptor, Object> getAllFieldsMutable(
+ boolean getBytesForString) {
+ final TreeMap<FieldDescriptor, Object> result =
+ new TreeMap<FieldDescriptor, Object>();
+ final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
+ final List<FieldDescriptor> fields = descriptor.getFields();
+
+ for (int i = 0; i < fields.size(); i++) {
+ FieldDescriptor field = fields.get(i);
+ final OneofDescriptor oneofDescriptor = field.getContainingOneof();
+
+ /*
+ * If the field is part of a Oneof, then at maximum one field in the Oneof is set
+ * and it is not repeated. There is no need to iterate through the others.
+ */
+ if (oneofDescriptor != null) {
+ // Skip other fields in the Oneof we know are not set
+ i += oneofDescriptor.getFieldCount() - 1;
+ if (!hasOneof(oneofDescriptor)) {
+ // If no field is set in the Oneof, skip all the fields in the Oneof
+ continue;
+ }
+ // Get the pointer to the only field which is set in the Oneof
+ field = getOneofFieldDescriptor(oneofDescriptor);
+ } else {
+ // If we are not in a Oneof, we need to check if the field is set and if it is repeated
+ if (field.isRepeated()) {
+ final List<?> value = (List<?>) getField(field);
+ if (!value.isEmpty()) {
+ result.put(field, value);
+ }
+ continue;
+ }
+ if (!hasField(field)) {
+ continue;
+ }
+ }
+ // Add the field to the map
+ if (getBytesForString && field.getJavaType() == FieldDescriptor.JavaType.STRING) {
+ result.put(field, getFieldRaw(field));
+ } else {
+ result.put(field, getField(field));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ for (final FieldDescriptor field : getDescriptorForType().getFields()) {
+ // Check that all required fields are present.
+ if (field.isRequired()) {
+ if (!hasField(field)) {
+ return false;
+ }
+ }
+ // Check that embedded messages are initialized.
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isRepeated()) {
+ @SuppressWarnings("unchecked") final
+ List<Message> messageList = (List<Message>) getField(field);
+ for (final Message element : messageList) {
+ if (!element.isInitialized()) {
+ return false;
+ }
+ }
+ } else {
+ if (hasField(field) && !((Message) getField(field)).isInitialized()) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ return Collections.unmodifiableMap(
+ getAllFieldsMutable(/* getBytesForString = */ false));
+ }
+
+ /**
+ * Returns a collection of all the fields in this message which are set
+ * and their corresponding values. A singular ("required" or "optional")
+ * field is set iff hasField() returns true for that field. A "repeated"
+ * field is set iff getRepeatedFieldCount() is greater than zero. The
+ * values are exactly what would be returned by calling
+ * {@link #getFieldRaw(Descriptors.FieldDescriptor)} for each field. The map
+ * is guaranteed to be a sorted map, so iterating over it will return fields
+ * in order by field number.
+ */
+ Map<FieldDescriptor, Object> getAllFieldsRaw() {
+ return Collections.unmodifiableMap(
+ getAllFieldsMutable(/* getBytesForString = */ true));
+ }
+
+ @Override
+ public boolean hasOneof(final OneofDescriptor oneof) {
+ return internalGetFieldAccessorTable().getOneof(oneof).has(this);
+ }
+
+ @Override
+ public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
+ return internalGetFieldAccessorTable().getOneof(oneof).get(this);
+ }
+
+ @Override
+ public boolean hasField(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).has(this);
+ }
+
+ @Override
+ public Object getField(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).get(this);
+ }
+
+ /**
+ * Obtains the value of the given field, or the default value if it is
+ * not set. For primitive fields, the boxed primitive value is returned.
+ * For enum fields, the EnumValueDescriptor for the value is returned. For
+ * embedded message fields, the sub-message is returned. For repeated
+ * fields, a java.util.List is returned. For present string fields, a
+ * ByteString is returned representing the bytes that the field contains.
+ */
+ Object getFieldRaw(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).getRaw(this);
+ }
+
+ @Override
+ public int getRepeatedFieldCount(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field)
+ .getRepeatedCount(this);
+ }
+
+ @Override
+ public Object getRepeatedField(final FieldDescriptor field, final int index) {
+ return internalGetFieldAccessorTable().getField(field)
+ .getRepeated(this, index);
+ }
+
+ @Override
+ public UnknownFieldSet getUnknownFields() {
+ throw new UnsupportedOperationException(
+ "This is supposed to be overridden by subclasses.");
+ }
+
+ /**
+ * Called by subclasses to parse an unknown field.
+ *
+ * @return {@code true} unless the tag is an end-group tag.
+ */
+ protected boolean parseUnknownField(
+ CodedInputStream input,
+ UnknownFieldSet.Builder unknownFields,
+ ExtensionRegistryLite extensionRegistry,
+ int tag)
+ throws IOException {
+ if (input.shouldDiscardUnknownFields()) {
+ return input.skipField(tag);
+ }
+ return unknownFields.mergeFieldFrom(tag, input);
+ }
+
+ protected boolean parseUnknownFieldProto3(
+ CodedInputStream input,
+ UnknownFieldSet.Builder unknownFields,
+ ExtensionRegistryLite extensionRegistry,
+ int tag)
+ throws IOException {
+ if (input.shouldDiscardUnknownFieldsProto3()) {
+ return input.skipField(tag);
+ }
+ return unknownFields.mergeFieldFrom(tag, input);
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser, InputStream input)
+ throws IOException {
+ try {
+ return parser.parseFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser, InputStream input,
+ ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser,
+ CodedInputStream input) throws IOException {
+ try {
+ return parser.parseFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseWithIOException(Parser<M> parser,
+ CodedInputStream input, ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseDelimitedWithIOException(Parser<M> parser,
+ InputStream input) throws IOException {
+ try {
+ return parser.parseDelimitedFrom(input);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static <M extends Message> M parseDelimitedWithIOException(Parser<M> parser,
+ InputStream input, ExtensionRegistryLite extensions) throws IOException {
+ try {
+ return parser.parseDelimitedFrom(input, extensions);
+ } catch (InvalidProtocolBufferException e) {
+ throw e.unwrapIOException();
+ }
+ }
+
+ protected static boolean canUseUnsafe() {
+ return UnsafeUtil.hasUnsafeArrayOperations() && UnsafeUtil.hasUnsafeByteBufferOperations();
+ }
+
+ @Override
+ public void writeTo(final CodedOutputStream output) throws IOException {
+ MessageReflection.writeMessageTo(this, getAllFieldsRaw(), output, false);
+ }
+
+ @Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) {
+ return size;
+ }
+
+ memoizedSize = MessageReflection.getSerializedSize(
+ this, getAllFieldsRaw());
+ return memoizedSize;
+ }
+
+
+
+ /**
+ * Used by parsing constructors in generated classes.
+ */
+ protected void makeExtensionsImmutable() {
+ // Noop for messages without extensions.
+ }
+
+ /**
+ * TODO(xiaofeng): remove this after b/29368482 is fixed. We need to move this
+ * interface to AbstractMessage in order to versioning GeneratedMessageV3 but
+ * this move breaks binary compatibility for AppEngine. After AppEngine is
+ * fixed we can exlude this from google3.
+ */
+ protected interface BuilderParent extends AbstractMessage.BuilderParent {}
+
+ /**
+ * TODO(xiaofeng): remove this together with GeneratedMessageV3.BuilderParent.
+ */
+ protected abstract Message.Builder newBuilderForType(BuilderParent parent);
+
+ @Override
+ protected Message.Builder newBuilderForType(final AbstractMessage.BuilderParent parent) {
+ return newBuilderForType(new BuilderParent() {
+ @Override
+ public void markDirty() {
+ parent.markDirty();
+ }
+ });
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public abstract static class Builder <BuilderType extends Builder<BuilderType>>
+ extends AbstractMessage.Builder<BuilderType> {
+
+ private BuilderParent builderParent;
+
+ private BuilderParentImpl meAsParent;
+
+ // Indicates that we've built a message and so we are now obligated
+ // to dispatch dirty invalidations. See GeneratedMessageV3.BuilderListener.
+ private boolean isClean;
+
+ private UnknownFieldSet unknownFields =
+ UnknownFieldSet.getDefaultInstance();
+
+ protected Builder() {
+ this(null);
+ }
+
+ protected Builder(BuilderParent builderParent) {
+ this.builderParent = builderParent;
+ }
+
+ @Override
+ void dispose() {
+ builderParent = null;
+ }
+
+ /**
+ * Called by the subclass when a message is built.
+ */
+ protected void onBuilt() {
+ if (builderParent != null) {
+ markClean();
+ }
+ }
+
+ /**
+ * Called by the subclass or a builder to notify us that a message was
+ * built and may be cached and therefore invalidations are needed.
+ */
+ @Override
+ protected void markClean() {
+ this.isClean = true;
+ }
+
+ /**
+ * Gets whether invalidations are needed
+ *
+ * @return whether invalidations are needed
+ */
+ protected boolean isClean() {
+ return isClean;
+ }
+
+ @Override
+ public BuilderType clone() {
+ BuilderType builder =
+ (BuilderType) getDefaultInstanceForType().newBuilderForType();
+ builder.mergeFrom(buildPartial());
+ return builder;
+ }
+
+ /**
+ * Called by the initialization and clear code paths to allow subclasses to
+ * reset any of their builtin fields back to the initial values.
+ */
+ @Override
+ public BuilderType clear() {
+ unknownFields = UnknownFieldSet.getDefaultInstance();
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ /**
+ * Get the FieldAccessorTable for this type. We can't have the message
+ * class pass this in to the constructor because of bootstrapping trouble
+ * with DescriptorProtos.
+ */
+ protected abstract FieldAccessorTable internalGetFieldAccessorTable();
+
+ @Override
+ public Descriptor getDescriptorForType() {
+ return internalGetFieldAccessorTable().descriptor;
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ return Collections.unmodifiableMap(getAllFieldsMutable());
+ }
+
+ /** Internal helper which returns a mutable map. */
+ private Map<FieldDescriptor, Object> getAllFieldsMutable() {
+ final TreeMap<FieldDescriptor, Object> result =
+ new TreeMap<FieldDescriptor, Object>();
+ final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
+ final List<FieldDescriptor> fields = descriptor.getFields();
+
+ for (int i = 0; i < fields.size(); i++) {
+ FieldDescriptor field = fields.get(i);
+ final OneofDescriptor oneofDescriptor = field.getContainingOneof();
+
+ /*
+ * If the field is part of a Oneof, then at maximum one field in the Oneof is set
+ * and it is not repeated. There is no need to iterate through the others.
+ */
+ if (oneofDescriptor != null) {
+ // Skip other fields in the Oneof we know are not set
+ i += oneofDescriptor.getFieldCount() - 1;
+ if (!hasOneof(oneofDescriptor)) {
+ // If no field is set in the Oneof, skip all the fields in the Oneof
+ continue;
+ }
+ // Get the pointer to the only field which is set in the Oneof
+ field = getOneofFieldDescriptor(oneofDescriptor);
+ } else {
+ // If we are not in a Oneof, we need to check if the field is set and if it is repeated
+ if (field.isRepeated()) {
+ final List<?> value = (List<?>) getField(field);
+ if (!value.isEmpty()) {
+ result.put(field, value);
+ }
+ continue;
+ }
+ if (!hasField(field)) {
+ continue;
+ }
+ }
+ // Add the field to the map
+ result.put(field, getField(field));
+ }
+ return result;
+ }
+
+ @Override
+ public Message.Builder newBuilderForField(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).newBuilder();
+ }
+
+ @Override
+ public Message.Builder getFieldBuilder(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).getBuilder(this);
+ }
+
+ @Override
+ public Message.Builder getRepeatedFieldBuilder(final FieldDescriptor field, int index) {
+ return internalGetFieldAccessorTable().getField(field).getRepeatedBuilder(
+ this, index);
+ }
+
+ @Override
+ public boolean hasOneof(final OneofDescriptor oneof) {
+ return internalGetFieldAccessorTable().getOneof(oneof).has(this);
+ }
+
+ @Override
+ public FieldDescriptor getOneofFieldDescriptor(final OneofDescriptor oneof) {
+ return internalGetFieldAccessorTable().getOneof(oneof).get(this);
+ }
+
+ @Override
+ public boolean hasField(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field).has(this);
+ }
+
+ @Override
+ public Object getField(final FieldDescriptor field) {
+ Object object = internalGetFieldAccessorTable().getField(field).get(this);
+ if (field.isRepeated()) {
+ // The underlying list object is still modifiable at this point.
+ // Make sure not to expose the modifiable list to the caller.
+ return Collections.unmodifiableList((List) object);
+ } else {
+ return object;
+ }
+ }
+
+ @Override
+ public BuilderType setField(final FieldDescriptor field, final Object value) {
+ internalGetFieldAccessorTable().getField(field).set(this, value);
+ return (BuilderType) this;
+ }
+
+ @Override
+ public BuilderType clearField(final FieldDescriptor field) {
+ internalGetFieldAccessorTable().getField(field).clear(this);
+ return (BuilderType) this;
+ }
+
+ @Override
+ public BuilderType clearOneof(final OneofDescriptor oneof) {
+ internalGetFieldAccessorTable().getOneof(oneof).clear(this);
+ return (BuilderType) this;
+ }
+
+ @Override
+ public int getRepeatedFieldCount(final FieldDescriptor field) {
+ return internalGetFieldAccessorTable().getField(field)
+ .getRepeatedCount(this);
+ }
+
+ @Override
+ public Object getRepeatedField(final FieldDescriptor field, final int index) {
+ return internalGetFieldAccessorTable().getField(field)
+ .getRepeated(this, index);
+ }
+
+ @Override
+ public BuilderType setRepeatedField(
+ final FieldDescriptor field, final int index, final Object value) {
+ internalGetFieldAccessorTable().getField(field)
+ .setRepeated(this, index, value);
+ return (BuilderType) this;
+ }
+
+ @Override
+ public BuilderType addRepeatedField(final FieldDescriptor field, final Object value) {
+ internalGetFieldAccessorTable().getField(field).addRepeated(this, value);
+ return (BuilderType) this;
+ }
+
+ @Override
+ public BuilderType setUnknownFields(final UnknownFieldSet unknownFields) {
+ this.unknownFields = unknownFields;
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ protected BuilderType setUnknownFieldsProto3(final UnknownFieldSet unknownFields) {
+ if (CodedInputStream.getProto3DiscardUnknownFieldsDefault()) {
+ return (BuilderType) this;
+ }
+ this.unknownFields = unknownFields;
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ @Override
+ public BuilderType mergeUnknownFields(
+ final UnknownFieldSet unknownFields) {
+ return setUnknownFields(
+ UnknownFieldSet.newBuilder(this.unknownFields)
+ .mergeFrom(unknownFields)
+ .build());
+ }
+
+
+ @Override
+ public boolean isInitialized() {
+ for (final FieldDescriptor field : getDescriptorForType().getFields()) {
+ // Check that all required fields are present.
+ if (field.isRequired()) {
+ if (!hasField(field)) {
+ return false;
+ }
+ }
+ // Check that embedded messages are initialized.
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isRepeated()) {
+ @SuppressWarnings("unchecked") final
+ List<Message> messageList = (List<Message>) getField(field);
+ for (final Message element : messageList) {
+ if (!element.isInitialized()) {
+ return false;
+ }
+ }
+ } else {
+ if (hasField(field) &&
+ !((Message) getField(field)).isInitialized()) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public final UnknownFieldSet getUnknownFields() {
+ return unknownFields;
+ }
+
+ /**
+ * Implementation of {@link BuilderParent} for giving to our children. This
+ * small inner class makes it so we don't publicly expose the BuilderParent
+ * methods.
+ */
+ private class BuilderParentImpl implements BuilderParent {
+
+ @Override
+ public void markDirty() {
+ onChanged();
+ }
+ }
+
+ /**
+ * Gets the {@link BuilderParent} for giving to our children.
+ * @return The builder parent for our children.
+ */
+ protected BuilderParent getParentForChildren() {
+ if (meAsParent == null) {
+ meAsParent = new BuilderParentImpl();
+ }
+ return meAsParent;
+ }
+
+ /**
+ * Called when a the builder or one of its nested children has changed
+ * and any parent should be notified of its invalidation.
+ */
+ protected final void onChanged() {
+ if (isClean && builderParent != null) {
+ builderParent.markDirty();
+
+ // Don't keep dispatching invalidations until build is called again.
+ isClean = false;
+ }
+ }
+
+ /**
+ * Gets the map field with the given field number. This method should be
+ * overridden in the generated message class if the message contains map
+ * fields.
+ *
+ * Unlike other field types, reflection support for map fields can't be
+ * implemented based on generated public API because we need to access a
+ * map field as a list in reflection API but the generated API only allows
+ * us to access it as a map. This method returns the underlying map field
+ * directly and thus enables us to access the map field as a list.
+ */
+ @SuppressWarnings({"unused", "rawtypes"})
+ protected MapField internalGetMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
+
+ /** Like {@link #internalGetMapField} but return a mutable version. */
+ @SuppressWarnings({"unused", "rawtypes"})
+ protected MapField internalGetMutableMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
+ }
+
+ // =================================================================
+ // Extensions-related stuff
+
+ public interface ExtendableMessageOrBuilder<
+ MessageType extends ExtendableMessage> extends MessageOrBuilder {
+ // Re-define for return type covariance.
+ @Override
+ Message getDefaultInstanceForType();
+
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ ExtensionLite<MessageType, Type> extension);
+
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ ExtensionLite<MessageType, List<Type>> extension);
+
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ ExtensionLite<MessageType, Type> extension);
+
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ ExtensionLite<MessageType, List<Type>> extension,
+ int index);
+
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ Extension<MessageType, Type> extension);
+ /** Check if a singular extension is present. */
+ <Type> boolean hasExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ Extension<MessageType, List<Type>> extension);
+ /** Get the number of elements in a repeated extension. */
+ <Type> int getExtensionCount(
+ GeneratedExtension<MessageType, List<Type>> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, Type> extension);
+ /** Get the value of an extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, Type> extension);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ Extension<MessageType, List<Type>> extension,
+ int index);
+ /** Get one element of a repeated extension. */
+ <Type> Type getExtension(
+ GeneratedExtension<MessageType, List<Type>> extension,
+ int index);
+ }
+
+ /**
+ * Generated message classes for message types that contain extension ranges
+ * subclass this.
+ *
+ * <p>This class implements type-safe accessors for extensions. They
+ * implement all the same operations that you can do with normal fields --
+ * e.g. "has", "get", and "getCount" -- but for extensions. The extensions
+ * are identified using instances of the class {@link GeneratedExtension};
+ * the protocol compiler generates a static instance of this class for every
+ * extension in its input. Through the magic of generics, all is made
+ * type-safe.
+ *
+ * <p>For example, imagine you have the {@code .proto} file:
+ *
+ * <pre>
+ * option java_class = "MyProto";
+ *
+ * message Foo {
+ * extensions 1000 to max;
+ * }
+ *
+ * extend Foo {
+ * optional int32 bar;
+ * }
+ * </pre>
+ *
+ * <p>Then you might write code like:
+ *
+ * <pre>
+ * MyProto.Foo foo = getFoo();
+ * int i = foo.getExtension(MyProto.bar);
+ * </pre>
+ *
+ * <p>See also {@link ExtendableBuilder}.
+ */
+ public abstract static class ExtendableMessage<
+ MessageType extends ExtendableMessage>
+ extends GeneratedMessageV3
+ implements ExtendableMessageOrBuilder<MessageType> {
+
+ private static final long serialVersionUID = 1L;
+
+ private final FieldSet<FieldDescriptor> extensions;
+
+ protected ExtendableMessage() {
+ this.extensions = FieldSet.newFieldSet();
+ }
+
+ protected ExtendableMessage(
+ ExtendableBuilder<MessageType, ?> builder) {
+ super(builder);
+ this.extensions = builder.buildExtensions();
+ }
+
+ private void verifyExtensionContainingType(
+ final Extension<MessageType, ?> extension) {
+ if (extension.getDescriptor().getContainingType() !=
+ getDescriptorForType()) {
+ // This can only happen if someone uses unchecked operations.
+ throw new IllegalArgumentException(
+ "Extension is for type \"" +
+ extension.getDescriptor().getContainingType().getFullName() +
+ "\" which does not match message type \"" +
+ getDescriptorForType().getFullName() + "\".");
+ }
+ }
+
+ /** Check if a singular extension is present. */
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ return extensions.hasField(extension.getDescriptor());
+ }
+
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <Type> int getExtensionCount(
+ final ExtensionLite<MessageType, List<Type>> extensionLite) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ final FieldDescriptor descriptor = extension.getDescriptor();
+ return extensions.getRepeatedFieldCount(descriptor);
+ }
+
+ /** Get the value of an extension. */
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ FieldDescriptor descriptor = extension.getDescriptor();
+ final Object value = extensions.getField(descriptor);
+ if (value == null) {
+ if (descriptor.isRepeated()) {
+ return (Type) Collections.emptyList();
+ } else if (descriptor.getJavaType() ==
+ FieldDescriptor.JavaType.MESSAGE) {
+ return (Type) extension.getMessageDefaultInstance();
+ } else {
+ return (Type) extension.fromReflectionType(
+ descriptor.getDefaultValue());
+ }
+ } else {
+ return (Type) extension.fromReflectionType(value);
+ }
+ }
+
+ /** Get one element of a repeated extension. */
+ @Override
+ @SuppressWarnings("unchecked")
+ public final <Type> Type getExtension(
+ final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ FieldDescriptor descriptor = extension.getDescriptor();
+ return (Type) extension.singularFromReflectionType(
+ extensions.getRepeatedField(descriptor, index));
+ }
+
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+
+ /** Called by subclasses to check if all extensions are initialized. */
+ protected boolean extensionsAreInitialized() {
+ return extensions.isInitialized();
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return super.isInitialized() && extensionsAreInitialized();
+ }
+
+ @Override
+ protected boolean parseUnknownField(
+ CodedInputStream input,
+ UnknownFieldSet.Builder unknownFields,
+ ExtensionRegistryLite extensionRegistry,
+ int tag) throws IOException {
+ return MessageReflection.mergeFieldFrom(
+ input, input.shouldDiscardUnknownFields() ? null : unknownFields, extensionRegistry,
+ getDescriptorForType(), new MessageReflection.ExtensionAdapter(extensions), tag);
+ }
+
+ @Override
+ protected boolean parseUnknownFieldProto3(
+ CodedInputStream input,
+ UnknownFieldSet.Builder unknownFields,
+ ExtensionRegistryLite extensionRegistry,
+ int tag) throws IOException {
+ return MessageReflection.mergeFieldFrom(
+ input,
+ input.shouldDiscardUnknownFieldsProto3() ? null : unknownFields,
+ extensionRegistry,
+ getDescriptorForType(),
+ new MessageReflection.ExtensionAdapter(extensions),
+ tag);
+ }
+
+
+ /**
+ * Used by parsing constructors in generated classes.
+ */
+ @Override
+ protected void makeExtensionsImmutable() {
+ extensions.makeImmutable();
+ }
+
+ /**
+ * Used by subclasses to serialize extensions. Extension ranges may be
+ * interleaved with field numbers, but we must write them in canonical
+ * (sorted by field number) order. ExtensionWriter helps us write
+ * individual ranges of extensions at once.
+ */
+ protected class ExtensionWriter {
+ // Imagine how much simpler this code would be if Java iterators had
+ // a way to get the next element without advancing the iterator.
+
+ private final Iterator<Map.Entry<FieldDescriptor, Object>> iter =
+ extensions.iterator();
+ private Map.Entry<FieldDescriptor, Object> next;
+ private final boolean messageSetWireFormat;
+
+ private ExtensionWriter(final boolean messageSetWireFormat) {
+ if (iter.hasNext()) {
+ next = iter.next();
+ }
+ this.messageSetWireFormat = messageSetWireFormat;
+ }
+
+ public void writeUntil(final int end, final CodedOutputStream output)
+ throws IOException {
+ while (next != null && next.getKey().getNumber() < end) {
+ FieldDescriptor descriptor = next.getKey();
+ if (messageSetWireFormat && descriptor.getLiteJavaType() ==
+ WireFormat.JavaType.MESSAGE &&
+ !descriptor.isRepeated()) {
+ if (next instanceof LazyField.LazyEntry<?>) {
+ output.writeRawMessageSetExtension(descriptor.getNumber(),
+ ((LazyField.LazyEntry<?>) next).getField().toByteString());
+ } else {
+ output.writeMessageSetExtension(descriptor.getNumber(),
+ (Message) next.getValue());
+ }
+ } else {
+ // TODO(xiangl): Taken care of following code, it may cause
+ // problem when we use LazyField for normal fields/extensions.
+ // Due to the optional field can be duplicated at the end of
+ // serialized bytes, which will make the serialized size change
+ // after lazy field parsed. So when we use LazyField globally,
+ // we need to change the following write method to write cached
+ // bytes directly rather than write the parsed message.
+ FieldSet.writeField(descriptor, next.getValue(), output);
+ }
+ if (iter.hasNext()) {
+ next = iter.next();
+ } else {
+ next = null;
+ }
+ }
+ }
+ }
+
+ protected ExtensionWriter newExtensionWriter() {
+ return new ExtensionWriter(false);
+ }
+ protected ExtensionWriter newMessageSetExtensionWriter() {
+ return new ExtensionWriter(true);
+ }
+
+ /** Called by subclasses to compute the size of extensions. */
+ protected int extensionsSerializedSize() {
+ return extensions.getSerializedSize();
+ }
+ protected int extensionsSerializedSizeAsMessageSet() {
+ return extensions.getMessageSetSerializedSize();
+ }
+
+ // ---------------------------------------------------------------
+ // Reflection
+
+ protected Map<FieldDescriptor, Object> getExtensionFields() {
+ return extensions.getAllFields();
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ final Map<FieldDescriptor, Object> result =
+ super.getAllFieldsMutable(/* getBytesForString = */ false);
+ result.putAll(getExtensionFields());
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFieldsRaw() {
+ final Map<FieldDescriptor, Object> result =
+ super.getAllFieldsMutable(/* getBytesForString = */ false);
+ result.putAll(getExtensionFields());
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public boolean hasField(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.hasField(field);
+ } else {
+ return super.hasField(field);
+ }
+ }
+
+ @Override
+ public Object getField(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ final Object value = extensions.getField(field);
+ if (value == null) {
+ if (field.isRepeated()) {
+ return Collections.emptyList();
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ // Lacking an ExtensionRegistry, we have no way to determine the
+ // extension's real type, so we return a DynamicMessage.
+ return DynamicMessage.getDefaultInstance(field.getMessageType());
+ } else {
+ return field.getDefaultValue();
+ }
+ } else {
+ return value;
+ }
+ } else {
+ return super.getField(field);
+ }
+ }
+
+ @Override
+ public int getRepeatedFieldCount(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.getRepeatedFieldCount(field);
+ } else {
+ return super.getRepeatedFieldCount(field);
+ }
+ }
+
+ @Override
+ public Object getRepeatedField(final FieldDescriptor field,
+ final int index) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.getRepeatedField(field, index);
+ } else {
+ return super.getRepeatedField(field, index);
+ }
+ }
+
+ private void verifyContainingType(final FieldDescriptor field) {
+ if (field.getContainingType() != getDescriptorForType()) {
+ throw new IllegalArgumentException(
+ "FieldDescriptor does not match message type.");
+ }
+ }
+ }
+
+ /**
+ * Generated message builders for message types that contain extension ranges
+ * subclass this.
+ *
+ * <p>This class implements type-safe accessors for extensions. They
+ * implement all the same operations that you can do with normal fields --
+ * e.g. "get", "set", and "add" -- but for extensions. The extensions are
+ * identified using instances of the class {@link GeneratedExtension}; the
+ * protocol compiler generates a static instance of this class for every
+ * extension in its input. Through the magic of generics, all is made
+ * type-safe.
+ *
+ * <p>For example, imagine you have the {@code .proto} file:
+ *
+ * <pre>
+ * option java_class = "MyProto";
+ *
+ * message Foo {
+ * extensions 1000 to max;
+ * }
+ *
+ * extend Foo {
+ * optional int32 bar;
+ * }
+ * </pre>
+ *
+ * <p>Then you might write code like:
+ *
+ * <pre>
+ * MyProto.Foo foo =
+ * MyProto.Foo.newBuilder()
+ * .setExtension(MyProto.bar, 123)
+ * .build();
+ * </pre>
+ *
+ * <p>See also {@link ExtendableMessage}.
+ */
+ @SuppressWarnings("unchecked")
+ public abstract static class ExtendableBuilder<
+ MessageType extends ExtendableMessage,
+ BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
+ extends Builder<BuilderType>
+ implements ExtendableMessageOrBuilder<MessageType> {
+
+ private FieldSet<FieldDescriptor> extensions = FieldSet.emptySet();
+
+ protected ExtendableBuilder() {}
+
+ protected ExtendableBuilder(
+ BuilderParent parent) {
+ super(parent);
+ }
+
+ // For immutable message conversion.
+ void internalSetExtensionSet(FieldSet<FieldDescriptor> extensions) {
+ this.extensions = extensions;
+ }
+
+ @Override
+ public BuilderType clear() {
+ extensions = FieldSet.emptySet();
+ return super.clear();
+ }
+
+ private void ensureExtensionsIsMutable() {
+ if (extensions.isImmutable()) {
+ extensions = extensions.clone();
+ }
+ }
+
+ private void verifyExtensionContainingType(
+ final Extension<MessageType, ?> extension) {
+ if (extension.getDescriptor().getContainingType() !=
+ getDescriptorForType()) {
+ // This can only happen if someone uses unchecked operations.
+ throw new IllegalArgumentException(
+ "Extension is for type \"" +
+ extension.getDescriptor().getContainingType().getFullName() +
+ "\" which does not match message type \"" +
+ getDescriptorForType().getFullName() + "\".");
+ }
+ }
+
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ return extensions.hasField(extension.getDescriptor());
+ }
+
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final ExtensionLite<MessageType, List<Type>> extensionLite) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ final FieldDescriptor descriptor = extension.getDescriptor();
+ return extensions.getRepeatedFieldCount(descriptor);
+ }
+
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final ExtensionLite<MessageType, Type> extensionLite) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ FieldDescriptor descriptor = extension.getDescriptor();
+ final Object value = extensions.getField(descriptor);
+ if (value == null) {
+ if (descriptor.isRepeated()) {
+ return (Type) Collections.emptyList();
+ } else if (descriptor.getJavaType() ==
+ FieldDescriptor.JavaType.MESSAGE) {
+ return (Type) extension.getMessageDefaultInstance();
+ } else {
+ return (Type) extension.fromReflectionType(
+ descriptor.getDefaultValue());
+ }
+ } else {
+ return (Type) extension.fromReflectionType(value);
+ }
+ }
+
+ /** Get one element of a repeated extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final ExtensionLite<MessageType, List<Type>> extensionLite, final int index) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ FieldDescriptor descriptor = extension.getDescriptor();
+ return (Type) extension.singularFromReflectionType(
+ extensions.getRepeatedField(descriptor, index));
+ }
+
+ /** Set the value of an extension. */
+ public final <Type> BuilderType setExtension(
+ final ExtensionLite<MessageType, Type> extensionLite,
+ final Type value) {
+ Extension<MessageType, Type> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ ensureExtensionsIsMutable();
+ final FieldDescriptor descriptor = extension.getDescriptor();
+ extensions.setField(descriptor, extension.toReflectionType(value));
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ /** Set the value of one element of a repeated extension. */
+ public final <Type> BuilderType setExtension(
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
+ final int index, final Type value) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ ensureExtensionsIsMutable();
+ final FieldDescriptor descriptor = extension.getDescriptor();
+ extensions.setRepeatedField(
+ descriptor, index,
+ extension.singularToReflectionType(value));
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ /** Append a value to a repeated extension. */
+ public final <Type> BuilderType addExtension(
+ final ExtensionLite<MessageType, List<Type>> extensionLite,
+ final Type value) {
+ Extension<MessageType, List<Type>> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ ensureExtensionsIsMutable();
+ final FieldDescriptor descriptor = extension.getDescriptor();
+ extensions.addRepeatedField(
+ descriptor, extension.singularToReflectionType(value));
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ /** Clear an extension. */
+ public final <Type> BuilderType clearExtension(
+ final ExtensionLite<MessageType, ?> extensionLite) {
+ Extension<MessageType, ?> extension = checkNotLite(extensionLite);
+
+ verifyExtensionContainingType(extension);
+ ensureExtensionsIsMutable();
+ extensions.clearField(extension.getDescriptor());
+ onChanged();
+ return (BuilderType) this;
+ }
+
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(final Extension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Check if a singular extension is present. */
+ @Override
+ public final <Type> boolean hasExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return hasExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final Extension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the number of elements in a repeated extension. */
+ @Override
+ public final <Type> int getExtensionCount(
+ final GeneratedExtension<MessageType, List<Type>> extension) {
+ return getExtensionCount((ExtensionLite<MessageType, List<Type>>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(final Extension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, Type> extension) {
+ return getExtension((ExtensionLite<MessageType, Type>) extension);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final Extension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Get the value of an extension. */
+ @Override
+ public final <Type> Type getExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final int index) {
+ return getExtension((ExtensionLite<MessageType, List<Type>>) extension, index);
+ }
+ /** Set the value of an extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of an extension. */
+ public <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, Type> extension, final Type value) {
+ return setExtension((ExtensionLite<MessageType, Type>) extension, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public final <Type> BuilderType setExtension(
+ final Extension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Set the value of one element of a repeated extension. */
+ public <Type> BuilderType setExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension,
+ final int index, final Type value) {
+ return setExtension((ExtensionLite<MessageType, List<Type>>) extension, index, value);
+ }
+ /** Append a value to a repeated extension. */
+ public final <Type> BuilderType addExtension(
+ final Extension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Append a value to a repeated extension. */
+ public <Type> BuilderType addExtension(
+ final GeneratedExtension<MessageType, List<Type>> extension, final Type value) {
+ return addExtension((ExtensionLite<MessageType, List<Type>>) extension, value);
+ }
+ /** Clear an extension. */
+ public final <Type> BuilderType clearExtension(
+ final Extension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+ /** Clear an extension. */
+ public <Type> BuilderType clearExtension(
+ final GeneratedExtension<MessageType, ?> extension) {
+ return clearExtension((ExtensionLite<MessageType, ?>) extension);
+ }
+
+ /** Called by subclasses to check if all extensions are initialized. */
+ protected boolean extensionsAreInitialized() {
+ return extensions.isInitialized();
+ }
+
+ /**
+ * Called by the build code path to create a copy of the extensions for
+ * building the message.
+ */
+ private FieldSet<FieldDescriptor> buildExtensions() {
+ extensions.makeImmutable();
+ return extensions;
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return super.isInitialized() && extensionsAreInitialized();
+ }
+
+ // ---------------------------------------------------------------
+ // Reflection
+
+ @Override
+ public Map<FieldDescriptor, Object> getAllFields() {
+ final Map<FieldDescriptor, Object> result = super.getAllFieldsMutable();
+ result.putAll(extensions.getAllFields());
+ return Collections.unmodifiableMap(result);
+ }
+
+ @Override
+ public Object getField(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ final Object value = extensions.getField(field);
+ if (value == null) {
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ // Lacking an ExtensionRegistry, we have no way to determine the
+ // extension's real type, so we return a DynamicMessage.
+ return DynamicMessage.getDefaultInstance(field.getMessageType());
+ } else {
+ return field.getDefaultValue();
+ }
+ } else {
+ return value;
+ }
+ } else {
+ return super.getField(field);
+ }
+ }
+
+ @Override
+ public int getRepeatedFieldCount(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.getRepeatedFieldCount(field);
+ } else {
+ return super.getRepeatedFieldCount(field);
+ }
+ }
+
+ @Override
+ public Object getRepeatedField(final FieldDescriptor field,
+ final int index) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.getRepeatedField(field, index);
+ } else {
+ return super.getRepeatedField(field, index);
+ }
+ }
+
+ @Override
+ public boolean hasField(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ return extensions.hasField(field);
+ } else {
+ return super.hasField(field);
+ }
+ }
+
+ @Override
+ public BuilderType setField(final FieldDescriptor field,
+ final Object value) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ ensureExtensionsIsMutable();
+ extensions.setField(field, value);
+ onChanged();
+ return (BuilderType) this;
+ } else {
+ return super.setField(field, value);
+ }
+ }
+
+ @Override
+ public BuilderType clearField(final FieldDescriptor field) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ ensureExtensionsIsMutable();
+ extensions.clearField(field);
+ onChanged();
+ return (BuilderType) this;
+ } else {
+ return super.clearField(field);
+ }
+ }
+
+ @Override
+ public BuilderType setRepeatedField(final FieldDescriptor field,
+ final int index, final Object value) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ ensureExtensionsIsMutable();
+ extensions.setRepeatedField(field, index, value);
+ onChanged();
+ return (BuilderType) this;
+ } else {
+ return super.setRepeatedField(field, index, value);
+ }
+ }
+
+ @Override
+ public BuilderType addRepeatedField(final FieldDescriptor field,
+ final Object value) {
+ if (field.isExtension()) {
+ verifyContainingType(field);
+ ensureExtensionsIsMutable();
+ extensions.addRepeatedField(field, value);
+ onChanged();
+ return (BuilderType) this;
+ } else {
+ return super.addRepeatedField(field, value);
+ }
+ }
+
+ protected final void mergeExtensionFields(final ExtendableMessage other) {
+ ensureExtensionsIsMutable();
+ extensions.mergeFrom(other.extensions);
+ onChanged();
+ }
+
+ private void verifyContainingType(final FieldDescriptor field) {
+ if (field.getContainingType() != getDescriptorForType()) {
+ throw new IllegalArgumentException(
+ "FieldDescriptor does not match message type.");
+ }
+ }
+ }
+
+ // -----------------------------------------------------------------
+
+ /**
+ * Gets the descriptor for an extension. The implementation depends on whether
+ * the extension is scoped in the top level of a file or scoped in a Message.
+ */
+ static interface ExtensionDescriptorRetriever {
+ FieldDescriptor getDescriptor();
+ }
+
+
+ // =================================================================
+
+ /** Calls Class.getMethod and throws a RuntimeException if it fails. */
+ @SuppressWarnings("unchecked")
+ private static Method getMethodOrDie(
+ final Class clazz, final String name, final Class... params) {
+ try {
+ return clazz.getMethod(name, params);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(
+ "Generated message class \"" + clazz.getName() +
+ "\" missing method \"" + name + "\".", e);
+ }
+ }
+
+ /** Calls invoke and throws a RuntimeException if it fails. */
+ private static Object invokeOrDie(
+ final Method method, final Object object, final Object... params) {
+ try {
+ return method.invoke(object, params);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(
+ "Couldn't use Java reflection to implement protocol message " +
+ "reflection.", e);
+ } catch (InvocationTargetException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new RuntimeException(
+ "Unexpected exception thrown by generated accessor method.", cause);
+ }
+ }
+ }
+
+ /**
+ * Gets the map field with the given field number. This method should be
+ * overridden in the generated message class if the message contains map
+ * fields.
+ *
+ * Unlike other field types, reflection support for map fields can't be
+ * implemented based on generated public API because we need to access a
+ * map field as a list in reflection API but the generated API only allows
+ * us to access it as a map. This method returns the underlying map field
+ * directly and thus enables us to access the map field as a list.
+ */
+ @SuppressWarnings({"rawtypes", "unused"})
+ protected MapField internalGetMapField(int fieldNumber) {
+ // Note that we can't use descriptor names here because this method will
+ // be called when descriptor is being initialized.
+ throw new RuntimeException(
+ "No map fields found in " + getClass().getName());
+ }
+
+ /**
+ * Users should ignore this class. This class provides the implementation
+ * with access to the fields of a message object using Java reflection.
+ */
+ public static final class FieldAccessorTable {
+
+ /**
+ * Construct a FieldAccessorTable for a particular message class. Only
+ * one FieldAccessorTable should ever be constructed per class.
+ *
+ * @param descriptor The type's descriptor.
+ * @param camelCaseNames The camelcase names of all fields in the message.
+ * These are used to derive the accessor method names.
+ * @param messageClass The message type.
+ * @param builderClass The builder type.
+ */
+ public FieldAccessorTable(
+ final Descriptor descriptor,
+ final String[] camelCaseNames,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ this(descriptor, camelCaseNames);
+ ensureFieldAccessorsInitialized(messageClass, builderClass);
+ }
+
+ /**
+ * Construct a FieldAccessorTable for a particular message class without
+ * initializing FieldAccessors.
+ */
+ public FieldAccessorTable(
+ final Descriptor descriptor,
+ final String[] camelCaseNames) {
+ this.descriptor = descriptor;
+ this.camelCaseNames = camelCaseNames;
+ fields = new FieldAccessor[descriptor.getFields().size()];
+ oneofs = new OneofAccessor[descriptor.getOneofs().size()];
+ initialized = false;
+ }
+
+ /**
+ * Ensures the field accessors are initialized. This method is thread-safe.
+ *
+ * @param messageClass The message type.
+ * @param builderClass The builder type.
+ * @return this
+ */
+ public FieldAccessorTable ensureFieldAccessorsInitialized(
+ Class<? extends GeneratedMessageV3> messageClass,
+ Class<? extends Builder> builderClass) {
+ if (initialized) { return this; }
+ synchronized (this) {
+ if (initialized) { return this; }
+ int fieldsSize = fields.length;
+ for (int i = 0; i < fieldsSize; i++) {
+ FieldDescriptor field = descriptor.getFields().get(i);
+ String containingOneofCamelCaseName = null;
+ if (field.getContainingOneof() != null) {
+ containingOneofCamelCaseName =
+ camelCaseNames[fieldsSize + field.getContainingOneof().getIndex()];
+ }
+ if (field.isRepeated()) {
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ if (field.isMapField()) {
+ fields[i] = new MapFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ } else {
+ fields[i] = new RepeatedMessageFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ }
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) {
+ fields[i] = new RepeatedEnumFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ } else {
+ fields[i] = new RepeatedFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass);
+ }
+ } else {
+ if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
+ fields[i] = new SingularMessageFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass,
+ containingOneofCamelCaseName);
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) {
+ fields[i] = new SingularEnumFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass,
+ containingOneofCamelCaseName);
+ } else if (field.getJavaType() == FieldDescriptor.JavaType.STRING) {
+ fields[i] = new SingularStringFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass,
+ containingOneofCamelCaseName);
+ } else {
+ fields[i] = new SingularFieldAccessor(
+ field, camelCaseNames[i], messageClass, builderClass,
+ containingOneofCamelCaseName);
+ }
+ }
+ }
+
+ int oneofsSize = oneofs.length;
+ for (int i = 0; i < oneofsSize; i++) {
+ oneofs[i] = new OneofAccessor(
+ descriptor, camelCaseNames[i + fieldsSize],
+ messageClass, builderClass);
+ }
+ initialized = true;
+ camelCaseNames = null;
+ return this;
+ }
+ }
+
+ private final Descriptor descriptor;
+ private final FieldAccessor[] fields;
+ private String[] camelCaseNames;
+ private final OneofAccessor[] oneofs;
+ private volatile boolean initialized;
+
+ /** Get the FieldAccessor for a particular field. */
+ private FieldAccessor getField(final FieldDescriptor field) {
+ if (field.getContainingType() != descriptor) {
+ throw new IllegalArgumentException(
+ "FieldDescriptor does not match message type.");
+ } else if (field.isExtension()) {
+ // If this type had extensions, it would subclass ExtendableMessage,
+ // which overrides the reflection interface to handle extensions.
+ throw new IllegalArgumentException(
+ "This type does not have extensions.");
+ }
+ return fields[field.getIndex()];
+ }
+
+ /** Get the OneofAccessor for a particular oneof. */
+ private OneofAccessor getOneof(final OneofDescriptor oneof) {
+ if (oneof.getContainingType() != descriptor) {
+ throw new IllegalArgumentException(
+ "OneofDescriptor does not match message type.");
+ }
+ return oneofs[oneof.getIndex()];
+ }
+
+ /**
+ * Abstract interface that provides access to a single field. This is
+ * implemented differently depending on the field type and cardinality.
+ */
+ private interface FieldAccessor {
+ Object get(GeneratedMessageV3 message);
+ Object get(GeneratedMessageV3.Builder builder);
+ Object getRaw(GeneratedMessageV3 message);
+ Object getRaw(GeneratedMessageV3.Builder builder);
+ void set(Builder builder, Object value);
+ Object getRepeated(GeneratedMessageV3 message, int index);
+ Object getRepeated(GeneratedMessageV3.Builder builder, int index);
+ Object getRepeatedRaw(GeneratedMessageV3 message, int index);
+ Object getRepeatedRaw(GeneratedMessageV3.Builder builder, int index);
+ void setRepeated(Builder builder,
+ int index, Object value);
+ void addRepeated(Builder builder, Object value);
+ boolean has(GeneratedMessageV3 message);
+ boolean has(GeneratedMessageV3.Builder builder);
+ int getRepeatedCount(GeneratedMessageV3 message);
+ int getRepeatedCount(GeneratedMessageV3.Builder builder);
+ void clear(Builder builder);
+ Message.Builder newBuilder();
+ Message.Builder getBuilder(GeneratedMessageV3.Builder builder);
+ Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder,
+ int index);
+ }
+
+ /** OneofAccessor provides access to a single oneof. */
+ private static class OneofAccessor {
+ OneofAccessor(
+ final Descriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ this.descriptor = descriptor;
+ caseMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Case");
+ caseMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Case");
+ clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
+ }
+
+ private final Descriptor descriptor;
+ private final Method caseMethod;
+ private final Method caseMethodBuilder;
+ private final Method clearMethod;
+
+ public boolean has(final GeneratedMessageV3 message) {
+ if (((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean has(GeneratedMessageV3.Builder builder) {
+ if (((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber() == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public FieldDescriptor get(final GeneratedMessageV3 message) {
+ int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
+ if (fieldNumber > 0) {
+ return descriptor.findFieldByNumber(fieldNumber);
+ }
+ return null;
+ }
+
+ public FieldDescriptor get(GeneratedMessageV3.Builder builder) {
+ int fieldNumber = ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
+ if (fieldNumber > 0) {
+ return descriptor.findFieldByNumber(fieldNumber);
+ }
+ return null;
+ }
+
+ public void clear(final Builder builder) {
+ invokeOrDie(clearMethod, builder);
+ }
+ }
+
+ private static boolean supportFieldPresence(FileDescriptor file) {
+ return file.getSyntax() == FileDescriptor.Syntax.PROTO2;
+ }
+
+ // ---------------------------------------------------------------
+
+ private static class SingularFieldAccessor implements FieldAccessor {
+ SingularFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass,
+ final String containingOneofCamelCaseName) {
+ field = descriptor;
+ isOneofField = descriptor.getContainingOneof() != null;
+ hasHasMethod = supportFieldPresence(descriptor.getFile())
+ || (!isOneofField && descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE);
+ getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
+ getMethodBuilder = getMethodOrDie(builderClass, "get" + camelCaseName);
+ type = getMethod.getReturnType();
+ setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
+ hasMethod =
+ hasHasMethod ? getMethodOrDie(messageClass, "has" + camelCaseName) : null;
+ hasMethodBuilder =
+ hasHasMethod ? getMethodOrDie(builderClass, "has" + camelCaseName) : null;
+ clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
+ caseMethod = isOneofField ? getMethodOrDie(
+ messageClass, "get" + containingOneofCamelCaseName + "Case") : null;
+ caseMethodBuilder = isOneofField ? getMethodOrDie(
+ builderClass, "get" + containingOneofCamelCaseName + "Case") : null;
+ }
+
+ // Note: We use Java reflection to call public methods rather than
+ // access private fields directly as this avoids runtime security
+ // checks.
+ protected final Class<?> type;
+ protected final Method getMethod;
+ protected final Method getMethodBuilder;
+ protected final Method setMethod;
+ protected final Method hasMethod;
+ protected final Method hasMethodBuilder;
+ protected final Method clearMethod;
+ protected final Method caseMethod;
+ protected final Method caseMethodBuilder;
+ protected final FieldDescriptor field;
+ protected final boolean isOneofField;
+ protected final boolean hasHasMethod;
+
+ private int getOneofFieldNumber(final GeneratedMessageV3 message) {
+ return ((Internal.EnumLite) invokeOrDie(caseMethod, message)).getNumber();
+ }
+
+ private int getOneofFieldNumber(final GeneratedMessageV3.Builder builder) {
+ return ((Internal.EnumLite) invokeOrDie(caseMethodBuilder, builder)).getNumber();
+ }
+
+ @Override
+ public Object get(final GeneratedMessageV3 message) {
+ return invokeOrDie(getMethod, message);
+ }
+ @Override
+ public Object get(GeneratedMessageV3.Builder builder) {
+ return invokeOrDie(getMethodBuilder, builder);
+ }
+ @Override
+ public Object getRaw(final GeneratedMessageV3 message) {
+ return get(message);
+ }
+ @Override
+ public Object getRaw(GeneratedMessageV3.Builder builder) {
+ return get(builder);
+ }
+ @Override
+ public void set(final Builder builder, final Object value) {
+ invokeOrDie(setMethod, builder, value);
+ }
+ @Override
+ public Object getRepeated(final GeneratedMessageV3 message, final int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedField() called on a singular field.");
+ }
+ @Override
+ public Object getRepeatedRaw(final GeneratedMessageV3 message, final int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldRaw() called on a singular field.");
+ }
+ @Override
+ public Object getRepeated(GeneratedMessageV3.Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedField() called on a singular field.");
+ }
+ @Override
+ public Object getRepeatedRaw(GeneratedMessageV3.Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldRaw() called on a singular field.");
+ }
+ @Override
+ public void setRepeated(final Builder builder, final int index, final Object value) {
+ throw new UnsupportedOperationException(
+ "setRepeatedField() called on a singular field.");
+ }
+ @Override
+ public void addRepeated(final Builder builder, final Object value) {
+ throw new UnsupportedOperationException(
+ "addRepeatedField() called on a singular field.");
+ }
+ @Override
+ public boolean has(final GeneratedMessageV3 message) {
+ if (!hasHasMethod) {
+ if (isOneofField) {
+ return getOneofFieldNumber(message) == field.getNumber();
+ }
+ return !get(message).equals(field.getDefaultValue());
+ }
+ return (Boolean) invokeOrDie(hasMethod, message);
+ }
+ @Override
+ public boolean has(GeneratedMessageV3.Builder builder) {
+ if (!hasHasMethod) {
+ if (isOneofField) {
+ return getOneofFieldNumber(builder) == field.getNumber();
+ }
+ return !get(builder).equals(field.getDefaultValue());
+ }
+ return (Boolean) invokeOrDie(hasMethodBuilder, builder);
+ }
+ @Override
+ public int getRepeatedCount(final GeneratedMessageV3 message) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldSize() called on a singular field.");
+ }
+ @Override
+ public int getRepeatedCount(GeneratedMessageV3.Builder builder) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldSize() called on a singular field.");
+ }
+ @Override
+ public void clear(final Builder builder) {
+ invokeOrDie(clearMethod, builder);
+ }
+ @Override
+ public Message.Builder newBuilder() {
+ throw new UnsupportedOperationException(
+ "newBuilderForField() called on a non-Message type.");
+ }
+ @Override
+ public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) {
+ throw new UnsupportedOperationException(
+ "getFieldBuilder() called on a non-Message type.");
+ }
+ @Override
+ public Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ }
+
+ private static class RepeatedFieldAccessor implements FieldAccessor {
+ protected final Class type;
+ protected final Method getMethod;
+ protected final Method getMethodBuilder;
+ protected final Method getRepeatedMethod;
+ protected final Method getRepeatedMethodBuilder;
+ protected final Method setRepeatedMethod;
+ protected final Method addRepeatedMethod;
+ protected final Method getCountMethod;
+ protected final Method getCountMethodBuilder;
+ protected final Method clearMethod;
+
+ RepeatedFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ getMethod = getMethodOrDie(messageClass,
+ "get" + camelCaseName + "List");
+ getMethodBuilder = getMethodOrDie(builderClass,
+ "get" + camelCaseName + "List");
+ getRepeatedMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE);
+ getRepeatedMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName, Integer.TYPE);
+ type = getRepeatedMethod.getReturnType();
+ setRepeatedMethod =
+ getMethodOrDie(builderClass, "set" + camelCaseName,
+ Integer.TYPE, type);
+ addRepeatedMethod =
+ getMethodOrDie(builderClass, "add" + camelCaseName, type);
+ getCountMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Count");
+ getCountMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Count");
+
+ clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName);
+ }
+
+ @Override
+ public Object get(final GeneratedMessageV3 message) {
+ return invokeOrDie(getMethod, message);
+ }
+ @Override
+ public Object get(GeneratedMessageV3.Builder builder) {
+ return invokeOrDie(getMethodBuilder, builder);
+ }
+ @Override
+ public Object getRaw(final GeneratedMessageV3 message) {
+ return get(message);
+ }
+ @Override
+ public Object getRaw(GeneratedMessageV3.Builder builder) {
+ return get(builder);
+ }
+ @Override
+ public void set(final Builder builder, final Object value) {
+ // Add all the elements individually. This serves two purposes:
+ // 1) Verifies that each element has the correct type.
+ // 2) Insures that the caller cannot modify the list later on and
+ // have the modifications be reflected in the message.
+ clear(builder);
+ for (final Object element : (List<?>) value) {
+ addRepeated(builder, element);
+ }
+ }
+ @Override
+ public Object getRepeated(final GeneratedMessageV3 message, final int index) {
+ return invokeOrDie(getRepeatedMethod, message, index);
+ }
+ @Override
+ public Object getRepeated(GeneratedMessageV3.Builder builder, int index) {
+ return invokeOrDie(getRepeatedMethodBuilder, builder, index);
+ }
+ @Override
+ public Object getRepeatedRaw(GeneratedMessageV3 message, int index) {
+ return getRepeated(message, index);
+ }
+ @Override
+ public Object getRepeatedRaw(GeneratedMessageV3.Builder builder, int index) {
+ return getRepeated(builder, index);
+ }
+ @Override
+ public void setRepeated(final Builder builder, final int index, final Object value) {
+ invokeOrDie(setRepeatedMethod, builder, index, value);
+ }
+ @Override
+ public void addRepeated(final Builder builder, final Object value) {
+ invokeOrDie(addRepeatedMethod, builder, value);
+ }
+ @Override
+ public boolean has(final GeneratedMessageV3 message) {
+ throw new UnsupportedOperationException(
+ "hasField() called on a repeated field.");
+ }
+ @Override
+ public boolean has(GeneratedMessageV3.Builder builder) {
+ throw new UnsupportedOperationException(
+ "hasField() called on a repeated field.");
+ }
+ @Override
+ public int getRepeatedCount(final GeneratedMessageV3 message) {
+ return (Integer) invokeOrDie(getCountMethod, message);
+ }
+ @Override
+ public int getRepeatedCount(GeneratedMessageV3.Builder builder) {
+ return (Integer) invokeOrDie(getCountMethodBuilder, builder);
+ }
+ @Override
+ public void clear(final Builder builder) {
+ invokeOrDie(clearMethod, builder);
+ }
+ @Override
+ public Message.Builder newBuilder() {
+ throw new UnsupportedOperationException(
+ "newBuilderForField() called on a non-Message type.");
+ }
+ @Override
+ public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) {
+ throw new UnsupportedOperationException(
+ "getFieldBuilder() called on a non-Message type.");
+ }
+ @Override
+ public Message.Builder getRepeatedBuilder(GeneratedMessageV3.Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "getRepeatedFieldBuilder() called on a non-Message type.");
+ }
+ }
+
+ private static class MapFieldAccessor implements FieldAccessor {
+ MapFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ field = descriptor;
+ Method getDefaultInstanceMethod =
+ getMethodOrDie(messageClass, "getDefaultInstance");
+ MapField defaultMapField = getMapField(
+ (GeneratedMessageV3) invokeOrDie(getDefaultInstanceMethod, null));
+ mapEntryMessageDefaultInstance =
+ defaultMapField.getMapEntryMessageDefaultInstance();
+ }
+
+ private final FieldDescriptor field;
+ private final Message mapEntryMessageDefaultInstance;
+
+ private MapField<?, ?> getMapField(GeneratedMessageV3 message) {
+ return (MapField<?, ?>) message.internalGetMapField(field.getNumber());
+ }
+
+ private MapField<?, ?> getMapField(GeneratedMessageV3.Builder builder) {
+ return (MapField<?, ?>) builder.internalGetMapField(field.getNumber());
+ }
+
+ private MapField<?, ?> getMutableMapField(
+ GeneratedMessageV3.Builder builder) {
+ return (MapField<?, ?>) builder.internalGetMutableMapField(
+ field.getNumber());
+ }
+
+ private Message coerceType(Message value) {
+ if (value == null) {
+ return null;
+ }
+ if (mapEntryMessageDefaultInstance.getClass().isInstance(value)) {
+ return value;
+ }
+ // The value is not the exact right message type. However, if it
+ // is an alternative implementation of the same type -- e.g. a
+ // DynamicMessage -- we should accept it. In this case we can make
+ // a copy of the message.
+ return mapEntryMessageDefaultInstance.toBuilder().mergeFrom(value).build();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object get(GeneratedMessageV3 message) {
+ List result = new ArrayList();
+ for (int i = 0; i < getRepeatedCount(message); i++) {
+ result.add(getRepeated(message, i));
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object get(Builder builder) {
+ List result = new ArrayList();
+ for (int i = 0; i < getRepeatedCount(builder); i++) {
+ result.add(getRepeated(builder, i));
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ @Override
+ public Object getRaw(GeneratedMessageV3 message) {
+ return get(message);
+ }
+
+ @Override
+ public Object getRaw(GeneratedMessageV3.Builder builder) {
+ return get(builder);
+ }
+
+ @Override
+ public void set(Builder builder, Object value) {
+ clear(builder);
+ for (Object entry : (List) value) {
+ addRepeated(builder, entry);
+ }
+ }
+
+ @Override
+ public Object getRepeated(GeneratedMessageV3 message, int index) {
+ return getMapField(message).getList().get(index);
+ }
+
+ @Override
+ public Object getRepeated(Builder builder, int index) {
+ return getMapField(builder).getList().get(index);
+ }
+
+ @Override
+ public Object getRepeatedRaw(GeneratedMessageV3 message, int index) {
+ return getRepeated(message, index);
+ }
+
+ @Override
+ public Object getRepeatedRaw(Builder builder, int index) {
+ return getRepeated(builder, index);
+ }
+
+ @Override
+ public void setRepeated(Builder builder, int index, Object value) {
+ getMutableMapField(builder).getMutableList().set(index, coerceType((Message) value));
+ }
+
+ @Override
+ public void addRepeated(Builder builder, Object value) {
+ getMutableMapField(builder).getMutableList().add(coerceType((Message) value));
+ }
+
+ @Override
+ public boolean has(GeneratedMessageV3 message) {
+ throw new UnsupportedOperationException(
+ "hasField() is not supported for repeated fields.");
+ }
+
+ @Override
+ public boolean has(Builder builder) {
+ throw new UnsupportedOperationException(
+ "hasField() is not supported for repeated fields.");
+ }
+
+ @Override
+ public int getRepeatedCount(GeneratedMessageV3 message) {
+ return getMapField(message).getList().size();
+ }
+
+ @Override
+ public int getRepeatedCount(Builder builder) {
+ return getMapField(builder).getList().size();
+ }
+
+ @Override
+ public void clear(Builder builder) {
+ getMutableMapField(builder).getMutableList().clear();
+ }
+
+ @Override
+ public com.google.protobuf.Message.Builder newBuilder() {
+ return mapEntryMessageDefaultInstance.newBuilderForType();
+ }
+
+ @Override
+ public com.google.protobuf.Message.Builder getBuilder(Builder builder) {
+ throw new UnsupportedOperationException(
+ "Nested builder not supported for map fields.");
+ }
+
+ @Override
+ public com.google.protobuf.Message.Builder getRepeatedBuilder(Builder builder, int index) {
+ throw new UnsupportedOperationException(
+ "Nested builder not supported for map fields.");
+ }
+ }
+
+ // ---------------------------------------------------------------
+
+ private static final class SingularEnumFieldAccessor
+ extends SingularFieldAccessor {
+ SingularEnumFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass,
+ final String containingOneofCamelCaseName) {
+ super(descriptor, camelCaseName, messageClass, builderClass, containingOneofCamelCaseName);
+
+ enumDescriptor = descriptor.getEnumType();
+
+ valueOfMethod = getMethodOrDie(type, "valueOf",
+ EnumValueDescriptor.class);
+ getValueDescriptorMethod =
+ getMethodOrDie(type, "getValueDescriptor");
+
+ supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+ if (supportUnknownEnumValue) {
+ getValueMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Value");
+ getValueMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Value");
+ setValueMethod =
+ getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class);
+ }
+ }
+
+ private EnumDescriptor enumDescriptor;
+
+ private Method valueOfMethod;
+ private Method getValueDescriptorMethod;
+
+ private boolean supportUnknownEnumValue;
+ private Method getValueMethod;
+ private Method getValueMethodBuilder;
+ private Method setValueMethod;
+
+ @Override
+ public Object get(final GeneratedMessageV3 message) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getValueMethod, message);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
+ return invokeOrDie(getValueDescriptorMethod, super.get(message));
+ }
+
+ @Override
+ public Object get(final GeneratedMessageV3.Builder builder) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getValueMethodBuilder, builder);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
+ return invokeOrDie(getValueDescriptorMethod, super.get(builder));
+ }
+
+ @Override
+ public void set(final Builder builder, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(setValueMethod, builder,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
+ super.set(builder, invokeOrDie(valueOfMethod, null, value));
+ }
+ }
+
+ private static final class RepeatedEnumFieldAccessor
+ extends RepeatedFieldAccessor {
+ RepeatedEnumFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ super(descriptor, camelCaseName, messageClass, builderClass);
+
+ enumDescriptor = descriptor.getEnumType();
+
+ valueOfMethod = getMethodOrDie(type, "valueOf",
+ EnumValueDescriptor.class);
+ getValueDescriptorMethod =
+ getMethodOrDie(type, "getValueDescriptor");
+
+ supportUnknownEnumValue = descriptor.getFile().supportsUnknownEnumValue();
+ if (supportUnknownEnumValue) {
+ getRepeatedValueMethod =
+ getMethodOrDie(messageClass, "get" + camelCaseName + "Value", int.class);
+ getRepeatedValueMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Value", int.class);
+ setRepeatedValueMethod =
+ getMethodOrDie(builderClass, "set" + camelCaseName + "Value", int.class, int.class);
+ addRepeatedValueMethod =
+ getMethodOrDie(builderClass, "add" + camelCaseName + "Value", int.class);
+ }
+ }
+ private EnumDescriptor enumDescriptor;
+
+ private final Method valueOfMethod;
+ private final Method getValueDescriptorMethod;
+
+ private boolean supportUnknownEnumValue;
+ private Method getRepeatedValueMethod;
+ private Method getRepeatedValueMethodBuilder;
+ private Method setRepeatedValueMethod;
+ private Method addRepeatedValueMethod;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object get(final GeneratedMessageV3 message) {
+ final List newList = new ArrayList();
+ final int size = getRepeatedCount(message);
+ for (int i = 0; i < size; i++) {
+ newList.add(getRepeated(message, i));
+ }
+ return Collections.unmodifiableList(newList);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object get(final GeneratedMessageV3.Builder builder) {
+ final List newList = new ArrayList();
+ final int size = getRepeatedCount(builder);
+ for (int i = 0; i < size; i++) {
+ newList.add(getRepeated(builder, i));
+ }
+ return Collections.unmodifiableList(newList);
+ }
+
+ @Override
+ public Object getRepeated(final GeneratedMessageV3 message,
+ final int index) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getRepeatedValueMethod, message, index);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
+ return invokeOrDie(getValueDescriptorMethod,
+ super.getRepeated(message, index));
+ }
+ @Override
+ public Object getRepeated(final GeneratedMessageV3.Builder builder,
+ final int index) {
+ if (supportUnknownEnumValue) {
+ int value = (Integer) invokeOrDie(getRepeatedValueMethodBuilder, builder, index);
+ return enumDescriptor.findValueByNumberCreatingIfUnknown(value);
+ }
+ return invokeOrDie(getValueDescriptorMethod,
+ super.getRepeated(builder, index));
+ }
+ @Override
+ public void setRepeated(final Builder builder,
+ final int index, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(setRepeatedValueMethod, builder, index,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
+ super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null,
+ value));
+ }
+ @Override
+ public void addRepeated(final Builder builder, final Object value) {
+ if (supportUnknownEnumValue) {
+ invokeOrDie(addRepeatedValueMethod, builder,
+ ((EnumValueDescriptor) value).getNumber());
+ return;
+ }
+ super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value));
+ }
+ }
+
+ // ---------------------------------------------------------------
+
+ /**
+ * Field accessor for string fields.
+ *
+ * <p>This class makes getFooBytes() and setFooBytes() available for
+ * reflection API so that reflection based serialize/parse functions can
+ * access the raw bytes of the field to preserve non-UTF8 bytes in the
+ * string.
+ *
+ * <p>This ensures the serialize/parse round-trip safety, which is important
+ * for servers which forward messages.
+ */
+ private static final class SingularStringFieldAccessor
+ extends SingularFieldAccessor {
+ SingularStringFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass,
+ final String containingOneofCamelCaseName) {
+ super(descriptor, camelCaseName, messageClass, builderClass,
+ containingOneofCamelCaseName);
+ getBytesMethod = getMethodOrDie(messageClass,
+ "get" + camelCaseName + "Bytes");
+ getBytesMethodBuilder = getMethodOrDie(builderClass,
+ "get" + camelCaseName + "Bytes");
+ setBytesMethodBuilder = getMethodOrDie(builderClass,
+ "set" + camelCaseName + "Bytes", ByteString.class);
+ }
+
+ private final Method getBytesMethod;
+ private final Method getBytesMethodBuilder;
+ private final Method setBytesMethodBuilder;
+
+ @Override
+ public Object getRaw(final GeneratedMessageV3 message) {
+ return invokeOrDie(getBytesMethod, message);
+ }
+
+ @Override
+ public Object getRaw(GeneratedMessageV3.Builder builder) {
+ return invokeOrDie(getBytesMethodBuilder, builder);
+ }
+
+ @Override
+ public void set(GeneratedMessageV3.Builder builder, Object value) {
+ if (value instanceof ByteString) {
+ invokeOrDie(setBytesMethodBuilder, builder, value);
+ } else {
+ super.set(builder, value);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------
+
+ private static final class SingularMessageFieldAccessor
+ extends SingularFieldAccessor {
+ SingularMessageFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass,
+ final String containingOneofCamelCaseName) {
+ super(descriptor, camelCaseName, messageClass, builderClass,
+ containingOneofCamelCaseName);
+
+ newBuilderMethod = getMethodOrDie(type, "newBuilder");
+ getBuilderMethodBuilder =
+ getMethodOrDie(builderClass, "get" + camelCaseName + "Builder");
+ }
+
+ private final Method newBuilderMethod;
+ private final Method getBuilderMethodBuilder;
+
+ private Object coerceType(final Object value) {
+ if (type.isInstance(value)) {
+ return value;
+ } else {
+ // The value is not the exact right message type. However, if it
+ // is an alternative implementation of the same type -- e.g. a
+ // DynamicMessage -- we should accept it. In this case we can make
+ // a copy of the message.
+ return ((Message.Builder) invokeOrDie(newBuilderMethod, null))
+ .mergeFrom((Message) value).buildPartial();
+ }
+ }
+
+ @Override
+ public void set(final Builder builder, final Object value) {
+ super.set(builder, coerceType(value));
+ }
+ @Override
+ public Message.Builder newBuilder() {
+ return (Message.Builder) invokeOrDie(newBuilderMethod, null);
+ }
+ @Override
+ public Message.Builder getBuilder(GeneratedMessageV3.Builder builder) {
+ return (Message.Builder) invokeOrDie(getBuilderMethodBuilder, builder);
+ }
+ }
+
+ private static final class RepeatedMessageFieldAccessor
+ extends RepeatedFieldAccessor {
+ RepeatedMessageFieldAccessor(
+ final FieldDescriptor descriptor, final String camelCaseName,
+ final Class<? extends GeneratedMessageV3> messageClass,
+ final Class<? extends Builder> builderClass) {
+ super(descriptor, camelCaseName, messageClass, builderClass);
+
+ newBuilderMethod = getMethodOrDie(type, "newBuilder");
+ getBuilderMethodBuilder = getMethodOrDie(builderClass,
+ "get" + camelCaseName + "Builder", Integer.TYPE);
+ }
+
+ private final Method newBuilderMethod;
+ private final Method getBuilderMethodBuilder;
+
+ private Object coerceType(final Object value) {
+ if (type.isInstance(value)) {
+ return value;
+ } else {
+ // The value is not the exact right message type. However, if it
+ // is an alternative implementation of the same type -- e.g. a
+ // DynamicMessage -- we should accept it. In this case we can make
+ // a copy of the message.
+ return ((Message.Builder) invokeOrDie(newBuilderMethod, null))
+ .mergeFrom((Message) value).build();
+ }
+ }
+
+ @Override
+ public void setRepeated(final Builder builder,
+ final int index, final Object value) {
+ super.setRepeated(builder, index, coerceType(value));
+ }
+ @Override
+ public void addRepeated(final Builder builder, final Object value) {
+ super.addRepeated(builder, coerceType(value));
+ }
+ @Override
+ public Message.Builder newBuilder() {
+ return (Message.Builder) invokeOrDie(newBuilderMethod, null);
+ }
+ @Override
+ public Message.Builder getRepeatedBuilder(
+ final GeneratedMessageV3.Builder builder, final int index) {
+ return (Message.Builder) invokeOrDie(
+ getBuilderMethodBuilder, builder, index);
+ }
+ }
+ }
+
+ /**
+ * Replaces this object in the output stream with a serialized form.
+ * Part of Java's serialization magic. Generated sub-classes must override
+ * this method by calling {@code return super.writeReplace();}
+ * @return a SerializedForm of this message
+ */
+ protected Object writeReplace() throws ObjectStreamException {
+ return new GeneratedMessageLite.SerializedForm(this);
+ }
+
+ /**
+ * Checks that the {@link Extension} is non-Lite and returns it as a
+ * {@link GeneratedExtension}.
+ */
+ private static <MessageType extends ExtendableMessage<MessageType>, T>
+ Extension<MessageType, T> checkNotLite(
+ ExtensionLite<MessageType, T> extension) {
+ if (extension.isLite()) {
+ throw new IllegalArgumentException("Expected non-lite extension.");
+ }
+
+ return (Extension<MessageType, T>) extension;
+ }
+
+ protected static int computeStringSize(final int fieldNumber, final Object value) {
+ if (value instanceof String) {
+ return CodedOutputStream.computeStringSize(fieldNumber, (String) value);
+ } else {
+ return CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) value);
+ }
+ }
+
+ protected static int computeStringSizeNoTag(final Object value) {
+ if (value instanceof String) {
+ return CodedOutputStream.computeStringSizeNoTag((String) value);
+ } else {
+ return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
+ }
+ }
+
+ protected static void writeString(
+ CodedOutputStream output, final int fieldNumber, final Object value) throws IOException {
+ if (value instanceof String) {
+ output.writeString(fieldNumber, (String) value);
+ } else {
+ output.writeBytes(fieldNumber, (ByteString) value);
+ }
+ }
+
+ protected static void writeStringNoTag(
+ CodedOutputStream output, final Object value) throws IOException {
+ if (value instanceof String) {
+ output.writeStringNoTag((String) value);
+ } else {
+ output.writeBytesNoTag((ByteString) value);
+ }
+ }
+
+ protected static <V> void serializeIntegerMapTo(
+ CodedOutputStream out,
+ MapField<Integer, V> field,
+ MapEntry<Integer, V> defaultEntry,
+ int fieldNumber) throws IOException {
+ Map<Integer, V> m = field.getMap();
+ if (!out.isSerializationDeterministic()) {
+ serializeMapTo(out, m, defaultEntry, fieldNumber);
+ return;
+ }
+ // Sorting the unboxed keys and then look up the values during serialziation is 2x faster
+ // than sorting map entries with a custom comparator directly.
+ int[] keys = new int[m.size()];
+ int index = 0;
+ for (int k : m.keySet()) {
+ keys[index++] = k;
+ }
+ Arrays.sort(keys);
+ for (int key : keys) {
+ out.writeMessage(fieldNumber,
+ defaultEntry.newBuilderForType()
+ .setKey(key)
+ .setValue(m.get(key))
+ .build());
+ }
+ }
+
+ protected static <V> void serializeLongMapTo(
+ CodedOutputStream out,
+ MapField<Long, V> field,
+ MapEntry<Long, V> defaultEntry,
+ int fieldNumber)
+ throws IOException {
+ Map<Long, V> m = field.getMap();
+ if (!out.isSerializationDeterministic()) {
+ serializeMapTo(out, m, defaultEntry, fieldNumber);
+ return;
+ }
+
+ long[] keys = new long[m.size()];
+ int index = 0;
+ for (long k : m.keySet()) {
+ keys[index++] = k;
+ }
+ Arrays.sort(keys);
+ for (long key : keys) {
+ out.writeMessage(fieldNumber,
+ defaultEntry.newBuilderForType()
+ .setKey(key)
+ .setValue(m.get(key))
+ .build());
+ }
+ }
+
+ protected static <V> void serializeStringMapTo(
+ CodedOutputStream out,
+ MapField<String, V> field,
+ MapEntry<String, V> defaultEntry,
+ int fieldNumber)
+ throws IOException {
+ Map<String, V> m = field.getMap();
+ if (!out.isSerializationDeterministic()) {
+ serializeMapTo(out, m, defaultEntry, fieldNumber);
+ return;
+ }
+
+ // Sorting the String keys and then look up the values during serialziation is 25% faster than
+ // sorting map entries with a custom comparator directly.
+ String[] keys = new String[m.size()];
+ keys = m.keySet().toArray(keys);
+ Arrays.sort(keys);
+ for (String key : keys) {
+ out.writeMessage(fieldNumber,
+ defaultEntry.newBuilderForType()
+ .setKey(key)
+ .setValue(m.get(key))
+ .build());
+ }
+ }
+
+ protected static <V> void serializeBooleanMapTo(
+ CodedOutputStream out,
+ MapField<Boolean, V> field,
+ MapEntry<Boolean, V> defaultEntry,
+ int fieldNumber)
+ throws IOException {
+ Map<Boolean, V> m = field.getMap();
+ if (!out.isSerializationDeterministic()) {
+ serializeMapTo(out, m, defaultEntry, fieldNumber);
+ return;
+ }
+ maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, false);
+ maybeSerializeBooleanEntryTo(out, m, defaultEntry, fieldNumber, true);
+ }
+
+ private static <V> void maybeSerializeBooleanEntryTo(
+ CodedOutputStream out,
+ Map<Boolean, V> m,
+ MapEntry<Boolean, V> defaultEntry,
+ int fieldNumber,
+ boolean key)
+ throws IOException {
+ if (m.containsKey(key)) {
+ out.writeMessage(fieldNumber,
+ defaultEntry.newBuilderForType()
+ .setKey(key)
+ .setValue(m.get(key))
+ .build());
+ }
+ }
+
+ /** Serialize the map using the iteration order. */
+ private static <K, V> void serializeMapTo(
+ CodedOutputStream out,
+ Map<K, V> m,
+ MapEntry<K, V> defaultEntry,
+ int fieldNumber)
+ throws IOException {
+ for (Map.Entry<K, V> entry : m.entrySet()) {
+ out.writeMessage(fieldNumber,
+ defaultEntry.newBuilderForType()
+ .setKey(entry.getKey())
+ .setValue(entry.getValue())
+ .build());
+ }
+ }
+}
+
diff --git a/java/core/src/main/java/com/google/protobuf/IntArrayList.java b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
index f4e68ed8..aacd71e1 100644
--- a/java/core/src/main/java/com/google/protobuf/IntArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/IntArrayList.java
@@ -30,36 +30,35 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.IntList;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.IntList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.RandomAccess;
/**
* An implementation of {@link IntList} on top of a primitive array.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
-final class IntArrayList extends AbstractProtobufList<Integer> implements IntList, RandomAccess {
-
- private static final int DEFAULT_CAPACITY = 10;
-
+final class IntArrayList extends AbstractProtobufList<Integer>
+ implements IntList, RandomAccess, PrimitiveNonBoxingCollection {
+
private static final IntArrayList EMPTY_LIST = new IntArrayList();
static {
EMPTY_LIST.makeImmutable();
}
-
+
public static IntArrayList emptyList() {
return EMPTY_LIST;
}
-
+
/**
* The backing store for the list.
*/
private int[] array;
-
+
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@@ -70,34 +69,70 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
* Constructs a new mutable {@code IntArrayList} with default capacity.
*/
IntArrayList() {
- this(DEFAULT_CAPACITY);
+ this(new int[DEFAULT_CAPACITY], 0);
}
/**
- * Constructs a new mutable {@code IntArrayList} with the provided capacity.
+ * Constructs a new mutable {@code IntArrayList}
+ * containing the same elements as {@code other}.
*/
- IntArrayList(int capacity) {
- array = new int[capacity];
- size = 0;
+ private IntArrayList(int[] other, int size) {
+ array = other;
+ this.size = size;
}
- /**
- * Constructs a new mutable {@code IntArrayList} containing the same elements as {@code other}.
- */
- IntArrayList(List<Integer> other) {
- if (other instanceof IntArrayList) {
- IntArrayList list = (IntArrayList) other;
- array = list.array.clone();
- size = list.size;
- } else {
- size = other.size();
- array = new int[size];
- for (int i = 0; i < size; i++) {
- array[i] = other.get(i);
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ ensureIsMutable();
+ if (toIndex < fromIndex) {
+ throw new IndexOutOfBoundsException("toIndex < fromIndex");
+ }
+
+ System.arraycopy(array, toIndex, array, fromIndex, size - toIndex);
+ size -= (toIndex - fromIndex);
+ modCount++;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof IntArrayList)) {
+ return super.equals(o);
+ }
+ IntArrayList other = (IntArrayList) o;
+ if (size != other.size) {
+ return false;
+ }
+
+ final int[] arr = other.array;
+ for (int i = 0; i < size; i++) {
+ if (array[i] != arr[i]) {
+ return false;
}
}
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size; i++) {
+ result = (31 * result) + array[i];
+ }
+ return result;
}
-
+
+ @Override
+ public IntList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size) {
+ throw new IllegalArgumentException();
+ }
+ return new IntArrayList(Arrays.copyOf(array, capacity), size);
+ }
+
@Override
public Integer get(int index) {
return getInt(index);
@@ -149,7 +184,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
-
+
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@@ -157,10 +192,10 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
int[] newArray = new int[length];
-
+
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
-
+
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@@ -174,38 +209,36 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
@Override
public boolean addAll(Collection<? extends Integer> collection) {
ensureIsMutable();
-
- if (collection == null) {
- throw new NullPointerException();
- }
-
+
+ checkNotNull(collection);
+
// We specialize when adding another IntArrayList to avoid boxing elements.
if (!(collection instanceof IntArrayList)) {
return super.addAll(collection);
}
-
+
IntArrayList list = (IntArrayList) collection;
if (list.size == 0) {
return false;
}
-
+
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
-
+
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
-
+
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
-
+
@Override
public boolean remove(Object o) {
ensureIsMutable();
@@ -225,7 +258,9 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
ensureIsMutable();
ensureIndexInRange(index);
int value = array[index];
- System.arraycopy(array, index + 1, array, index, size - index);
+ if (index < size - 1) {
+ System.arraycopy(array, index + 1, array, index, size - index);
+ }
size--;
modCount++;
return value;
@@ -234,7 +269,7 @@ final class IntArrayList extends AbstractProtobufList<Integer> implements IntLis
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
- *
+ *
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {
diff --git a/java/core/src/main/java/com/google/protobuf/Internal.java b/java/core/src/main/java/com/google/protobuf/Internal.java
index e19b6dca..848cad03 100644
--- a/java/core/src/main/java/com/google/protobuf/Internal.java
+++ b/java/core/src/main/java/com/google/protobuf/Internal.java
@@ -41,6 +41,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.RandomAccess;
import java.util.Set;
/**
@@ -51,10 +52,32 @@ import java.util.Set;
*
* @author kenton@google.com (Kenton Varda)
*/
-public class Internal {
+public final class Internal {
- protected static final Charset UTF_8 = Charset.forName("UTF-8");
- protected static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+ private Internal() {}
+
+ static final Charset UTF_8 = Charset.forName("UTF-8");
+ static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+
+ /**
+ * Throws an appropriate {@link NullPointerException} if the given objects is {@code null}.
+ */
+ static <T> T checkNotNull(T obj) {
+ if (obj == null) {
+ throw new NullPointerException();
+ }
+ return obj;
+ }
+
+ /**
+ * Throws an appropriate {@link NullPointerException} if the given objects is {@code null}.
+ */
+ static <T> T checkNotNull(T obj, String message) {
+ if (obj == null) {
+ throw new NullPointerException(message);
+ }
+ return obj;
+ }
/**
* Helper called by generated code to construct default values for string
@@ -391,9 +414,8 @@ public class Internal {
}
}
- /**
- * An empty byte array constant used in generated code.
- */
+
+ /** An empty byte array constant used in generated code. */
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
/**
@@ -406,6 +428,12 @@ public class Internal {
public static final CodedInputStream EMPTY_CODED_INPUT_STREAM =
CodedInputStream.newInstance(EMPTY_BYTE_ARRAY);
+
+ /** Helper method to merge two MessageLite instances. */
+ static Object mergeMessage(Object destination, Object source) {
+ return ((MessageLite) destination).toBuilder().mergeFrom((MessageLite) source).buildPartial();
+ }
+
/**
* Provides an immutable view of {@code List<T>} around a {@code List<F>}.
*
@@ -454,10 +482,13 @@ public class Internal {
public static <T extends EnumLite> Converter<Integer, T> newEnumConverter(
final EnumLiteMap<T> enumMap, final T unrecognizedValue) {
return new Converter<Integer, T>() {
+ @Override
public T doForward(Integer value) {
T result = enumMap.findValueByNumber(value);
return result == null ? unrecognizedValue : result;
}
+
+ @Override
public Integer doBackward(T value) {
return value.getNumber();
}
@@ -570,8 +601,10 @@ public class Internal {
/**
* Extends {@link List} to add the capability to make the list immutable and inspect if it is
* modifiable.
+ * <p>
+ * All implementations must support efficient random access.
*/
- public static interface ProtobufList<E> extends List<E> {
+ public static interface ProtobufList<E> extends List<E>, RandomAccess {
/**
* Makes this list immutable. All subsequent modifications will throw an
@@ -583,6 +616,11 @@ public class Internal {
* Returns whether this list can be modified via the publicly accessible {@link List} methods.
*/
boolean isModifiable();
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ ProtobufList<E> mutableCopyWithCapacity(int capacity);
}
/**
@@ -605,6 +643,12 @@ public class Internal {
* Like {@link #set(int, Object)} but more efficient in that it doesn't box the element.
*/
int setInt(int index, int element);
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ @Override
+ IntList mutableCopyWithCapacity(int capacity);
}
/**
@@ -627,6 +671,12 @@ public class Internal {
* Like {@link #set(int, Object)} but more efficient in that it doesn't box the element.
*/
boolean setBoolean(int index, boolean element);
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ @Override
+ BooleanList mutableCopyWithCapacity(int capacity);
}
/**
@@ -649,6 +699,12 @@ public class Internal {
* Like {@link #set(int, Object)} but more efficient in that it doesn't box the element.
*/
long setLong(int index, long element);
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ @Override
+ LongList mutableCopyWithCapacity(int capacity);
}
/**
@@ -671,6 +727,12 @@ public class Internal {
* Like {@link #set(int, Object)} but more efficient in that it doesn't box the element.
*/
double setDouble(int index, double element);
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ @Override
+ DoubleList mutableCopyWithCapacity(int capacity);
}
/**
@@ -693,5 +755,11 @@ public class Internal {
* Like {@link #set(int, Object)} but more efficient in that it doesn't box the element.
*/
float setFloat(int index, float element);
+
+ /**
+ * Returns a mutable clone of this list with the specified capacity.
+ */
+ @Override
+ FloatList mutableCopyWithCapacity(int capacity);
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
index 85ce7b24..510c6aac 100644
--- a/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
+++ b/java/core/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
@@ -50,6 +50,10 @@ public class InvalidProtocolBufferException extends IOException {
super(e.getMessage(), e);
}
+ public InvalidProtocolBufferException(final String description, IOException e) {
+ super(description, e);
+ }
+
/**
* Attaches an unfinished message to the exception to support best-effort
* parsing in {@code Parser} interface.
@@ -107,11 +111,23 @@ public class InvalidProtocolBufferException extends IOException {
"Protocol message end-group tag did not match expected tag.");
}
- static InvalidProtocolBufferException invalidWireType() {
- return new InvalidProtocolBufferException(
+ static InvalidWireTypeException invalidWireType() {
+ return new InvalidWireTypeException(
"Protocol message tag had invalid wire type.");
}
+ /**
+ * Exception indicating that and unexpected wire type was encountered for a field.
+ */
+ @ExperimentalApi
+ public static class InvalidWireTypeException extends InvalidProtocolBufferException {
+ private static final long serialVersionUID = 3283890091615336259L;
+
+ public InvalidWireTypeException(String description) {
+ super(description);
+ }
+ }
+
static InvalidProtocolBufferException recursionLimitExceeded() {
return new InvalidProtocolBufferException(
"Protocol message had too many levels of nesting. May be malicious. " +
diff --git a/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java b/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java
new file mode 100644
index 00000000..713e8064
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/IterableByteBufferInputStream.java
@@ -0,0 +1,150 @@
+// 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.protobuf.Internal.EMPTY_BYTE_BUFFER;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+class IterableByteBufferInputStream extends InputStream {
+ /** The {@link Iterator} with type {@link ByteBuffer} of {@code input} */
+ private Iterator<ByteBuffer> iterator;
+ /** The current ByteBuffer; */
+ private ByteBuffer currentByteBuffer;
+ /** The number of ByteBuffers in the input data. */
+ private int dataSize;
+ /**
+ * Current {@code ByteBuffer}'s index
+ *
+ * <p>If index equals dataSize, then all the data in the InputStream has been consumed
+ */
+ private int currentIndex;
+ /** The current position for current ByteBuffer */
+ private int currentByteBufferPos;
+ /** Whether current ByteBuffer has an array */
+ private boolean hasArray;
+ /**
+ * If the current ByteBuffer is unsafe-direct based, currentArray is null; otherwise should be the
+ * array inside ByteBuffer.
+ */
+ private byte[] currentArray;
+ /** Current ByteBuffer's array offset */
+ private int currentArrayOffset;
+ /**
+ * If the current ByteBuffer is unsafe-direct based, currentAddress is the start address of this
+ * ByteBuffer; otherwise should be zero.
+ */
+ private long currentAddress;
+
+ IterableByteBufferInputStream(Iterable<ByteBuffer> data) {
+ iterator = data.iterator();
+ dataSize = 0;
+ for (ByteBuffer unused : data) {
+ dataSize++;
+ }
+ currentIndex = -1;
+
+ if (!getNextByteBuffer()) {
+ currentByteBuffer = EMPTY_BYTE_BUFFER;
+ currentIndex = 0;
+ currentByteBufferPos = 0;
+ currentAddress = 0;
+ }
+ }
+
+ private boolean getNextByteBuffer() {
+ currentIndex++;
+ if (!iterator.hasNext()) {
+ return false;
+ }
+ currentByteBuffer = iterator.next();
+ currentByteBufferPos = currentByteBuffer.position();
+ if (currentByteBuffer.hasArray()) {
+ hasArray = true;
+ currentArray = currentByteBuffer.array();
+ currentArrayOffset = currentByteBuffer.arrayOffset();
+ } else {
+ hasArray = false;
+ currentAddress = UnsafeUtil.addressOffset(currentByteBuffer);
+ currentArray = null;
+ }
+ return true;
+ }
+
+ private void updateCurrentByteBufferPos(int numberOfBytesRead) {
+ currentByteBufferPos += numberOfBytesRead;
+ if (currentByteBufferPos == currentByteBuffer.limit()) {
+ getNextByteBuffer();
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (currentIndex == dataSize) {
+ return -1;
+ }
+ if (hasArray) {
+ int result = currentArray[currentByteBufferPos + currentArrayOffset] & 0xFF;
+ updateCurrentByteBufferPos(1);
+ return result;
+ } else {
+ int result = UnsafeUtil.getByte(currentByteBufferPos + currentAddress) & 0xFF;
+ updateCurrentByteBufferPos(1);
+ return result;
+ }
+ }
+
+ @Override
+ public int read(byte[] output, int offset, int length) throws IOException {
+ if (currentIndex == dataSize) {
+ return -1;
+ }
+ int remaining = currentByteBuffer.limit() - currentByteBufferPos;
+ if (length > remaining) {
+ length = remaining;
+ }
+ if (hasArray) {
+ System.arraycopy(
+ currentArray, currentByteBufferPos + currentArrayOffset, output, offset, length);
+ updateCurrentByteBufferPos(length);
+ } else {
+ int prevPos = currentByteBuffer.position();
+ currentByteBuffer.position(currentByteBufferPos);
+ currentByteBuffer.get(output, offset, length);
+ currentByteBuffer.position(prevPos);
+ updateCurrentByteBufferPos(length);
+ }
+ return length;
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/LazyField.java b/java/core/src/main/java/com/google/protobuf/LazyField.java
index 5e0a485c..98e13ca1 100644
--- a/java/core/src/main/java/com/google/protobuf/LazyField.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyField.java
@@ -39,14 +39,14 @@ import java.util.Map.Entry;
*
* Most of key methods are implemented in {@link LazyFieldLite} but this class
* can contain default instance of the message to provide {@code hashCode()},
- * {@code equals()} and {@code toString()}.
+ * {@code euqals()} and {@code toString()}.
*
* @author xiangl@google.com (Xiang Li)
*/
public class LazyField extends LazyFieldLite {
/**
- * Carry a message's default instance which is used by {@code hashCode()}, {@code equals()} and
+ * Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and
* {@code toString()}.
*/
private final MessageLite defaultInstance;
@@ -95,12 +95,12 @@ public class LazyField extends LazyFieldLite {
this.entry = entry;
}
- // @Override
+ @Override
public K getKey() {
return entry.getKey();
}
- // @Override
+ @Override
public Object getValue() {
LazyField field = entry.getValue();
if (field == null) {
@@ -113,7 +113,7 @@ public class LazyField extends LazyFieldLite {
return entry.getValue();
}
- // @Override
+ @Override
public Object setValue(Object value) {
if (!(value instanceof MessageLite)) {
throw new IllegalArgumentException(
@@ -131,13 +131,13 @@ public class LazyField extends LazyFieldLite {
this.iterator = iterator;
}
- // @Override
+ @Override
public boolean hasNext() {
return iterator.hasNext();
}
+ @Override
@SuppressWarnings("unchecked")
- // @Override
public Entry<K, Object> next() {
Entry<K, ?> entry = iterator.next();
if (entry.getValue() instanceof LazyField) {
@@ -146,7 +146,7 @@ public class LazyField extends LazyFieldLite {
return (Entry<K, Object>) entry;
}
- // @Override
+ @Override
public void remove() {
iterator.remove();
}
diff --git a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
index eea1fe3c..49ecfc0b 100644
--- a/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyFieldLite.java
@@ -30,14 +30,26 @@
package com.google.protobuf;
+import java.io.IOException;
+
/**
* LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores
- * the message in a ByteString initially and then parse it on-demand.
+ * the message in a ByteString initially and then parses it on-demand.
+ *
+ * LazyFieldLite is thread-compatible: concurrent reads are safe once the proto that this
+ * LazyFieldLite is a part of is no longer being mutated by its Builder. However, explicit
+ * synchronization is needed under read/write situations.
*
- * LazyField is thread-compatible e.g. concurrent read are safe, however,
- * synchronizations are needed under read/write situations.
+ * When a LazyFieldLite is used in the context of a MessageLite object, its behavior is considered
+ * to be immutable and none of the setter methods in its API are expected to be invoked. All of the
+ * getters are expected to be thread-safe. When used in the context of a MessageLite.Builder,
+ * setters can be invoked, but there is no guarantee of thread safety.
+ *
+ * TODO(yatin,dweis): Consider splitting this class's functionality and put the mutable methods
+ * into a separate builder class to allow us to give stronger compile-time guarantees.
*
- * This class is internal implementation detail, so you don't need to use it directly.
+ * This class is internal implementation detail of the protobuf library, so you don't need to use it
+ * directly.
*
* @author xiangl@google.com (Xiang Li)
*/
@@ -46,8 +58,34 @@ public class LazyFieldLite {
ExtensionRegistryLite.getEmptyRegistry();
/**
- * A delayed-parsed version of the bytes. When this is non-null then {@code extensionRegistry } is
- * also non-null and {@code value} and {@code memoizedBytes} are null.
+ * The value associated with the LazyFieldLite object is stored in one or more of the following
+ * three fields (delayedBytes, value, memoizedBytes). They should together be interpreted as
+ * follows.
+ * 1) delayedBytes can be non-null, while value and memoizedBytes is null. The object will be in
+ * this state while the value for the object has not yet been parsed.
+ * 2) Both delayedBytes and value are non-null. The object transitions to this state as soon as
+ * some caller needs to access the value (by invoking getValue()).
+ * 3) memoizedBytes is merely an optimization for calls to LazyFieldLite.toByteString() to avoid
+ * recomputing the ByteString representation on each call. Instead, when the value is parsed
+ * from delayedBytes, we will also assign the contents of delayedBytes to memoizedBytes (since
+ * that is the ByteString representation of value).
+ * 4) Finally, if the LazyFieldLite was created directly with a parsed MessageLite value, then
+ * delayedBytes will be null, and memoizedBytes will be initialized only upon the first call to
+ * LazyFieldLite.toByteString().
+ *
+ * Given the above conditions, any caller that needs a serialized representation of this object
+ * must first check if the memoizedBytes or delayedBytes ByteString is non-null and use it
+ * directly; if both of those are null, it can look at the parsed value field. Similarly, any
+ * caller that needs a parsed value must first check if the value field is already non-null, if
+ * not it must parse the value from delayedBytes.
+ */
+
+ /**
+ * A delayed-parsed version of the contents of this field. When this field is non-null, then the
+ * "value" field is allowed to be null until the time that the value needs to be read.
+ *
+ * When delayedBytes is non-null then {@code extensionRegistry} is required to also be non-null.
+ * {@code value} and {@code memoizedBytes} will be initialized lazily.
*/
private ByteString delayedBytes;
@@ -60,12 +98,15 @@ public class LazyFieldLite {
private ExtensionRegistryLite extensionRegistry;
/**
- * The parsed value. When this is non-null then {@code delayedBytes} will be null.
+ * The parsed value. When this is null and a caller needs access to the MessageLite value, then
+ * {@code delayedBytes} will be parsed lazily at that time.
*/
protected volatile MessageLite value;
/**
- * The memoized bytes for {@code value}. Will be null when {@code value} is null.
+ * The memoized bytes for {@code value}. This is an optimization for the toByteString() method to
+ * not have to recompute its return-value on each invocation.
+ * TODO(yatin): Figure out whether this optimization is actually necessary.
*/
private volatile ByteString memoizedBytes;
@@ -94,6 +135,43 @@ public class LazyFieldLite {
return lf;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof LazyFieldLite)) {
+ return false;
+ }
+
+ LazyFieldLite other = (LazyFieldLite) o;
+
+ // Lazy fields do not work well with equals... If both are delayedBytes, we do not have a
+ // mechanism to deserialize them so we rely on bytes equality. Otherwise we coerce into an
+ // actual message (if necessary) and call equals on the message itself. This implies that two
+ // messages can by unequal but then be turned equal simply be invoking a getter on a lazy field.
+ MessageLite value1 = value;
+ MessageLite value2 = other.value;
+ if (value1 == null && value2 == null) {
+ return toByteString().equals(other.toByteString());
+ } else if (value1 != null && value2 != null) {
+ return value1.equals(value2);
+ } else if (value1 != null) {
+ return value1.equals(other.getValue(value1.getDefaultInstanceForType()));
+ } else {
+ return getValue(value2.getDefaultInstanceForType()).equals(value2);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ // We can't provide a memoizable hash code for lazy fields. The byte strings may have different
+ // hash codes but evaluate to equivalent messages. And we have no facility for constructing
+ // a message here if we were not already holding a value.
+ return 1;
+ }
+
/**
* Determines whether this LazyFieldLite instance represents the default instance of this type.
*/
@@ -206,29 +284,48 @@ public class LazyFieldLite {
return;
}
- // At this point we have two fully parsed messages. We can't merge directly from one to the
- // other because only generated builder code contains methods to mergeFrom another parsed
- // message. We have to serialize one instance and then merge the bytes into the other. This may
- // drop extensions from one of the messages if one of the values had an extension set on it
- // directly.
- //
- // To mitigate this we prefer serializing a message that has an extension registry, and
- // therefore a chance that all extensions set on it are in that registry.
- //
- // NOTE: The check for other.extensionRegistry not being null must come first because at this
- // point in time if other.extensionRegistry is not null then this.extensionRegistry will not be
- // null either.
- if (other.extensionRegistry != null) {
- setValue(mergeValueAndBytes(this.value, other.toByteString(), other.extensionRegistry));
- return;
- } else if (this.extensionRegistry != null) {
- setValue(mergeValueAndBytes(other.value, this.toByteString(), this.extensionRegistry));
+ // At this point we have two fully parsed messages.
+ setValue(this.value.toBuilder().mergeFrom(other.value).build());
+ }
+
+ /**
+ * Merges another instance's contents from a stream.
+ *
+ * <p>LazyField is not thread-safe for write access. Synchronizations are needed
+ * under read/write situations.
+ */
+ public void mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ if (this.containsDefaultInstance()) {
+ setByteString(input.readBytes(), extensionRegistry);
return;
- } else {
- // All extensions from the other message will be dropped because we have no registry.
- setValue(mergeValueAndBytes(this.value, other.toByteString(), EMPTY_REGISTRY));
+ }
+
+ // If the other field has an extension registry but this does not, copy over the other extension
+ // registry.
+ if (this.extensionRegistry == null) {
+ this.extensionRegistry = extensionRegistry;
+ }
+
+ // In the case that both of them are not parsed we simply concatenate the bytes to save time. In
+ // the (probably rare) case that they have different extension registries there is a chance that
+ // some of the extensions may be dropped, but the tradeoff of making this operation fast seems
+ // to outway the benefits of combining the extension registries, which is not normally done for
+ // lite protos anyways.
+ if (this.delayedBytes != null) {
+ setByteString(this.delayedBytes.concat(input.readBytes()), this.extensionRegistry);
return;
}
+
+ // We are parsed and both contain data. We won't drop any extensions here directly, but in the
+ // case that the extension registries are not the same then we might in the future if we
+ // need to serialize and parse a message again.
+ try {
+ setValue(value.toBuilder().mergeFrom(input, extensionRegistry).build());
+ } catch (InvalidProtocolBufferException e) {
+ // Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
+ // was invalid.
+ }
}
private static MessageLite mergeValueAndBytes(
@@ -259,10 +356,12 @@ public class LazyFieldLite {
* parsed. Be careful when using this method.
*/
public int getSerializedSize() {
- if (delayedBytes != null) {
- return delayedBytes.size();
- } else if (memoizedBytes != null) {
+ // We *must* return delayed bytes size if it was ever set because the dependent messages may
+ // have memoized serialized size based off of it.
+ if (memoizedBytes != null) {
return memoizedBytes.size();
+ } else if (delayedBytes != null) {
+ return delayedBytes.size();
} else if (value != null) {
return value.getSerializedSize();
} else {
@@ -274,12 +373,14 @@ public class LazyFieldLite {
* Returns a BytesString for this field in a thread-safe way.
*/
public ByteString toByteString() {
- if (delayedBytes != null) {
- return delayedBytes;
- }
if (memoizedBytes != null) {
return memoizedBytes;
}
+ // We *must* return delayed bytes if it was set because the dependent messages may have
+ // memoized serialized size based off of it.
+ if (delayedBytes != null) {
+ return delayedBytes;
+ }
synchronized (this) {
if (memoizedBytes != null) {
return memoizedBytes;
@@ -293,6 +394,7 @@ public class LazyFieldLite {
}
}
+
/**
* Might lazily parse the bytes that were previously passed in. Is thread-safe.
*/
@@ -311,18 +413,15 @@ public class LazyFieldLite {
.parseFrom(delayedBytes, extensionRegistry);
this.value = parsedValue;
this.memoizedBytes = delayedBytes;
- this.delayedBytes = null;
} else {
this.value = defaultInstance;
this.memoizedBytes = ByteString.EMPTY;
- this.delayedBytes = null;
}
} catch (InvalidProtocolBufferException e) {
// Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto
// was invalid.
this.value = defaultInstance;
this.memoizedBytes = ByteString.EMPTY;
- this.delayedBytes = null;
}
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
index c3be3cca..d474c51e 100644
--- a/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -30,12 +30,12 @@
package com.google.protobuf;
-import java.util.Arrays;
-import java.util.List;
import java.util.AbstractList;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import java.util.RandomAccess;
/**
@@ -64,7 +64,7 @@ import java.util.RandomAccess;
*/
public class LazyStringArrayList extends AbstractProtobufList<String>
implements LazyStringList, RandomAccess {
-
+
private static final LazyStringArrayList EMPTY_LIST = new LazyStringArrayList();
static {
EMPTY_LIST.makeImmutable();
@@ -80,11 +80,11 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
private final List<Object> list;
public LazyStringArrayList() {
- list = new ArrayList<Object>();
+ this(DEFAULT_CAPACITY);
}
public LazyStringArrayList(int intialCapacity) {
- list = new ArrayList<Object>(intialCapacity);
+ this(new ArrayList<Object>(intialCapacity));
}
public LazyStringArrayList(LazyStringList from) {
@@ -93,7 +93,21 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
}
public LazyStringArrayList(List<String> from) {
- list = new ArrayList<Object>(from);
+ this(new ArrayList<Object>(from));
+ }
+
+ private LazyStringArrayList(ArrayList<Object> list) {
+ this.list = list;
+ }
+
+ @Override
+ public LazyStringArrayList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size()) {
+ throw new IllegalArgumentException();
+ }
+ ArrayList<Object> newList = new ArrayList<Object>(capacity);
+ newList.addAll(list);
+ return new LazyStringArrayList(newList);
}
@Override
@@ -170,7 +184,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return ret;
}
- // @Override
+ @Override
public boolean addAllByteString(Collection<? extends ByteString> values) {
ensureIsMutable();
boolean ret = list.addAll(values);
@@ -178,7 +192,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return ret;
}
- // @Override
+ @Override
public boolean addAllByteArray(Collection<byte[]> c) {
ensureIsMutable();
boolean ret = list.addAll(c);
@@ -201,14 +215,14 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
modCount++;
}
- // @Override
+ @Override
public void add(ByteString element) {
ensureIsMutable();
list.add(element);
modCount++;
}
- // @Override
+ @Override
public void add(byte[] element) {
ensureIsMutable();
list.add(element);
@@ -220,7 +234,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return list.get(index);
}
- // @Override
+ @Override
public ByteString getByteString(int index) {
Object o = list.get(index);
ByteString b = asByteString(o);
@@ -230,7 +244,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return b;
}
- // @Override
+ @Override
public byte[] getByteArray(int index) {
Object o = list.get(index);
byte[] b = asByteArray(o);
@@ -240,7 +254,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return b;
}
- // @Override
+ @Override
public void set(int index, ByteString s) {
setAndReturn(index, s);
}
@@ -250,7 +264,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
return list.set(index, s);
}
- // @Override
+ @Override
public void set(int index, byte[] s) {
setAndReturn(index, s);
}
@@ -290,12 +304,12 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
}
}
- // @Override
+ @Override
public List<?> getUnderlyingElements() {
return Collections.unmodifiableList(list);
}
- // @Override
+ @Override
public void mergeFrom(LazyStringList other) {
ensureIsMutable();
for (Object o : other.getUnderlyingElements()) {
@@ -349,7 +363,7 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
}
}
- // @Override
+ @Override
public List<byte[]> asByteArrayList() {
return new ByteArrayListView(this);
}
@@ -393,12 +407,12 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
}
}
- // @Override
+ @Override
public List<ByteString> asByteStringList() {
return new ByteStringListView(this);
}
- // @Override
+ @Override
public LazyStringList getUnmodifiableView() {
if (isModifiable()) {
return new UnmodifiableLazyStringList(this);
diff --git a/java/core/src/main/java/com/google/protobuf/LongArrayList.java b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
index ebe62029..95945cb7 100644
--- a/java/core/src/main/java/com/google/protobuf/LongArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/LongArrayList.java
@@ -30,36 +30,35 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.LongList;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.LongList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.List;
import java.util.RandomAccess;
/**
* An implementation of {@link LongList} on top of a primitive array.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
-final class LongArrayList extends AbstractProtobufList<Long> implements LongList, RandomAccess {
-
- private static final int DEFAULT_CAPACITY = 10;
-
+final class LongArrayList extends AbstractProtobufList<Long>
+ implements LongList, RandomAccess, PrimitiveNonBoxingCollection {
+
private static final LongArrayList EMPTY_LIST = new LongArrayList();
static {
EMPTY_LIST.makeImmutable();
}
-
+
public static LongArrayList emptyList() {
return EMPTY_LIST;
}
-
+
/**
* The backing store for the list.
*/
private long[] array;
-
+
/**
* The size of the list distinct from the length of the array. That is, it is the number of
* elements set in the list.
@@ -70,34 +69,70 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
* Constructs a new mutable {@code LongArrayList} with default capacity.
*/
LongArrayList() {
- this(DEFAULT_CAPACITY);
+ this(new long[DEFAULT_CAPACITY], 0);
}
/**
- * Constructs a new mutable {@code LongArrayList} with the provided capacity.
+ * Constructs a new mutable {@code LongArrayList}
+ * containing the same elements as {@code other}.
*/
- LongArrayList(int capacity) {
- array = new long[capacity];
- size = 0;
+ private LongArrayList(long[] other, int size) {
+ array = other;
+ this.size = size;
}
- /**
- * Constructs a new mutable {@code LongArrayList} containing the same elements as {@code other}.
- */
- LongArrayList(List<Long> other) {
- if (other instanceof LongArrayList) {
- LongArrayList list = (LongArrayList) other;
- array = list.array.clone();
- size = list.size;
- } else {
- size = other.size();
- array = new long[size];
- for (int i = 0; i < size; i++) {
- array[i] = other.get(i);
+ @Override
+ protected void removeRange(int fromIndex, int toIndex) {
+ ensureIsMutable();
+ if (toIndex < fromIndex) {
+ throw new IndexOutOfBoundsException("toIndex < fromIndex");
+ }
+
+ System.arraycopy(array, toIndex, array, fromIndex, size - toIndex);
+ size -= (toIndex - fromIndex);
+ modCount++;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof LongArrayList)) {
+ return super.equals(o);
+ }
+ LongArrayList other = (LongArrayList) o;
+ if (size != other.size) {
+ return false;
+ }
+
+ final long[] arr = other.array;
+ for (int i = 0; i < size; i++) {
+ if (array[i] != arr[i]) {
+ return false;
}
}
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ for (int i = 0; i < size; i++) {
+ result = (31 * result) + Internal.hashLong(array[i]);
+ }
+ return result;
}
-
+
+ @Override
+ public LongList mutableCopyWithCapacity(int capacity) {
+ if (capacity < size) {
+ throw new IllegalArgumentException();
+ }
+ return new LongArrayList(Arrays.copyOf(array, capacity), size);
+ }
+
@Override
public Long get(int index) {
return getLong(index);
@@ -149,7 +184,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
if (index < 0 || index > size) {
throw new IndexOutOfBoundsException(makeOutOfBoundsExceptionMessage(index));
}
-
+
if (size < array.length) {
// Shift everything over to make room
System.arraycopy(array, index, array, index + 1, size - index);
@@ -157,10 +192,10 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
// Resize to 1.5x the size
int length = ((size * 3) / 2) + 1;
long[] newArray = new long[length];
-
+
// Copy the first part directly
System.arraycopy(array, 0, newArray, 0, index);
-
+
// Copy the rest shifted over by one to make room
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
@@ -174,38 +209,36 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
@Override
public boolean addAll(Collection<? extends Long> collection) {
ensureIsMutable();
-
- if (collection == null) {
- throw new NullPointerException();
- }
-
+
+ checkNotNull(collection);
+
// We specialize when adding another LongArrayList to avoid boxing elements.
if (!(collection instanceof LongArrayList)) {
return super.addAll(collection);
}
-
+
LongArrayList list = (LongArrayList) collection;
if (list.size == 0) {
return false;
}
-
+
int overflow = Integer.MAX_VALUE - size;
if (overflow < list.size) {
// We can't actually represent a list this large.
throw new OutOfMemoryError();
}
-
+
int newSize = size + list.size;
if (newSize > array.length) {
array = Arrays.copyOf(array, newSize);
}
-
+
System.arraycopy(list.array, 0, array, size, list.size);
size = newSize;
modCount++;
return true;
}
-
+
@Override
public boolean remove(Object o) {
ensureIsMutable();
@@ -225,7 +258,9 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
ensureIsMutable();
ensureIndexInRange(index);
long value = array[index];
- System.arraycopy(array, index + 1, array, index, size - index);
+ if (index < size - 1) {
+ System.arraycopy(array, index + 1, array, index, size - index);
+ }
size--;
modCount++;
return value;
@@ -234,7 +269,7 @@ final class LongArrayList extends AbstractProtobufList<Long> implements LongList
/**
* Ensures that the provided {@code index} is within the range of {@code [0, size]}. Throws an
* {@link IndexOutOfBoundsException} if it is not.
- *
+ *
* @param index the index to verify is in range
*/
private void ensureIndexInRange(int index) {
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntry.java b/java/core/src/main/java/com/google/protobuf/MapEntry.java
index 31414bb4..0849b821 100644
--- a/java/core/src/main/java/com/google/protobuf/MapEntry.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntry.java
@@ -33,7 +33,6 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
@@ -41,63 +40,84 @@ import java.util.TreeMap;
/**
* Implements MapEntry messages.
- *
+ *
* In reflection API, map fields will be treated as repeated message fields and
* each map entry is accessed as a message. This MapEntry class is used to
* represent these map entry messages in reflection API.
- *
+ *
* Protobuf internal. Users shouldn't use this class.
*/
public final class MapEntry<K, V> extends AbstractMessage {
- private static class Metadata<K, V> {
- public final Descriptor descriptor;
- public final MapEntry<K, V> defaultInstance;
- public final AbstractParser<MapEntry<K, V>> parser;
-
+
+ private static final class Metadata<K, V> extends MapEntryLite.Metadata<K, V> {
+
+ public final Descriptor descriptor;
+ public final Parser<MapEntry<K, V>> parser;
+
public Metadata(
- final Descriptor descriptor, final MapEntry<K, V> defaultInstance) {
+ Descriptor descriptor,
+ MapEntry<K, V> defaultInstance,
+ WireFormat.FieldType keyType,
+ WireFormat.FieldType valueType) {
+ super(keyType, defaultInstance.key, valueType, defaultInstance.value);
this.descriptor = descriptor;
- this.defaultInstance = defaultInstance;
- final Metadata<K, V> thisMetadata = this;
this.parser = new AbstractParser<MapEntry<K, V>>() {
- private final Parser<MapEntryLite<K, V>> dataParser =
- defaultInstance.data.getParserForType();
+
@Override
public MapEntry<K, V> parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
- MapEntryLite<K, V> data =
- dataParser.parsePartialFrom(input, extensionRegistry);
- return new MapEntry<K, V>(thisMetadata, data);
+ return new MapEntry<K, V>(Metadata.this, input, extensionRegistry);
}
-
};
}
}
-
+
+ private final K key;
+ private final V value;
private final Metadata<K, V> metadata;
- private final MapEntryLite<K, V> data;
-
+
/** Create a default MapEntry instance. */
- private MapEntry(Descriptor descriptor,
+ private MapEntry(
+ Descriptor descriptor,
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
- this.data = MapEntryLite.newDefaultInstance(
- keyType, defaultKey, valueType, defaultValue);
- this.metadata = new Metadata<K, V>(descriptor, this);
+ this.key = defaultKey;
+ this.value = defaultValue;
+ this.metadata = new Metadata<K, V>(descriptor, this, keyType, valueType);
}
-
- /** Create a new MapEntry message. */
- private MapEntry(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+
+ /** Create a MapEntry with the provided key and value. */
+ @SuppressWarnings("unchecked")
+ private MapEntry(Metadata metadata, K key, V value) {
+ this.key = key;
+ this.value = value;
this.metadata = metadata;
- this.data = data;
}
-
+
+ /** Parsing constructor. */
+ private MapEntry(
+ Metadata<K, V> metadata,
+ CodedInputStream input,
+ ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException {
+ try {
+ this.metadata = metadata;
+ Map.Entry<K, V> entry = MapEntryLite.parseEntry(input, metadata, extensionRegistry);
+ this.key = entry.getKey();
+ this.value = entry.getValue();
+ } catch (InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (IOException e) {
+ throw new InvalidProtocolBufferException(e).setUnfinishedMessage(this);
+ }
+ }
+
/**
* Create a default MapEntry instance. A default MapEntry instance should be
* created only once for each map entry message type. Generated code should
* store the created default instance and use it later to create new MapEntry
- * messages of the same type.
+ * messages of the same type.
*/
public static <K, V> MapEntry<K, V> newDefaultInstance(
Descriptor descriptor,
@@ -106,30 +126,38 @@ public final class MapEntry<K, V> extends AbstractMessage {
return new MapEntry<K, V>(
descriptor, keyType, defaultKey, valueType, defaultValue);
}
-
+
public K getKey() {
- return data.getKey();
+ return key;
}
-
+
public V getValue() {
- return data.getValue();
+ return value;
}
-
+
+ private volatile int cachedSerializedSize = -1;
+
@Override
public int getSerializedSize() {
- return data.getSerializedSize();
+ if (cachedSerializedSize != -1) {
+ return cachedSerializedSize;
+ }
+
+ int size = MapEntryLite.computeSerializedSize(metadata, key, value);
+ cachedSerializedSize = size;
+ return size;
}
-
+
@Override
public void writeTo(CodedOutputStream output) throws IOException {
- data.writeTo(output);
+ MapEntryLite.writeTo(output, metadata, key, value);
}
-
+
@Override
public boolean isInitialized() {
- return data.isInitialized();
+ return isInitialized(metadata, value);
}
-
+
@Override
public Parser<MapEntry<K, V>> getParserForType() {
return metadata.parser;
@@ -139,15 +167,15 @@ public final class MapEntry<K, V> extends AbstractMessage {
public Builder<K, V> newBuilderForType() {
return new Builder<K, V>(metadata);
}
-
+
@Override
public Builder<K, V> toBuilder() {
- return new Builder<K, V>(metadata, data);
+ return new Builder<K, V>(metadata, key, value, true, true);
}
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
@@ -157,8 +185,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public Map<FieldDescriptor, Object> getAllFields() {
- final TreeMap<FieldDescriptor, Object> result =
- new TreeMap<FieldDescriptor, Object>();
+ TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) {
result.put(field, getField(field));
@@ -166,12 +193,12 @@ public final class MapEntry<K, V> extends AbstractMessage {
}
return Collections.unmodifiableMap(result);
}
-
+
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
- + "\" used in message \"" + metadata.descriptor.getFullName());
+ + "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@@ -217,56 +244,52 @@ public final class MapEntry<K, V> extends AbstractMessage {
public static class Builder<K, V>
extends AbstractMessage.Builder<Builder<K, V>> {
private final Metadata<K, V> metadata;
- private MapEntryLite<K, V> data;
- private MapEntryLite.Builder<K, V> dataBuilder;
-
+ private K key;
+ private V value;
+ private boolean hasKey;
+ private boolean hasValue;
+
private Builder(Metadata<K, V> metadata) {
- this.metadata = metadata;
- this.data = metadata.defaultInstance.data;
- this.dataBuilder = null;
+ this(metadata, metadata.defaultKey, metadata.defaultValue, false, false);
}
-
- private Builder(Metadata<K, V> metadata, MapEntryLite<K, V> data) {
+
+ private Builder(Metadata<K, V> metadata, K key, V value, boolean hasKey, boolean hasValue) {
this.metadata = metadata;
- this.data = data;
- this.dataBuilder = null;
+ this.key = key;
+ this.value = value;
+ this.hasKey = hasKey;
+ this.hasValue = hasValue;
}
-
+
public K getKey() {
- return dataBuilder == null ? data.getKey() : dataBuilder.getKey();
+ return key;
}
-
+
public V getValue() {
- return dataBuilder == null ? data.getValue() : dataBuilder.getValue();
- }
-
- private void ensureMutable() {
- if (dataBuilder == null) {
- dataBuilder = data.toBuilder();
- }
+ return value;
}
-
+
public Builder<K, V> setKey(K key) {
- ensureMutable();
- dataBuilder.setKey(key);
+ this.key = key;
+ this.hasKey = true;
return this;
}
-
+
public Builder<K, V> clearKey() {
- ensureMutable();
- dataBuilder.clearKey();
+ this.key = metadata.defaultKey;
+ this.hasKey = false;
return this;
}
-
+
public Builder<K, V> setValue(V value) {
- ensureMutable();
- dataBuilder.setValue(value);
+ this.value = value;
+ this.hasValue = true;
return this;
}
-
+
public Builder<K, V> clearValue() {
- ensureMutable();
- dataBuilder.clearValue();
+ this.value = metadata.defaultValue;
+ this.hasValue = false;
return this;
}
@@ -281,29 +304,24 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> buildPartial() {
- if (dataBuilder != null) {
- data = dataBuilder.buildPartial();
- dataBuilder = null;
- }
- return new MapEntry<K, V>(metadata, data);
+ return new MapEntry<K, V>(metadata, key, value);
}
@Override
public Descriptor getDescriptorForType() {
return metadata.descriptor;
}
-
+
private void checkFieldDescriptor(FieldDescriptor field) {
if (field.getContainingType() != metadata.descriptor) {
throw new RuntimeException(
"Wrong FieldDescriptor \"" + field.getFullName()
- + "\" used in message \"" + metadata.descriptor.getFullName());
+ + "\" used in message \"" + metadata.descriptor.getFullName());
}
}
@Override
- public com.google.protobuf.Message.Builder newBuilderForField(
- FieldDescriptor field) {
+ public Message.Builder newBuilderForField(FieldDescriptor field) {
checkFieldDescriptor(field);;
// This method should be called for message fields and in a MapEntry
// message only the value field can possibly be a message field.
@@ -312,7 +330,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"\"" + field.getFullName() + "\" is not a message value field.");
}
- return ((Message) data.getValue()).newBuilderForType();
+ return ((Message) value).newBuilderForType();
}
@SuppressWarnings("unchecked")
@@ -324,6 +342,15 @@ public final class MapEntry<K, V> extends AbstractMessage {
} else {
if (field.getType() == FieldDescriptor.Type.ENUM) {
value = ((EnumValueDescriptor) value).getNumber();
+ } else if (field.getType() == FieldDescriptor.Type.MESSAGE) {
+ if (value != null && !metadata.defaultValue.getClass().isInstance(value)) {
+ // The value is not the exact right message type. However, if it
+ // is an alternative implementation of the same type -- e.g. a
+ // DynamicMessage -- we should accept it. In this case we can make
+ // a copy of the message.
+ value =
+ ((Message) metadata.defaultValue).toBuilder().mergeFrom((Message) value).build();
+ }
}
setValue((V) value);
}
@@ -362,22 +389,17 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public MapEntry<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ return new MapEntry<K, V>(metadata, metadata.defaultKey, metadata.defaultValue);
}
@Override
public boolean isInitialized() {
- if (dataBuilder != null) {
- return dataBuilder.isInitialized();
- } else {
- return data.isInitialized();
- }
+ return MapEntry.isInitialized(metadata, value);
}
@Override
public Map<FieldDescriptor, Object> getAllFields() {
- final TreeMap<FieldDescriptor, Object> result =
- new TreeMap<FieldDescriptor, Object>();
+ final TreeMap<FieldDescriptor, Object> result = new TreeMap<FieldDescriptor, Object>();
for (final FieldDescriptor field : metadata.descriptor.getFields()) {
if (hasField(field)) {
result.put(field, getField(field));
@@ -389,7 +411,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
@Override
public boolean hasField(FieldDescriptor field) {
checkFieldDescriptor(field);
- return true;
+ return field.getNumber() == 1 ? hasKey : hasValue;
}
@Override
@@ -398,8 +420,7 @@ public final class MapEntry<K, V> extends AbstractMessage {
Object result = field.getNumber() == 1 ? getKey() : getValue();
// Convert enums to EnumValueDescriptor.
if (field.getType() == FieldDescriptor.Type.ENUM) {
- result = field.getEnumType().findValueByNumberCreatingIfUnknown(
- (java.lang.Integer) result);
+ result = field.getEnumType().findValueByNumberCreatingIfUnknown((Integer) result);
}
return result;
}
@@ -409,25 +430,34 @@ public final class MapEntry<K, V> extends AbstractMessage {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
-
+
@Override
public Object getRepeatedField(FieldDescriptor field, int index) {
throw new RuntimeException(
"There is no repeated field in a map entry message.");
}
-
+
@Override
public UnknownFieldSet getUnknownFields() {
return UnknownFieldSet.getDefaultInstance();
}
@Override
+ @SuppressWarnings("unchecked")
public Builder<K, V> clone() {
- if (dataBuilder == null) {
- return new Builder<K, V>(metadata, data);
- } else {
- return new Builder<K, V>(metadata, dataBuilder.build());
- }
+ return new Builder(metadata, key, value, hasKey, hasValue);
+ }
+ }
+
+ private static <V> boolean isInitialized(Metadata metadata, V value) {
+ if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
+ return ((MessageLite) value).isInitialized();
}
+ return true;
+ }
+
+ /** Returns the metadata only for experimental runtime. */
+ final Metadata<K, V> getMetadata() {
+ return metadata;
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
index bcffa946..dcb5dfad 100644
--- a/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapEntryLite.java
@@ -31,79 +31,74 @@
package com.google.protobuf;
import java.io.IOException;
+import java.util.AbstractMap;
+import java.util.Map;
/**
* Implements the lite version of map entry messages.
- *
+ *
* This class serves as an utility class to help do serialization/parsing of
* map entries. It's used in generated code and also in the full version
* MapEntry message.
- *
+ *
* Protobuf internal. Users shouldn't use.
*/
-public class MapEntryLite<K, V> extends AbstractMessageLite {
- private static class Metadata<K, V> {
- public final MapEntryLite<K, V> defaultInstance;
+public class MapEntryLite<K, V> {
+
+ static class Metadata<K, V> {
public final WireFormat.FieldType keyType;
+ public final K defaultKey;
public final WireFormat.FieldType valueType;
- public final Parser<MapEntryLite<K, V>> parser;
+ public final V defaultValue;
+
public Metadata(
- MapEntryLite<K, V> defaultInstance,
- WireFormat.FieldType keyType,
- WireFormat.FieldType valueType) {
- this.defaultInstance = defaultInstance;
+ WireFormat.FieldType keyType, K defaultKey,
+ WireFormat.FieldType valueType, V defaultValue) {
this.keyType = keyType;
+ this.defaultKey = defaultKey;
this.valueType = valueType;
- final Metadata<K, V> finalThis = this;
- this.parser = new AbstractParser<MapEntryLite<K, V>>() {
- @Override
- public MapEntryLite<K, V> parsePartialFrom(
- CodedInputStream input, ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
- return new MapEntryLite<K, V>(finalThis, input, extensionRegistry);
- }
- };
+ this.defaultValue = defaultValue;
}
}
-
+
private static final int KEY_FIELD_NUMBER = 1;
private static final int VALUE_FIELD_NUMBER = 2;
-
+
private final Metadata<K, V> metadata;
private final K key;
private final V value;
-
+
/** Creates a default MapEntryLite message instance. */
private MapEntryLite(
WireFormat.FieldType keyType, K defaultKey,
WireFormat.FieldType valueType, V defaultValue) {
- this.metadata = new Metadata<K, V>(this, keyType, valueType);
+ this.metadata = new Metadata<K, V>(keyType, defaultKey, valueType, defaultValue);
this.key = defaultKey;
this.value = defaultValue;
}
-
+
/** Creates a new MapEntryLite message. */
private MapEntryLite(Metadata<K, V> metadata, K key, V value) {
this.metadata = metadata;
this.key = key;
this.value = value;
}
-
+
public K getKey() {
return key;
}
-
+
public V getValue() {
return value;
}
/**
* Creates a default MapEntryLite message instance.
- *
+ *
* This method is used by generated code to create the default instance for
* a map entry message. The created default instance should be used to create
* new map entry messages of the same type. For each map entry message, only
- * one default instance should be created.
+ * one default instance should be created.
*/
public static <K, V> MapEntryLite<K, V> newDefaultInstance(
WireFormat.FieldType keyType, K defaultKey,
@@ -111,80 +106,20 @@ public class MapEntryLite<K, V> extends AbstractMessageLite {
return new MapEntryLite<K, V>(
keyType, defaultKey, valueType, defaultValue);
}
-
- @Override
- public void writeTo(CodedOutputStream output) throws IOException {
- writeField(KEY_FIELD_NUMBER, metadata.keyType, key, output);
- writeField(VALUE_FIELD_NUMBER, metadata.valueType, value, output);
- }
- private void writeField(
- int number, WireFormat.FieldType type, Object value,
- CodedOutputStream output) throws IOException {
- output.writeTag(number, type.getWireType());
- FieldSet.writeElementNoTag(output, type, value);
+ static <K, V> void writeTo(CodedOutputStream output, Metadata<K, V> metadata, K key, V value)
+ throws IOException {
+ FieldSet.writeElement(output, metadata.keyType, KEY_FIELD_NUMBER, key);
+ FieldSet.writeElement(output, metadata.valueType, VALUE_FIELD_NUMBER, value);
}
- private volatile int cachedSerializedSize = -1;
- @Override
- public int getSerializedSize() {
- if (cachedSerializedSize != -1) {
- return cachedSerializedSize;
- }
- int size = 0;
- size += getFieldSize(KEY_FIELD_NUMBER, metadata.keyType, key);
- size += getFieldSize(VALUE_FIELD_NUMBER, metadata.valueType, value);
- cachedSerializedSize = size;
- return size;
+ static <K, V> int computeSerializedSize(Metadata<K, V> metadata, K key, V value) {
+ return FieldSet.computeElementSize(metadata.keyType, KEY_FIELD_NUMBER, key)
+ + FieldSet.computeElementSize(metadata.valueType, VALUE_FIELD_NUMBER, value);
}
- private int getFieldSize(
- int number, WireFormat.FieldType type, Object value) {
- return CodedOutputStream.computeTagSize(number)
- + FieldSet.computeElementSizeNoTag(type, value);
- }
-
- /** Parsing constructor. */
- private MapEntryLite(
- Metadata<K, V> metadata,
- CodedInputStream input,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException {
- try {
- K key = metadata.defaultInstance.key;
- V value = metadata.defaultInstance.value;
- while (true) {
- int tag = input.readTag();
- if (tag == 0) {
- break;
- }
- if (tag == WireFormat.makeTag(
- KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
- key = mergeField(
- input, extensionRegistry, metadata.keyType, key);
- } else if (tag == WireFormat.makeTag(
- VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
- value = mergeField(
- input, extensionRegistry, metadata.valueType, value);
- } else {
- if (!input.skipField(tag)) {
- break;
- }
- }
- }
- this.metadata = metadata;
- this.key = key;
- this.value = value;
- } catch (InvalidProtocolBufferException e) {
- throw e.setUnfinishedMessage(this);
- } catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
- .setUnfinishedMessage(this);
- }
- }
-
@SuppressWarnings("unchecked")
- private <T> T mergeField(
+ static <T> T parseField(
CodedInputStream input, ExtensionRegistryLite extensionRegistry,
WireFormat.FieldType type, T value) throws IOException {
switch (type) {
@@ -201,131 +136,96 @@ public class MapEntryLite<K, V> extends AbstractMessageLite {
}
}
- @Override
- public Parser<MapEntryLite<K, V>> getParserForType() {
- return metadata.parser;
- }
-
- @Override
- public Builder<K, V> newBuilderForType() {
- return new Builder<K, V>(metadata);
- }
-
- @Override
- public Builder<K, V> toBuilder() {
- return new Builder<K, V>(metadata, key, value);
+ /**
+ * Serializes the provided key and value as though they were wrapped by a {@link MapEntryLite}
+ * to the output stream. This helper method avoids allocation of a {@link MapEntryLite}
+ * built with a key and value and is called from generated code directly.
+ */
+ public void serializeTo(CodedOutputStream output, int fieldNumber, K key, V value)
+ throws IOException {
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeUInt32NoTag(computeSerializedSize(metadata, key, value));
+ writeTo(output, metadata, key, value);
}
- @Override
- public MapEntryLite<K, V> getDefaultInstanceForType() {
- return metadata.defaultInstance;
+ /**
+ * Computes the message size for the provided key and value as though they were wrapped
+ * by a {@link MapEntryLite}. This helper method avoids allocation of a {@link MapEntryLite}
+ * built with a key and value and is called from generated code directly.
+ */
+ public int computeMessageSize(int fieldNumber, K key, V value) {
+ return CodedOutputStream.computeTagSize(fieldNumber)
+ + CodedOutputStream.computeLengthDelimitedFieldSize(
+ computeSerializedSize(metadata, key, value));
}
- @Override
- public boolean isInitialized() {
- if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
- return ((MessageLite) value).isInitialized();
+ /**
+ * Parses an entry off of the input as a {@link Map.Entry}. This helper requires an allocation
+ * so using {@link #parseInto} is preferred if possible.
+ */
+ public Map.Entry<K, V> parseEntry(ByteString bytes, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ return parseEntry(bytes.newCodedInput(), metadata, extensionRegistry);
+ }
+
+ static <K, V> Map.Entry<K, V> parseEntry(
+ CodedInputStream input, Metadata<K, V> metadata, ExtensionRegistryLite extensionRegistry)
+ throws IOException{
+ K key = metadata.defaultKey;
+ V value = metadata.defaultValue;
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+ if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
+ key = parseField(input, extensionRegistry, metadata.keyType, key);
+ } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
+ value = parseField(input, extensionRegistry, metadata.valueType, value);
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
+ }
}
- return true;
+ return new AbstractMap.SimpleImmutableEntry<K, V>(key, value);
}
/**
- * Builder used to create {@link MapEntryLite} messages.
+ * Parses an entry off of the input into the map. This helper avoids allocaton of a
+ * {@link MapEntryLite} by parsing directly into the provided {@link MapFieldLite}.
*/
- public static class Builder<K, V>
- extends AbstractMessageLite.Builder<Builder<K, V>> {
- private final Metadata<K, V> metadata;
- private K key;
- private V value;
-
- private Builder(Metadata<K, V> metadata) {
- this.metadata = metadata;
- this.key = metadata.defaultInstance.key;
- this.value = metadata.defaultInstance.value;
- }
-
- public K getKey() {
- return key;
- }
-
- public V getValue() {
- return value;
- }
-
- public Builder<K, V> setKey(K key) {
- this.key = key;
- return this;
- }
-
- public Builder<K, V> setValue(V value) {
- this.value = value;
- return this;
- }
-
- public Builder<K, V> clearKey() {
- this.key = metadata.defaultInstance.key;
- return this;
- }
-
- public Builder<K, V> clearValue() {
- this.value = metadata.defaultInstance.value;
- return this;
- }
-
- @Override
- public Builder<K, V> clear() {
- this.key = metadata.defaultInstance.key;
- this.value = metadata.defaultInstance.value;
- return this;
- }
-
- @Override
- public MapEntryLite<K, V> build() {
- MapEntryLite<K, V> result = buildPartial();
- if (!result.isInitialized()) {
- throw newUninitializedMessageException(result);
+ public void parseInto(
+ MapFieldLite<K, V> map, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
+ int length = input.readRawVarint32();
+ final int oldLimit = input.pushLimit(length);
+ K key = metadata.defaultKey;
+ V value = metadata.defaultValue;
+
+ while (true) {
+ int tag = input.readTag();
+ if (tag == 0) {
+ break;
}
- return result;
- }
-
- @Override
- public MapEntryLite<K, V> buildPartial() {
- return new MapEntryLite<K, V>(metadata, key, value);
- }
-
- @Override
- public MessageLite getDefaultInstanceForType() {
- return metadata.defaultInstance;
- }
-
- @Override
- public boolean isInitialized() {
- if (metadata.valueType.getJavaType() == WireFormat.JavaType.MESSAGE) {
- return ((MessageLite) value).isInitialized();
+ if (tag == WireFormat.makeTag(KEY_FIELD_NUMBER, metadata.keyType.getWireType())) {
+ key = parseField(input, extensionRegistry, metadata.keyType, key);
+ } else if (tag == WireFormat.makeTag(VALUE_FIELD_NUMBER, metadata.valueType.getWireType())) {
+ value = parseField(input, extensionRegistry, metadata.valueType, value);
+ } else {
+ if (!input.skipField(tag)) {
+ break;
+ }
}
- return true;
}
- private Builder(Metadata<K, V> metadata, K key, V value) {
- this.metadata = metadata;
- this.key = key;
- this.value = value;
- }
-
- @Override
- public Builder<K, V> clone() {
- return new Builder<K, V>(metadata, key, value);
- }
+ input.checkLastTagWas(0);
+ input.popLimit(oldLimit);
+ map.put(key, value);
+ }
- @Override
- public Builder<K, V> mergeFrom(
- CodedInputStream input, ExtensionRegistryLite extensionRegistry)
- throws IOException {
- MapEntryLite<K, V> entry =
- new MapEntryLite<K, V>(metadata, input, extensionRegistry);
- this.key = entry.key;
- this.value = entry.value;
- return this;
- }
+ /** For experimental runtime internal use only. */
+ Metadata<K, V> getMetadata() {
+ return metadata;
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/MapField.java b/java/core/src/main/java/com/google/protobuf/MapField.java
index b290993c..ad8ceb02 100644
--- a/java/core/src/main/java/com/google/protobuf/MapField.java
+++ b/java/core/src/main/java/com/google/protobuf/MapField.java
@@ -30,25 +30,28 @@
package com.google.protobuf;
-import com.google.protobuf.MapFieldLite.MutatabilityAwareMap;
+import static com.google.protobuf.Internal.checkNotNull;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Internal representation of map fields in generated messages.
- *
+ *
* This class supports accessing the map field as a {@link Map} to be used in
* generated API and also supports accessing the field as a {@link List} to be
* used in reflection API. It keeps track of where the data is currently stored
- * and do necessary conversions between map and list.
- *
+ * and do necessary conversions between map and list.
+ *
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
- *
+ *
* THREAD-SAFETY NOTE: Read-only access is thread-safe. Users can call getMap()
* and getList() concurrently in multiple threads. If write-access is needed,
* all access must be synchronized.
@@ -56,21 +59,21 @@ import java.util.Map;
public class MapField<K, V> implements MutabilityOracle {
/**
* Indicates where the data of this map field is currently stored.
- *
+ *
* MAP: Data is stored in mapData.
* LIST: Data is stored in listData.
* BOTH: mapData and listData have the same data.
*
* When the map field is accessed (through generated API or reflection API),
* it will shift between these 3 modes:
- *
+ *
* getMap() getList() getMutableMap() getMutableList()
* MAP MAP BOTH MAP LIST
* LIST BOTH LIST MAP LIST
* BOTH BOTH BOTH MAP LIST
- *
+ *
* As the map field changes its mode, the list/map reference returned in a
- * previous method call may be invalidated.
+ * previous method call may be invalidated.
*/
private enum StorageMode {MAP, LIST, BOTH}
@@ -78,38 +81,42 @@ public class MapField<K, V> implements MutabilityOracle {
private volatile StorageMode mode;
private MutatabilityAwareMap<K, V> mapData;
private List<Message> listData;
-
+
// Convert between a map entry Message and a key-value pair.
private static interface Converter<K, V> {
Message convertKeyAndValueToMessage(K key, V value);
void convertMessageToKeyAndValue(Message message, Map<K, V> map);
-
+
Message getMessageDefaultInstance();
}
-
+
private static class ImmutableMessageConverter<K, V> implements Converter<K, V> {
private final MapEntry<K, V> defaultEntry;
public ImmutableMessageConverter(MapEntry<K, V> defaultEntry) {
this.defaultEntry = defaultEntry;
}
-
+
+ @Override
public Message convertKeyAndValueToMessage(K key, V value) {
return defaultEntry.newBuilderForType().setKey(key).setValue(value).buildPartial();
}
-
+
+ @Override
+ @SuppressWarnings("unchecked")
public void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
MapEntry<K, V> entry = (MapEntry<K, V>) message;
map.put(entry.getKey(), entry.getValue());
}
+ @Override
public Message getMessageDefaultInstance() {
return defaultEntry;
}
}
-
+
private final Converter<K, V> converter;
-
+
private MapField(
Converter<K, V> converter,
StorageMode mode,
@@ -120,34 +127,34 @@ public class MapField<K, V> implements MutabilityOracle {
this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
this.listData = null;
}
-
+
private MapField(
MapEntry<K, V> defaultEntry,
StorageMode mode,
Map<K, V> mapData) {
this(new ImmutableMessageConverter<K, V>(defaultEntry), mode, mapData);
}
-
-
+
+
/** Returns an immutable empty MapField. */
public static <K, V> MapField<K, V> emptyMapField(
MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
defaultEntry, StorageMode.MAP, Collections.<K, V>emptyMap());
}
-
-
+
+
/** Creates a new mutable empty MapField. */
public static <K, V> MapField<K, V> newMapField(MapEntry<K, V> defaultEntry) {
return new MapField<K, V>(
defaultEntry, StorageMode.MAP, new LinkedHashMap<K, V>());
}
-
-
+
+
private Message convertKeyAndValueToMessage(K key, V value) {
return converter.convertKeyAndValueToMessage(key, value);
}
-
+
@SuppressWarnings("unchecked")
private void convertMessageToKeyAndValue(Message message, Map<K, V> map) {
converter.convertMessageToKeyAndValue(message, map);
@@ -170,7 +177,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return new MutatabilityAwareMap<K, V>(this, mapData);
}
-
+
/** Returns the content of this MapField as a read-only Map. */
public Map<K, V> getMap() {
if (mode == StorageMode.LIST) {
@@ -183,7 +190,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableMap(mapData);
}
-
+
/** Gets a mutable Map view of this MapField. */
public Map<K, V> getMutableMap() {
if (mode != StorageMode.MAP) {
@@ -191,20 +198,20 @@ public class MapField<K, V> implements MutabilityOracle {
mapData = convertListToMap(listData);
}
listData = null;
- mode = StorageMode.MAP;
+ mode = StorageMode.MAP;
}
return mapData;
}
-
+
public void mergeFrom(MapField<K, V> other) {
getMutableMap().putAll(MapFieldLite.copy(other.getMap()));
}
-
+
public void clear() {
mapData = new MutatabilityAwareMap<K, V>(this, new LinkedHashMap<K, V>());
mode = StorageMode.MAP;
}
-
+
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
@@ -214,18 +221,18 @@ public class MapField<K, V> implements MutabilityOracle {
MapField<K, V> other = (MapField<K, V>) object;
return MapFieldLite.<K, V>equals(getMap(), other.getMap());
}
-
+
@Override
public int hashCode() {
return MapFieldLite.<K, V>calculateHashCodeForMap(getMap());
}
-
+
/** Returns a deep copy of this MapField. */
public MapField<K, V> copy() {
return new MapField<K, V>(
converter, StorageMode.MAP, MapFieldLite.copy(getMap()));
}
-
+
/** Gets the content of this MapField as a read-only List. */
List<Message> getList() {
if (mode == StorageMode.MAP) {
@@ -238,7 +245,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return Collections.unmodifiableList(listData);
}
-
+
/** Gets a mutable List view of this MapField. */
List<Message> getMutableList() {
if (mode != StorageMode.LIST) {
@@ -250,7 +257,7 @@ public class MapField<K, V> implements MutabilityOracle {
}
return listData;
}
-
+
/**
* Gets the default instance of the message stored in the list view of this
* map field.
@@ -258,7 +265,7 @@ public class MapField<K, V> implements MutabilityOracle {
Message getMapEntryMessageDefaultInstance() {
return converter.getMessageDefaultInstance();
}
-
+
/**
* Makes this list immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@@ -266,14 +273,14 @@ public class MapField<K, V> implements MutabilityOracle {
public void makeImmutable() {
isMutable = false;
}
-
+
/**
* Returns whether this field can be modified.
*/
public boolean isMutable() {
return isMutable;
}
-
+
/* (non-Javadoc)
* @see com.google.protobuf.MutabilityOracle#ensureMutable()
*/
@@ -283,4 +290,344 @@ public class MapField<K, V> implements MutabilityOracle {
throw new UnsupportedOperationException();
}
}
+
+ /**
+ * An internal map that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareMap<K, V> implements Map<K, V> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Map<K, V> delegate;
+
+ MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return delegate.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ mutabilityOracle.ensureMutable();
+ checkNotNull(key);
+ checkNotNull(value);
+ return delegate.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ mutabilityOracle.ensureMutable();
+ for (K key : m.keySet()) {
+ checkNotNull(key);
+ checkNotNull(m.get(key));
+ }
+ delegate.putAll(m);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet());
+ }
+
+ @Override
+ public Collection<V> values() {
+ return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values());
+ }
+
+ @Override
+ public Set<java.util.Map.Entry<K, V>> entrySet() {
+ return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+
+ /**
+ * An internal collection that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareCollection<E> implements Collection<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Collection<E> delegate;
+
+ MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return delegate.contains(o);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
+ }
+
+ @Override
+ public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return delegate.toArray(a);
+ }
+
+ @Override
+ public boolean add(E e) {
+ // Unsupported operation in the delegate.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return delegate.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ // Unsupported operation in the delegate.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.removeAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.retainAll(c);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+
+ /**
+ * An internal set that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareSet<E> implements Set<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Set<E> delegate;
+
+ MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public int size() {
+ return delegate.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return delegate.contains(o);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
+ }
+
+ @Override
+ public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ @Override
+ public <T> T[] toArray(T[] a) {
+ return delegate.toArray(a);
+ }
+
+ @Override
+ public boolean add(E e) {
+ mutabilityOracle.ensureMutable();
+ return delegate.add(e);
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ mutabilityOracle.ensureMutable();
+ return delegate.remove(o);
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> c) {
+ return delegate.containsAll(c);
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends E> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.addAll(c);
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.retainAll(c);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ mutabilityOracle.ensureMutable();
+ return delegate.removeAll(c);
+ }
+
+ @Override
+ public void clear() {
+ mutabilityOracle.ensureMutable();
+ delegate.clear();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return delegate.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+
+ /**
+ * An internal iterator that checks for mutability before delegating.
+ */
+ private static class MutatabilityAwareIterator<E> implements Iterator<E> {
+ private final MutabilityOracle mutabilityOracle;
+ private final Iterator<E> delegate;
+
+ MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) {
+ this.mutabilityOracle = mutabilityOracle;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ @Override
+ public E next() {
+ return delegate.next();
+ }
+
+ @Override
+ public void remove() {
+ mutabilityOracle.ensureMutable();
+ delegate.remove();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return delegate.toString();
+ }
+ }
+ }
}
diff --git a/java/core/src/main/java/com/google/protobuf/MapFieldLite.java b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
index 960b6339..a8b3dd88 100644
--- a/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MapFieldLite.java
@@ -30,74 +30,100 @@
package com.google.protobuf;
-import com.google.protobuf.Internal.EnumLite;
+import static com.google.protobuf.Internal.checkNotNull;
+import com.google.protobuf.Internal.EnumLite;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Internal representation of map fields in generated lite-runtime messages.
- *
+ *
* This class is a protobuf implementation detail. Users shouldn't use this
* class directly.
*/
-public final class MapFieldLite<K, V> implements MutabilityOracle {
- private MutatabilityAwareMap<K, V> mapData;
+public final class MapFieldLite<K, V> extends LinkedHashMap<K, V> {
+
private boolean isMutable;
-
+
+ private MapFieldLite() {
+ this.isMutable = true;
+ }
+
private MapFieldLite(Map<K, V> mapData) {
- this.mapData = new MutatabilityAwareMap<K, V>(this, mapData);
+ super(mapData);
this.isMutable = true;
}
-
+
@SuppressWarnings({"rawtypes", "unchecked"})
- private static final MapFieldLite EMPTY_MAP_FIELD =
- new MapFieldLite(Collections.emptyMap());
+ private static final MapFieldLite EMPTY_MAP_FIELD = new MapFieldLite();
static {
EMPTY_MAP_FIELD.makeImmutable();
}
-
+
/** Returns an singleton immutable empty MapFieldLite instance. */
@SuppressWarnings({"unchecked", "cast"})
public static <K, V> MapFieldLite<K, V> emptyMapField() {
return (MapFieldLite<K, V>) EMPTY_MAP_FIELD;
}
-
- /** Creates a new MapFieldLite instance. */
- public static <K, V> MapFieldLite<K, V> newMapField() {
- return new MapFieldLite<K, V>(new LinkedHashMap<K, V>());
+
+ public void mergeFrom(MapFieldLite<K, V> other) {
+ ensureMutable();
+ if (!other.isEmpty()) {
+ putAll(other);
+ }
}
-
- /** Gets the content of this MapField as a read-only Map. */
- public Map<K, V> getMap() {
- return Collections.unmodifiableMap(mapData);
+
+ @SuppressWarnings({"unchecked", "cast"})
+ @Override public Set<Map.Entry<K, V>> entrySet() {
+ return isEmpty() ? Collections.<Map.Entry<K, V>>emptySet() : super.entrySet();
}
-
- /** Gets a mutable Map view of this MapField. */
- public Map<K, V> getMutableMap() {
- return mapData;
+
+ @Override public void clear() {
+ ensureMutable();
+ super.clear();
}
-
- public void mergeFrom(MapFieldLite<K, V> other) {
- mapData.putAll(copy(other.mapData));
+
+ @Override public V put(K key, V value) {
+ ensureMutable();
+ checkNotNull(key);
+
+ checkNotNull(value);
+ return super.put(key, value);
+ }
+
+ public V put(Map.Entry<K, V> entry) {
+ return put(entry.getKey(), entry.getValue());
+ }
+
+ @Override public void putAll(Map<? extends K, ? extends V> m) {
+ ensureMutable();
+ checkForNullKeysAndValues(m);
+ super.putAll(m);
+ }
+
+ @Override public V remove(Object key) {
+ ensureMutable();
+ return super.remove(key);
}
-
- public void clear() {
- mapData.clear();
+
+ private static void checkForNullKeysAndValues(Map<?, ?> m) {
+ for (Object key : m.keySet()) {
+ checkNotNull(key);
+ checkNotNull(m.get(key));
+ }
}
-
+
private static boolean equals(Object a, Object b) {
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[]) a, (byte[]) b);
}
return a.equals(b);
}
-
+
/**
* Checks whether two {@link Map}s are equal. We don't use the default equals
* method of {@link Map} because it compares by identity not by content for
@@ -120,20 +146,16 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return true;
}
-
+
/**
* Checks whether two map fields are equal.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
- if (!(object instanceof MapFieldLite)) {
- return false;
- }
- MapFieldLite<K, V> other = (MapFieldLite<K, V>) object;
- return equals(mapData, other.mapData);
+ return (object instanceof Map) && equals(this, (Map<K, V>) object);
}
-
+
private static int calculateHashCodeForObject(Object a) {
if (a instanceof byte[]) {
return Internal.hashCode((byte[]) a);
@@ -156,14 +178,14 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
result += calculateHashCodeForObject(entry.getKey())
^ calculateHashCodeForObject(entry.getValue());
}
- return result;
+ return result;
}
-
+
@Override
public int hashCode() {
- return calculateHashCodeForMap(mapData);
+ return calculateHashCodeForMap(this);
}
-
+
private static Object copy(Object object) {
if (object instanceof byte[]) {
byte[] data = (byte[]) object;
@@ -171,7 +193,7 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return object;
}
-
+
/**
* Makes a deep copy of a {@link Map}. Immutable objects in the map will be
* shared (e.g., integers, strings, immutable messages) and mutable ones will
@@ -185,12 +207,12 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
}
return result;
}
-
+
/** Returns a deep copy of this map field. */
- public MapFieldLite<K, V> copy() {
- return new MapFieldLite<K, V>(copy(mapData));
+ public MapFieldLite<K, V> mutableCopy() {
+ return isEmpty() ? new MapFieldLite<K, V>() : new MapFieldLite<K, V>(this);
}
-
+
/**
* Makes this field immutable. All subsequent modifications will throw an
* {@link UnsupportedOperationException}.
@@ -198,352 +220,17 @@ public final class MapFieldLite<K, V> implements MutabilityOracle {
public void makeImmutable() {
isMutable = false;
}
-
+
/**
* Returns whether this field can be modified.
*/
public boolean isMutable() {
return isMutable;
}
-
- @Override
- public void ensureMutable() {
- if (!isMutable()) {
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * An internal map that checks for mutability before delegating.
- */
- static class MutatabilityAwareMap<K, V> implements Map<K, V> {
- private final MutabilityOracle mutabilityOracle;
- private final Map<K, V> delegate;
-
- MutatabilityAwareMap(MutabilityOracle mutabilityOracle, Map<K, V> delegate) {
- this.mutabilityOracle = mutabilityOracle;
- this.delegate = delegate;
- }
-
- @Override
- public int size() {
- return delegate.size();
- }
-
- @Override
- public boolean isEmpty() {
- return delegate.isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key) {
- return delegate.containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value) {
- return delegate.containsValue(value);
- }
-
- @Override
- public V get(Object key) {
- return delegate.get(key);
- }
-
- @Override
- public V put(K key, V value) {
- mutabilityOracle.ensureMutable();
- return delegate.put(key, value);
- }
-
- @Override
- public V remove(Object key) {
- mutabilityOracle.ensureMutable();
- return delegate.remove(key);
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- mutabilityOracle.ensureMutable();
- delegate.putAll(m);
- }
-
- @Override
- public void clear() {
- mutabilityOracle.ensureMutable();
- delegate.clear();
- }
-
- @Override
- public Set<K> keySet() {
- return new MutatabilityAwareSet<K>(mutabilityOracle, delegate.keySet());
- }
-
- @Override
- public Collection<V> values() {
- return new MutatabilityAwareCollection<V>(mutabilityOracle, delegate.values());
- }
-
- @Override
- public Set<java.util.Map.Entry<K, V>> entrySet() {
- return new MutatabilityAwareSet<Entry<K, V>>(mutabilityOracle, delegate.entrySet());
- }
-
- @Override
- public boolean equals(Object o) {
- return delegate.equals(o);
- }
-
- @Override
- public int hashCode() {
- return delegate.hashCode();
- }
-
- @Override
- public String toString() {
- return delegate.toString();
- }
- }
-
- /**
- * An internal collection that checks for mutability before delegating.
- */
- private static class MutatabilityAwareCollection<E> implements Collection<E> {
- private final MutabilityOracle mutabilityOracle;
- private final Collection<E> delegate;
-
- MutatabilityAwareCollection(MutabilityOracle mutabilityOracle, Collection<E> delegate) {
- this.mutabilityOracle = mutabilityOracle;
- this.delegate = delegate;
- }
- @Override
- public int size() {
- return delegate.size();
- }
-
- @Override
- public boolean isEmpty() {
- return delegate.isEmpty();
- }
-
- @Override
- public boolean contains(Object o) {
- return delegate.contains(o);
- }
-
- @Override
- public Iterator<E> iterator() {
- return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
- }
-
- @Override
- public Object[] toArray() {
- return delegate.toArray();
- }
-
- @Override
- public <T> T[] toArray(T[] a) {
- return delegate.toArray(a);
- }
-
- @Override
- public boolean add(E e) {
- // Unsupported operation in the delegate.
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean remove(Object o) {
- mutabilityOracle.ensureMutable();
- return delegate.remove(o);
- }
-
- @Override
- public boolean containsAll(Collection<?> c) {
- return delegate.containsAll(c);
- }
-
- @Override
- public boolean addAll(Collection<? extends E> c) {
- // Unsupported operation in the delegate.
+ private void ensureMutable() {
+ if (!isMutable()) {
throw new UnsupportedOperationException();
}
-
- @Override
- public boolean removeAll(Collection<?> c) {
- mutabilityOracle.ensureMutable();
- return delegate.removeAll(c);
- }
-
- @Override
- public boolean retainAll(Collection<?> c) {
- mutabilityOracle.ensureMutable();
- return delegate.retainAll(c);
- }
-
- @Override
- public void clear() {
- mutabilityOracle.ensureMutable();
- delegate.clear();
- }
-
- @Override
- public boolean equals(Object o) {
- return delegate.equals(o);
- }
-
- @Override
- public int hashCode() {
- return delegate.hashCode();
- }
-
- @Override
- public String toString() {
- return delegate.toString();
- }
- }
-
- /**
- * An internal set that checks for mutability before delegating.
- */
- private static class MutatabilityAwareSet<E> implements Set<E> {
- private final MutabilityOracle mutabilityOracle;
- private final Set<E> delegate;
-
- MutatabilityAwareSet(MutabilityOracle mutabilityOracle, Set<E> delegate) {
- this.mutabilityOracle = mutabilityOracle;
- this.delegate = delegate;
- }
-
- @Override
- public int size() {
- return delegate.size();
- }
-
- @Override
- public boolean isEmpty() {
- return delegate.isEmpty();
- }
-
- @Override
- public boolean contains(Object o) {
- return delegate.contains(o);
- }
-
- @Override
- public Iterator<E> iterator() {
- return new MutatabilityAwareIterator<E>(mutabilityOracle, delegate.iterator());
- }
-
- @Override
- public Object[] toArray() {
- return delegate.toArray();
- }
-
- @Override
- public <T> T[] toArray(T[] a) {
- return delegate.toArray(a);
- }
-
- @Override
- public boolean add(E e) {
- mutabilityOracle.ensureMutable();
- return delegate.add(e);
- }
-
- @Override
- public boolean remove(Object o) {
- mutabilityOracle.ensureMutable();
- return delegate.remove(o);
- }
-
- @Override
- public boolean containsAll(Collection<?> c) {
- return delegate.containsAll(c);
- }
-
- @Override
- public boolean addAll(Collection<? extends E> c) {
- mutabilityOracle.ensureMutable();
- return delegate.addAll(c);
- }
-
- @Override
- public boolean retainAll(Collection<?> c) {
- mutabilityOracle.ensureMutable();
- return delegate.retainAll(c);
- }
-
- @Override
- public boolean removeAll(Collection<?> c) {
- mutabilityOracle.ensureMutable();
- return delegate.removeAll(c);
- }
-
- @Override
- public void clear() {
- mutabilityOracle.ensureMutable();
- delegate.clear();
- }
-
- @Override
- public boolean equals(Object o) {
- return delegate.equals(o);
- }
-
- @Override
- public int hashCode() {
- return delegate.hashCode();
- }
-
- @Override
- public String toString() {
- return delegate.toString();
- }
- }
-
- /**
- * An internal iterator that checks for mutability before delegating.
- */
- private static class MutatabilityAwareIterator<E> implements Iterator<E> {
- private final MutabilityOracle mutabilityOracle;
- private final Iterator<E> delegate;
-
- MutatabilityAwareIterator(MutabilityOracle mutabilityOracle, Iterator<E> delegate) {
- this.mutabilityOracle = mutabilityOracle;
- this.delegate = delegate;
- }
-
- @Override
- public boolean hasNext() {
- return delegate.hasNext();
- }
-
- @Override
- public E next() {
- return delegate.next();
- }
-
- @Override
- public void remove() {
- mutabilityOracle.ensureMutable();
- delegate.remove();
- }
-
- @Override
- public boolean equals(Object obj) {
- return delegate.equals(obj);
- }
-
- @Override
- public int hashCode() {
- return delegate.hashCode();
- }
-
- @Override
- public String toString() {
- return delegate.toString();
- }
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/Message.java b/java/core/src/main/java/com/google/protobuf/Message.java
index 9516d71f..0770d417 100644
--- a/java/core/src/main/java/com/google/protobuf/Message.java
+++ b/java/core/src/main/java/com/google/protobuf/Message.java
@@ -51,6 +51,7 @@ import java.util.Map;
public interface Message extends MessageLite, MessageOrBuilder {
// (From MessageLite, re-declared here only for return type covariance.)
+ @Override
Parser<? extends Message> getParserForType();
@@ -97,7 +98,10 @@ public interface Message extends MessageLite, MessageOrBuilder {
// Builders
// (From MessageLite, re-declared here only for return type covariance.)
+ @Override
Builder newBuilderForType();
+
+ @Override
Builder toBuilder();
/**
@@ -106,6 +110,7 @@ public interface Message extends MessageLite, MessageOrBuilder {
interface Builder extends MessageLite.Builder, MessageOrBuilder {
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
+ @Override
Builder clear();
/**
@@ -120,7 +125,7 @@ public interface Message extends MessageLite, MessageOrBuilder {
* it is merged into the corresponding sub-message of this message
* using the same merging rules.<br>
* * For repeated fields, the elements in {@code other} are concatenated
- * with the elements in this message.
+ * with the elements in this message.<br>
* * For oneof groups, if the other message has one of the fields set,
* the group of this message is cleared and replaced by the field
* of the other message, so that the oneof constraint is preserved.
@@ -131,18 +136,27 @@ public interface Message extends MessageLite, MessageOrBuilder {
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
+ @Override
Message build();
+
+ @Override
Message buildPartial();
+
+ @Override
Builder clone();
+
+ @Override
Builder mergeFrom(CodedInputStream input) throws IOException;
- Builder mergeFrom(CodedInputStream input,
- ExtensionRegistryLite extensionRegistry)
- throws IOException;
+
+ @Override
+ Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException;
/**
* Get the message's type's descriptor.
* See {@link Message#getDescriptorForType()}.
*/
+ @Override
Descriptors.Descriptor getDescriptorForType();
/**
@@ -240,27 +254,39 @@ public interface Message extends MessageLite, MessageOrBuilder {
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
+ @Override
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
- Builder mergeFrom(ByteString data,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException;
+
+ @Override
+ Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException;
+
+ @Override
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
- Builder mergeFrom(byte[] data, int off, int len)
- throws InvalidProtocolBufferException;
- Builder mergeFrom(byte[] data,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException;
- Builder mergeFrom(byte[] data, int off, int len,
- ExtensionRegistryLite extensionRegistry)
- throws InvalidProtocolBufferException;
+
+ @Override
+ Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException;
+
+ @Override
+ Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException;
+
+ @Override
+ Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException;
+
+ @Override
Builder mergeFrom(InputStream input) throws IOException;
- Builder mergeFrom(InputStream input,
- ExtensionRegistryLite extensionRegistry)
- throws IOException;
- boolean mergeDelimitedFrom(InputStream input)
- throws IOException;
- boolean mergeDelimitedFrom(InputStream input,
- ExtensionRegistryLite extensionRegistry)
- throws IOException;
+
+ @Override
+ Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException;
+
+ @Override
+ boolean mergeDelimitedFrom(InputStream input) throws IOException;
+
+ @Override
+ boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException;
}
}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageLite.java b/java/core/src/main/java/com/google/protobuf/MessageLite.java
index 798b7943..88f531df 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageLite.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLite.java
@@ -295,6 +295,27 @@ public interface MessageLite extends MessageLiteOrBuilder {
Builder mergeFrom(InputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
+
+ /**
+ * Merge {@code other} into the message being built. {@code other} must
+ * have the exact same type as {@code this} (i.e.
+ * {@code getClass().equals(getDefaultInstanceForType().getClass())}).
+ *
+ * Merging occurs as follows. For each field:<br>
+ * * For singular primitive fields, if the field is set in {@code other},
+ * then {@code other}'s value overwrites the value in this message.<br>
+ * * For singular message fields, if the field is set in {@code other},
+ * it is merged into the corresponding sub-message of this message
+ * using the same merging rules.<br>
+ * * For repeated fields, the elements in {@code other} are concatenated
+ * with the elements in this message.
+ * * For oneof groups, if the other message has one of the fields set,
+ * the group of this message is cleared and replaced by the field
+ * of the other message, so that the oneof constraint is preserved.
+ *
+ * This is equivalent to the {@code Message::MergeFrom} method in C++.
+ */
+ Builder mergeFrom(MessageLite other);
/**
* Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
diff --git a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
index e69de29b..8e265935 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java
@@ -0,0 +1,281 @@
+// 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 java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/** Helps generate {@link String} representations of {@link MessageLite} protos. */
+final class MessageLiteToString {
+
+ private static final String LIST_SUFFIX = "List";
+ private static final String BUILDER_LIST_SUFFIX = "OrBuilderList";
+ private static final String MAP_SUFFIX = "Map";
+ private static final String BYTES_SUFFIX = "Bytes";
+
+ /**
+ * Returns a {@link String} representation of the {@link MessageLite} object. The first line of
+ * the {@code String} representation representation includes a comment string to uniquely identify
+ * the object instance. This acts as an indicator that this should not be relied on for
+ * comparisons.
+ *
+ * <p>For use by generated code only.
+ */
+ static String toString(MessageLite messageLite, String commentString) {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("# ").append(commentString);
+ reflectivePrintWithIndent(messageLite, buffer, 0);
+ return buffer.toString();
+ }
+
+ /**
+ * Reflectively prints the {@link MessageLite} to the buffer at given {@code indent} level.
+ *
+ * @param buffer the buffer to write to
+ * @param indent the number of spaces to indent the proto by
+ */
+ private static void reflectivePrintWithIndent(
+ MessageLite messageLite, StringBuilder buffer, int indent) {
+ // Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(),
+ // getFooList() and getFooMap() which might be useful for building an object's string
+ // representation.
+ Map<String, Method> nameToNoArgMethod = new HashMap<String, Method>();
+ Map<String, Method> nameToMethod = new HashMap<String, Method>();
+ Set<String> getters = new TreeSet<String>();
+ for (Method method : messageLite.getClass().getDeclaredMethods()) {
+ nameToMethod.put(method.getName(), method);
+ if (method.getParameterTypes().length == 0) {
+ nameToNoArgMethod.put(method.getName(), method);
+
+ if (method.getName().startsWith("get")) {
+ getters.add(method.getName());
+ }
+ }
+ }
+
+ for (String getter : getters) {
+ String suffix = getter.replaceFirst("get", "");
+ if (suffix.endsWith(LIST_SUFFIX)
+ && !suffix.endsWith(BUILDER_LIST_SUFFIX)
+ // Sometimes people have fields named 'list' that aren't repeated.
+ && !suffix.equals(LIST_SUFFIX)) {
+ String camelCase =
+ suffix.substring(0, 1).toLowerCase()
+ + suffix.substring(1, suffix.length() - LIST_SUFFIX.length());
+ // Try to reflectively get the value and toString() the field as if it were repeated. This
+ // only works if the method names have not been proguarded out or renamed.
+ Method listMethod = nameToNoArgMethod.get(getter);
+ if (listMethod != null && listMethod.getReturnType().equals(List.class)) {
+ printField(
+ buffer,
+ indent,
+ camelCaseToSnakeCase(camelCase),
+ GeneratedMessageLite.invokeOrDie(listMethod, messageLite));
+ continue;
+ }
+ }
+ if (suffix.endsWith(MAP_SUFFIX)
+ // Sometimes people have fields named 'map' that aren't maps.
+ && !suffix.equals(MAP_SUFFIX)) {
+ String camelCase =
+ suffix.substring(0, 1).toLowerCase()
+ + suffix.substring(1, suffix.length() - MAP_SUFFIX.length());
+ // Try to reflectively get the value and toString() the field as if it were a map. This only
+ // works if the method names have not been proguarded out or renamed.
+ Method mapMethod = nameToNoArgMethod.get(getter);
+ if (mapMethod != null
+ && mapMethod.getReturnType().equals(Map.class)
+ // Skip the deprecated getter method with no prefix "Map" when the field name ends with
+ // "map".
+ && !mapMethod.isAnnotationPresent(Deprecated.class)
+ // Skip the internal mutable getter method.
+ && Modifier.isPublic(mapMethod.getModifiers())) {
+ printField(
+ buffer,
+ indent,
+ camelCaseToSnakeCase(camelCase),
+ GeneratedMessageLite.invokeOrDie(mapMethod, messageLite));
+ continue;
+ }
+ }
+
+ Method setter = nameToMethod.get("set" + suffix);
+ if (setter == null) {
+ continue;
+ }
+ if (suffix.endsWith(BYTES_SUFFIX)
+ && nameToNoArgMethod.containsKey(
+ "get" + suffix.substring(0, suffix.length() - "Bytes".length()))) {
+ // Heuristic to skip bytes based accessors for string fields.
+ continue;
+ }
+
+ String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1);
+
+ // Try to reflectively get the value and toString() the field as if it were optional. This
+ // only works if the method names have not been proguarded out or renamed.
+ Method getMethod = nameToNoArgMethod.get("get" + suffix);
+ Method hasMethod = nameToNoArgMethod.get("has" + suffix);
+ // TODO(dweis): Fix proto3 semantics.
+ if (getMethod != null) {
+ Object value = GeneratedMessageLite.invokeOrDie(getMethod, messageLite);
+ final boolean hasValue =
+ hasMethod == null
+ ? !isDefaultValue(value)
+ : (Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite);
+ // TODO(dweis): This doesn't stop printing oneof case twice: value and enum style.
+ if (hasValue) {
+ printField(buffer, indent, camelCaseToSnakeCase(camelCase), value);
+ }
+ continue;
+ }
+ }
+
+ if (messageLite instanceof GeneratedMessageLite.ExtendableMessage) {
+ Iterator<Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object>> iter =
+ ((GeneratedMessageLite.ExtendableMessage<?, ?>) messageLite).extensions.iterator();
+ while (iter.hasNext()) {
+ Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object> entry = iter.next();
+ printField(buffer, indent, "[" + entry.getKey().getNumber() + "]", entry.getValue());
+ }
+ }
+
+ if (((GeneratedMessageLite<?, ?>) messageLite).unknownFields != null) {
+ ((GeneratedMessageLite<?, ?>) messageLite).unknownFields.printWithIndent(buffer, indent);
+ }
+ }
+
+ private static boolean isDefaultValue(Object o) {
+ if (o instanceof Boolean) {
+ return !((Boolean) o);
+ }
+ if (o instanceof Integer) {
+ return ((Integer) o) == 0;
+ }
+ if (o instanceof Float) {
+ return ((Float) o) == 0f;
+ }
+ if (o instanceof Double) {
+ return ((Double) o) == 0d;
+ }
+ if (o instanceof String) {
+ return o.equals("");
+ }
+ if (o instanceof ByteString) {
+ return o.equals(ByteString.EMPTY);
+ }
+ if (o instanceof MessageLite) { // Can happen in oneofs.
+ return o == ((MessageLite) o).getDefaultInstanceForType();
+ }
+ if (o instanceof java.lang.Enum<?>) { // Catches oneof enums.
+ return ((java.lang.Enum<?>) o).ordinal() == 0;
+ }
+
+ return false;
+ }
+
+ /**
+ * Formats a text proto field.
+ *
+ * <p>For use by generated code only.
+ *
+ * @param buffer the buffer to write to
+ * @param indent the number of spaces the proto should be indented by
+ * @param name the field name (in lower underscore case)
+ * @param object the object value of the field
+ */
+ static final void printField(StringBuilder buffer, int indent, String name, Object object) {
+ if (object instanceof List<?>) {
+ List<?> list = (List<?>) object;
+ for (Object entry : list) {
+ printField(buffer, indent, name, entry);
+ }
+ return;
+ }
+ if (object instanceof Map<?, ?>) {
+ Map<?, ?> map = (Map<?, ?>) object;
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ printField(buffer, indent, name, entry);
+ }
+ return;
+ }
+
+ buffer.append('\n');
+ for (int i = 0; i < indent; i++) {
+ buffer.append(' ');
+ }
+ buffer.append(name);
+
+ if (object instanceof String) {
+ buffer.append(": \"").append(TextFormatEscaper.escapeText((String) object)).append('"');
+ } else if (object instanceof ByteString) {
+ buffer.append(": \"").append(TextFormatEscaper.escapeBytes((ByteString) object)).append('"');
+ } else if (object instanceof GeneratedMessageLite) {
+ buffer.append(" {");
+ reflectivePrintWithIndent((GeneratedMessageLite<?, ?>) object, buffer, indent + 2);
+ buffer.append("\n");
+ for (int i = 0; i < indent; i++) {
+ buffer.append(' ');
+ }
+ buffer.append("}");
+ } else if (object instanceof Map.Entry<?, ?>) {
+ buffer.append(" {");
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) object;
+ printField(buffer, indent + 2, "key", entry.getKey());
+ printField(buffer, indent + 2, "value", entry.getValue());
+ buffer.append("\n");
+ for (int i = 0; i < indent; i++) {
+ buffer.append(' ');
+ }
+ buffer.append("}");
+ } else {
+ buffer.append(": ").append(object.toString());
+ }
+ }
+
+ private static final String camelCaseToSnakeCase(String camelCase) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < camelCase.length(); i++) {
+ char ch = camelCase.charAt(i);
+ if (Character.isUpperCase(ch)) {
+ builder.append("_");
+ }
+ builder.append(Character.toLowerCase(ch));
+ }
+ return builder.toString();
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
index f0fc4859..5e7d7821 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageOrBuilder.java
@@ -42,7 +42,7 @@ import java.util.Map;
public interface MessageOrBuilder extends MessageLiteOrBuilder {
// (From MessageLite, re-declared here only for return type covariance.)
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
Message getDefaultInstanceForType();
/**
diff --git a/java/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
index de4bfd3e..69ad7ddf 100644
--- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java
+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java
@@ -31,7 +31,6 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -370,6 +369,7 @@ class MessageReflection {
private final Message.Builder builder;
+ @Override
public Descriptors.Descriptor getDescriptorForType() {
return builder.getDescriptorForType();
}
@@ -378,6 +378,7 @@ class MessageReflection {
this.builder = builder;
}
+ @Override
public Object getField(Descriptors.FieldDescriptor field) {
return builder.getField(field);
}
@@ -387,25 +388,27 @@ class MessageReflection {
return builder.hasField(field);
}
- public MergeTarget setField(Descriptors.FieldDescriptor field,
- Object value) {
+ @Override
+ public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) {
builder.setField(field, value);
return this;
}
+ @Override
public MergeTarget clearField(Descriptors.FieldDescriptor field) {
builder.clearField(field);
return this;
}
+ @Override
public MergeTarget setRepeatedField(
Descriptors.FieldDescriptor field, int index, Object value) {
builder.setRepeatedField(field, index, value);
return this;
}
- public MergeTarget addRepeatedField(
- Descriptors.FieldDescriptor field, Object value) {
+ @Override
+ public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
builder.addRepeatedField(field, value);
return this;
}
@@ -426,25 +429,30 @@ class MessageReflection {
return builder.getOneofFieldDescriptor(oneof);
}
+ @Override
public ContainerType getContainerType() {
return ContainerType.MESSAGE;
}
+ @Override
public ExtensionRegistry.ExtensionInfo findExtensionByName(
ExtensionRegistry registry, String name) {
return registry.findImmutableExtensionByName(name);
}
+ @Override
public ExtensionRegistry.ExtensionInfo findExtensionByNumber(
- ExtensionRegistry registry, Descriptors.Descriptor containingType,
- int fieldNumber) {
+ ExtensionRegistry registry, Descriptors.Descriptor containingType, int fieldNumber) {
return registry.findImmutableExtensionByNumber(containingType,
fieldNumber);
}
- public Object parseGroup(CodedInputStream input,
+ @Override
+ public Object parseGroup(
+ CodedInputStream input,
ExtensionRegistryLite extensionRegistry,
- Descriptors.FieldDescriptor field, Message defaultInstance)
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
@@ -463,9 +471,12 @@ class MessageReflection {
return subBuilder.buildPartial();
}
- public Object parseMessage(CodedInputStream input,
+ @Override
+ public Object parseMessage(
+ CodedInputStream input,
ExtensionRegistryLite extensionRegistry,
- Descriptors.FieldDescriptor field, Message defaultInstance)
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
@@ -484,9 +495,12 @@ class MessageReflection {
return subBuilder.buildPartial();
}
- public Object parseMessageFromBytes(ByteString bytes,
+ @Override
+ public Object parseMessageFromBytes(
+ ByteString bytes,
ExtensionRegistryLite extensionRegistry,
- Descriptors.FieldDescriptor field, Message defaultInstance)
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
throws IOException {
Message.Builder subBuilder;
// When default instance is not null. The field is an extension field.
@@ -505,8 +519,9 @@ class MessageReflection {
return subBuilder.buildPartial();
}
- public MergeTarget newMergeTargetForField(Descriptors.FieldDescriptor field,
- Message defaultInstance) {
+ @Override
+ public MergeTarget newMergeTargetForField(
+ Descriptors.FieldDescriptor field, Message defaultInstance) {
if (defaultInstance != null) {
return new BuilderAdapter(
defaultInstance.newBuilderForType());
@@ -515,8 +530,8 @@ class MessageReflection {
}
}
- public WireFormat.Utf8Validation
- getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
+ @Override
+ public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
if (descriptor.needsUtf8Check()) {
return WireFormat.Utf8Validation.STRICT;
}
@@ -528,6 +543,7 @@ class MessageReflection {
return WireFormat.Utf8Validation.LOOSE;
}
+ @Override
public Object finish() {
return builder.buildPartial();
}
@@ -542,38 +558,43 @@ class MessageReflection {
this.extensions = extensions;
}
+ @Override
public Descriptors.Descriptor getDescriptorForType() {
throw new UnsupportedOperationException(
"getDescriptorForType() called on FieldSet object");
}
+ @Override
public Object getField(Descriptors.FieldDescriptor field) {
return extensions.getField(field);
}
+ @Override
public boolean hasField(Descriptors.FieldDescriptor field) {
return extensions.hasField(field);
}
- public MergeTarget setField(Descriptors.FieldDescriptor field,
- Object value) {
+ @Override
+ public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) {
extensions.setField(field, value);
return this;
}
+ @Override
public MergeTarget clearField(Descriptors.FieldDescriptor field) {
extensions.clearField(field);
return this;
}
+ @Override
public MergeTarget setRepeatedField(
Descriptors.FieldDescriptor field, int index, Object value) {
extensions.setRepeatedField(field, index, value);
return this;
}
- public MergeTarget addRepeatedField(
- Descriptors.FieldDescriptor field, Object value) {
+ @Override
+ public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
extensions.addRepeatedField(field, value);
return this;
}
@@ -594,25 +615,31 @@ class MessageReflection {
return null;
}
+ @Override
public ContainerType getContainerType() {
return ContainerType.EXTENSION_SET;
}
+ @Override
public ExtensionRegistry.ExtensionInfo findExtensionByName(
ExtensionRegistry registry, String name) {
return registry.findImmutableExtensionByName(name);
}
+ @Override
public ExtensionRegistry.ExtensionInfo findExtensionByNumber(
- ExtensionRegistry registry, Descriptors.Descriptor containingType,
- int fieldNumber) {
+ ExtensionRegistry registry, Descriptors.Descriptor containingType, int fieldNumber) {
return registry.findImmutableExtensionByNumber(containingType,
fieldNumber);
}
- public Object parseGroup(CodedInputStream input,
- ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
- Message defaultInstance) throws IOException {
+ @Override
+ public Object parseGroup(
+ CodedInputStream input,
+ ExtensionRegistryLite registry,
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
+ throws IOException {
Message.Builder subBuilder =
defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
@@ -625,9 +652,13 @@ class MessageReflection {
return subBuilder.buildPartial();
}
- public Object parseMessage(CodedInputStream input,
- ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
- Message defaultInstance) throws IOException {
+ @Override
+ public Object parseMessage(
+ CodedInputStream input,
+ ExtensionRegistryLite registry,
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
+ throws IOException {
Message.Builder subBuilder =
defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
@@ -640,9 +671,13 @@ class MessageReflection {
return subBuilder.buildPartial();
}
- public Object parseMessageFromBytes(ByteString bytes,
- ExtensionRegistryLite registry, Descriptors.FieldDescriptor field,
- Message defaultInstance) throws IOException {
+ @Override
+ public Object parseMessageFromBytes(
+ ByteString bytes,
+ ExtensionRegistryLite registry,
+ Descriptors.FieldDescriptor field,
+ Message defaultInstance)
+ throws IOException {
Message.Builder subBuilder = defaultInstance.newBuilderForType();
if (!field.isRepeated()) {
Message originalMessage = (Message) getField(field);
@@ -654,14 +689,15 @@ class MessageReflection {
return subBuilder.buildPartial();
}
+ @Override
public MergeTarget newMergeTargetForField(
Descriptors.FieldDescriptor descriptor, Message defaultInstance) {
throw new UnsupportedOperationException(
"newMergeTargetForField() called on FieldSet object");
}
- public WireFormat.Utf8Validation
- getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
+ @Override
+ public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor) {
if (descriptor.needsUtf8Check()) {
return WireFormat.Utf8Validation.STRICT;
}
@@ -669,6 +705,7 @@ class MessageReflection {
return WireFormat.Utf8Validation.LOOSE;
}
+ @Override
public Object finish() {
throw new UnsupportedOperationException(
"finish() called on FieldSet object");
@@ -676,12 +713,14 @@ class MessageReflection {
}
/**
- * Parses a single field into MergeTarget. The target can be Message.Builder,
- * FieldSet or MutableMessage.
+ * Parses a single field into MergeTarget. The target can be Message.Builder, FieldSet or
+ * MutableMessage.
*
- * Package-private because it is used by GeneratedMessage.ExtendableMessage.
+ * <p>Package-private because it is used by GeneratedMessage.ExtendableMessage.
*
* @param tag The tag, which should have already been read.
+ * @param unknownFields If not null, unknown fields will be merged to this {@link
+ * UnknownFieldSet}, otherwise unknown fields will be discarded.
* @return {@code true} unless the tag is an end-group tag.
*/
static boolean mergeFieldFrom(
@@ -690,7 +729,8 @@ class MessageReflection {
ExtensionRegistryLite extensionRegistry,
Descriptors.Descriptor type,
MergeTarget target,
- int tag) throws IOException {
+ int tag)
+ throws IOException {
if (type.getOptions().getMessageSetWireFormat() &&
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
mergeMessageSetExtensionFromCodedStream(
@@ -754,7 +794,11 @@ class MessageReflection {
}
if (unknown) { // Unknown field or wrong wire type. Skip.
- return unknownFields.mergeFieldFrom(tag, input);
+ if (unknownFields != null) {
+ return unknownFields.mergeFieldFrom(tag, input);
+ } else {
+ return input.skipField(tag);
+ }
}
if (packed) {
@@ -806,7 +850,9 @@ class MessageReflection {
// If the number isn't recognized as a valid value for this enum,
// drop it.
if (value == null) {
- unknownFields.mergeVarintField(fieldNumber, rawValue);
+ if (unknownFields != null) {
+ unknownFields.mergeVarintField(fieldNumber, rawValue);
+ }
return true;
}
}
@@ -909,7 +955,7 @@ class MessageReflection {
mergeMessageSetExtensionFromBytes(
rawBytes, extension, extensionRegistry, target);
} else { // We don't know how to parse this. Ignore it.
- if (rawBytes != null) {
+ if (rawBytes != null && unknownFields != null) {
unknownFields.mergeField(typeId, UnknownFieldSet.Field.newBuilder()
.addLengthDelimited(rawBytes).build());
}
diff --git a/java/core/src/main/java/com/google/protobuf/NioByteString.java b/java/core/src/main/java/com/google/protobuf/NioByteString.java
index f71e41b2..76594809 100644
--- a/java/core/src/main/java/com/google/protobuf/NioByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/NioByteString.java
@@ -30,15 +30,16 @@
package com.google.protobuf;
-import java.io.FileOutputStream;
+import static com.google.protobuf.Internal.checkNotNull;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.InvalidMarkException;
-import java.nio.channels.Channels;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
@@ -50,11 +51,10 @@ final class NioByteString extends ByteString.LeafByteString {
private final ByteBuffer buffer;
NioByteString(ByteBuffer buffer) {
- if (buffer == null) {
- throw new NullPointerException("buffer");
- }
+ checkNotNull(buffer, "buffer");
- this.buffer = buffer.slice();
+ // Use native byte order for fast fixed32/64 operations.
+ this.buffer = buffer.slice().order(ByteOrder.nativeOrder());
}
// =================================================================
@@ -119,7 +119,7 @@ final class NioByteString extends ByteString.LeafByteString {
@Override
public void writeTo(OutputStream out) throws IOException {
- writeToInternal(out, buffer.position(), buffer.remaining());
+ out.write(toByteArray());
}
@Override
@@ -137,14 +137,12 @@ final class NioByteString extends ByteString.LeafByteString {
return;
}
- // Slow path
- if (out instanceof FileOutputStream || numberToWrite >= 8192) {
- // Use a channel to write out the ByteBuffer.
- Channels.newChannel(out).write(slice(sourceOffset, sourceOffset + numberToWrite));
- } else {
- // Just copy the data to an array and write it.
- out.write(toByteArray());
- }
+ ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out);
+ }
+
+ @Override
+ void writeTo(ByteOutput output) throws IOException {
+ output.writeLazy(buffer.slice());
}
@Override
@@ -159,46 +157,30 @@ final class NioByteString extends ByteString.LeafByteString {
@Override
protected String toStringInternal(Charset charset) {
- byte[] bytes;
- int offset;
+ final byte[] bytes;
+ final int offset;
+ final int length;
if (buffer.hasArray()) {
bytes = buffer.array();
offset = buffer.arrayOffset() + buffer.position();
+ length = buffer.remaining();
} else {
+ // TODO(nathanmittler): Can we optimize this?
bytes = toByteArray();
offset = 0;
+ length = bytes.length;
}
- return new String(bytes, offset, size(), charset);
+ return new String(bytes, offset, length, charset);
}
@Override
public boolean isValidUtf8() {
- // TODO(nathanmittler): add a ByteBuffer fork for Utf8.isValidUtf8 to avoid the copy
- byte[] bytes;
- int startIndex;
- if (buffer.hasArray()) {
- bytes = buffer.array();
- startIndex = buffer.arrayOffset() + buffer.position();
- } else {
- bytes = toByteArray();
- startIndex = 0;
- }
- return Utf8.isValidUtf8(bytes, startIndex, startIndex + size());
+ return Utf8.isValidUtf8(buffer);
}
@Override
protected int partialIsValidUtf8(int state, int offset, int length) {
- // TODO(nathanmittler): TODO add a ByteBuffer fork for Utf8.partialIsValidUtf8 to avoid the copy
- byte[] bytes;
- int startIndex;
- if (buffer.hasArray()) {
- bytes = buffer.array();
- startIndex = buffer.arrayOffset() + buffer.position();
- } else {
- bytes = toByteArray();
- startIndex = 0;
- }
- return Utf8.partialIsValidUtf8(state, bytes, startIndex, startIndex + size());
+ return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length);
}
@Override
@@ -285,7 +267,7 @@ final class NioByteString extends ByteString.LeafByteString {
@Override
public CodedInputStream newCodedInput() {
- return CodedInputStream.newInstance(buffer);
+ return CodedInputStream.newInstance(buffer, true);
}
/**
diff --git a/java/core/src/main/java/com/google/protobuf/Parser.java b/java/core/src/main/java/com/google/protobuf/Parser.java
index 3fa11c3b..e07c6895 100644
--- a/java/core/src/main/java/com/google/protobuf/Parser.java
+++ b/java/core/src/main/java/com/google/protobuf/Parser.java
@@ -30,8 +30,8 @@
package com.google.protobuf;
-import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
/**
* Abstract interface for parsing Protocol Messages.
@@ -40,7 +40,7 @@ import java.io.InputStream;
*
* <p>All methods may throw {@link InvalidProtocolBufferException}. In the event of invalid data,
* like an encoding error, the cause of the thrown exception will be {@code null}. However, if an
- * I/O problem occurs, an exception is thrown with an {@link IOException} cause.
+ * I/O problem occurs, an exception is thrown with an {@link java.io.IOException} cause.
*
* @author liujisi@google.com (Pherl Liu)
*/
@@ -94,6 +94,18 @@ public interface Parser<MessageType> {
// Convenience methods.
/**
+ * Parses {@code data} as a message of {@code MessageType}. This is just a small wrapper around
+ * {@link #parseFrom(CodedInputStream)}.
+ */
+ public MessageType parseFrom(ByteBuffer data) throws InvalidProtocolBufferException;
+
+ /**
+ * Parses {@code data} as a message of {@code MessageType}. This is just a small wrapper around
+ * {@link #parseFrom(CodedInputStream, ExtensionRegistryLite)}.
+ */
+ public MessageType parseFrom(ByteBuffer data, ExtensionRegistryLite extensionRegistry)
+ throws InvalidProtocolBufferException;
+ /**
* Parses {@code data} as a message of {@code MessageType}.
* This is just a small wrapper around {@link #parseFrom(CodedInputStream)}.
*/
diff --git a/java/core/src/main/java/com/google/protobuf/PrimitiveNonBoxingCollection.java b/java/core/src/main/java/com/google/protobuf/PrimitiveNonBoxingCollection.java
new file mode 100644
index 00000000..79b5769d
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/PrimitiveNonBoxingCollection.java
@@ -0,0 +1,34 @@
+// 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;
+
+/** A marker interface indicating that the collection supports primitives and is non-boxing. */
+interface PrimitiveNonBoxingCollection {}
diff --git a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
index d2f82ac5..81255ec2 100644
--- a/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtobufArrayList.java
@@ -38,7 +38,7 @@ import java.util.List;
/**
* Implements {@link ProtobufList} for non-primitive and {@link String} types.
*/
-class ProtobufArrayList<E> extends AbstractProtobufList<E> {
+final class ProtobufArrayList<E> extends AbstractProtobufList<E> {
private static final ProtobufArrayList<Object> EMPTY_LIST = new ProtobufArrayList<Object>();
static {
@@ -51,17 +51,23 @@ class ProtobufArrayList<E> extends AbstractProtobufList<E> {
}
private final List<E> list;
-
+
ProtobufArrayList() {
- list = new ArrayList<E>();
+ this(new ArrayList<E>(DEFAULT_CAPACITY));
}
- ProtobufArrayList(List<E> toCopy) {
- list = new ArrayList<E>(toCopy);
+ private ProtobufArrayList(List<E> list) {
+ this.list = list;
}
-
- ProtobufArrayList(int capacity) {
- list = new ArrayList<E>(capacity);
+
+ @Override
+ public ProtobufArrayList<E> mutableCopyWithCapacity(int capacity) {
+ if (capacity < size()) {
+ throw new IllegalArgumentException();
+ }
+ List<E> newList = new ArrayList<E>(capacity);
+ newList.addAll(list);
+ return new ProtobufArrayList<E>(newList);
}
@Override
diff --git a/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
index 0c8df989..a596d301 100644
--- a/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
+++ b/java/core/src/main/java/com/google/protobuf/ProtocolMessageEnum.java
@@ -42,6 +42,7 @@ public interface ProtocolMessageEnum extends Internal.EnumLite {
/**
* Return the value's numeric value as defined in the .proto file.
*/
+ @Override
int getNumber();
/**
diff --git a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
index f91cdbce..29f567dc 100644
--- a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
@@ -579,7 +579,7 @@ public class RepeatedFieldBuilder
}
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void markDirty() {
onChanged();
}
@@ -621,10 +621,12 @@ public class RepeatedFieldBuilder
this.builder = builder;
}
+ @Override
public int size() {
return this.builder.getCount();
}
+ @Override
public MType get(int index) {
return builder.getMessage(index);
}
@@ -654,10 +656,12 @@ public class RepeatedFieldBuilder
this.builder = builder;
}
+ @Override
public int size() {
return this.builder.getCount();
}
+ @Override
public BType get(int index) {
return builder.getBuilder(index);
}
@@ -687,10 +691,12 @@ public class RepeatedFieldBuilder
this.builder = builder;
}
+ @Override
public int size() {
return this.builder.getCount();
}
+ @Override
public IType get(int index) {
return builder.getMessageOrBuilder(index);
}
diff --git a/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java
new file mode 100644
index 00000000..30c991d4
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/RepeatedFieldBuilderV3.java
@@ -0,0 +1,702 @@
+// 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.protobuf.Internal.checkNotNull;
+
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * {@code RepeatedFieldBuilderV3} implements a structure that a protocol
+ * message uses to hold a repeated field of other protocol messages. It supports
+ * the classical use case of adding immutable {@link Message}'s to the
+ * repeated field and is highly optimized around this (no extra memory
+ * allocations and sharing of immutable arrays).
+ * <br>
+ * It also supports the additional use case of adding a {@link Message.Builder}
+ * to the repeated field and deferring conversion of that {@code Builder}
+ * to an immutable {@code Message}. In this way, it's possible to maintain
+ * a tree of {@code Builder}'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occurred in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public class RepeatedFieldBuilderV3
+ <MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ implements AbstractMessage.BuilderParent {
+
+ // Parent to send changes to.
+ private AbstractMessage.BuilderParent parent;
+
+ // List of messages. Never null. It may be immutable, in which case
+ // isMessagesListMutable will be false. See note below.
+ private List<MType> messages;
+
+ // Whether messages is an mutable array that can be modified.
+ private boolean isMessagesListMutable;
+
+ // List of builders. May be null, in which case, no nested builders were
+ // created. If not null, entries represent the builder for that index.
+ private List<SingleFieldBuilderV3<MType, BType, IType>> builders;
+
+ // Here are the invariants for messages and builders:
+ // 1. messages is never null and its count corresponds to the number of items
+ // in the repeated field.
+ // 2. If builders is non-null, messages and builders MUST always
+ // contain the same number of items.
+ // 3. Entries in either array can be null, but for any index, there MUST be
+ // either a Message in messages or a builder in builders.
+ // 4. If the builder at an index is non-null, the builder is
+ // authoritative. This is the case where a Builder was set on the index.
+ // Any message in the messages array MUST be ignored.
+ // t. If the builder at an index is null, the message in the messages
+ // list is authoritative. This is the case where a Message (not a Builder)
+ // was set directly for an index.
+
+ // Indicates that we've built a message and so we are now obligated
+ // to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
+ private boolean isClean;
+
+ // A view of this builder that exposes a List interface of messages. This is
+ // initialized on demand. This is fully backed by this object and all changes
+ // are reflected in it. Access to any item converts it to a message if it
+ // was a builder.
+ private MessageExternalList<MType, BType, IType> externalMessageList;
+
+ // A view of this builder that exposes a List interface of builders. This is
+ // initialized on demand. This is fully backed by this object and all changes
+ // are reflected in it. Access to any item converts it to a builder if it
+ // was a message.
+ private BuilderExternalList<MType, BType, IType> externalBuilderList;
+
+ // A view of this builder that exposes a List interface of the interface
+ // implemented by messages and builders. This is initialized on demand. This
+ // is fully backed by this object and all changes are reflected in it.
+ // Access to any item returns either a builder or message depending on
+ // what is most efficient.
+ private MessageOrBuilderExternalList<MType, BType, IType>
+ externalMessageOrBuilderList;
+
+ /**
+ * Constructs a new builder with an empty list of messages.
+ *
+ * @param messages the current list of messages
+ * @param isMessagesListMutable Whether the messages list is mutable
+ * @param parent a listener to notify of changes
+ * @param isClean whether the builder is initially marked clean
+ */
+ public RepeatedFieldBuilderV3(
+ List<MType> messages,
+ boolean isMessagesListMutable,
+ AbstractMessage.BuilderParent parent,
+ boolean isClean) {
+ this.messages = messages;
+ this.isMessagesListMutable = isMessagesListMutable;
+ this.parent = parent;
+ this.isClean = isClean;
+ }
+
+ public void dispose() {
+ // Null out parent so we stop sending it invalidations.
+ parent = null;
+ }
+
+ /**
+ * Ensures that the list of messages is mutable so it can be updated. If it's
+ * immutable, a copy is made.
+ */
+ private void ensureMutableMessageList() {
+ if (!isMessagesListMutable) {
+ messages = new ArrayList<MType>(messages);
+ isMessagesListMutable = true;
+ }
+ }
+
+ /**
+ * Ensures that the list of builders is not null. If it's null, the list is
+ * created and initialized to be the same size as the messages list with
+ * null entries.
+ */
+ private void ensureBuilders() {
+ if (this.builders == null) {
+ this.builders =
+ new ArrayList<SingleFieldBuilderV3<MType, BType, IType>>(
+ messages.size());
+ for (int i = 0; i < messages.size(); i++) {
+ builders.add(null);
+ }
+ }
+ }
+
+ /**
+ * Gets the count of items in the list.
+ *
+ * @return the count of items in the list.
+ */
+ public int getCount() {
+ return messages.size();
+ }
+
+ /**
+ * Gets whether the list is empty.
+ *
+ * @return whether the list is empty
+ */
+ public boolean isEmpty() {
+ return messages.isEmpty();
+ }
+
+ /**
+ * Get the message at the specified index. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it.
+ *
+ * @param index the index of the message to get
+ * @return the message for the specified index
+ */
+ public MType getMessage(int index) {
+ return getMessage(index, false);
+ }
+
+ /**
+ * Get the message at the specified index. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it.
+ *
+ * @param index the index of the message to get
+ * @param forBuild this is being called for build so we want to make sure
+ * we SingleFieldBuilderV3.build to send dirty invalidations
+ * @return the message for the specified index
+ */
+ private MType getMessage(int index, boolean forBuild) {
+ if (this.builders == null) {
+ // We don't have any builders -- return the current Message.
+ // This is the case where no builder was created, so we MUST have a
+ // Message.
+ return messages.get(index);
+ }
+
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ // We don't have a builder -- return the current message.
+ // This is the case where no builder was created for the entry at index,
+ // so we MUST have a message.
+ return messages.get(index);
+
+ } else {
+ return forBuild ? builder.build() : builder.getMessage();
+ }
+ }
+
+ /**
+ * Gets a builder for the specified index. If no builder has been created for
+ * that index, a builder is created on demand by calling
+ * {@link Message#toBuilder}.
+ *
+ * @param index the index of the message to get
+ * @return The builder for that index
+ */
+ public BType getBuilder(int index) {
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ MType message = messages.get(index);
+ builder = new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ builders.set(index, builder);
+ }
+ return builder.getBuilder();
+ }
+
+ /**
+ * Gets the base class interface for the specified index. This may either be
+ * a builder or a message. It will return whatever is more efficient.
+ *
+ * @param index the index of the message to get
+ * @return the message or builder for the index as the base class interface
+ */
+ @SuppressWarnings("unchecked")
+ public IType getMessageOrBuilder(int index) {
+ if (this.builders == null) {
+ // We don't have any builders -- return the current Message.
+ // This is the case where no builder was created, so we MUST have a
+ // Message.
+ return (IType) messages.get(index);
+ }
+
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(index);
+ if (builder == null) {
+ // We don't have a builder -- return the current message.
+ // This is the case where no builder was created for the entry at index,
+ // so we MUST have a message.
+ return (IType) messages.get(index);
+
+ } else {
+ return builder.getMessageOrBuilder();
+ }
+ }
+
+ /**
+ * Sets a message at the specified index replacing the existing item at
+ * that index.
+ *
+ * @param index the index to set.
+ * @param message the message to set
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> setMessage(
+ int index, MType message) {
+ checkNotNull(message);
+ ensureMutableMessageList();
+ messages.set(index, message);
+ if (builders != null) {
+ SingleFieldBuilderV3<MType, BType, IType> entry =
+ builders.set(index, null);
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends the specified element to the end of this list.
+ *
+ * @param message the message to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
+ MType message) {
+ checkNotNull(message);
+ ensureMutableMessageList();
+ messages.add(message);
+ if (builders != null) {
+ builders.add(null);
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Inserts the specified message at the specified position in this list.
+ * Shifts the element currently at that position (if any) and any subsequent
+ * elements to the right (adds one to their indices).
+ *
+ * @param index the index at which to insert the message
+ * @param message the message to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addMessage(
+ int index, MType message) {
+ checkNotNull(message);
+ ensureMutableMessageList();
+ messages.add(index, message);
+ if (builders != null) {
+ builders.add(index, null);
+ }
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends all of the messages in the specified collection to the end of
+ * this list, in the order that they are returned by the specified
+ * collection's iterator.
+ *
+ * @param values the messages to add
+ * @return the builder
+ */
+ public RepeatedFieldBuilderV3<MType, BType, IType> addAllMessages(
+ Iterable<? extends MType> values) {
+ for (final MType value : values) {
+ checkNotNull(value);
+ }
+
+ // If we can inspect the size, we can more efficiently add messages.
+ int size = -1;
+ if (values instanceof Collection) {
+ @SuppressWarnings("unchecked") final
+ Collection<MType> collection = (Collection<MType>) values;
+ if (collection.size() == 0) {
+ return this;
+ }
+ size = collection.size();
+ }
+ ensureMutableMessageList();
+
+ if (size >= 0 && messages instanceof ArrayList) {
+ ((ArrayList<MType>) messages)
+ .ensureCapacity(messages.size() + size);
+ }
+
+ for (MType value : values) {
+ addMessage(value);
+ }
+
+ onChanged();
+ incrementModCounts();
+ return this;
+ }
+
+ /**
+ * Appends a new builder to the end of this list and returns the builder.
+ *
+ * @param message the message to add which is the basis of the builder
+ * @return the new builder
+ */
+ public BType addBuilder(MType message) {
+ ensureMutableMessageList();
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder =
+ new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ messages.add(null);
+ builders.add(builder);
+ onChanged();
+ incrementModCounts();
+ return builder.getBuilder();
+ }
+
+ /**
+ * Inserts a new builder at the specified position in this list.
+ * Shifts the element currently at that position (if any) and any subsequent
+ * elements to the right (adds one to their indices).
+ *
+ * @param index the index at which to insert the builder
+ * @param message the message to add which is the basis of the builder
+ * @return the builder
+ */
+ public BType addBuilder(int index, MType message) {
+ ensureMutableMessageList();
+ ensureBuilders();
+ SingleFieldBuilderV3<MType, BType, IType> builder =
+ new SingleFieldBuilderV3<MType, BType, IType>(
+ message, this, isClean);
+ messages.add(index, null);
+ builders.add(index, builder);
+ onChanged();
+ incrementModCounts();
+ return builder.getBuilder();
+ }
+
+ /**
+ * Removes the element at the specified position in this list. Shifts any
+ * subsequent elements to the left (subtracts one from their indices).
+ * Returns the element that was removed from the list.
+ *
+ * @param index the index at which to remove the message
+ */
+ public void remove(int index) {
+ ensureMutableMessageList();
+ messages.remove(index);
+ if (builders != null) {
+ SingleFieldBuilderV3<MType, BType, IType> entry =
+ builders.remove(index);
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ onChanged();
+ incrementModCounts();
+ }
+
+ /**
+ * Removes all of the elements from this list.
+ * The list will be empty after this call returns.
+ */
+ public void clear() {
+ messages = Collections.emptyList();
+ isMessagesListMutable = false;
+ if (builders != null) {
+ for (SingleFieldBuilderV3<MType, BType, IType> entry :
+ builders) {
+ if (entry != null) {
+ entry.dispose();
+ }
+ }
+ builders = null;
+ }
+ onChanged();
+ incrementModCounts();
+ }
+
+ /**
+ * Builds the list of messages from the builder and returns them.
+ *
+ * @return an immutable list of messages
+ */
+ public List<MType> build() {
+ // Now that build has been called, we are required to dispatch
+ // invalidations.
+ isClean = true;
+
+ if (!isMessagesListMutable && builders == null) {
+ // We still have an immutable list and we never created a builder.
+ return messages;
+ }
+
+ boolean allMessagesInSync = true;
+ if (!isMessagesListMutable) {
+ // We still have an immutable list. Let's see if any of them are out
+ // of sync with their builders.
+ for (int i = 0; i < messages.size(); i++) {
+ Message message = messages.get(i);
+ SingleFieldBuilderV3<MType, BType, IType> builder = builders.get(i);
+ if (builder != null) {
+ if (builder.build() != message) {
+ allMessagesInSync = false;
+ break;
+ }
+ }
+ }
+ if (allMessagesInSync) {
+ // Immutable list is still in sync.
+ return messages;
+ }
+ }
+
+ // Need to make sure messages is up to date
+ ensureMutableMessageList();
+ for (int i = 0; i < messages.size(); i++) {
+ messages.set(i, getMessage(i, true));
+ }
+
+ // We're going to return our list as immutable so we mark that we can
+ // no longer update it.
+ messages = Collections.unmodifiableList(messages);
+ isMessagesListMutable = false;
+ return messages;
+ }
+
+ /**
+ * Gets a view of the builder as a list of messages. The returned list is live
+ * and will reflect any changes to the underlying builder.
+ *
+ * @return the messages in the list
+ */
+ public List<MType> getMessageList() {
+ if (externalMessageList == null) {
+ externalMessageList =
+ new MessageExternalList<MType, BType, IType>(this);
+ }
+ return externalMessageList;
+ }
+
+ /**
+ * Gets a view of the builder as a list of builders. This returned list is
+ * live and will reflect any changes to the underlying builder.
+ *
+ * @return the builders in the list
+ */
+ public List<BType> getBuilderList() {
+ if (externalBuilderList == null) {
+ externalBuilderList =
+ new BuilderExternalList<MType, BType, IType>(this);
+ }
+ return externalBuilderList;
+ }
+
+ /**
+ * Gets a view of the builder as a list of MessageOrBuilders. This returned
+ * list is live and will reflect any changes to the underlying builder.
+ *
+ * @return the builders in the list
+ */
+ public List<IType> getMessageOrBuilderList() {
+ if (externalMessageOrBuilderList == null) {
+ externalMessageOrBuilderList =
+ new MessageOrBuilderExternalList<MType, BType, IType>(this);
+ }
+ return externalMessageOrBuilderList;
+ }
+
+ /**
+ * Called when a the builder or one of its nested children has changed
+ * and any parent should be notified of its invalidation.
+ */
+ private void onChanged() {
+ if (isClean && parent != null) {
+ parent.markDirty();
+
+ // Don't keep dispatching invalidations until build is called again.
+ isClean = false;
+ }
+ }
+
+ @Override
+ public void markDirty() {
+ onChanged();
+ }
+
+ /**
+ * Increments the mod counts so that an ConcurrentModificationException can
+ * be thrown if calling code tries to modify the builder while its iterating
+ * the list.
+ */
+ private void incrementModCounts() {
+ if (externalMessageList != null) {
+ externalMessageList.incrementModCount();
+ }
+ if (externalBuilderList != null) {
+ externalBuilderList.incrementModCount();
+ }
+ if (externalMessageOrBuilderList != null) {
+ externalMessageOrBuilderList.incrementModCount();
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of messages.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class MessageExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<MType> implements List<MType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ MessageExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public MType get(int index) {
+ return builder.getMessage(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of builders.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class BuilderExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<BType> implements List<BType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ BuilderExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public BType get(int index) {
+ return builder.getBuilder(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+
+ /**
+ * Provides a live view of the builder as a list of builders.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ */
+ private static class MessageOrBuilderExternalList<
+ MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ extends AbstractList<IType> implements List<IType> {
+
+ RepeatedFieldBuilderV3<MType, BType, IType> builder;
+
+ MessageOrBuilderExternalList(
+ RepeatedFieldBuilderV3<MType, BType, IType> builder) {
+ this.builder = builder;
+ }
+
+ @Override
+ public int size() {
+ return this.builder.getCount();
+ }
+
+ @Override
+ public IType get(int index) {
+ return builder.getMessageOrBuilder(index);
+ }
+
+ void incrementModCount() {
+ modCount++;
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/RopeByteString.java b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
index 8badfabd..6fa555df 100644
--- a/java/core/src/main/java/com/google/protobuf/RopeByteString.java
+++ b/java/core/src/main/java/com/google/protobuf/RopeByteString.java
@@ -48,10 +48,11 @@ import java.util.Stack;
/**
* Class to represent {@code ByteStrings} formed by concatenation of other
* ByteStrings, without copying the data in the pieces. The concatenation is
- * represented as a tree whose leaf nodes are each a {@link LiteralByteString}.
+ * represented as a tree whose leaf nodes are each a
+ * {@link com.google.protobuf.ByteString.LeafByteString}.
*
* <p>Most of the operation here is inspired by the now-famous paper <a
- * href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
+ * href="https://web.archive.org/web/20060202015456/http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
* BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
* michael plass
*
@@ -139,8 +140,9 @@ final class RopeByteString extends ByteString {
/**
* Concatenate the given strings while performing various optimizations to
* slow the growth rate of tree depth and tree node count. The result is
- * either a {@link LiteralByteString} or a {@link RopeByteString}
- * depending on which optimizations, if any, were applied.
+ * either a {@link com.google.protobuf.ByteString.LeafByteString} or a
+ * {@link RopeByteString} depending on which optimizations, if any, were
+ * applied.
*
* <p>Small pieces of length less than {@link
* ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
@@ -294,8 +296,7 @@ final class RopeByteString extends ByteString {
*
* <p>Substrings of {@code length < 2} should result in at most a single
* recursive call chain, terminating at a leaf node. Thus the result will be a
- * {@link LiteralByteString}. {@link #RopeByteString(ByteString,
- * ByteString)}.
+ * {@link com.google.protobuf.ByteString.LeafByteString}.
*
* @param beginIndex start at this index
* @param endIndex the last character is the one before this index
@@ -368,7 +369,7 @@ final class RopeByteString extends ByteString {
@Override
public List<ByteBuffer> asReadOnlyByteBufferList() {
- // Walk through the list of LiteralByteString's that make up this
+ // Walk through the list of LeafByteString's that make up this
// rope, and add each one as a read-only ByteBuffer.
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
PieceIterator pieces = new PieceIterator(this);
@@ -400,6 +401,13 @@ final class RopeByteString extends ByteString {
}
@Override
+ void writeTo(ByteOutput output) throws IOException {
+ left.writeTo(output);
+ right.writeTo(output);
+ }
+
+
+ @Override
protected String toStringInternal(Charset charset) {
return new String(toByteArray(), charset);
}
@@ -709,9 +717,10 @@ final class RopeByteString extends ByteString {
}
/**
- * Returns the next item and advances one {@code LiteralByteString}.
+ * Returns the next item and advances one
+ * {@link com.google.protobuf.ByteString.LeafByteString}.
*
- * @return next non-empty LiteralByteString or {@code null}
+ * @return next non-empty LeafByteString or {@code null}
*/
@Override
public LeafByteString next() {
diff --git a/java/core/src/main/java/com/google/protobuf/RpcUtil.java b/java/core/src/main/java/com/google/protobuf/RpcUtil.java
index 694b8d13..f7d555ae 100644
--- a/java/core/src/main/java/com/google/protobuf/RpcUtil.java
+++ b/java/core/src/main/java/com/google/protobuf/RpcUtil.java
@@ -71,6 +71,7 @@ public final class RpcUtil {
final Class<Type> originalClass,
final Type defaultInstance) {
return new RpcCallback<Message>() {
+ @Override
public void run(final Message parameter) {
Type typedParameter;
try {
@@ -107,8 +108,9 @@ public final class RpcUtil {
return new RpcCallback<ParameterType>() {
private boolean alreadyCalled = false;
+ @Override
public void run(final ParameterType parameter) {
- synchronized(this) {
+ synchronized (this) {
if (alreadyCalled) {
throw new AlreadyCalledException();
}
diff --git a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java
index aba65e32..941b5def 100644
--- a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java
+++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilder.java
@@ -234,7 +234,7 @@ public class SingleFieldBuilder
}
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void markDirty() {
onChanged();
}
diff --git a/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java
new file mode 100644
index 00000000..8ab0f26d
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/SingleFieldBuilderV3.java
@@ -0,0 +1,237 @@
+// 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.protobuf.Internal.checkNotNull;
+
+/**
+ * {@code SingleFieldBuilderV3} implements a structure that a protocol
+ * message uses to hold a single field of another protocol message. It supports
+ * the classical use case of setting an immutable {@link Message} as the value
+ * of the field and is highly optimized around this.
+ * <br>
+ * It also supports the additional use case of setting a {@link Message.Builder}
+ * as the field and deferring conversion of that {@code Builder}
+ * to an immutable {@code Message}. In this way, it's possible to maintain
+ * a tree of {@code Builder}'s that acts as a fully read/write data
+ * structure.
+ * <br>
+ * Logically, one can think of a tree of builders as converting the entire tree
+ * to messages when build is called on the root or when any method is called
+ * that desires a Message instead of a Builder. In terms of the implementation,
+ * the {@code SingleFieldBuilderV3} and {@code RepeatedFieldBuilderV3}
+ * classes cache messages that were created so that messages only need to be
+ * created when some change occurred in its builder or a builder for one of its
+ * descendants.
+ *
+ * @param <MType> the type of message for the field
+ * @param <BType> the type of builder for the field
+ * @param <IType> the common interface for the message and the builder
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public class SingleFieldBuilderV3
+ <MType extends AbstractMessage,
+ BType extends AbstractMessage.Builder,
+ IType extends MessageOrBuilder>
+ implements AbstractMessage.BuilderParent {
+
+ // Parent to send changes to.
+ private AbstractMessage.BuilderParent parent;
+
+ // Invariant: one of builder or message fields must be non-null.
+
+ // If set, this is the case where we are backed by a builder. In this case,
+ // message field represents a cached message for the builder (or null if
+ // there is no cached message).
+ private BType builder;
+
+ // If builder is non-null, this represents a cached message from the builder.
+ // If builder is null, this is the authoritative message for the field.
+ private MType message;
+
+ // Indicates that we've built a message and so we are now obligated
+ // to dispatch dirty invalidations. See AbstractMessage.BuilderListener.
+ private boolean isClean;
+
+ public SingleFieldBuilderV3(
+ MType message,
+ AbstractMessage.BuilderParent parent,
+ boolean isClean) {
+ this.message = checkNotNull(message);
+ this.parent = parent;
+ this.isClean = isClean;
+ }
+
+ public void dispose() {
+ // Null out parent so we stop sending it invalidations.
+ parent = null;
+ }
+
+ /**
+ * Get the message for the field. If the message is currently stored
+ * as a {@code Builder}, it is converted to a {@code Message} by
+ * calling {@link Message.Builder#buildPartial} on it. If no message has
+ * been set, returns the default instance of the message.
+ *
+ * @return the message for the field
+ */
+ @SuppressWarnings("unchecked")
+ public MType getMessage() {
+ if (message == null) {
+ // If message is null, the invariant is that we must be have a builder.
+ message = (MType) builder.buildPartial();
+ }
+ return message;
+ }
+
+ /**
+ * Builds the message and returns it.
+ *
+ * @return the message
+ */
+ public MType build() {
+ // Now that build has been called, we are required to dispatch
+ // invalidations.
+ isClean = true;
+ return getMessage();
+ }
+
+ /**
+ * Gets a builder for the field. If no builder has been created yet, a
+ * builder is created on demand by calling {@link Message#toBuilder}.
+ *
+ * @return The builder for the field
+ */
+ @SuppressWarnings("unchecked")
+ public BType getBuilder() {
+ if (builder == null) {
+ // builder.mergeFrom() on a fresh builder
+ // does not create any sub-objects with independent clean/dirty states,
+ // therefore setting the builder itself to clean without actually calling
+ // build() cannot break any invariants.
+ builder = (BType) message.newBuilderForType(this);
+ builder.mergeFrom(message); // no-op if message is the default message
+ builder.markClean();
+ }
+ return builder;
+ }
+
+ /**
+ * Gets the base class interface for the field. This may either be a builder
+ * or a message. It will return whatever is more efficient.
+ *
+ * @return the message or builder for the field as the base class interface
+ */
+ @SuppressWarnings("unchecked")
+ public IType getMessageOrBuilder() {
+ if (builder != null) {
+ return (IType) builder;
+ } else {
+ return (IType) message;
+ }
+ }
+
+ /**
+ * Sets a message for the field replacing any existing value.
+ *
+ * @param message the message to set
+ * @return the builder
+ */
+ public SingleFieldBuilderV3<MType, BType, IType> setMessage(
+ MType message) {
+ this.message = checkNotNull(message);
+ if (builder != null) {
+ builder.dispose();
+ builder = null;
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Merges the field from another field.
+ *
+ * @param value the value to merge from
+ * @return the builder
+ */
+ public SingleFieldBuilderV3<MType, BType, IType> mergeFrom(
+ MType value) {
+ if (builder == null && message == message.getDefaultInstanceForType()) {
+ message = value;
+ } else {
+ getBuilder().mergeFrom(value);
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Clears the value of the field.
+ *
+ * @return the builder
+ */
+ @SuppressWarnings("unchecked")
+ public SingleFieldBuilderV3<MType, BType, IType> clear() {
+ message = (MType) (message != null ?
+ message.getDefaultInstanceForType() :
+ builder.getDefaultInstanceForType());
+ if (builder != null) {
+ builder.dispose();
+ builder = null;
+ }
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Called when a the builder or one of its nested children has changed
+ * and any parent should be notified of its invalidation.
+ */
+ private void onChanged() {
+ // If builder is null, this is the case where onChanged is being called
+ // from setMessage or clear.
+ if (builder != null) {
+ message = null;
+ }
+ if (isClean && parent != null) {
+ parent.markDirty();
+
+ // Don't keep dispatching invalidations until build is called again.
+ isClean = false;
+ }
+ }
+
+ @Override
+ public void markDirty() {
+ onChanged();
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java
index 0674d2e2..279edc4d 100644
--- a/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java
+++ b/java/core/src/main/java/com/google/protobuf/SmallSortedMap.java
@@ -35,12 +35,12 @@ import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
-import java.util.TreeMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
+import java.util.TreeMap;
/**
* A custom map implementation from FieldDescriptor to Object optimized to
@@ -197,6 +197,7 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
overflowEntries.entrySet();
}
+
@Override
public int size() {
return entryList.size() + overflowEntries.size();
@@ -356,6 +357,7 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
return lazyEntrySet;
}
+
/**
* @throws UnsupportedOperationException if {@link #makeImmutable()} has
* has been called.
@@ -411,22 +413,22 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
this.value = value;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public K getKey() {
return key;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public V getValue() {
return value;
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public int compareTo(Entry other) {
return getKey().compareTo(other.getKey());
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public V setValue(V newValue) {
checkMutable();
final V oldValue = this.value;
@@ -525,6 +527,7 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
}
}
+
/**
* Iterator implementation that switches from the entry array to the overflow
* entries appropriately.
@@ -535,13 +538,13 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
private boolean nextCalledBeforeRemove;
private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasNext() {
- return (pos + 1) < entryList.size() ||
- getOverflowIterator().hasNext();
+ return (pos + 1) < entryList.size()
+ || (!overflowEntries.isEmpty() && getOverflowIterator().hasNext());
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public Map.Entry<K, V> next() {
nextCalledBeforeRemove = true;
// Always increment pos so that we know whether the last returned value
@@ -552,7 +555,7 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
return getOverflowIterator().next();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void remove() {
if (!nextCalledBeforeRemove) {
throw new IllegalStateException("remove() was called before next()");
@@ -588,31 +591,83 @@ class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
*/
private static class EmptySet {
- private static final Iterator<Object> ITERATOR = new Iterator<Object>() {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public boolean hasNext() {
- return false;
- }
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public Object next() {
- throw new NoSuchElementException();
- }
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
+ private static final Iterator<Object> ITERATOR =
+ new Iterator<Object>() {
+ @Override
+ public boolean hasNext() {
+ return false;
+ }
+ @Override
+ public Object next() {
+ throw new NoSuchElementException();
+ }
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
- private static final Iterable<Object> ITERABLE = new Iterable<Object>() {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
- public Iterator<Object> iterator() {
- return ITERATOR;
- }
- };
+ private static final Iterable<Object> ITERABLE =
+ new Iterable<Object>() {
+ @Override
+ public Iterator<Object> iterator() {
+ return ITERATOR;
+ }
+ };
@SuppressWarnings("unchecked")
static <T> Iterable<T> iterable() {
return (Iterable<T>) ITERABLE;
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof SmallSortedMap)) {
+ return super.equals(o);
+ }
+
+ SmallSortedMap<?, ?> other = (SmallSortedMap<?, ?>) o;
+ final int size = size();
+ if (size != other.size()) {
+ return false;
+ }
+
+ // Best effort try to avoid allocating an entry set.
+ final int numArrayEntries = getNumArrayEntries();
+ if (numArrayEntries != other.getNumArrayEntries()) {
+ return entrySet().equals(other.entrySet());
+ }
+
+ for (int i = 0; i < numArrayEntries; i++) {
+ if (!getArrayEntryAt(i).equals(other.getArrayEntryAt(i))) {
+ return false;
+ }
+ }
+
+ if (numArrayEntries != size) {
+ return overflowEntries.equals(other.overflowEntries);
+ }
+
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ final int listSize = getNumArrayEntries();
+ for (int i = 0; i < listSize; i++) {
+ h += entryList.get(i).hashCode();
+ }
+ // Avoid the iterator allocation if possible.
+ if (getNumOverflowEntries() > 0) {
+ h += overflowEntries.hashCode();
+ }
+ return h;
+ }
}
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java
index c99b5285..25c3474f 100644
--- a/java/core/src/main/java/com/google/protobuf/TextFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java
@@ -34,7 +34,6 @@ import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-
import java.io.IOException;
import java.math.BigInteger;
import java.nio.CharBuffer;
@@ -56,14 +55,7 @@ import java.util.regex.Pattern;
public final class TextFormat {
private TextFormat() {}
- private static final Logger logger =
- Logger.getLogger(TextFormat.class.getName());
-
- private static final Printer DEFAULT_PRINTER = new Printer();
- private static final Printer SINGLE_LINE_PRINTER =
- (new Printer()).setSingleLineMode(true);
- private static final Printer UNICODE_PRINTER =
- (new Printer()).setEscapeNonAscii(false);
+ private static final Logger logger = Logger.getLogger(TextFormat.class.getName());
/**
* Outputs a textual representation of the Protocol Message supplied into
@@ -73,14 +65,14 @@ public final class TextFormat {
public static void print(
final MessageOrBuilder message, final Appendable output)
throws IOException {
- DEFAULT_PRINTER.print(message, new TextGenerator(output));
+ Printer.DEFAULT.print(message, multiLineOutput(output));
}
/** Outputs a textual representation of {@code fields} to {@code output}. */
public static void print(final UnknownFieldSet fields,
final Appendable output)
throws IOException {
- DEFAULT_PRINTER.printUnknownFields(fields, new TextGenerator(output));
+ Printer.DEFAULT.printUnknownFields(fields, multiLineOutput(output));
}
/**
@@ -90,7 +82,7 @@ public final class TextFormat {
public static void printUnicode(
final MessageOrBuilder message, final Appendable output)
throws IOException {
- UNICODE_PRINTER.print(message, new TextGenerator(output));
+ Printer.UNICODE.print(message, multiLineOutput(output));
}
/**
@@ -100,7 +92,7 @@ public final class TextFormat {
public static void printUnicode(final UnknownFieldSet fields,
final Appendable output)
throws IOException {
- UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(output));
+ Printer.UNICODE.printUnknownFields(fields, multiLineOutput(output));
}
/**
@@ -109,10 +101,9 @@ public final class TextFormat {
*/
public static String shortDebugString(final MessageOrBuilder message) {
try {
- final StringBuilder sb = new StringBuilder();
- SINGLE_LINE_PRINTER.print(message, new TextGenerator(sb));
- // Single line mode currently might have an extra space at the end.
- return sb.toString().trim();
+ final StringBuilder text = new StringBuilder();
+ Printer.DEFAULT.print(message, singleLineOutput(text));
+ return text.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -125,11 +116,11 @@ public final class TextFormat {
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();
+ final StringBuilder text = new StringBuilder();
+ Printer.DEFAULT.printField(field, value, singleLineOutput(text));
+ return text.toString();
} catch (IOException e) {
- throw new IllegalStateException(e);
+ throw new IllegalStateException(e);
}
}
@@ -139,10 +130,9 @@ public final class TextFormat {
*/
public static String shortDebugString(final UnknownFieldSet fields) {
try {
- final StringBuilder sb = new StringBuilder();
- SINGLE_LINE_PRINTER.printUnknownFields(fields, new TextGenerator(sb));
- // Single line mode currently might have an extra space at the end.
- return sb.toString().trim();
+ final StringBuilder text = new StringBuilder();
+ Printer.DEFAULT.printUnknownFields(fields, singleLineOutput(text));
+ return text.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
@@ -183,7 +173,7 @@ public final class TextFormat {
public static String printToUnicodeString(final MessageOrBuilder message) {
try {
final StringBuilder text = new StringBuilder();
- UNICODE_PRINTER.print(message, new TextGenerator(text));
+ Printer.UNICODE.print(message, multiLineOutput(text));
return text.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
@@ -197,7 +187,7 @@ public final class TextFormat {
public static String printToUnicodeString(final UnknownFieldSet fields) {
try {
final StringBuilder text = new StringBuilder();
- UNICODE_PRINTER.printUnknownFields(fields, new TextGenerator(text));
+ Printer.UNICODE.printUnknownFields(fields, multiLineOutput(text));
return text.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
@@ -208,7 +198,7 @@ public final class TextFormat {
final Object value,
final Appendable output)
throws IOException {
- DEFAULT_PRINTER.printField(field, value, new TextGenerator(output));
+ Printer.DEFAULT.printField(field, value, multiLineOutput(output));
}
public static String printFieldToString(final FieldDescriptor field,
@@ -223,6 +213,23 @@ public final class TextFormat {
}
/**
+ * Outputs a unicode textual representation of the value of given field value.
+ *
+ * <p>Same as {@code printFieldValue()}, except that non-ASCII characters in string type fields
+ * are not escaped in backslash+octals.
+ *
+ * @param field the descriptor of the field
+ * @param value the value of the field
+ * @param output the output to which to append the formatted value
+ * @throws ClassCastException if the value is not appropriate for the given field descriptor
+ * @throws IOException if there is an exception writing to the output
+ */
+ public static void printUnicodeFieldValue(
+ final FieldDescriptor field, final Object value, final Appendable output) throws IOException {
+ Printer.UNICODE.printFieldValue(field, value, multiLineOutput(output));
+ }
+
+ /**
* Outputs a textual representation of the value of given field value.
*
* @param field the descriptor of the field
@@ -236,7 +243,7 @@ public final class TextFormat {
final Object value,
final Appendable output)
throws IOException {
- DEFAULT_PRINTER.printFieldValue(field, value, new TextGenerator(output));
+ Printer.DEFAULT.printFieldValue(field, value, multiLineOutput(output));
}
/**
@@ -253,7 +260,7 @@ public final class TextFormat {
final Object value,
final Appendable output)
throws IOException {
- printUnknownFieldValue(tag, value, new TextGenerator(output));
+ printUnknownFieldValue(tag, value, multiLineOutput(output));
}
private static void printUnknownFieldValue(final int tag,
@@ -272,12 +279,24 @@ public final class TextFormat {
generator.print(String.format((Locale) null, "0x%016x", (Long) value));
break;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
- generator.print("\"");
- generator.print(escapeBytes((ByteString) value));
- generator.print("\"");
+ try {
+ // Try to parse and print the field as an embedded message
+ UnknownFieldSet message = UnknownFieldSet.parseFrom((ByteString) value);
+ generator.print("{");
+ generator.eol();
+ generator.indent();
+ Printer.DEFAULT.printUnknownFields(message, generator);
+ generator.outdent();
+ generator.print("}");
+ } catch (InvalidProtocolBufferException e) {
+ // If not parseable as a message, print as a String
+ generator.print("\"");
+ generator.print(escapeBytes((ByteString) value));
+ generator.print("\"");
+ }
break;
case WireFormat.WIRETYPE_START_GROUP:
- DEFAULT_PRINTER.printUnknownFields((UnknownFieldSet) value, generator);
+ Printer.DEFAULT.printUnknownFields((UnknownFieldSet) value, generator);
break;
default:
throw new IllegalArgumentException("Bad tag: " + tag);
@@ -286,24 +305,16 @@ public final class TextFormat {
/** Helper class for converting protobufs to text. */
private static final class Printer {
- /** Whether to omit newlines from the output. */
- boolean singleLineMode = false;
+ // Printer instance which escapes non-ASCII characters.
+ static final Printer DEFAULT = new Printer(true);
+ // Printer instance which emits Unicode (it still escapes newlines and quotes in strings).
+ static final Printer UNICODE = new Printer(false);
/** Whether to escape non ASCII characters with backslash and octal. */
- boolean escapeNonAscii = true;
-
- private Printer() {}
-
- /** Setter of singleLineMode */
- private Printer setSingleLineMode(boolean singleLineMode) {
- this.singleLineMode = singleLineMode;
- return this;
- }
+ private final boolean escapeNonAscii;
- /** Setter of escapeNonAscii */
- private Printer setEscapeNonAscii(boolean escapeNonAscii) {
+ private Printer(boolean escapeNonAscii) {
this.escapeNonAscii = escapeNonAscii;
- return this;
}
private void print(
@@ -355,12 +366,9 @@ public final class TextFormat {
}
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
- if (singleLineMode) {
- generator.print(" { ");
- } else {
- generator.print(" {\n");
- generator.indent();
- }
+ generator.print(" {");
+ generator.eol();
+ generator.indent();
} else {
generator.print(": ");
}
@@ -368,19 +376,10 @@ public final class TextFormat {
printFieldValue(field, value, generator);
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
- if (singleLineMode) {
- generator.print("} ");
- } else {
- generator.outdent();
- generator.print("}\n");
- }
- } else {
- if (singleLineMode) {
- generator.print(" ");
- } else {
- generator.print("\n");
- }
+ generator.outdent();
+ generator.print("}");
}
+ generator.eol();
}
private void printFieldValue(final FieldDescriptor field,
@@ -425,7 +424,7 @@ public final class TextFormat {
case STRING:
generator.print("\"");
generator.print(escapeNonAscii
- ? escapeText((String) value)
+ ? TextFormatEscaper.escapeText((String) value)
: escapeDoubleQuotesAndBackslashes((String) value)
.replace("\n", "\\n"));
generator.print("\"");
@@ -469,19 +468,13 @@ public final class TextFormat {
field.getLengthDelimitedList(), generator);
for (final UnknownFieldSet value : field.getGroupList()) {
generator.print(entry.getKey().toString());
- if (singleLineMode) {
- generator.print(" { ");
- } else {
- generator.print(" {\n");
- generator.indent();
- }
+ generator.print(" {");
+ generator.eol();
+ generator.indent();
printUnknownFields(value, generator);
- if (singleLineMode) {
- generator.print("} ");
- } else {
- generator.outdent();
- generator.print("}\n");
- }
+ generator.outdent();
+ generator.print("}");
+ generator.eol();
}
}
}
@@ -495,7 +488,7 @@ public final class TextFormat {
generator.print(String.valueOf(number));
generator.print(": ");
printUnknownFieldValue(wireType, value, generator);
- generator.print(singleLineMode ? " " : "\n");
+ generator.eol();
}
}
}
@@ -521,16 +514,29 @@ public final class TextFormat {
}
}
- /**
+ private static TextGenerator multiLineOutput(Appendable output) {
+ return new TextGenerator(output, false);
+ }
+
+ private static TextGenerator singleLineOutput(Appendable output) {
+ return new TextGenerator(output, true);
+ }
+
+ /**
* An inner class for writing text to the output stream.
*/
private static final class TextGenerator {
private final Appendable output;
private final StringBuilder indent = new StringBuilder();
- private boolean atStartOfLine = true;
+ private final boolean singleLineMode;
+ // While technically we are "at the start of a line" at the very beginning of the output, all
+ // we would do in response to this is emit the (zero length) indentation, so it has no effect.
+ // Setting it false here does however suppress an unwanted leading space in single-line mode.
+ private boolean atStartOfLine = false;
- private TextGenerator(final Appendable output) {
+ private TextGenerator(final Appendable output, boolean singleLineMode) {
this.output = output;
+ this.singleLineMode = singleLineMode;
}
/**
@@ -552,35 +558,31 @@ public final class TextFormat {
throw new IllegalArgumentException(
" Outdent() without matching Indent().");
}
- indent.delete(length - 2, length);
+ indent.setLength(length - 2);
}
/**
- * Print text to the output stream.
+ * Print text to the output stream. Bare newlines are never expected to be passed to this
+ * method; to indicate the end of a line, call "eol()".
*/
public void print(final CharSequence text) throws IOException {
- final int size = text.length();
- int pos = 0;
-
- for (int i = 0; i < size; i++) {
- if (text.charAt(i) == '\n') {
- write(text.subSequence(pos, i + 1));
- pos = i + 1;
- atStartOfLine = true;
- }
+ if (atStartOfLine) {
+ atStartOfLine = false;
+ output.append(singleLineMode ? " " : indent);
}
- write(text.subSequence(pos, size));
+ output.append(text);
}
- private void write(final CharSequence data) throws IOException {
- if (data.length() == 0) {
- return;
- }
- if (atStartOfLine) {
- atStartOfLine = false;
- output.append(indent);
+ /**
+ * Signifies reaching the "end of the current line" in the output. In single-line mode, this
+ * does not result in a newline being emitted, but ensures that a separating space is written
+ * before the next output.
+ */
+ public void eol() throws IOException {
+ if (!singleLineMode) {
+ output.append("\n");
}
- output.append(data);
+ atStartOfLine = true;
}
}
@@ -661,6 +663,22 @@ public final class TextFormat {
nextToken();
}
+ int getPreviousLine() {
+ return previousLine;
+ }
+
+ int getPreviousColumn() {
+ return previousColumn;
+ }
+
+ int getLine() {
+ return line;
+ }
+
+ int getColumn() {
+ return column;
+ }
+
/** Are we at the end of the input? */
public boolean atEnd() {
return currentToken.length() == 0;
@@ -957,17 +975,19 @@ public final class TextFormat {
*/
public boolean consumeBoolean() throws ParseException {
if (currentToken.equals("true")
+ || currentToken.equals("True")
|| currentToken.equals("t")
|| currentToken.equals("1")) {
nextToken();
return true;
} else if (currentToken.equals("false")
+ || currentToken.equals("False")
|| currentToken.equals("f")
|| currentToken.equals("0")) {
nextToken();
return false;
} else {
- throw parseException("Expected \"true\" or \"false\".");
+ throw parseException("Expected \"true\" or \"false\". Found \"" + currentToken + "\".");
}
}
@@ -1074,7 +1094,7 @@ public final class TextFormat {
private ParseException floatParseException(final NumberFormatException e) {
return parseException("Couldn't parse number: " + e.getMessage());
}
-
+
/**
* Returns a {@link UnknownFieldParseException} with the line and column
* numbers of the previous token in the description, and the unknown field
@@ -1133,7 +1153,7 @@ public final class TextFormat {
return column;
}
}
-
+
/**
* Thrown when encountering an unknown field while parsing
* a text format message.
@@ -1204,6 +1224,22 @@ public final class TextFormat {
}
/**
+ * Parse a text-format message from {@code input}.
+ *
+ * @return the parsed message, guaranteed initialized
+ */
+ public static <T extends Message> T parse(final CharSequence input,
+ final Class<T> protoClass)
+ throws ParseException {
+ Message.Builder builder =
+ Internal.getDefaultInstance(protoClass).newBuilderForType();
+ merge(input, builder);
+ @SuppressWarnings("unchecked")
+ T output = (T) builder.build();
+ return output;
+ }
+
+ /**
* Parse a text-format message from {@code input} and merge the contents
* into {@code builder}. Extensions will be recognized if they are
* registered in {@code extensionRegistry}.
@@ -1228,6 +1264,25 @@ public final class TextFormat {
PARSER.merge(input, extensionRegistry, builder);
}
+ /**
+ * Parse a text-format message from {@code input}. Extensions will be
+ * recognized if they are registered in {@code extensionRegistry}.
+ *
+ * @return the parsed message, guaranteed initialized
+ */
+ public static <T extends Message> T parse(
+ final CharSequence input,
+ final ExtensionRegistry extensionRegistry,
+ final Class<T> protoClass)
+ throws ParseException {
+ Message.Builder builder =
+ Internal.getDefaultInstance(protoClass).newBuilderForType();
+ merge(input, extensionRegistry, builder);
+ @SuppressWarnings("unchecked")
+ T output = (T) builder.build();
+ return output;
+ }
+
/**
* Parser for text-format proto2 instances. This class is thread-safe.
@@ -1256,12 +1311,19 @@ public final class TextFormat {
}
private final boolean allowUnknownFields;
+ private final boolean allowUnknownEnumValues;
private final SingularOverwritePolicy singularOverwritePolicy;
+ private TextFormatParseInfoTree.Builder parseInfoTreeBuilder;
- private Parser(boolean allowUnknownFields,
- SingularOverwritePolicy singularOverwritePolicy) {
+ private Parser(
+ boolean allowUnknownFields,
+ boolean allowUnknownEnumValues,
+ SingularOverwritePolicy singularOverwritePolicy,
+ TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
this.allowUnknownFields = allowUnknownFields;
+ this.allowUnknownEnumValues = allowUnknownEnumValues;
this.singularOverwritePolicy = singularOverwritePolicy;
+ this.parseInfoTreeBuilder = parseInfoTreeBuilder;
}
/**
@@ -1276,8 +1338,10 @@ public final class TextFormat {
*/
public static class Builder {
private boolean allowUnknownFields = false;
+ private boolean allowUnknownEnumValues = false;
private SingularOverwritePolicy singularOverwritePolicy =
SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES;
+ private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null;
/**
@@ -1288,8 +1352,18 @@ public final class TextFormat {
return this;
}
+ public Builder setParseInfoTreeBuilder(
+ TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
+ this.parseInfoTreeBuilder = parseInfoTreeBuilder;
+ return this;
+ }
+
public Parser build() {
- return new Parser(allowUnknownFields, singularOverwritePolicy);
+ return new Parser(
+ allowUnknownFields,
+ allowUnknownEnumValues,
+ singularOverwritePolicy,
+ parseInfoTreeBuilder);
}
}
@@ -1353,6 +1427,28 @@ public final class TextFormat {
return text;
}
+ // Check both unknown fields and unknown extensions and log warning messages
+ // or throw exceptions according to the flag.
+ private void checkUnknownFields(final List<String> unknownFields)
+ throws ParseException {
+ if (unknownFields.isEmpty()) {
+ return;
+ }
+
+ StringBuilder msg = new StringBuilder("Input contains unknown fields and/or extensions:");
+ for (String field : unknownFields) {
+ msg.append('\n').append(field);
+ }
+
+ if (allowUnknownFields) {
+ logger.warning(msg.toString());
+ } else {
+ String[] lineColumn = unknownFields.get(0).split(":");
+ throw new ParseException(Integer.valueOf(lineColumn[0]),
+ Integer.valueOf(lineColumn[1]), msg.toString());
+ }
+ }
+
/**
* Parse a text-format message from {@code input} and merge the contents
* into {@code builder}. Extensions will be recognized if they are
@@ -1366,9 +1462,13 @@ public final class TextFormat {
MessageReflection.BuilderAdapter target =
new MessageReflection.BuilderAdapter(builder);
+ List<String> unknownFields = new ArrayList<String>();
+
while (!tokenizer.atEnd()) {
- mergeField(tokenizer, extensionRegistry, target);
+ mergeField(tokenizer, extensionRegistry, target, unknownFields);
}
+
+ checkUnknownFields(unknownFields);
}
@@ -1378,9 +1478,26 @@ public final class TextFormat {
*/
private void mergeField(final Tokenizer tokenizer,
final ExtensionRegistry extensionRegistry,
- final MessageReflection.MergeTarget target)
+ final MessageReflection.MergeTarget target,
+ List<String> unknownFields)
+ throws ParseException {
+ mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder,
+ unknownFields);
+ }
+
+ /**
+ * Parse a single field from {@code tokenizer} and merge it into
+ * {@code target}.
+ */
+ private void mergeField(final Tokenizer tokenizer,
+ final ExtensionRegistry extensionRegistry,
+ final MessageReflection.MergeTarget target,
+ TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
throws ParseException {
FieldDescriptor field = null;
+ int startLine = tokenizer.getLine();
+ int startColumn = tokenizer.getColumn();
final Descriptor type = target.getDescriptorForType();
ExtensionRegistry.ExtensionInfo extension = null;
@@ -1397,13 +1514,15 @@ public final class TextFormat {
extensionRegistry, name.toString());
if (extension == null) {
- if (!allowUnknownFields) {
- throw tokenizer.parseExceptionPreviousToken(
- "Extension \"" + name + "\" not found in the ExtensionRegistry.");
- } else {
- logger.warning(
- "Extension \"" + name + "\" not found in the ExtensionRegistry.");
- }
+ unknownFields.add(
+ (tokenizer.getPreviousLine() + 1)
+ + ":"
+ + (tokenizer.getPreviousColumn() + 1)
+ + ":\t"
+ + type.getFullName()
+ + ".["
+ + name
+ + "]");
} else {
if (extension.descriptor.getContainingType() != type) {
throw tokenizer.parseExceptionPreviousToken(
@@ -1438,16 +1557,14 @@ public final class TextFormat {
}
if (field == null) {
- if (!allowUnknownFields) {
- throw tokenizer.unknownFieldParseExceptionPreviousToken(
- name,
- "Message type \"" + type.getFullName()
- + "\" has no field named \"" + name + "\".");
- } else {
- logger.warning(
- "Message type \"" + type.getFullName()
- + "\" has no field named \"" + name + "\".");
- }
+ unknownFields.add(
+ (tokenizer.getPreviousLine() + 1)
+ + ":"
+ + (tokenizer.getPreviousColumn() + 1)
+ + ":\t"
+ + type.getFullName()
+ + "."
+ + name);
}
}
@@ -1472,22 +1589,24 @@ public final class TextFormat {
// Handle potential ':'.
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
tokenizer.tryConsume(":"); // optional
+ if (parseTreeBuilder != null) {
+ TextFormatParseInfoTree.Builder childParseTreeBuilder =
+ parseTreeBuilder.getBuilderForSubMessageField(field);
+ consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
+ childParseTreeBuilder, unknownFields);
+ } else {
+ consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
+ parseTreeBuilder, unknownFields);
+ }
} else {
tokenizer.consume(":"); // required
+ consumeFieldValues(tokenizer, extensionRegistry, target, field,
+ extension, parseTreeBuilder, unknownFields);
}
- // Support specifying repeated field values as a comma-separated list.
- // Ex."foo: [1, 2, 3]"
- if (field.isRepeated() && tokenizer.tryConsume("[")) {
- while (true) {
- consumeFieldValue(tokenizer, extensionRegistry, target, field, extension);
- if (tokenizer.tryConsume("]")) {
- // End of list.
- break;
- }
- tokenizer.consume(",");
- }
- } else {
- consumeFieldValue(tokenizer, extensionRegistry, target, field, extension);
+
+ if (parseTreeBuilder != null) {
+ parseTreeBuilder.setLocation(
+ field, TextFormatParseLocation.create(startLine, startColumn));
}
// For historical reasons, fields may optionally be separated by commas or
@@ -1498,6 +1617,45 @@ public final class TextFormat {
}
/**
+ * Parse a one or more field values from {@code tokenizer} and merge it into
+ * {@code builder}.
+ */
+ private void consumeFieldValues(
+ final Tokenizer tokenizer,
+ final ExtensionRegistry extensionRegistry,
+ final MessageReflection.MergeTarget target,
+ final FieldDescriptor field,
+ final ExtensionRegistry.ExtensionInfo extension,
+ final TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
+ throws ParseException {
+ // Support specifying repeated field values as a comma-separated list.
+ // Ex."foo: [1, 2, 3]"
+ if (field.isRepeated() && tokenizer.tryConsume("[")) {
+ if (!tokenizer.tryConsume("]")) { // Allow "foo: []" to be treated as empty.
+ while (true) {
+ consumeFieldValue(
+ tokenizer,
+ extensionRegistry,
+ target,
+ field,
+ extension,
+ parseTreeBuilder,
+ unknownFields);
+ if (tokenizer.tryConsume("]")) {
+ // End of list.
+ break;
+ }
+ tokenizer.consume(",");
+ }
+ }
+ } else {
+ consumeFieldValue(tokenizer, extensionRegistry, target, field,
+ extension, parseTreeBuilder, unknownFields);
+ }
+ }
+
+ /**
* Parse a single field value from {@code tokenizer} and merge it into
* {@code builder}.
*/
@@ -1506,7 +1664,9 @@ public final class TextFormat {
final ExtensionRegistry extensionRegistry,
final MessageReflection.MergeTarget target,
final FieldDescriptor field,
- final ExtensionRegistry.ExtensionInfo extension)
+ final ExtensionRegistry.ExtensionInfo extension,
+ final TextFormatParseInfoTree.Builder parseTreeBuilder,
+ List<String> unknownFields)
throws ParseException {
Object value = null;
@@ -1528,7 +1688,8 @@ public final class TextFormat {
throw tokenizer.parseException(
"Expected \"" + endToken + "\".");
}
- mergeField(tokenizer, extensionRegistry, subField);
+ mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder,
+ unknownFields);
}
value = subField.finish();
@@ -1584,17 +1745,40 @@ public final class TextFormat {
final int number = tokenizer.consumeInt32();
value = enumType.findValueByNumber(number);
if (value == null) {
- throw tokenizer.parseExceptionPreviousToken(
- "Enum type \"" + enumType.getFullName()
- + "\" has no value with number " + number + '.');
+ String unknownValueMsg =
+ "Enum type \""
+ + enumType.getFullName()
+ + "\" has no value with number "
+ + number
+ + '.';
+ if (allowUnknownEnumValues) {
+ logger.warning(unknownValueMsg);
+ return;
+ } else {
+ throw tokenizer.parseExceptionPreviousToken(
+ "Enum type \""
+ + enumType.getFullName()
+ + "\" has no value with number "
+ + number
+ + '.');
+ }
}
} else {
final String id = tokenizer.consumeIdentifier();
value = enumType.findValueByName(id);
if (value == null) {
- throw tokenizer.parseExceptionPreviousToken(
- "Enum type \"" + enumType.getFullName()
- + "\" has no value named \"" + id + "\".");
+ String unknownValueMsg =
+ "Enum type \""
+ + enumType.getFullName()
+ + "\" has no value named \""
+ + id
+ + "\".";
+ if (allowUnknownEnumValues) {
+ logger.warning(unknownValueMsg);
+ return;
+ } else {
+ throw tokenizer.parseExceptionPreviousToken(unknownValueMsg);
+ }
}
}
@@ -1607,6 +1791,8 @@ public final class TextFormat {
}
if (field.isRepeated()) {
+ // TODO(b/29122459): If field.isMapField() and FORBID_SINGULAR_OVERWRITES mode,
+ // check for duplicate map keys here.
target.addRepeatedField(field, value);
} else if ((singularOverwritePolicy
== SingularOverwritePolicy.FORBID_SINGULAR_OVERWRITES)
@@ -1704,11 +1890,6 @@ public final class TextFormat {
// Some of these methods are package-private because Descriptors.java uses
// them.
- private interface ByteSequence {
- int size();
- byte byteAt(int offset);
- }
-
/**
* Escapes bytes in the format used in protocol buffer text format, which
* is the same as the format used for C string literals. All bytes
@@ -1717,74 +1898,15 @@ public final class TextFormat {
* which no defined short-hand escape sequence is defined will be escaped
* using 3-digit octal sequences.
*/
- public static String escapeBytes(final ByteSequence input) {
- final StringBuilder builder = new StringBuilder(input.size());
- for (int i = 0; i < input.size(); i++) {
- final byte b = input.byteAt(i);
- switch (b) {
- // Java does not recognize \a or \v, apparently.
- case 0x07: builder.append("\\a"); break;
- case '\b': builder.append("\\b"); break;
- case '\f': builder.append("\\f"); break;
- case '\n': builder.append("\\n"); break;
- case '\r': builder.append("\\r"); break;
- case '\t': builder.append("\\t"); break;
- case 0x0b: builder.append("\\v"); break;
- case '\\': builder.append("\\\\"); break;
- case '\'': builder.append("\\\'"); break;
- case '"' : builder.append("\\\""); break;
- default:
- // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
- // printable. Other byte values must be escaped.
- if (b >= 0x20 && b <= 0x7e) {
- builder.append((char) b);
- } else {
- builder.append('\\');
- builder.append((char) ('0' + ((b >>> 6) & 3)));
- builder.append((char) ('0' + ((b >>> 3) & 7)));
- builder.append((char) ('0' + (b & 7)));
- }
- break;
- }
- }
- return builder.toString();
- }
-
- /**
- * Escapes bytes in the format used in protocol buffer text format, which
- * is the same as the format used for C string literals. All bytes
- * that are not printable 7-bit ASCII characters are escaped, as well as
- * backslash, single-quote, and double-quote characters. Characters for
- * which no defined short-hand escape sequence is defined will be escaped
- * using 3-digit octal sequences.
- */
- public static String escapeBytes(final ByteString input) {
- return escapeBytes(new ByteSequence() {
- @Override
- public int size() {
- return input.size();
- }
- @Override
- public byte byteAt(int offset) {
- return input.byteAt(offset);
- }
- });
+ public static String escapeBytes(ByteString input) {
+ return TextFormatEscaper.escapeBytes(input);
}
/**
* Like {@link #escapeBytes(ByteString)}, but used for byte array.
*/
- public static String escapeBytes(final byte[] input) {
- return escapeBytes(new ByteSequence() {
- @Override
- public int size() {
- return input.length;
- }
- @Override
- public byte byteAt(int offset) {
- return input[offset];
- }
- });
+ public static String escapeBytes(byte[] input) {
+ return TextFormatEscaper.escapeBytes(input);
}
/**
@@ -1868,7 +1990,9 @@ public final class TextFormat {
}
}
- return ByteString.copyFrom(result, 0, pos);
+ return result.length == pos
+ ? ByteString.wrap(result) // This reference has not been out of our control.
+ : ByteString.copyFrom(result, 0, pos);
}
/**
@@ -1896,7 +2020,7 @@ public final class TextFormat {
* Escape double quotes and backslashes in a String for unicode output of a message.
*/
public static String escapeDoubleQuotesAndBackslashes(final String input) {
- return input.replace("\\", "\\\\").replace("\"", "\\\"");
+ return TextFormatEscaper.escapeDoubleQuotesAndBackslashes(input);
}
/**
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java
index e69de29b..da9ceadd 100644
--- a/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java
+++ b/java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java
@@ -0,0 +1,137 @@
+// 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;
+
+/**
+ * Provide text format escaping support for proto2 instances.
+ */
+final class TextFormatEscaper {
+ private TextFormatEscaper() {}
+
+ private interface ByteSequence {
+ int size();
+ byte byteAt(int offset);
+ }
+
+ /**
+ * Escapes bytes in the format used in protocol buffer text format, which
+ * is the same as the format used for C string literals. All bytes
+ * that are not printable 7-bit ASCII characters are escaped, as well as
+ * backslash, single-quote, and double-quote characters. Characters for
+ * which no defined short-hand escape sequence is defined will be escaped
+ * using 3-digit octal sequences.
+ */
+ static String escapeBytes(final ByteSequence input) {
+ final StringBuilder builder = new StringBuilder(input.size());
+ for (int i = 0; i < input.size(); i++) {
+ final byte b = input.byteAt(i);
+ switch (b) {
+ // Java does not recognize \a or \v, apparently.
+ case 0x07: builder.append("\\a"); break;
+ case '\b': builder.append("\\b"); break;
+ case '\f': builder.append("\\f"); break;
+ case '\n': builder.append("\\n"); break;
+ case '\r': builder.append("\\r"); break;
+ case '\t': builder.append("\\t"); break;
+ case 0x0b: builder.append("\\v"); break;
+ case '\\': builder.append("\\\\"); break;
+ case '\'': builder.append("\\\'"); break;
+ case '"' : builder.append("\\\""); break;
+ default:
+ // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
+ // printable. Other byte values must be escaped.
+ if (b >= 0x20 && b <= 0x7e) {
+ builder.append((char) b);
+ } else {
+ builder.append('\\');
+ builder.append((char) ('0' + ((b >>> 6) & 3)));
+ builder.append((char) ('0' + ((b >>> 3) & 7)));
+ builder.append((char) ('0' + (b & 7)));
+ }
+ break;
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Escapes bytes in the format used in protocol buffer text format, which
+ * is the same as the format used for C string literals. All bytes
+ * that are not printable 7-bit ASCII characters are escaped, as well as
+ * backslash, single-quote, and double-quote characters. Characters for
+ * which no defined short-hand escape sequence is defined will be escaped
+ * using 3-digit octal sequences.
+ */
+ static String escapeBytes(final ByteString input) {
+ return escapeBytes(new ByteSequence() {
+ @Override
+ public int size() {
+ return input.size();
+ }
+ @Override
+ public byte byteAt(int offset) {
+ return input.byteAt(offset);
+ }
+ });
+ }
+
+ /**
+ * Like {@link #escapeBytes(ByteString)}, but used for byte array.
+ */
+ static String escapeBytes(final byte[] input) {
+ return escapeBytes(new ByteSequence() {
+ @Override
+ public int size() {
+ return input.length;
+ }
+ @Override
+ public byte byteAt(int offset) {
+ return input[offset];
+ }
+ });
+ }
+
+ /**
+ * Like {@link #escapeBytes(ByteString)}, but escapes a text string.
+ * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped
+ * individually as a 3-digit octal escape. Yes, it's weird.
+ */
+ static String escapeText(final String input) {
+ return escapeBytes(ByteString.copyFromUtf8(input));
+ }
+
+ /**
+ * Escape double quotes and backslashes in a String for unicode output of a message.
+ */
+ static String escapeDoubleQuotesAndBackslashes(final String input) {
+ return input.replace("\\", "\\\\").replace("\"", "\\\"");
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java b/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java
new file mode 100644
index 00000000..0127ce92
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java
@@ -0,0 +1,226 @@
+// 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 com.google.protobuf.Descriptors.FieldDescriptor;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+/**
+ * Data structure which is populated with the locations of each field value parsed from the text.
+ *
+ * <p>The locations of primary fields values are retrieved by {@code getLocation} or
+ * {@code getLocations}. The locations of sub message values are within nested
+ * {@code TextFormatParseInfoTree}s and are retrieve by {@code getNestedTree} or
+ * {@code getNestedTrees}.
+ *
+ * <p>The {@code TextFormatParseInfoTree} is created by a Builder.
+ */
+public class TextFormatParseInfoTree {
+
+ // Defines a mapping between each field's descriptor to the list of locations where
+ // its value(s) were was encountered.
+ private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
+
+ // Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for
+ // sub message location information.
+ Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField;
+
+ /**
+ * Construct a {@code TextFormatParseInfoTree}.
+ *
+ * @param locationsFromField a map of fields to location in the source code
+ * @param subtreeBuildersFromField a map of fields to parse tree location information builders
+ */
+ private TextFormatParseInfoTree(
+ Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField,
+ Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) {
+
+ // The maps are unmodifiable. The values in the maps are unmodifiable.
+ Map<FieldDescriptor, List<TextFormatParseLocation>> locs =
+ new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
+ for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) {
+ locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue()));
+ }
+ this.locationsFromField = Collections.unmodifiableMap(locs);
+
+ Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs =
+ new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>();
+ for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) {
+ List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>();
+ for (Builder subBuilder : kv.getValue()) {
+ submessagesOfField.add(subBuilder.build());
+ }
+ subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField));
+ }
+ this.subtreesFromField = Collections.unmodifiableMap(subs);
+ }
+
+ /**
+ * Retrieve all the locations of a field.
+ *
+ * @param fieldDescriptor the the @{link FieldDescriptor} of the desired field
+ * @return a list of the locations of values of the field. If there are not values
+ * or the field doesn't exist, an empty list is returned.
+ */
+ public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) {
+ List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor);
+ return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result;
+ }
+
+ /**
+ * Get the location in the source of a field's value.
+ *
+ * <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed
+ * text.
+ *
+ * @param fieldDescriptor the @{link FieldDescriptor} of the desired field
+ * @param index the index of the value.
+ * @return the {@link TextFormatParseLocation} of the value
+ * @throws IllegalArgumentException index is out of range
+ */
+ public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) {
+ return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor);
+ }
+
+ /**
+ * Retrieve a list of all the location information trees for a sub message field.
+ *
+ * @param fieldDescriptor the @{link FieldDescriptor} of the desired field
+ * @return A list of {@link TextFormatParseInfoTree}
+ */
+ public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) {
+ List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor);
+ return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result;
+ }
+
+ /**
+ * Returns the parse info tree for the given field, which must be a message type.
+ *
+ * @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message
+ * @param index the index of message value.
+ * @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field
+ * doesn't exist or the index is out of range.
+ * @throws IllegalArgumentException if index is out of range
+ */
+ public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) {
+ return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor);
+ }
+
+ /**
+ * Create a builder for a {@code ParseInfoTree}.
+ *
+ * @return the builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) {
+ if (index >= list.size() || index < 0) {
+ throw new IllegalArgumentException(String.format("Illegal index field: %s, index %d",
+ fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index));
+ }
+ return list.get(index);
+ }
+
+ /**
+ * Builder for a {@link TextFormatParseInfoTree}.
+ */
+ public static class Builder {
+
+ private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
+
+ // Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for
+ // sub message location information.
+ private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField;
+
+ /**
+ * Create a root level {@ParseInfoTree} builder.
+ */
+ private Builder() {
+ locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
+ subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>();
+ }
+
+ /**
+ * Record the starting location of a single value for a field.
+ *
+ * @param fieldDescriptor the field
+ * @param location source code location information
+ */
+ public Builder setLocation(
+ final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) {
+ List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor);
+ if (fieldLocations == null) {
+ fieldLocations = new ArrayList<TextFormatParseLocation>();
+ locationsFromField.put(fieldDescriptor, fieldLocations);
+ }
+ fieldLocations.add(location);
+ return this;
+ }
+
+ /**
+ * Set for a sub message.
+ *
+ * <p>A new builder is created for a sub message. The builder that is returned is a new builder.
+ * The return is <em>not</em> the invoked {@code builder.getBuilderForSubMessageField}.
+ *
+ * @param fieldDescriptor the field whose value is the submessage
+ * @return a new Builder for the sub message
+ */
+ public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) {
+ List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor);
+ if (submessageBuilders == null) {
+ submessageBuilders = new ArrayList<Builder>();
+ subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders);
+ }
+ Builder subtreeBuilder = new Builder();
+ submessageBuilders.add(subtreeBuilder);
+ return subtreeBuilder;
+ }
+
+ /**
+ * Build the {@code TextFormatParseInfoTree}.
+ *
+ * @return the {@code TextFormatParseInfoTree}
+ */
+ public TextFormatParseInfoTree build() {
+ return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField);
+ }
+ }
+}
diff --git a/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java b/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java
new file mode 100644
index 00000000..cce286e1
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java
@@ -0,0 +1,104 @@
+// 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 java.util.Arrays;
+
+/**
+ * A location in the source code.
+ *
+ * <p>A location is the starting line number and starting column number.
+ */
+public final class TextFormatParseLocation {
+
+ /**
+ * The empty location.
+ */
+ public static final TextFormatParseLocation EMPTY = new TextFormatParseLocation(-1, -1);
+
+ /**
+ * Create a location.
+ *
+ * @param line the starting line number
+ * @param column the starting column number
+ * @return a {@code ParseLocation}
+ */
+ static TextFormatParseLocation create(int line, int column) {
+ if (line == -1 && column == -1) {
+ return EMPTY;
+ }
+ if (line < 0 || column < 0) {
+ throw new IllegalArgumentException(
+ String.format("line and column values must be >= 0: line %d, column: %d", line, column));
+ }
+ return new TextFormatParseLocation(line, column);
+ }
+
+ private final int line;
+ private final int column;
+
+ private TextFormatParseLocation(int line, int column) {
+ this.line = line;
+ this.column = column;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("ParseLocation{line=%d, column=%d}", line, column);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof TextFormatParseLocation)) {
+ return false;
+ }
+ TextFormatParseLocation that = (TextFormatParseLocation) o;
+ return (this.line == that.getLine())
+ && (this.column == that.getColumn());
+ }
+
+ @Override
+ public int hashCode() {
+ int[] values = {line, column};
+ return Arrays.hashCode(values);
+ }
+}
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 7cd2250e..37d64633 100644
--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java
@@ -31,7 +31,6 @@
package com.google.protobuf;
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -39,6 +38,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.TreeMap;
@@ -57,7 +57,10 @@ import java.util.TreeMap;
* @author kenton@google.com Kenton Varda
*/
public final class UnknownFieldSet implements MessageLite {
- private UnknownFieldSet() {}
+
+ private UnknownFieldSet() {
+ fields = null;
+ }
/** Create a new {@link Builder}. */
public static Builder newBuilder() {
@@ -76,20 +79,23 @@ public final class UnknownFieldSet implements MessageLite {
public static UnknownFieldSet getDefaultInstance() {
return defaultInstance;
}
+ @Override
public UnknownFieldSet getDefaultInstanceForType() {
return defaultInstance;
}
private static final UnknownFieldSet defaultInstance =
- new UnknownFieldSet(Collections.<Integer, Field>emptyMap());
+ 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.
*/
- private UnknownFieldSet(final Map<Integer, Field> fields) {
+ UnknownFieldSet(final Map<Integer, Field> fields,
+ final Map<Integer, Field> fieldsDescending) {
this.fields = fields;
}
- private Map<Integer, Field> fields;
+ private final Map<Integer, Field> fields;
@Override
@@ -126,9 +132,11 @@ public final class UnknownFieldSet implements MessageLite {
}
/** 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()) {
- entry.getValue().writeTo(entry.getKey(), output);
+ Field field = entry.getValue();
+ field.writeTo(entry.getKey(), output);
}
}
@@ -146,6 +154,7 @@ public final class UnknownFieldSet implements MessageLite {
* Serializes the message to a {@code ByteString} and returns it. This is
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
+ @Override
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
@@ -163,6 +172,7 @@ public final class UnknownFieldSet implements MessageLite {
* Serializes the message to a {@code byte} array and returns it. This is
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
+ @Override
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
@@ -181,12 +191,14 @@ public final class UnknownFieldSet implements MessageLite {
* Serializes the message and writes it to {@code output}. This is just a
* trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
+ @Override
public void writeTo(final OutputStream output) throws IOException {
final 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());
@@ -195,6 +207,7 @@ public final class UnknownFieldSet implements MessageLite {
}
/** Get the number of bytes required to encode this set. */
+ @Override
public int getSerializedSize() {
int result = 0;
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
@@ -215,10 +228,8 @@ public final class UnknownFieldSet implements MessageLite {
}
}
- /**
- * Get the number of bytes required to encode this set using
- * {@code MessageSet} wire format.
- */
+
+ /** 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()) {
@@ -228,6 +239,7 @@ public final class UnknownFieldSet implements MessageLite {
return result;
}
+ @Override
public boolean isInitialized() {
// UnknownFieldSets do not have required fields, so they are always
// initialized.
@@ -258,10 +270,12 @@ public final class UnknownFieldSet implements MessageLite {
return newBuilder().mergeFrom(input).build();
}
+ @Override
public Builder newBuilderForType() {
return newBuilder();
}
+ @Override
public Builder toBuilder() {
return newBuilder().mergeFrom(this);
}
@@ -329,18 +343,21 @@ public final class UnknownFieldSet implements MessageLite {
* in undefined behavior and can cause a {@code NullPointerException} to be
* thrown.
*/
+ @Override
public UnknownFieldSet build() {
- getFieldBuilder(0); // Force lastField to be built.
+ getFieldBuilder(0); // Force lastField to be built.
final UnknownFieldSet result;
if (fields.isEmpty()) {
result = getDefaultInstance();
} else {
- result = new UnknownFieldSet(Collections.unmodifiableMap(fields));
+ Map<Integer, Field> descendingFields = null;
+ result = new UnknownFieldSet(Collections.unmodifiableMap(fields), descendingFields);
}
fields = null;
return result;
}
+ @Override
public UnknownFieldSet buildPartial() {
// No required fields, so this is the same as build().
return build();
@@ -349,10 +366,12 @@ public final class UnknownFieldSet implements MessageLite {
@Override
public Builder clone() {
getFieldBuilder(0); // Force lastField to be built.
+ Map<Integer, Field> descendingFields = null;
return UnknownFieldSet.newBuilder().mergeFrom(
- new UnknownFieldSet(fields));
+ new UnknownFieldSet(fields, descendingFields));
}
+ @Override
public UnknownFieldSet getDefaultInstanceForType() {
return UnknownFieldSet.getDefaultInstance();
}
@@ -364,6 +383,7 @@ public final class UnknownFieldSet implements MessageLite {
}
/** Reset the builder to an empty set. */
+ @Override
public Builder clear() {
reinitialize();
return this;
@@ -487,6 +507,7 @@ public final class UnknownFieldSet implements MessageLite {
* Parse an entire message from {@code input} and merge its fields into
* this set.
*/
+ @Override
public Builder mergeFrom(final CodedInputStream input) throws IOException {
while (true) {
final int tag = input.readTag();
@@ -536,8 +557,8 @@ public final class UnknownFieldSet implements MessageLite {
* set being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
- public Builder mergeFrom(final ByteString data)
- throws InvalidProtocolBufferException {
+ @Override
+ public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
mergeFrom(input);
@@ -557,8 +578,8 @@ public final class UnknownFieldSet implements MessageLite {
* set being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
- public Builder mergeFrom(final byte[] data)
- throws InvalidProtocolBufferException {
+ @Override
+ public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException {
try {
final CodedInputStream input = CodedInputStream.newInstance(data);
mergeFrom(input);
@@ -578,6 +599,7 @@ public final class UnknownFieldSet implements MessageLite {
* set being built. 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);
mergeFrom(codedInput);
@@ -585,8 +607,8 @@ public final class UnknownFieldSet implements MessageLite {
return this;
}
- public boolean mergeDelimitedFrom(InputStream input)
- throws IOException {
+ @Override
+ public boolean mergeDelimitedFrom(InputStream input) throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
return false;
@@ -597,30 +619,29 @@ public final class UnknownFieldSet implements MessageLite {
return true;
}
- public boolean mergeDelimitedFrom(
- InputStream input,
- ExtensionRegistryLite extensionRegistry) throws IOException {
+ @Override
+ public boolean mergeDelimitedFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
// UnknownFieldSet has no extensions.
return mergeDelimitedFrom(input);
}
- public Builder mergeFrom(
- CodedInputStream input,
- ExtensionRegistryLite extensionRegistry) throws IOException {
+ @Override
+ public Builder mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
// UnknownFieldSet has no extensions.
return mergeFrom(input);
}
- public Builder mergeFrom(
- ByteString data,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public Builder mergeFrom(ByteString data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data);
}
- public Builder mergeFrom(byte[] data, int off, int len)
- throws InvalidProtocolBufferException {
+ @Override
+ public Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException {
try {
final CodedInputStream input =
CodedInputStream.newInstance(data, off, len);
@@ -636,29 +657,37 @@ public final class UnknownFieldSet implements MessageLite {
}
}
- public Builder mergeFrom(
- byte[] data,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public Builder mergeFrom(byte[] data, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data);
}
- public Builder mergeFrom(
- byte[] data, int off, int len,
- ExtensionRegistryLite extensionRegistry)
+ @Override
+ public Builder mergeFrom(byte[] data, int off, int len, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data, off, len);
}
- public Builder mergeFrom(
- InputStream input,
- ExtensionRegistryLite extensionRegistry) throws IOException {
+ @Override
+ public Builder mergeFrom(InputStream input, ExtensionRegistryLite extensionRegistry)
+ throws IOException {
// UnknownFieldSet has no extensions.
return mergeFrom(input);
}
+ @Override
+ public Builder mergeFrom(MessageLite m) {
+ if (m instanceof UnknownFieldSet) {
+ return mergeFrom((UnknownFieldSet) m);
+ }
+ throw new IllegalArgumentException(
+ "mergeFrom(MessageLite) can only merge messages of the same type.");
+ }
+
+ @Override
public boolean isInitialized() {
// UnknownFieldSets do not have required fields, so they are always
// initialized.
@@ -816,9 +845,10 @@ public final class UnknownFieldSet implements MessageLite {
}
}
+
/**
- * Get the number of bytes required to encode this field, including field
- * number, using {@code MessageSet} wire format.
+ * Get the number of bytes required to encode this field, including field number, using {@code
+ * MessageSet} wire format.
*/
public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) {
int result = 0;
@@ -987,6 +1017,7 @@ public final class UnknownFieldSet implements MessageLite {
* Parser to implement MessageLite interface.
*/
public static final class Parser extends AbstractParser<UnknownFieldSet> {
+ @Override
public UnknownFieldSet parsePartialFrom(
CodedInputStream input, ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
@@ -996,7 +1027,7 @@ public final class UnknownFieldSet implements MessageLite {
} catch (InvalidProtocolBufferException e) {
throw e.setUnfinishedMessage(builder.buildPartial());
} catch (IOException e) {
- throw new InvalidProtocolBufferException(e.getMessage())
+ throw new InvalidProtocolBufferException(e)
.setUnfinishedMessage(builder.buildPartial());
}
return builder.buildPartial();
@@ -1004,6 +1035,7 @@ public final class UnknownFieldSet implements MessageLite {
}
private static final Parser PARSER = new Parser();
+ @Override
public final Parser getParserForType() {
return PARSER;
}
diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
index 435ad4d4..f0b919ad 100644
--- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
@@ -61,15 +61,6 @@ public final class UnknownFieldSetLite {
public static UnknownFieldSetLite getDefaultInstance() {
return DEFAULT_INSTANCE;
}
-
- /**
- * 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.
@@ -90,7 +81,7 @@ public final class UnknownFieldSetLite {
System.arraycopy(second.objects, 0, objects, first.count, second.count);
return new UnknownFieldSetLite(count, tags, objects, true /* isMutable */);
}
-
+
/**
* The number of elements in the set.
*/
@@ -185,6 +176,42 @@ public final class UnknownFieldSetLite {
}
/**
+ * Serializes the set and writes it to {@code output} using {@code MessageSet} wire format.
+ *
+ * <p>For use by generated code only.
+ */
+ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException {
+ for (int i = 0; i < count; i++) {
+ int fieldNumber = WireFormat.getTagFieldNumber(tags[i]);
+ output.writeRawMessageSetExtension(fieldNumber, (ByteString) objects[i]);
+ }
+ }
+
+
+ /**
+ * Get the number of bytes required to encode this field, including field number, using {@code
+ * MessageSet} wire format.
+ */
+ public int getSerializedSizeAsMessageSet() {
+ int size = memoizedSerializedSize;
+ if (size != -1) {
+ return size;
+ }
+
+ size = 0;
+ for (int i = 0; i < count; i++) {
+ int tag = tags[i];
+ int fieldNumber = WireFormat.getTagFieldNumber(tag);
+ size += CodedOutputStream.computeRawMessageSetExtensionSize(
+ fieldNumber, (ByteString) objects[i]);
+ }
+
+ memoizedSerializedSize = size;
+
+ return size;
+ }
+
+ /**
* Get the number of bytes required to encode this set.
*
* <p>For use by generated code only.
@@ -225,6 +252,24 @@ public final class UnknownFieldSetLite {
return size;
}
+
+ private static boolean equals(int[] tags1, int[] tags2, int count) {
+ for (int i = 0; i < count; ++i) {
+ if (tags1[i] != tags2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean equals(Object[] objects1, Object[] objects2, int count) {
+ for (int i = 0; i < count; ++i) {
+ if (!objects1[i].equals(objects2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
@Override
public boolean equals(Object obj) {
@@ -242,27 +287,59 @@ public final class UnknownFieldSetLite {
UnknownFieldSetLite other = (UnknownFieldSetLite) obj;
if (count != other.count
- // TODO(dweis): Only have to compare up to count but at worst 2x worse than we need to do.
- || !Arrays.equals(tags, other.tags)
- || !Arrays.deepEquals(objects, other.objects)) {
+ || !equals(tags, other.tags, count)
+ || !equals(objects, other.objects, count)) {
return false;
}
return true;
}
+ private static int hashCode(int[] tags, int count) {
+ int hashCode = 17;
+ for (int i = 0; i < count; ++i) {
+ hashCode = 31 * hashCode + tags[i];
+ }
+ return hashCode;
+ }
+
+ private static int hashCode(Object[] objects, int count) {
+ int hashCode = 17;
+ for (int i = 0; i < count; ++i) {
+ hashCode = 31 * hashCode + objects[i].hashCode();
+ }
+ return hashCode;
+ }
+
@Override
public int hashCode() {
int hashCode = 17;
-
+
hashCode = 31 * hashCode + count;
- hashCode = 31 * hashCode + Arrays.hashCode(tags);
- hashCode = 31 * hashCode + Arrays.deepHashCode(objects);
-
+ hashCode = 31 * hashCode + hashCode(tags, count);
+ hashCode = 31 * hashCode + hashCode(objects, count);
+
return hashCode;
}
- private void storeField(int tag, Object value) {
+ /**
+ * Prints a String representation of the unknown field set.
+ *
+ * <p>For use by generated code only.
+ *
+ * @param buffer the buffer to write to
+ * @param indent the number of spaces the fields should be indented by
+ */
+ final void printWithIndent(StringBuilder buffer, int indent) {
+ for (int i = 0; i < count; i++) {
+ int fieldNumber = WireFormat.getTagFieldNumber(tags[i]);
+ MessageLiteToString.printField(buffer, indent, String.valueOf(fieldNumber), objects[i]);
+ }
+ }
+
+ // Package private for unsafe experimental runtime.
+ void storeField(int tag, Object value) {
+ checkMutable();
ensureCapacity();
tags[count] = tag;
@@ -369,90 +446,4 @@ public final class UnknownFieldSetLite {
}
return this;
}
-
- /**
- * Builder for {@link UnknownFieldSetLite}s.
- *
- * <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
- *
- * <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 {
-
- private UnknownFieldSetLite set;
-
- private Builder() {
- this.set = null;
- }
-
- /**
- * Ensures internal state is initialized for use.
- */
- private void ensureNotBuilt() {
- if (set == null) {
- set = new UnknownFieldSetLite();
- }
-
- set.checkMutable();
- }
-
- /**
- * 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 {
- ensureNotBuilt();
- return set.mergeFieldFrom(tag, input);
- }
-
- /**
- * 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.
- */
- Builder mergeVarintField(int fieldNumber, int value) {
- ensureNotBuilt();
- 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) {
- ensureNotBuilt();
- set.mergeLengthDelimitedField(fieldNumber, value);
- 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 an
- * {@code UnsupportedOperationException} to be thrown.
- *
- * <p>For use by generated code only.
- */
- public UnknownFieldSetLite build() {
- if (set == null) {
- return DEFAULT_INSTANCE;
- }
-
- set.checkMutable();
- set.makeImmutable();
-
- return set;
- }
- }
}
diff --git a/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
index 5257c5a2..30e87911 100644
--- a/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
+++ b/java/core/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
@@ -68,42 +68,42 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
return list.size();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public ByteString getByteString(int index) {
return list.getByteString(index);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void add(ByteString element) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void set(int index, ByteString element) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean addAllByteString(Collection<? extends ByteString> element) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public byte[] getByteArray(int index) {
return list.getByteArray(index);
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void add(byte[] element) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void set(int index, byte[] element) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean addAllByteArray(Collection<byte[]> element) {
throw new UnsupportedOperationException();
}
@@ -113,47 +113,47 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
return new ListIterator<String>() {
ListIterator<String> iter = list.listIterator(index);
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasNext() {
return iter.hasNext();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public String next() {
return iter.next();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasPrevious() {
return iter.hasPrevious();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public String previous() {
return iter.previous();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public int nextIndex() {
return iter.nextIndex();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public int previousIndex() {
return iter.previousIndex();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void remove() {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void set(String o) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void add(String o) {
throw new UnsupportedOperationException();
}
@@ -165,45 +165,45 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
return new Iterator<String>() {
Iterator<String> iter = list.iterator();
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public boolean hasNext() {
return iter.hasNext();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public String next() {
return iter.next();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public List<?> getUnderlyingElements() {
// The returned value is already unmodifiable.
return list.getUnderlyingElements();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void mergeFrom(LazyStringList other) {
throw new UnsupportedOperationException();
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public List<byte[]> asByteArrayList() {
return Collections.unmodifiableList(list.asByteArrayList());
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public List<ByteString> asByteStringList() {
return Collections.unmodifiableList(list.asByteStringList());
}
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public LazyStringList getUnmodifiableView() {
return this;
}
diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
index f443ee39..878c7758 100644
--- a/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
+++ b/java/core/src/main/java/com/google/protobuf/UnsafeByteOperations.java
@@ -30,6 +30,7 @@
package com.google.protobuf;
+import java.io.IOException;
import java.nio.ByteBuffer;
/**
@@ -41,6 +42,23 @@ import java.nio.ByteBuffer;
* guaranteed that the buffer backing the {@link ByteString} will never change! Mutation of a
* {@link ByteString} can lead to unexpected and undesirable consequences in your application,
* and will likely be difficult to debug. Proceed with caution!
+ *
+ * <p>This can have a number of significant side affects that have
+ * spooky-action-at-a-distance-like behavior. In particular, if the bytes value changes out from
+ * under a Protocol Buffer:
+ * <ul>
+ * <li>serialization may throw
+ * <li>serialization may succeed but the wrong bytes may be written out
+ * <li>messages are no longer threadsafe
+ * <li>hashCode may be incorrect
+ * <ul>
+ * <li>can result in a permanent memory leak when used as a key in a long-lived HashMap
+ * <li> the semantics of many programs may be violated if this is the case
+ * </ul>
+ * </ul>
+ * Each of these issues will occur in parts of the code base that are entirely distinct from the
+ * parts of the code base modifying the buffer. In fact, both parts of the code base may be correct
+ * - it is the bridging with the unsafe operations that was in error!
*/
@ExperimentalApi
public final class UnsafeByteOperations {
@@ -49,15 +67,54 @@ public final class UnsafeByteOperations {
/**
* An unsafe operation that returns a {@link ByteString} that is backed by the provided buffer.
*
- * @param buffer the Java NIO buffer to be wrapped.
- * @return a {@link ByteString} backed by the provided buffer.
+ * @param buffer the buffer to be wrapped
+ * @return a {@link ByteString} backed by the provided buffer
+ */
+ public static ByteString unsafeWrap(byte[] buffer) {
+ return ByteString.wrap(buffer);
+ }
+
+ /**
+ * An unsafe operation that returns a {@link ByteString} that is backed by a subregion of the
+ * provided buffer.
+ *
+ * @param buffer the buffer to be wrapped
+ * @param offset the offset of the wrapped region
+ * @param length the number of bytes of the wrapped region
+ * @return a {@link ByteString} backed by the provided buffer
+ */
+ public static ByteString unsafeWrap(byte[] buffer, int offset, int length) {
+ return ByteString.wrap(buffer, offset, length);
+ }
+
+ /**
+ * An unsafe operation that returns a {@link ByteString} that is backed by the provided buffer.
+ *
+ * @param buffer the Java NIO buffer to be wrapped
+ * @return a {@link ByteString} backed by the provided buffer
*/
public static ByteString unsafeWrap(ByteBuffer buffer) {
- if (buffer.hasArray()) {
- final int offset = buffer.arrayOffset();
- return ByteString.wrap(buffer.array(), offset + buffer.position(), buffer.remaining());
- } else {
- return new NioByteString(buffer);
- }
+ return ByteString.wrap(buffer);
+ }
+
+ /**
+ * Writes the given {@link ByteString} to the provided {@link ByteOutput}. Calling this method may
+ * result in multiple operations on the target {@link ByteOutput}
+ * (i.e. for roped {@link ByteString}s).
+ *
+ * <p>This method exposes the internal backing buffer(s) of the {@link ByteString} to the {@link
+ * ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious
+ * {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution!
+ *
+ * <p> NOTE: The {@link ByteOutput} <strong>MUST NOT</strong> modify the provided buffers. Doing
+ * so may result in corrupted data, which would be difficult to debug.
+ *
+ * @param bytes the {@link ByteString} to be written
+ * @param output the output to receive the bytes
+ * @throws IOException if an I/O error occurs
+ */
+ public static void unsafeWriteTo(ByteString bytes, ByteOutput output) throws IOException {
+ bytes.writeTo(output);
}
+
}
diff --git a/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
new file mode 100644
index 00000000..d84ef3c5
--- /dev/null
+++ b/java/core/src/main/java/com/google/protobuf/UnsafeUtil.java
@@ -0,0 +1,574 @@
+// 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 java.lang.reflect.Field;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/** Utility class for working with unsafe operations. */
+final class UnsafeUtil {
+ private static final Logger logger = Logger.getLogger(UnsafeUtil.class.getName());
+ private static final sun.misc.Unsafe UNSAFE = getUnsafe();
+ private static final MemoryAccessor MEMORY_ACCESSOR = getMemoryAccessor();
+ private static final boolean HAS_UNSAFE_BYTEBUFFER_OPERATIONS =
+ supportsUnsafeByteBufferOperations();
+ private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
+
+ private static final long BYTE_ARRAY_BASE_OFFSET = arrayBaseOffset(byte[].class);
+ // Micro-optimization: we can assume a scale of 1 and skip the multiply
+ // private static final long BYTE_ARRAY_INDEX_SCALE = 1;
+
+ private static final long BOOLEAN_ARRAY_BASE_OFFSET = arrayBaseOffset(boolean[].class);
+ private static final long BOOLEAN_ARRAY_INDEX_SCALE = arrayIndexScale(boolean[].class);
+
+ private static final long INT_ARRAY_BASE_OFFSET = arrayBaseOffset(int[].class);
+ private static final long INT_ARRAY_INDEX_SCALE = arrayIndexScale(int[].class);
+
+ private static final long LONG_ARRAY_BASE_OFFSET = arrayBaseOffset(long[].class);
+ private static final long LONG_ARRAY_INDEX_SCALE = arrayIndexScale(long[].class);
+
+ private static final long FLOAT_ARRAY_BASE_OFFSET = arrayBaseOffset(float[].class);
+ private static final long FLOAT_ARRAY_INDEX_SCALE = arrayIndexScale(float[].class);
+
+ private static final long DOUBLE_ARRAY_BASE_OFFSET = arrayBaseOffset(double[].class);
+ private static final long DOUBLE_ARRAY_INDEX_SCALE = arrayIndexScale(double[].class);
+
+ private static final long OBJECT_ARRAY_BASE_OFFSET = arrayBaseOffset(Object[].class);
+ private static final long OBJECT_ARRAY_INDEX_SCALE = arrayIndexScale(Object[].class);
+
+ private static final long BUFFER_ADDRESS_OFFSET = fieldOffset(bufferAddressField());
+
+ private UnsafeUtil() {}
+
+ static boolean hasUnsafeArrayOperations() {
+ return HAS_UNSAFE_ARRAY_OPERATIONS;
+ }
+
+ static boolean hasUnsafeByteBufferOperations() {
+ return HAS_UNSAFE_BYTEBUFFER_OPERATIONS;
+ }
+
+
+ static long objectFieldOffset(Field field) {
+ return MEMORY_ACCESSOR.objectFieldOffset(field);
+ }
+
+ private static int arrayBaseOffset(Class<?> clazz) {
+ return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayBaseOffset(clazz) : -1;
+ }
+
+ private static int arrayIndexScale(Class<?> clazz) {
+ return HAS_UNSAFE_ARRAY_OPERATIONS ? MEMORY_ACCESSOR.arrayIndexScale(clazz) : -1;
+ }
+
+ static byte getByte(Object target, long offset) {
+ return MEMORY_ACCESSOR.getByte(target, offset);
+ }
+
+ static void putByte(Object target, long offset, byte value) {
+ MEMORY_ACCESSOR.putByte(target, offset, value);
+ }
+
+ static int getInt(Object target, long offset) {
+ return MEMORY_ACCESSOR.getInt(target, offset);
+ }
+
+ static void putInt(Object target, long offset, int value) {
+ MEMORY_ACCESSOR.putInt(target, offset, value);
+ }
+
+ static long getLong(Object target, long offset) {
+ return MEMORY_ACCESSOR.getLong(target, offset);
+ }
+
+ static void putLong(Object target, long offset, long value) {
+ MEMORY_ACCESSOR.putLong(target, offset, value);
+ }
+
+ static boolean getBoolean(Object target, long offset) {
+ return MEMORY_ACCESSOR.getBoolean(target, offset);
+ }
+
+ static void putBoolean(Object target, long offset, boolean value) {
+ MEMORY_ACCESSOR.putBoolean(target, offset, value);
+ }
+
+ static float getFloat(Object target, long offset) {
+ return MEMORY_ACCESSOR.getFloat(target, offset);
+ }
+
+ static void putFloat(Object target, long offset, float value) {
+ MEMORY_ACCESSOR.putFloat(target, offset, value);
+ }
+
+ static double getDouble(Object target, long offset) {
+ return MEMORY_ACCESSOR.getDouble(target, offset);
+ }
+
+ static void putDouble(Object target, long offset, double value) {
+ MEMORY_ACCESSOR.putDouble(target, offset, value);
+ }
+
+ static Object getObject(Object target, long offset) {
+ return MEMORY_ACCESSOR.getObject(target, offset);
+ }
+
+ static byte getByte(byte[] target, long index) {
+ return MEMORY_ACCESSOR.getByte(target, BYTE_ARRAY_BASE_OFFSET + index);
+ }
+
+ static void putByte(byte[] target, long index, byte value) {
+ MEMORY_ACCESSOR.putByte(target, BYTE_ARRAY_BASE_OFFSET + index, value);
+ }
+
+ static int getInt(int[] target, long index) {
+ return MEMORY_ACCESSOR.getInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE));
+ }
+
+ static void putInt(int[] target, long index, int value) {
+ MEMORY_ACCESSOR.putInt(target, INT_ARRAY_BASE_OFFSET + (index * INT_ARRAY_INDEX_SCALE), value);
+ }
+
+ static long getLong(long[] target, long index) {
+ return MEMORY_ACCESSOR.getLong(
+ target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE));
+ }
+
+ static void putLong(long[] target, long index, long value) {
+ MEMORY_ACCESSOR.putLong(
+ target, LONG_ARRAY_BASE_OFFSET + (index * LONG_ARRAY_INDEX_SCALE), value);
+ }
+
+ static boolean getBoolean(boolean[] target, long index) {
+ return MEMORY_ACCESSOR.getBoolean(
+ target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE));
+ }
+
+ static void putBoolean(boolean[] target, long index, boolean value) {
+ MEMORY_ACCESSOR.putBoolean(
+ target, BOOLEAN_ARRAY_BASE_OFFSET + (index * BOOLEAN_ARRAY_INDEX_SCALE), value);
+ }
+
+ static float getFloat(float[] target, long index) {
+ return MEMORY_ACCESSOR.getFloat(
+ target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE));
+ }
+
+ static void putFloat(float[] target, long index, float value) {
+ MEMORY_ACCESSOR.putFloat(
+ target, FLOAT_ARRAY_BASE_OFFSET + (index * FLOAT_ARRAY_INDEX_SCALE), value);
+ }
+
+ static double getDouble(double[] target, long index) {
+ return MEMORY_ACCESSOR.getDouble(
+ target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE));
+ }
+
+ static void putDouble(double[] target, long index, double value) {
+ MEMORY_ACCESSOR.putDouble(
+ target, DOUBLE_ARRAY_BASE_OFFSET + (index * DOUBLE_ARRAY_INDEX_SCALE), value);
+ }
+
+ static Object getObject(Object[] target, long index) {
+ return MEMORY_ACCESSOR.getObject(
+ target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE));
+ }
+
+ static void putObject(Object[] target, long index, Object value) {
+ MEMORY_ACCESSOR.putObject(
+ target, OBJECT_ARRAY_BASE_OFFSET + (index * OBJECT_ARRAY_INDEX_SCALE), value);
+ }
+
+ static void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) {
+ MEMORY_ACCESSOR.copyMemory(src, srcIndex, targetOffset, length);
+ }
+
+ static void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) {
+ MEMORY_ACCESSOR.copyMemory(srcOffset, target, targetIndex, length);
+ }
+
+ static void copyMemory(byte[] src, long srcIndex, byte[] target, long targetIndex, long length) {
+ System.arraycopy(src, (int) srcIndex, target, (int) targetIndex, (int) length);
+ }
+
+ static byte getByte(long address) {
+ return MEMORY_ACCESSOR.getByte(address);
+ }
+
+ static void putByte(long address, byte value) {
+ MEMORY_ACCESSOR.putByte(address, value);
+ }
+
+ static int getInt(long address) {
+ return MEMORY_ACCESSOR.getInt(address);
+ }
+
+ static void putInt(long address, int value) {
+ MEMORY_ACCESSOR.putInt(address, value);
+ }
+
+ static long getLong(long address) {
+ return MEMORY_ACCESSOR.getLong(address);
+ }
+
+ static void putLong(long address, long value) {
+ MEMORY_ACCESSOR.putLong(address, value);
+ }
+
+ /**
+ * Gets the offset of the {@code address} field of the given direct {@link ByteBuffer}.
+ */
+ static long addressOffset(ByteBuffer buffer) {
+ return MEMORY_ACCESSOR.getLong(buffer, BUFFER_ADDRESS_OFFSET);
+ }
+
+ static Object getStaticObject(Field field) {
+ return MEMORY_ACCESSOR.getStaticObject(field);
+ }
+
+ /**
+ * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this platform.
+ */
+ static sun.misc.Unsafe getUnsafe() {
+ sun.misc.Unsafe unsafe = null;
+ try {
+ unsafe =
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<sun.misc.Unsafe>() {
+ @Override
+ public sun.misc.Unsafe run() throws Exception {
+ Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
+
+ for (Field f : k.getDeclaredFields()) {
+ f.setAccessible(true);
+ Object x = f.get(null);
+ if (k.isInstance(x)) {
+ return k.cast(x);
+ }
+ }
+ // The sun.misc.Unsafe field does not exist.
+ return null;
+ }
+ });
+ } catch (Throwable e) {
+ // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
+ // for Unsafe.
+ }
+ return unsafe;
+ }
+
+ /** Get a {@link MemoryAccessor} appropriate for the platform, or null if not supported. */
+ private static MemoryAccessor getMemoryAccessor() {
+ if (UNSAFE == null) {
+ return null;
+ }
+ return new JvmMemoryAccessor(UNSAFE);
+ }
+
+ /** Indicates whether or not unsafe array operations are supported on this platform. */
+ private static boolean supportsUnsafeArrayOperations() {
+ if (UNSAFE == null) {
+ return false;
+ }
+ try {
+ Class<?> clazz = UNSAFE.getClass();
+ clazz.getMethod("objectFieldOffset", Field.class);
+ clazz.getMethod("arrayBaseOffset", Class.class);
+ clazz.getMethod("arrayIndexScale", Class.class);
+ clazz.getMethod("getInt", Object.class, long.class);
+ clazz.getMethod("putInt", Object.class, long.class, int.class);
+ clazz.getMethod("getLong", Object.class, long.class);
+ clazz.getMethod("putLong", Object.class, long.class, long.class);
+ clazz.getMethod("getObject", Object.class, long.class);
+ clazz.getMethod("putObject", Object.class, long.class, Object.class);
+ clazz.getMethod("getByte", Object.class, long.class);
+ clazz.getMethod("putByte", Object.class, long.class, byte.class);
+ clazz.getMethod("getBoolean", Object.class, long.class);
+ clazz.getMethod("putBoolean", Object.class, long.class, boolean.class);
+ clazz.getMethod("getFloat", Object.class, long.class);
+ clazz.getMethod("putFloat", Object.class, long.class, float.class);
+ clazz.getMethod("getDouble", Object.class, long.class);
+ clazz.getMethod("putDouble", Object.class, long.class, double.class);
+
+ return true;
+ } catch (Throwable e) {
+ logger.log(
+ Level.WARNING,
+ "platform method missing - proto runtime falling back to safer methods: " + e);
+ }
+ return false;
+ }
+
+ private static boolean supportsUnsafeByteBufferOperations() {
+ if (UNSAFE == null) {
+ return false;
+ }
+ try {
+ Class<?> clazz = UNSAFE.getClass();
+ // Methods for getting direct buffer address.
+ clazz.getMethod("objectFieldOffset", Field.class);
+ clazz.getMethod("getLong", Object.class, long.class);
+
+ if (bufferAddressField() == null) {
+ return false;
+ }
+
+ clazz.getMethod("getByte", long.class);
+ clazz.getMethod("putByte", long.class, byte.class);
+ clazz.getMethod("getInt", long.class);
+ clazz.getMethod("putInt", long.class, int.class);
+ clazz.getMethod("getLong", long.class);
+ clazz.getMethod("putLong", long.class, long.class);
+ clazz.getMethod("copyMemory", long.class, long.class, long.class);
+ clazz.getMethod("copyMemory", Object.class, long.class, Object.class, long.class, long.class);
+ return true;
+ } catch (Throwable e) {
+ logger.log(
+ Level.WARNING,
+ "platform method missing - proto runtime falling back to safer methods: " + e);
+ }
+ return false;
+ }
+
+
+ /** Finds the address field within a direct {@link Buffer}. */
+ private static Field bufferAddressField() {
+ Field field = field(Buffer.class, "address");
+ return field != null && field.getType() == long.class ? field : null;
+ }
+
+ /** Finds the value field within a {@link String}. */
+ private static Field stringValueField() {
+ Field field = field(String.class, "value");
+ return field != null && field.getType() == char[].class ? field : null;
+ }
+
+ /**
+ * Returns the offset of the provided field, or {@code -1} if {@code sun.misc.Unsafe} is not
+ * available.
+ */
+ private static long fieldOffset(Field field) {
+ return field == null || MEMORY_ACCESSOR == null ? -1 : MEMORY_ACCESSOR.objectFieldOffset(field);
+ }
+
+ /**
+ * Gets the field with the given name within the class, or {@code null} if not found. If found,
+ * the field is made accessible.
+ */
+ private static Field field(Class<?> clazz, String fieldName) {
+ Field field;
+ try {
+ field = clazz.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ } catch (Throwable t) {
+ // Failed to access the fields.
+ field = null;
+ }
+ return field;
+ }
+
+ private abstract static class MemoryAccessor {
+
+ sun.misc.Unsafe unsafe;
+
+ MemoryAccessor(sun.misc.Unsafe unsafe) {
+ this.unsafe = unsafe;
+ }
+
+ public final long objectFieldOffset(Field field) {
+ return unsafe.objectFieldOffset(field);
+ }
+
+ public abstract byte getByte(Object target, long offset);
+
+ public abstract void putByte(Object target, long offset, byte value);
+
+ public final int getInt(Object target, long offset) {
+ return unsafe.getInt(target, offset);
+ }
+
+ public final void putInt(Object target, long offset, int value) {
+ unsafe.putInt(target, offset, value);
+ }
+
+ public final long getLong(Object target, long offset) {
+ return unsafe.getLong(target, offset);
+ }
+
+ public final void putLong(Object target, long offset, long value) {
+ unsafe.putLong(target, offset, value);
+ }
+
+ public abstract boolean getBoolean(Object target, long offset);
+
+ public abstract void putBoolean(Object target, long offset, boolean value);
+
+ public abstract float getFloat(Object target, long offset);
+
+ public abstract void putFloat(Object target, long offset, float value);
+
+ public abstract double getDouble(Object target, long offset);
+
+ public abstract void putDouble(Object target, long offset, double value);
+
+ public final Object getObject(Object target, long offset) {
+ return unsafe.getObject(target, offset);
+ }
+
+ public final void putObject(Object target, long offset, Object value) {
+ unsafe.putObject(target, offset, value);
+ }
+
+ public final int arrayBaseOffset(Class<?> clazz) {
+ return unsafe.arrayBaseOffset(clazz);
+ }
+
+ public final int arrayIndexScale(Class<?> clazz) {
+ return unsafe.arrayIndexScale(clazz);
+ }
+
+ public abstract byte getByte(long address);
+
+ public abstract void putByte(long address, byte value);
+
+ public abstract int getInt(long address);
+
+ public abstract void putInt(long address, int value);
+
+ public abstract long getLong(long address);
+
+ public abstract void putLong(long address, long value);
+
+ public abstract Object getStaticObject(Field field);
+
+ public abstract void copyMemory(long srcOffset, byte[] target, long targetIndex, long length);
+
+ public abstract void copyMemory(byte[] src, long srcIndex, long targetOffset, long length);
+ }
+
+ private static final class JvmMemoryAccessor extends MemoryAccessor {
+
+ JvmMemoryAccessor(sun.misc.Unsafe unsafe) {
+ super(unsafe);
+ }
+
+ @Override
+ public byte getByte(long address) {
+ return unsafe.getByte(address);
+ }
+
+ @Override
+ public void putByte(long address, byte value) {
+ unsafe.putByte(address, value);
+ }
+
+ @Override
+ public int getInt(long address) {
+ return unsafe.getInt(address);
+ }
+
+ @Override
+ public void putInt(long address, int value) {
+ unsafe.putInt(address, value);
+ }
+
+ @Override
+ public long getLong(long address) {
+ return unsafe.getLong(address);
+ }
+
+ @Override
+ public void putLong(long address, long value) {
+ unsafe.putLong(address, value);
+ }
+
+ @Override
+ public byte getByte(Object target, long offset) {
+ return unsafe.getByte(target, offset);
+ }
+
+ @Override
+ public void putByte(Object target, long offset, byte value) {
+ unsafe.putByte(target, offset, value);
+ }
+
+ @Override
+ public boolean getBoolean(Object target, long offset) {
+ return unsafe.getBoolean(target, offset);
+ }
+
+ @Override
+ public void putBoolean(Object target, long offset, boolean value) {
+ unsafe.putBoolean(target, offset, value);
+ }
+
+ @Override
+ public float getFloat(Object target, long offset) {
+ return unsafe.getFloat(target, offset);
+ }
+
+ @Override
+ public void putFloat(Object target, long offset, float value) {
+ unsafe.putFloat(target, offset, value);
+ }
+
+ @Override
+ public double getDouble(Object target, long offset) {
+ return unsafe.getDouble(target, offset);
+ }
+
+ @Override
+ public void putDouble(Object target, long offset, double value) {
+ unsafe.putDouble(target, offset, value);
+ }
+
+ @Override
+ public void copyMemory(long srcOffset, byte[] target, long targetIndex, long length) {
+ unsafe.copyMemory(null, srcOffset, target, BYTE_ARRAY_BASE_OFFSET + targetIndex, length);
+ }
+
+ @Override
+ public void copyMemory(byte[] src, long srcIndex, long targetOffset, long length) {
+ unsafe.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, targetOffset, length);
+ }
+
+ @Override
+ public Object getStaticObject(Field field) {
+ return getObject(unsafe.staticFieldBase(field), unsafe.staticFieldOffset(field));
+ }
+ }
+
+}
diff --git a/java/core/src/main/java/com/google/protobuf/Utf8.java b/java/core/src/main/java/com/google/protobuf/Utf8.java
index 48c7e9e6..de75fe6b 100644
--- a/java/core/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/core/src/main/java/com/google/protobuf/Utf8.java
@@ -30,6 +30,20 @@
package com.google.protobuf;
+import static com.google.protobuf.UnsafeUtil.addressOffset;
+import static com.google.protobuf.UnsafeUtil.hasUnsafeArrayOperations;
+import static com.google.protobuf.UnsafeUtil.hasUnsafeByteBufferOperations;
+import static java.lang.Character.MAX_SURROGATE;
+import static java.lang.Character.MIN_HIGH_SURROGATE;
+import static java.lang.Character.MIN_LOW_SURROGATE;
+import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
+import static java.lang.Character.MIN_SURROGATE;
+import static java.lang.Character.isSurrogatePair;
+import static java.lang.Character.toCodePoint;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
/**
* A set of low-level, high-performance static utility methods related
* to the UTF-8 character encoding. This class has no dependencies
@@ -64,9 +78,23 @@ package com.google.protobuf;
*
* @author martinrb@google.com (Martin Buchholz)
*/
+// TODO(nathanmittler): Copy changes in this class back to Guava
final class Utf8 {
- private Utf8() {}
-
+
+ /**
+ * UTF-8 is a runtime hot spot so we attempt to provide heavily optimized implementations
+ * depending on what is available on the platform. The processor is the platform-optimized
+ * delegate for which all methods are delegated directly to.
+ */
+ private static final Processor processor =
+ UnsafeProcessor.isAvailable() ? new UnsafeProcessor() : new SafeProcessor();
+
+ /**
+ * A mask used when performing unsafe reads to determine if a long value contains any non-ASCII
+ * characters (i.e. any byte >= 0x80).
+ */
+ private static final long ASCII_MASK_LONG = 0x8080808080808080L;
+
/**
* Maximum number of bytes per Java UTF-16 char in UTF-8.
* @see java.nio.charset.CharsetEncoder#maxBytesPerChar()
@@ -85,6 +113,18 @@ final class Utf8 {
*/
public static final int MALFORMED = -1;
+ /**
+ * Used by {@code Unsafe} UTF-8 string validation logic to determine the minimum string length
+ * above which to employ an optimized algorithm for counting ASCII characters. The reason for this
+ * threshold is that for small strings, the optimization may not be beneficial or may even
+ * negatively impact performance since it requires additional logic to avoid unaligned reads
+ * (when calling {@code Unsafe.getLong}). This threshold guarantees that even if the initial
+ * offset is unaligned, we're guaranteed to make at least one call to {@code Unsafe.getLong()}
+ * which provides a performance improvement that entirely subsumes the cost of the additional
+ * logic.
+ */
+ private static final int UNSAFE_COUNT_ASCII_THRESHOLD = 16;
+
// Other state values include the partial bytes of the incomplete
// character to be decoded in the simplest way: we pack the bytes
// into the state int in little-endian order. For example:
@@ -112,7 +152,7 @@ final class Utf8 {
* isValidUtf8(bytes, 0, bytes.length)}.
*/
public static boolean isValidUtf8(byte[] bytes) {
- return isValidUtf8(bytes, 0, bytes.length);
+ return processor.isValidUtf8(bytes, 0, bytes.length);
}
/**
@@ -125,7 +165,7 @@ final class Utf8 {
* partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
*/
public static boolean isValidUtf8(byte[] bytes, int index, int limit) {
- return partialIsValidUtf8(bytes, index, limit) == COMPLETE;
+ return processor.isValidUtf8(bytes, index, limit);
}
/**
@@ -146,183 +186,8 @@ final class Utf8 {
* decode the character when passed to a subsequent invocation of a
* partial decoding method.
*/
- public static int partialIsValidUtf8(
- int state, byte[] bytes, int index, int limit) {
- if (state != COMPLETE) {
- // The previous decoding operation was incomplete (or malformed).
- // We look for a well-formed sequence consisting of bytes from
- // the previous decoding operation (stored in state) together
- // with bytes from the array slice.
- //
- // We expect such "straddler characters" to be rare.
-
- if (index >= limit) { // No bytes? No progress.
- return state;
- }
- int byte1 = (byte) state;
- // byte1 is never ASCII.
- if (byte1 < (byte) 0xE0) {
- // two-byte form
-
- // Simultaneously checks for illegal trailing-byte in
- // leading position and overlong 2-byte form.
- if (byte1 < (byte) 0xC2 ||
- // byte2 trailing-byte test
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- } else if (byte1 < (byte) 0xF0) {
- // three-byte form
-
- // Get byte2 from saved state or array
- int byte2 = (byte) ~(state >> 8);
- if (byte2 == 0) {
- byte2 = bytes[index++];
- if (index >= limit) {
- return incompleteStateFor(byte1, byte2);
- }
- }
- if (byte2 > (byte) 0xBF ||
- // overlong? 5 most significant bits must not all be zero
- (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
- // illegal surrogate codepoint?
- (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
- // byte3 trailing-byte test
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- } else {
- // four-byte form
-
- // Get byte2 and byte3 from saved state or array
- int byte2 = (byte) ~(state >> 8);
- int byte3 = 0;
- if (byte2 == 0) {
- byte2 = bytes[index++];
- if (index >= limit) {
- return incompleteStateFor(byte1, byte2);
- }
- } else {
- byte3 = (byte) (state >> 16);
- }
- if (byte3 == 0) {
- byte3 = bytes[index++];
- if (index >= limit) {
- return incompleteStateFor(byte1, byte2, byte3);
- }
- }
-
- // If we were called with state == MALFORMED, then byte1 is 0xFF,
- // which never occurs in well-formed UTF-8, and so we will return
- // MALFORMED again below.
-
- if (byte2 > (byte) 0xBF ||
- // Check that 1 <= plane <= 16. Tricky optimized form of:
- // if (byte1 > (byte) 0xF4 ||
- // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
- // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
- (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
- // byte3 trailing-byte test
- byte3 > (byte) 0xBF ||
- // byte4 trailing-byte test
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- }
- }
-
- return partialIsValidUtf8(bytes, index, limit);
- }
-
- /**
- * Tells whether the given byte array slice is a well-formed,
- * malformed, or incomplete UTF-8 byte sequence. The range of bytes
- * to be checked extends from index {@code index}, inclusive, to
- * {@code limit}, exclusive.
- *
- * <p>This is a convenience method, equivalent to a call to {@code
- * partialIsValidUtf8(Utf8.COMPLETE, bytes, index, limit)}.
- *
- * @return {@link #MALFORMED} if the partial byte sequence is
- * definitely not well-formed, {@link #COMPLETE} if it is well-formed
- * (no additional input needed), or if the byte sequence is
- * "incomplete", i.e. apparently terminated in the middle of a character,
- * an opaque integer "state" value containing enough information to
- * decode the character when passed to a subsequent invocation of a
- * partial decoding method.
- */
- public static int partialIsValidUtf8(
- byte[] bytes, int index, int limit) {
- // Optimize for 100% ASCII.
- // Hotspot loves small simple top-level loops like this.
- while (index < limit && bytes[index] >= 0) {
- index++;
- }
-
- return (index >= limit) ? COMPLETE :
- partialIsValidUtf8NonAscii(bytes, index, limit);
- }
-
- private static int partialIsValidUtf8NonAscii(
- byte[] bytes, int index, int limit) {
- for (;;) {
- int byte1, byte2;
-
- // Optimize for interior runs of ASCII bytes.
- do {
- if (index >= limit) {
- return COMPLETE;
- }
- } while ((byte1 = bytes[index++]) >= 0);
-
- if (byte1 < (byte) 0xE0) {
- // two-byte form
-
- if (index >= limit) {
- return byte1;
- }
-
- // Simultaneously checks for illegal trailing-byte in
- // leading position and overlong 2-byte form.
- if (byte1 < (byte) 0xC2 ||
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- } else if (byte1 < (byte) 0xF0) {
- // three-byte form
-
- if (index >= limit - 1) { // incomplete sequence
- return incompleteStateFor(bytes, index, limit);
- }
- if ((byte2 = bytes[index++]) > (byte) 0xBF ||
- // overlong? 5 most significant bits must not all be zero
- (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0) ||
- // check for illegal surrogate codepoints
- (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0) ||
- // byte3 trailing-byte test
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- } else {
- // four-byte form
-
- if (index >= limit - 2) { // incomplete sequence
- return incompleteStateFor(bytes, index, limit);
- }
- if ((byte2 = bytes[index++]) > (byte) 0xBF ||
- // Check that 1 <= plane <= 16. Tricky optimized form of:
- // if (byte1 > (byte) 0xF4 ||
- // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
- // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
- (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0 ||
- // byte3 trailing-byte test
- bytes[index++] > (byte) 0xBF ||
- // byte4 trailing-byte test
- bytes[index++] > (byte) 0xBF) {
- return MALFORMED;
- }
- }
- }
+ public static int partialIsValidUtf8(int state, byte[] bytes, int index, int limit) {
+ return processor.partialIsValidUtf8(state, bytes, index, limit);
}
private static int incompleteStateFor(int byte1) {
@@ -352,19 +217,31 @@ final class Utf8 {
default: throw new AssertionError();
}
}
-
+
+ private static int incompleteStateFor(
+ final ByteBuffer buffer, final int byte1, final int index, final int remaining) {
+ switch (remaining) {
+ case 0:
+ return incompleteStateFor(byte1);
+ case 1:
+ return incompleteStateFor(byte1, buffer.get(index));
+ case 2:
+ return incompleteStateFor(byte1, buffer.get(index), buffer.get(index + 1));
+ default:
+ throw new AssertionError();
+ }
+ }
// These UTF-8 handling methods are copied from Guava's Utf8 class with a modification to throw
// a protocol buffer local exception. This exception is then caught in CodedOutputStream so it can
// fallback to more lenient behavior.
static class UnpairedSurrogateException extends IllegalArgumentException {
-
- private UnpairedSurrogateException(int index, int length) {
+ UnpairedSurrogateException(int index, int length) {
super("Unpaired surrogate at index " + index + " of " + length);
}
}
-
+
/**
* Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
* this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
@@ -416,7 +293,7 @@ final class Utf8 {
if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) {
// Check that we have a well-formed surrogate pair.
int cp = Character.codePointAt(sequence, i);
- if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+ if (cp < MIN_SUPPLEMENTARY_CODE_POINT) {
throw new UnpairedSurrogateException(i, utf16Length);
}
i++;
@@ -426,56 +303,1732 @@ final class Utf8 {
return utf8Length;
}
- static int encode(CharSequence sequence, byte[] bytes, int offset, int length) {
- int utf16Length = sequence.length();
- int j = offset;
- int i = 0;
- int limit = offset + length;
- // Designed to take advantage of
- // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
- for (char c; i < utf16Length && i + j < limit && (c = sequence.charAt(i)) < 0x80; i++) {
- bytes[j + i] = (byte) c;
- }
- if (i == utf16Length) {
- return j + utf16Length;
- }
- j += i;
- for (char c; i < utf16Length; i++) {
- c = sequence.charAt(i);
- if (c < 0x80 && j < limit) {
- bytes[j++] = (byte) c;
- } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
- bytes[j++] = (byte) ((0xF << 6) | (c >>> 6));
- bytes[j++] = (byte) (0x80 | (0x3F & c));
- } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
- // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
- bytes[j++] = (byte) ((0xF << 5) | (c >>> 12));
- bytes[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
- bytes[j++] = (byte) (0x80 | (0x3F & c));
- } else if (j <= limit - 4) {
- // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 bytes
- final char low;
- if (i + 1 == sequence.length()
- || !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) {
- throw new UnpairedSurrogateException((i - 1), utf16Length);
- }
- int codePoint = Character.toCodePoint(c, low);
- bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
- bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
- bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
- bytes[j++] = (byte) (0x80 | (0x3F & codePoint));
+ static int encode(CharSequence in, byte[] out, int offset, int length) {
+ return processor.encodeUtf8(in, out, offset, length);
+ }
+ // End Guava UTF-8 methods.
+
+ /**
+ * Determines if the given {@link ByteBuffer} is a valid UTF-8 string.
+ *
+ * <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
+ * and the capabilities of the platform.
+ *
+ * @param buffer the buffer to check.
+ * @see Utf8#isValidUtf8(byte[], int, int)
+ */
+ static boolean isValidUtf8(ByteBuffer buffer) {
+ return processor.isValidUtf8(buffer, buffer.position(), buffer.remaining());
+ }
+
+ /**
+ * Determines if the given {@link ByteBuffer} is a partially valid UTF-8 string.
+ *
+ * <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
+ * and the capabilities of the platform.
+ *
+ * @param buffer the buffer to check.
+ * @see Utf8#partialIsValidUtf8(int, byte[], int, int)
+ */
+ static int partialIsValidUtf8(int state, ByteBuffer buffer, int index, int limit) {
+ return processor.partialIsValidUtf8(state, buffer, index, limit);
+ }
+
+ /**
+ * Decodes the given UTF-8 portion of the {@link ByteBuffer} into a {@link String}.
+ *
+ * @throws InvalidProtocolBufferException if the input is not valid UTF-8.
+ */
+ static String decodeUtf8(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException {
+ return processor.decodeUtf8(buffer, index, size);
+ }
+
+ /**
+ * Decodes the given UTF-8 encoded byte array slice into a {@link String}.
+ *
+ * @throws InvalidProtocolBufferException if the input is not valid UTF-8.
+ */
+ static String decodeUtf8(byte[] bytes, int index, int size)
+ throws InvalidProtocolBufferException {
+ return processor.decodeUtf8(bytes, index, size);
+ }
+
+ /**
+ * Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding.
+ *
+ * <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
+ * and the capabilities of the platform.
+ *
+ * @param in the source string to be encoded
+ * @param out the target buffer to receive the encoded string.
+ * @see Utf8#encode(CharSequence, byte[], int, int)
+ */
+ static void encodeUtf8(CharSequence in, ByteBuffer out) {
+ processor.encodeUtf8(in, out);
+ }
+
+ /**
+ * Counts (approximately) the number of consecutive ASCII characters in the given buffer.
+ * The byte order of the {@link ByteBuffer} does not matter, so performance can be improved if
+ * native byte order is used (i.e. no byte-swapping in {@link ByteBuffer#getLong(int)}).
+ *
+ * @param buffer the buffer to be scanned for ASCII chars
+ * @param index the starting index of the scan
+ * @param limit the limit within buffer for the scan
+ * @return the number of ASCII characters found. The stopping position will be at or
+ * before the first non-ASCII byte.
+ */
+ private static int estimateConsecutiveAscii(ByteBuffer buffer, int index, int limit) {
+ int i = index;
+ final int lim = limit - 7;
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ // To speed things up further, we're reading longs instead of bytes so we use a mask to
+ // determine if any byte in the current long is non-ASCII.
+ for (; i < lim && (buffer.getLong(i) & ASCII_MASK_LONG) == 0; i += 8) {}
+ return i - index;
+ }
+
+ /**
+ * A processor of UTF-8 strings, providing methods for checking validity and encoding.
+ */
+ // TODO(nathanmittler): Add support for Memory/MemoryBlock on Android.
+ abstract static class Processor {
+ /**
+ * Returns {@code true} if the given byte array slice is a
+ * well-formed UTF-8 byte sequence. The range of bytes to be
+ * checked extends from index {@code index}, inclusive, to {@code
+ * limit}, exclusive.
+ *
+ * <p>This is a convenience method, equivalent to {@code
+ * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
+ */
+ final boolean isValidUtf8(byte[] bytes, int index, int limit) {
+ return partialIsValidUtf8(COMPLETE, bytes, index, limit) == COMPLETE;
+ }
+
+ /**
+ * Tells whether the given byte array slice is a well-formed,
+ * malformed, or incomplete UTF-8 byte sequence. The range of bytes
+ * to be checked extends from index {@code index}, inclusive, to
+ * {@code limit}, exclusive.
+ *
+ * @param state either {@link Utf8#COMPLETE} (if this is the initial decoding
+ * operation) or the value returned from a call to a partial decoding method
+ * for the previous bytes
+ *
+ * @return {@link #MALFORMED} if the partial byte sequence is
+ * definitely not well-formed, {@link #COMPLETE} if it is well-formed
+ * (no additional input needed), or if the byte sequence is
+ * "incomplete", i.e. apparently terminated in the middle of a character,
+ * an opaque integer "state" value containing enough information to
+ * decode the character when passed to a subsequent invocation of a
+ * partial decoding method.
+ */
+ abstract int partialIsValidUtf8(int state, byte[] bytes, int index, int limit);
+
+ /**
+ * Returns {@code true} if the given portion of the {@link ByteBuffer} is a
+ * well-formed UTF-8 byte sequence. The range of bytes to be
+ * checked extends from index {@code index}, inclusive, to {@code
+ * limit}, exclusive.
+ *
+ * <p>This is a convenience method, equivalent to {@code
+ * partialIsValidUtf8(bytes, index, limit) == Utf8.COMPLETE}.
+ */
+ final boolean isValidUtf8(ByteBuffer buffer, int index, int limit) {
+ return partialIsValidUtf8(COMPLETE, buffer, index, limit) == COMPLETE;
+ }
+
+ /**
+ * Indicates whether or not the given buffer contains a valid UTF-8 string.
+ *
+ * @param buffer the buffer to check.
+ * @return {@code true} if the given buffer contains a valid UTF-8 string.
+ */
+ final int partialIsValidUtf8(
+ final int state, final ByteBuffer buffer, int index, final int limit) {
+ if (buffer.hasArray()) {
+ final int offset = buffer.arrayOffset();
+ return partialIsValidUtf8(state, buffer.array(), offset + index, offset + limit);
+ } else if (buffer.isDirect()){
+ return partialIsValidUtf8Direct(state, buffer, index, limit);
+ }
+ return partialIsValidUtf8Default(state, buffer, index, limit);
+ }
+
+ /**
+ * Performs validation for direct {@link ByteBuffer} instances.
+ */
+ abstract int partialIsValidUtf8Direct(
+ final int state, final ByteBuffer buffer, int index, final int limit);
+
+ /**
+ * Performs validation for {@link ByteBuffer} instances using the {@link ByteBuffer} API rather
+ * than potentially faster approaches. This first completes validation for the current
+ * character (provided by {@code state}) and then finishes validation for the sequence.
+ */
+ final int partialIsValidUtf8Default(
+ final int state, final ByteBuffer buffer, int index, final int limit) {
+ if (state != COMPLETE) {
+ // The previous decoding operation was incomplete (or malformed).
+ // We look for a well-formed sequence consisting of bytes from
+ // the previous decoding operation (stored in state) together
+ // with bytes from the array slice.
+ //
+ // We expect such "straddler characters" to be rare.
+
+ if (index >= limit) { // No bytes? No progress.
+ return state;
+ }
+
+ byte byte1 = (byte) state;
+ // byte1 is never ASCII.
+ if (byte1 < (byte) 0xE0) {
+ // two-byte form
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ // byte2 trailing-byte test
+ || buffer.get(index++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // three-byte form
+
+ // Get byte2 from saved state or array
+ byte byte2 = (byte) ~(state >> 8);
+ if (byte2 == 0) {
+ byte2 = buffer.get(index++);
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ }
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // illegal surrogate codepoint?
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || buffer.get(index++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // four-byte form
+
+ // Get byte2 and byte3 from saved state or array
+ byte byte2 = (byte) ~(state >> 8);
+ byte byte3 = 0;
+ if (byte2 == 0) {
+ byte2 = buffer.get(index++);
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ } else {
+ byte3 = (byte) (state >> 16);
+ }
+ if (byte3 == 0) {
+ byte3 = buffer.get(index++);
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2, byte3);
+ }
+ }
+
+ // If we were called with state == MALFORMED, then byte1 is 0xFF,
+ // which never occurs in well-formed UTF-8, and so we will return
+ // MALFORMED again below.
+
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || byte3 > (byte) 0xBF
+ // byte4 trailing-byte test
+ || buffer.get(index++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+
+ // Finish validation for the sequence.
+ return partialIsValidUtf8(buffer, index, limit);
+ }
+
+ /**
+ * Performs validation for {@link ByteBuffer} instances using the {@link ByteBuffer} API rather
+ * than potentially faster approaches.
+ */
+ private static int partialIsValidUtf8(final ByteBuffer buffer, int index, final int limit) {
+ index += estimateConsecutiveAscii(buffer, index, limit);
+
+ for (;;) {
+ // Optimize for interior runs of ASCII bytes.
+ // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
+ // Maybe after seeing a few in a row that are ASCII, go back to fast mode?
+ int byte1;
+ do {
+ if (index >= limit) {
+ return COMPLETE;
+ }
+ } while ((byte1 = buffer.get(index++)) >= 0);
+
+ // If we're here byte1 is not ASCII. Only need to handle 2-4 byte forms.
+ if (byte1 < (byte) 0xE0) {
+ // Two-byte form (110xxxxx 10xxxxxx)
+ if (index >= limit) {
+ // Incomplete sequence
+ return byte1;
+ }
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2 || buffer.get(index) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ index++;
+ } else if (byte1 < (byte) 0xF0) {
+ // Three-byte form (1110xxxx 10xxxxxx 10xxxxxx)
+ if (index >= limit - 1) {
+ // Incomplete sequence
+ return incompleteStateFor(buffer, byte1, index, limit - index);
+ }
+
+ final byte byte2 = buffer.get(index++);
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || buffer.get(index) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ index++;
+ } else {
+ // Four-byte form (1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ if (index >= limit - 2) {
+ // Incomplete sequence
+ return incompleteStateFor(buffer, byte1, index, limit - index);
+ }
+
+ // TODO(nathanmittler): Consider using getInt() to improve performance.
+ final int byte2 = buffer.get(index++);
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || buffer.get(index++) > (byte) 0xBF
+ // byte4 trailing-byte test
+ || buffer.get(index++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+ }
+
+ /**
+ * Decodes the given byte array slice into a {@link String}.
+ *
+ * @throws InvalidProtocolBufferException if the byte array slice is not valid UTF-8.
+ */
+ abstract String decodeUtf8(byte[] bytes, int index, int size)
+ throws InvalidProtocolBufferException;
+
+ /**
+ * Decodes the given portion of the {@link ByteBuffer} into a {@link String}.
+ *
+ * @throws InvalidProtocolBufferException if the portion of the buffer is not valid UTF-8.
+ */
+ final String decodeUtf8(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException {
+ if (buffer.hasArray()) {
+ final int offset = buffer.arrayOffset();
+ return decodeUtf8(buffer.array(), offset + index, size);
+ } else if (buffer.isDirect()) {
+ return decodeUtf8Direct(buffer, index, size);
+ }
+ return decodeUtf8Default(buffer, index, size);
+ }
+
+ /**
+ * Decodes direct {@link ByteBuffer} instances into {@link String}.
+ */
+ abstract String decodeUtf8Direct(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException;
+
+ /**
+ * Decodes {@link ByteBuffer} instances using the {@link ByteBuffer} API rather than
+ * potentially faster approaches.
+ */
+ final String decodeUtf8Default(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((index | size | buffer.limit() - index - size) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(), index, size));
+ }
+
+ int offset = index;
+ final int limit = offset + size;
+
+ // The longest possible resulting String is the same as the number of input bytes, when it is
+ // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+ char[] resultArr = new char[size];
+ int resultPos = 0;
+
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (offset < limit) {
+ byte b = buffer.get(offset);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+
+ while (offset < limit) {
+ byte byte1 = buffer.get(offset++);
+ if (DecodeUtil.isOneByte(byte1)) {
+ DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+ // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+ // extra optimized loop to take care of these runs.
+ while (offset < limit) {
+ byte b = buffer.get(offset);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+ } else if (DecodeUtil.isTwoBytes(byte1)) {
+ if (offset >= limit) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleTwoBytes(
+ byte1, /* byte2 */ buffer.get(offset++), resultArr, resultPos++);
+ } else if (DecodeUtil.isThreeBytes(byte1)) {
+ if (offset >= limit - 1) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleThreeBytes(
+ byte1,
+ /* byte2 */ buffer.get(offset++),
+ /* byte3 */ buffer.get(offset++),
+ resultArr,
+ resultPos++);
+ } else {
+ if (offset >= limit - 2) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleFourBytes(
+ byte1,
+ /* byte2 */ buffer.get(offset++),
+ /* byte3 */ buffer.get(offset++),
+ /* byte4 */ buffer.get(offset++),
+ resultArr,
+ resultPos++);
+ // 4-byte case requires two chars.
+ resultPos++;
+ }
+ }
+
+ return new String(resultArr, 0, resultPos);
+ }
+
+ /**
+ * Encodes an input character sequence ({@code in}) to UTF-8 in the target array ({@code out}).
+ * For a string, this method is similar to
+ * <pre>{@code
+ * byte[] a = string.getBytes(UTF_8);
+ * System.arraycopy(a, 0, bytes, offset, a.length);
+ * return offset + a.length;
+ * }</pre>
+ *
+ * but is more efficient in both time and space. One key difference is that this method
+ * requires paired surrogates, and therefore does not support chunking.
+ * While {@code String.getBytes(UTF_8)} replaces unpaired surrogates with the default
+ * replacement character, this method throws {@link UnpairedSurrogateException}.
+ *
+ * <p>To ensure sufficient space in the output buffer, either call {@link #encodedLength} to
+ * compute the exact amount needed, or leave room for
+ * {@code Utf8.MAX_BYTES_PER_CHAR * sequence.length()}, which is the largest possible number
+ * of bytes that any input can be encoded to.
+ *
+ * @param in the input character sequence to be encoded
+ * @param out the target array
+ * @param offset the starting offset in {@code bytes} to start writing at
+ * @param length the length of the {@code bytes}, starting from {@code offset}
+ * @throws UnpairedSurrogateException if {@code sequence} contains ill-formed UTF-16 (unpaired
+ * surrogates)
+ * @throws ArrayIndexOutOfBoundsException if {@code sequence} encoded in UTF-8 is longer than
+ * {@code bytes.length - offset}
+ * @return the new offset, equivalent to {@code offset + Utf8.encodedLength(sequence)}
+ */
+ abstract int encodeUtf8(CharSequence in, byte[] out, int offset, int length);
+
+ /**
+ * Encodes an input character sequence ({@code in}) to UTF-8 in the target buffer ({@code out}).
+ * Upon returning from this method, the {@code out} position will point to the position after
+ * the last encoded byte. This method requires paired surrogates, and therefore does not
+ * support chunking.
+ *
+ * <p>To ensure sufficient space in the output buffer, either call {@link #encodedLength} to
+ * compute the exact amount needed, or leave room for
+ * {@code Utf8.MAX_BYTES_PER_CHAR * in.length()}, which is the largest possible number
+ * of bytes that any input can be encoded to.
+ *
+ * @param in the source character sequence to be encoded
+ * @param out the target buffer
+ * @throws UnpairedSurrogateException if {@code in} contains ill-formed UTF-16 (unpaired
+ * surrogates)
+ * @throws ArrayIndexOutOfBoundsException if {@code in} encoded in UTF-8 is longer than
+ * {@code out.remaining()}
+ */
+ final void encodeUtf8(CharSequence in, ByteBuffer out) {
+ if (out.hasArray()) {
+ final int offset = out.arrayOffset();
+ int endIndex =
+ Utf8.encode(in, out.array(), offset + out.position(), out.remaining());
+ out.position(endIndex - offset);
+ } else if (out.isDirect()) {
+ encodeUtf8Direct(in, out);
} else {
- // If we are surrogates and we're not a surrogate pair, always throw an
- // IllegalArgumentException instead of an ArrayOutOfBoundsException.
- if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE)
- && (i + 1 == sequence.length()
- || !Character.isSurrogatePair(c, sequence.charAt(i + 1)))) {
- throw new UnpairedSurrogateException(i, utf16Length);
+ encodeUtf8Default(in, out);
+ }
+ }
+
+ /**
+ * Encodes the input character sequence to a direct {@link ByteBuffer} instance.
+ */
+ abstract void encodeUtf8Direct(CharSequence in, ByteBuffer out);
+
+ /**
+ * Encodes the input character sequence to a {@link ByteBuffer} instance using the {@link
+ * ByteBuffer} API, rather than potentially faster approaches.
+ */
+ final void encodeUtf8Default(CharSequence in, ByteBuffer out) {
+ final int inLength = in.length();
+ int outIx = out.position();
+ int inIx = 0;
+
+ // Since ByteBuffer.putXXX() already checks boundaries for us, no need to explicitly check
+ // access. Assume the buffer is big enough and let it handle the out of bounds exception
+ // if it occurs.
+ try {
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; inIx < inLength && (c = in.charAt(inIx)) < 0x80; ++inIx) {
+ out.put(outIx + inIx, (byte) c);
+ }
+ if (inIx == inLength) {
+ // Successfully encoded the entire string.
+ out.position(outIx + inIx);
+ return;
}
- throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
+
+ outIx += inIx;
+ for (char c; inIx < inLength; ++inIx, ++outIx) {
+ c = in.charAt(inIx);
+ if (c < 0x80) {
+ // One byte (0xxx xxxx)
+ out.put(outIx, (byte) c);
+ } else if (c < 0x800) {
+ // Two bytes (110x xxxx 10xx xxxx)
+
+ // Benchmarks show put performs better than putShort here (for HotSpot).
+ out.put(outIx++, (byte) (0xC0 | (c >>> 6)));
+ out.put(outIx, (byte) (0x80 | (0x3F & c)));
+ } else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
+ // Three bytes (1110 xxxx 10xx xxxx 10xx xxxx)
+ // Maximum single-char code point is 0xFFFF, 16 bits.
+
+ // Benchmarks show put performs better than putShort here (for HotSpot).
+ out.put(outIx++, (byte) (0xE0 | (c >>> 12)));
+ out.put(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+ out.put(outIx, (byte) (0x80 | (0x3F & c)));
+ } else {
+ // Four bytes (1111 xxxx 10xx xxxx 10xx xxxx 10xx xxxx)
+
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
+ // bytes
+ final char low;
+ if (inIx + 1 == inLength || !isSurrogatePair(c, (low = in.charAt(++inIx)))) {
+ throw new UnpairedSurrogateException(inIx, inLength);
+ }
+ // TODO(nathanmittler): Consider using putInt() to improve performance.
+ int codePoint = toCodePoint(c, low);
+ out.put(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ out.put(outIx, (byte) (0x80 | (0x3F & codePoint)));
+ }
+ }
+
+ // Successfully encoded the entire string.
+ out.position(outIx);
+ } catch (IndexOutOfBoundsException e) {
+ // TODO(nathanmittler): Consider making the API throw IndexOutOfBoundsException instead.
+
+ // If we failed in the outer ASCII loop, outIx will not have been updated. In this case,
+ // use inIx to determine the bad write index.
+ int badWriteIndex = out.position() + Math.max(inIx, outIx - out.position() + 1);
+ throw new ArrayIndexOutOfBoundsException(
+ "Failed writing " + in.charAt(inIx) + " at index " + badWriteIndex);
}
}
- return j;
}
- // End Guava UTF-8 methods.
+
+ /**
+ * {@link Processor} implementation that does not use any {@code sun.misc.Unsafe} methods.
+ */
+ static final class SafeProcessor extends Processor {
+ @Override
+ int partialIsValidUtf8(int state, byte[] bytes, int index, int limit) {
+ if (state != COMPLETE) {
+ // The previous decoding operation was incomplete (or malformed).
+ // We look for a well-formed sequence consisting of bytes from
+ // the previous decoding operation (stored in state) together
+ // with bytes from the array slice.
+ //
+ // We expect such "straddler characters" to be rare.
+
+ if (index >= limit) { // No bytes? No progress.
+ return state;
+ }
+ int byte1 = (byte) state;
+ // byte1 is never ASCII.
+ if (byte1 < (byte) 0xE0) {
+ // two-byte form
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ // byte2 trailing-byte test
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // three-byte form
+
+ // Get byte2 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ if (byte2 == 0) {
+ byte2 = bytes[index++];
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ }
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // illegal surrogate codepoint?
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // four-byte form
+
+ // Get byte2 and byte3 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ int byte3 = 0;
+ if (byte2 == 0) {
+ byte2 = bytes[index++];
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ } else {
+ byte3 = (byte) (state >> 16);
+ }
+ if (byte3 == 0) {
+ byte3 = bytes[index++];
+ if (index >= limit) {
+ return incompleteStateFor(byte1, byte2, byte3);
+ }
+ }
+
+ // If we were called with state == MALFORMED, then byte1 is 0xFF,
+ // which never occurs in well-formed UTF-8, and so we will return
+ // MALFORMED again below.
+
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || byte3 > (byte) 0xBF
+ // byte4 trailing-byte test
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+
+ return partialIsValidUtf8(bytes, index, limit);
+ }
+
+ @Override
+ int partialIsValidUtf8Direct(int state, ByteBuffer buffer, int index, int limit) {
+ // For safe processing, we have to use the ByteBuffer API.
+ return partialIsValidUtf8Default(state, buffer, index, limit);
+ }
+
+ @Override
+ String decodeUtf8(byte[] bytes, int index, int size) throws InvalidProtocolBufferException {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((index | size | bytes.length - index - size) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("buffer length=%d, index=%d, size=%d", bytes.length, index, size));
+ }
+
+ int offset = index;
+ final int limit = offset + size;
+
+ // The longest possible resulting String is the same as the number of input bytes, when it is
+ // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+ char[] resultArr = new char[size];
+ int resultPos = 0;
+
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (offset < limit) {
+ byte b = bytes[offset];
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+
+ while (offset < limit) {
+ byte byte1 = bytes[offset++];
+ if (DecodeUtil.isOneByte(byte1)) {
+ DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+ // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+ // extra optimized loop to take care of these runs.
+ while (offset < limit) {
+ byte b = bytes[offset];
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+ } else if (DecodeUtil.isTwoBytes(byte1)) {
+ if (offset >= limit) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleTwoBytes(byte1, /* byte2 */ bytes[offset++], resultArr, resultPos++);
+ } else if (DecodeUtil.isThreeBytes(byte1)) {
+ if (offset >= limit - 1) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleThreeBytes(
+ byte1,
+ /* byte2 */ bytes[offset++],
+ /* byte3 */ bytes[offset++],
+ resultArr,
+ resultPos++);
+ } else {
+ if (offset >= limit - 2) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleFourBytes(
+ byte1,
+ /* byte2 */ bytes[offset++],
+ /* byte3 */ bytes[offset++],
+ /* byte4 */ bytes[offset++],
+ resultArr,
+ resultPos++);
+ // 4-byte case requires two chars.
+ resultPos++;
+ }
+ }
+
+ return new String(resultArr, 0, resultPos);
+ }
+
+ @Override
+ String decodeUtf8Direct(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException {
+ // For safe processing, we have to use the ByteBufferAPI.
+ return decodeUtf8Default(buffer, index, size);
+ }
+
+ @Override
+ int encodeUtf8(CharSequence in, byte[] out, int offset, int length) {
+ int utf16Length = in.length();
+ int j = offset;
+ int i = 0;
+ int limit = offset + length;
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ for (char c; i < utf16Length && i + j < limit && (c = in.charAt(i)) < 0x80; i++) {
+ out[j + i] = (byte) c;
+ }
+ if (i == utf16Length) {
+ return j + utf16Length;
+ }
+ j += i;
+ for (char c; i < utf16Length; i++) {
+ c = in.charAt(i);
+ if (c < 0x80 && j < limit) {
+ out[j++] = (byte) c;
+ } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
+ out[j++] = (byte) ((0xF << 6) | (c >>> 6));
+ out[j++] = (byte) (0x80 | (0x3F & c));
+ } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ out[j++] = (byte) ((0xF << 5) | (c >>> 12));
+ out[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
+ out[j++] = (byte) (0x80 | (0x3F & c));
+ } else if (j <= limit - 4) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+ // four UTF-8 bytes
+ final char low;
+ if (i + 1 == in.length()
+ || !Character.isSurrogatePair(c, (low = in.charAt(++i)))) {
+ throw new UnpairedSurrogateException((i - 1), utf16Length);
+ }
+ int codePoint = Character.toCodePoint(c, low);
+ out[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
+ out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
+ out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
+ out[j++] = (byte) (0x80 | (0x3F & codePoint));
+ } else {
+ // If we are surrogates and we're not a surrogate pair, always throw an
+ // UnpairedSurrogateException instead of an ArrayOutOfBoundsException.
+ if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE)
+ && (i + 1 == in.length()
+ || !Character.isSurrogatePair(c, in.charAt(i + 1)))) {
+ throw new UnpairedSurrogateException(i, utf16Length);
+ }
+ throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
+ }
+ }
+ return j;
+ }
+
+ @Override
+ void encodeUtf8Direct(CharSequence in, ByteBuffer out) {
+ // For safe processing, we have to use the ByteBuffer API.
+ encodeUtf8Default(in, out);
+ }
+
+ private static int partialIsValidUtf8(byte[] bytes, int index, int limit) {
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (index < limit && bytes[index] >= 0) {
+ index++;
+ }
+
+ return (index >= limit) ? COMPLETE : partialIsValidUtf8NonAscii(bytes, index, limit);
+ }
+
+ private static int partialIsValidUtf8NonAscii(byte[] bytes, int index, int limit) {
+ for (;;) {
+ int byte1, byte2;
+
+ // Optimize for interior runs of ASCII bytes.
+ do {
+ if (index >= limit) {
+ return COMPLETE;
+ }
+ } while ((byte1 = bytes[index++]) >= 0);
+
+ if (byte1 < (byte) 0xE0) {
+ // two-byte form
+
+ if (index >= limit) {
+ // Incomplete sequence
+ return byte1;
+ }
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // three-byte form
+
+ if (index >= limit - 1) { // incomplete sequence
+ return incompleteStateFor(bytes, index, limit);
+ }
+ if ((byte2 = bytes[index++]) > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // four-byte form
+
+ if (index >= limit - 2) { // incomplete sequence
+ return incompleteStateFor(bytes, index, limit);
+ }
+ if ((byte2 = bytes[index++]) > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || bytes[index++] > (byte) 0xBF
+ // byte4 trailing-byte test
+ || bytes[index++] > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * {@link Processor} that uses {@code sun.misc.Unsafe} where possible to improve performance.
+ */
+ static final class UnsafeProcessor extends Processor {
+ /**
+ * Indicates whether or not all required unsafe operations are supported on this platform.
+ */
+ static boolean isAvailable() {
+ return hasUnsafeArrayOperations() && hasUnsafeByteBufferOperations();
+ }
+
+ @Override
+ int partialIsValidUtf8(int state, byte[] bytes, final int index, final int limit) {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((index | limit | bytes.length - limit) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("Array length=%d, index=%d, limit=%d", bytes.length, index, limit));
+ }
+ long offset = index;
+ final long offsetLimit = limit;
+ if (state != COMPLETE) {
+ // The previous decoding operation was incomplete (or malformed).
+ // We look for a well-formed sequence consisting of bytes from
+ // the previous decoding operation (stored in state) together
+ // with bytes from the array slice.
+ //
+ // We expect such "straddler characters" to be rare.
+
+ if (offset >= offsetLimit) { // No bytes? No progress.
+ return state;
+ }
+ int byte1 = (byte) state;
+ // byte1 is never ASCII.
+ if (byte1 < (byte) 0xE0) {
+ // two-byte form
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ // byte2 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // three-byte form
+
+ // Get byte2 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ if (byte2 == 0) {
+ byte2 = UnsafeUtil.getByte(bytes, offset++);
+ if (offset >= offsetLimit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ }
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // illegal surrogate codepoint?
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // four-byte form
+
+ // Get byte2 and byte3 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ int byte3 = 0;
+ if (byte2 == 0) {
+ byte2 = UnsafeUtil.getByte(bytes, offset++);
+ if (offset >= offsetLimit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ } else {
+ byte3 = (byte) (state >> 16);
+ }
+ if (byte3 == 0) {
+ byte3 = UnsafeUtil.getByte(bytes, offset++);
+ if (offset >= offsetLimit) {
+ return incompleteStateFor(byte1, byte2, byte3);
+ }
+ }
+
+ // If we were called with state == MALFORMED, then byte1 is 0xFF,
+ // which never occurs in well-formed UTF-8, and so we will return
+ // MALFORMED again below.
+
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || byte3 > (byte) 0xBF
+ // byte4 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+
+ return partialIsValidUtf8(bytes, offset, (int) (offsetLimit - offset));
+ }
+
+ @Override
+ int partialIsValidUtf8Direct(
+ final int state, ByteBuffer buffer, final int index, final int limit) {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((index | limit | buffer.limit() - limit) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(), index, limit));
+ }
+ long address = addressOffset(buffer) + index;
+ final long addressLimit = address + (limit - index);
+ if (state != COMPLETE) {
+ // The previous decoding operation was incomplete (or malformed).
+ // We look for a well-formed sequence consisting of bytes from
+ // the previous decoding operation (stored in state) together
+ // with bytes from the array slice.
+ //
+ // We expect such "straddler characters" to be rare.
+
+ if (address >= addressLimit) { // No bytes? No progress.
+ return state;
+ }
+
+ final int byte1 = (byte) state;
+ // byte1 is never ASCII.
+ if (byte1 < (byte) 0xE0) {
+ // two-byte form
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ // byte2 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // three-byte form
+
+ // Get byte2 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ if (byte2 == 0) {
+ byte2 = UnsafeUtil.getByte(address++);
+ if (address >= addressLimit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ }
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // illegal surrogate codepoint?
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // four-byte form
+
+ // Get byte2 and byte3 from saved state or array
+ int byte2 = (byte) ~(state >> 8);
+ int byte3 = 0;
+ if (byte2 == 0) {
+ byte2 = UnsafeUtil.getByte(address++);
+ if (address >= addressLimit) {
+ return incompleteStateFor(byte1, byte2);
+ }
+ } else {
+ byte3 = (byte) (state >> 16);
+ }
+ if (byte3 == 0) {
+ byte3 = UnsafeUtil.getByte(address++);
+ if (address >= addressLimit) {
+ return incompleteStateFor(byte1, byte2, byte3);
+ }
+ }
+
+ // If we were called with state == MALFORMED, then byte1 is 0xFF,
+ // which never occurs in well-formed UTF-8, and so we will return
+ // MALFORMED again below.
+
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || byte3 > (byte) 0xBF
+ // byte4 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+
+ return partialIsValidUtf8(address, (int) (addressLimit - address));
+ }
+
+ @Override
+ String decodeUtf8(byte[] bytes, int index, int size) throws InvalidProtocolBufferException {
+ if ((index | size | bytes.length - index - size) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("buffer length=%d, index=%d, size=%d", bytes.length, index, size));
+ }
+
+ int offset = index;
+ final int limit = offset + size;
+
+ // The longest possible resulting String is the same as the number of input bytes, when it is
+ // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+ char[] resultArr = new char[size];
+ int resultPos = 0;
+
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (offset < limit) {
+ byte b = UnsafeUtil.getByte(bytes, offset);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+
+ while (offset < limit) {
+ byte byte1 = UnsafeUtil.getByte(bytes, offset++);
+ if (DecodeUtil.isOneByte(byte1)) {
+ DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+ // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+ // extra optimized loop to take care of these runs.
+ while (offset < limit) {
+ byte b = UnsafeUtil.getByte(bytes, offset);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ offset++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+ } else if (DecodeUtil.isTwoBytes(byte1)) {
+ if (offset >= limit) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleTwoBytes(
+ byte1, /* byte2 */ UnsafeUtil.getByte(bytes, offset++), resultArr, resultPos++);
+ } else if (DecodeUtil.isThreeBytes(byte1)) {
+ if (offset >= limit - 1) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleThreeBytes(
+ byte1,
+ /* byte2 */ UnsafeUtil.getByte(bytes, offset++),
+ /* byte3 */ UnsafeUtil.getByte(bytes, offset++),
+ resultArr,
+ resultPos++);
+ } else {
+ if (offset >= limit - 2) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleFourBytes(
+ byte1,
+ /* byte2 */ UnsafeUtil.getByte(bytes, offset++),
+ /* byte3 */ UnsafeUtil.getByte(bytes, offset++),
+ /* byte4 */ UnsafeUtil.getByte(bytes, offset++),
+ resultArr,
+ resultPos++);
+ // 4-byte case requires two chars.
+ resultPos++;
+ }
+ }
+
+ return new String(resultArr, 0, resultPos);
+ }
+
+ @Override
+ String decodeUtf8Direct(ByteBuffer buffer, int index, int size)
+ throws InvalidProtocolBufferException {
+ // Bitwise OR combines the sign bits so any negative value fails the check.
+ if ((index | size | buffer.limit() - index - size) < 0) {
+ throw new ArrayIndexOutOfBoundsException(
+ String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(), index, size));
+ }
+ long address = UnsafeUtil.addressOffset(buffer) + index;
+ final long addressLimit = address + size;
+
+ // The longest possible resulting String is the same as the number of input bytes, when it is
+ // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+ char[] resultArr = new char[size];
+ int resultPos = 0;
+
+ // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ while (address < addressLimit) {
+ byte b = UnsafeUtil.getByte(address);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ address++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+
+ while (address < addressLimit) {
+ byte byte1 = UnsafeUtil.getByte(address++);
+ if (DecodeUtil.isOneByte(byte1)) {
+ DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+ // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+ // extra optimized loop to take care of these runs.
+ while (address < addressLimit) {
+ byte b = UnsafeUtil.getByte(address);
+ if (!DecodeUtil.isOneByte(b)) {
+ break;
+ }
+ address++;
+ DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+ }
+ } else if (DecodeUtil.isTwoBytes(byte1)) {
+ if (address >= addressLimit) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleTwoBytes(
+ byte1, /* byte2 */ UnsafeUtil.getByte(address++), resultArr, resultPos++);
+ } else if (DecodeUtil.isThreeBytes(byte1)) {
+ if (address >= addressLimit - 1) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleThreeBytes(
+ byte1,
+ /* byte2 */ UnsafeUtil.getByte(address++),
+ /* byte3 */ UnsafeUtil.getByte(address++),
+ resultArr,
+ resultPos++);
+ } else {
+ if (address >= addressLimit - 2) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ DecodeUtil.handleFourBytes(
+ byte1,
+ /* byte2 */ UnsafeUtil.getByte(address++),
+ /* byte3 */ UnsafeUtil.getByte(address++),
+ /* byte4 */ UnsafeUtil.getByte(address++),
+ resultArr,
+ resultPos++);
+ // 4-byte case requires two chars.
+ resultPos++;
+ }
+ }
+
+ return new String(resultArr, 0, resultPos);
+ }
+
+ @Override
+ int encodeUtf8(final CharSequence in, final byte[] out, final int offset, final int length) {
+ long outIx = offset;
+ final long outLimit = outIx + length;
+ final int inLimit = in.length();
+ if (inLimit > length || out.length - length < offset) {
+ // Not even enough room for an ASCII-encoded string.
+ throw new ArrayIndexOutOfBoundsException(
+ "Failed writing " + in.charAt(inLimit - 1) + " at index " + (offset + length));
+ }
+
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ int inIx = 0;
+ for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
+ UnsafeUtil.putByte(out, outIx++, (byte) c);
+ }
+ if (inIx == inLimit) {
+ // We're done, it was ASCII encoded.
+ return (int) outIx;
+ }
+
+ for (char c; inIx < inLimit; ++inIx) {
+ c = in.charAt(inIx);
+ if (c < 0x80 && outIx < outLimit) {
+ UnsafeUtil.putByte(out, outIx++, (byte) c);
+ } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 6) | (c >>> 6)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
+ } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 5) | (c >>> 12)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & c)));
+ } else if (outIx <= outLimit - 4L) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
+ // bytes
+ final char low;
+ if (inIx + 1 == inLimit || !isSurrogatePair(c, (low = in.charAt(++inIx)))) {
+ throw new UnpairedSurrogateException((inIx - 1), inLimit);
+ }
+ int codePoint = toCodePoint(c, low);
+ UnsafeUtil.putByte(out, outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(out, outIx++, (byte) (0x80 | (0x3F & codePoint)));
+ } else {
+ if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
+ && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
+ // We are surrogates and we're not a surrogate pair.
+ throw new UnpairedSurrogateException(inIx, inLimit);
+ }
+ // Not enough space in the output buffer.
+ throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + outIx);
+ }
+ }
+
+ // All bytes have been encoded.
+ return (int) outIx;
+ }
+
+ @Override
+ void encodeUtf8Direct(CharSequence in, ByteBuffer out) {
+ final long address = addressOffset(out);
+ long outIx = address + out.position();
+ final long outLimit = address + out.limit();
+ final int inLimit = in.length();
+ if (inLimit > outLimit - outIx) {
+ // Not even enough room for an ASCII-encoded string.
+ throw new ArrayIndexOutOfBoundsException(
+ "Failed writing " + in.charAt(inLimit - 1) + " at index " + out.limit());
+ }
+
+ // Designed to take advantage of
+ // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+ int inIx = 0;
+ for (char c; inIx < inLimit && (c = in.charAt(inIx)) < 0x80; ++inIx) {
+ UnsafeUtil.putByte(outIx++, (byte) c);
+ }
+ if (inIx == inLimit) {
+ // We're done, it was ASCII encoded.
+ out.position((int) (outIx - address));
+ return;
+ }
+
+ for (char c; inIx < inLimit; ++inIx) {
+ c = in.charAt(inIx);
+ if (c < 0x80 && outIx < outLimit) {
+ UnsafeUtil.putByte(outIx++, (byte) c);
+ } else if (c < 0x800 && outIx <= outLimit - 2L) { // 11 bits, two UTF-8 bytes
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 6) | (c >>> 6)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
+ } else if ((c < MIN_SURROGATE || MAX_SURROGATE < c) && outIx <= outLimit - 3L) {
+ // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 5) | (c >>> 12)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & c)));
+ } else if (outIx <= outLimit - 4L) {
+ // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
+ // bytes
+ final char low;
+ if (inIx + 1 == inLimit || !isSurrogatePair(c, (low = in.charAt(++inIx)))) {
+ throw new UnpairedSurrogateException((inIx - 1), inLimit);
+ }
+ int codePoint = toCodePoint(c, low);
+ UnsafeUtil.putByte(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+ UnsafeUtil.putByte(outIx++, (byte) (0x80 | (0x3F & codePoint)));
+ } else {
+ if ((MIN_SURROGATE <= c && c <= MAX_SURROGATE)
+ && (inIx + 1 == inLimit || !isSurrogatePair(c, in.charAt(inIx + 1)))) {
+ // We are surrogates and we're not a surrogate pair.
+ throw new UnpairedSurrogateException(inIx, inLimit);
+ }
+ // Not enough space in the output buffer.
+ throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + outIx);
+ }
+ }
+
+ // All bytes have been encoded.
+ out.position((int) (outIx - address));
+ }
+
+ /**
+ * Counts (approximately) the number of consecutive ASCII characters starting from the given
+ * position, using the most efficient method available to the platform.
+ *
+ * @param bytes the array containing the character sequence
+ * @param offset the offset position of the index (same as index + arrayBaseOffset)
+ * @param maxChars the maximum number of characters to count
+ * @return the number of ASCII characters found. The stopping position will be at or
+ * before the first non-ASCII byte.
+ */
+ private static int unsafeEstimateConsecutiveAscii(
+ byte[] bytes, long offset, final int maxChars) {
+ if (maxChars < UNSAFE_COUNT_ASCII_THRESHOLD) {
+ // Don't bother with small strings.
+ return 0;
+ }
+
+ for (int i = 0; i < maxChars; i++) {
+ if (UnsafeUtil.getByte(bytes, offset++) < 0) {
+ return i;
+ }
+ }
+ return maxChars;
+ }
+
+ /**
+ * Same as {@link Utf8#estimateConsecutiveAscii(ByteBuffer, int, int)} except that it uses the
+ * most efficient method available to the platform.
+ */
+ private static int unsafeEstimateConsecutiveAscii(long address, final int maxChars) {
+ int remaining = maxChars;
+ if (remaining < UNSAFE_COUNT_ASCII_THRESHOLD) {
+ // Don't bother with small strings.
+ return 0;
+ }
+
+ // Read bytes until 8-byte aligned so that we can read longs in the loop below.
+ // We do this by ANDing the address with 7 to determine the number of bytes that need to
+ // be read before we're 8-byte aligned.
+ final int unaligned = 8 - ((int) address & 7);
+ for (int j = unaligned; j > 0; j--) {
+ if (UnsafeUtil.getByte(address++) < 0) {
+ return unaligned - j;
+ }
+ }
+
+ // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+ // To speed things up further, we're reading longs instead of bytes so we use a mask to
+ // determine if any byte in the current long is non-ASCII.
+ remaining -= unaligned;
+ for (; remaining >= 8 && (UnsafeUtil.getLong(address) & ASCII_MASK_LONG) == 0;
+ address += 8, remaining -= 8) {}
+ return maxChars - remaining;
+ }
+
+ private static int partialIsValidUtf8(final byte[] bytes, long offset, int remaining) {
+ // Skip past ASCII characters as quickly as possible.
+ final int skipped = unsafeEstimateConsecutiveAscii(bytes, offset, remaining);
+ remaining -= skipped;
+ offset += skipped;
+
+ for (;;) {
+ // Optimize for interior runs of ASCII bytes.
+ // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
+ // Maybe after seeing a few in a row that are ASCII, go back to fast mode?
+ int byte1 = 0;
+ for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(bytes, offset++)) >= 0; --remaining) {
+ }
+ if (remaining == 0) {
+ return COMPLETE;
+ }
+ remaining--;
+
+ // If we're here byte1 is not ASCII. Only need to handle 2-4 byte forms.
+ if (byte1 < (byte) 0xE0) {
+ // Two-byte form (110xxxxx 10xxxxxx)
+ if (remaining == 0) {
+ // Incomplete sequence
+ return byte1;
+ }
+ remaining--;
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // Three-byte form (1110xxxx 10xxxxxx 10xxxxxx)
+ if (remaining < 2) {
+ // Incomplete sequence
+ return unsafeIncompleteStateFor(bytes, byte1, offset, remaining);
+ }
+ remaining -= 2;
+
+ final int byte2;
+ if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // Four-byte form (1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ if (remaining < 3) {
+ // Incomplete sequence
+ return unsafeIncompleteStateFor(bytes, byte1, offset, remaining);
+ }
+ remaining -= 3;
+
+ final int byte2;
+ if ((byte2 = UnsafeUtil.getByte(bytes, offset++)) > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF
+ // byte4 trailing-byte test
+ || UnsafeUtil.getByte(bytes, offset++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+ }
+
+ private static int partialIsValidUtf8(long address, int remaining) {
+ // Skip past ASCII characters as quickly as possible.
+ final int skipped = unsafeEstimateConsecutiveAscii(address, remaining);
+ address += skipped;
+ remaining -= skipped;
+
+ for (;;) {
+ // Optimize for interior runs of ASCII bytes.
+ // TODO(nathanmittler): Consider checking 8 bytes at a time after some threshold?
+ // Maybe after seeing a few in a row that are ASCII, go back to fast mode?
+ int byte1 = 0;
+ for (; remaining > 0 && (byte1 = UnsafeUtil.getByte(address++)) >= 0; --remaining) {
+ }
+ if (remaining == 0) {
+ return COMPLETE;
+ }
+ remaining--;
+
+ if (byte1 < (byte) 0xE0) {
+ // Two-byte form
+
+ if (remaining == 0) {
+ // Incomplete sequence
+ return byte1;
+ }
+ remaining--;
+
+ // Simultaneously checks for illegal trailing-byte in
+ // leading position and overlong 2-byte form.
+ if (byte1 < (byte) 0xC2 || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else if (byte1 < (byte) 0xF0) {
+ // Three-byte form
+
+ if (remaining < 2) {
+ // Incomplete sequence
+ return unsafeIncompleteStateFor(address, byte1, remaining);
+ }
+ remaining -= 2;
+
+ final byte byte2 = UnsafeUtil.getByte(address++);
+ if (byte2 > (byte) 0xBF
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ } else {
+ // Four-byte form
+
+ if (remaining < 3) {
+ // Incomplete sequence
+ return unsafeIncompleteStateFor(address, byte1, remaining);
+ }
+ remaining -= 3;
+
+ final byte byte2 = UnsafeUtil.getByte(address++);
+ if (byte2 > (byte) 0xBF
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // if (byte1 > (byte) 0xF4 ||
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ // byte3 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF
+ // byte4 trailing-byte test
+ || UnsafeUtil.getByte(address++) > (byte) 0xBF) {
+ return MALFORMED;
+ }
+ }
+ }
+ }
+
+ private static int unsafeIncompleteStateFor(byte[] bytes, int byte1, long offset,
+ int remaining) {
+ switch (remaining) {
+ case 0: {
+ return incompleteStateFor(byte1);
+ }
+ case 1: {
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset));
+ }
+ case 2: {
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(bytes, offset),
+ UnsafeUtil.getByte(bytes, offset + 1));
+ }
+ default: {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ private static int unsafeIncompleteStateFor(long address, final int byte1, int remaining) {
+ switch (remaining) {
+ case 0: {
+ return incompleteStateFor(byte1);
+ }
+ case 1: {
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(address));
+ }
+ case 2: {
+ return incompleteStateFor(byte1, UnsafeUtil.getByte(address),
+ UnsafeUtil.getByte(address + 1));
+ }
+ default: {
+ throw new AssertionError();
+ }
+ }
+ }
+ }
+
+ /**
+ * Utility methods for decoding bytes into {@link String}. Callers are responsible for extracting
+ * bytes (possibly using Unsafe methods), and checking remaining bytes. All other UTF-8 validity
+ * checks and codepoint conversion happen in this class.
+ */
+ private static class DecodeUtil {
+
+ /**
+ * Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'.
+ */
+ private static boolean isOneByte(byte b) {
+ return b >= 0;
+ }
+
+ /**
+ * Returns whether this is a two-byte codepoint with the form '10XXXXXX'.
+ */
+ private static boolean isTwoBytes(byte b) {
+ return b < (byte) 0xE0;
+ }
+
+ /**
+ * Returns whether this is a three-byte codepoint with the form '110XXXXX'.
+ */
+ private static boolean isThreeBytes(byte b) {
+ return b < (byte) 0xF0;
+ }
+
+ private static void handleOneByte(byte byte1, char[] resultArr, int resultPos) {
+ resultArr[resultPos] = (char) byte1;
+ }
+
+ private static void handleTwoBytes(
+ byte byte1, byte byte2, char[] resultArr, int resultPos)
+ throws InvalidProtocolBufferException {
+ // Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and
+ // overlong 2-byte, '11000001'.
+ if (byte1 < (byte) 0xC2
+ || isNotTrailingByte(byte2)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ resultArr[resultPos] = (char) (((byte1 & 0x1F) << 6) | trailingByteValue(byte2));
+ }
+
+ private static void handleThreeBytes(
+ byte byte1, byte byte2, byte byte3, char[] resultArr, int resultPos)
+ throws InvalidProtocolBufferException {
+ if (isNotTrailingByte(byte2)
+ // overlong? 5 most significant bits must not all be zero
+ || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+ // check for illegal surrogate codepoints
+ || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+ || isNotTrailingByte(byte3)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ resultArr[resultPos] = (char)
+ (((byte1 & 0x0F) << 12) | (trailingByteValue(byte2) << 6) | trailingByteValue(byte3));
+ }
+
+ private static void handleFourBytes(
+ byte byte1, byte byte2, byte byte3, byte byte4, char[] resultArr, int resultPos)
+ throws InvalidProtocolBufferException{
+ if (isNotTrailingByte(byte2)
+ // Check that 1 <= plane <= 16. Tricky optimized form of:
+ // valid 4-byte leading byte?
+ // if (byte1 > (byte) 0xF4 ||
+ // overlong? 4 most significant bits must not all be zero
+ // byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+ // codepoint larger than the highest code point (U+10FFFF)?
+ // byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+ || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+ || isNotTrailingByte(byte3)
+ || isNotTrailingByte(byte4)) {
+ throw InvalidProtocolBufferException.invalidUtf8();
+ }
+ int codepoint = ((byte1 & 0x07) << 18)
+ | (trailingByteValue(byte2) << 12)
+ | (trailingByteValue(byte3) << 6)
+ | trailingByteValue(byte4);
+ resultArr[resultPos] = DecodeUtil.highSurrogate(codepoint);
+ resultArr[resultPos + 1] = DecodeUtil.lowSurrogate(codepoint);
+ }
+
+ /**
+ * Returns whether the byte is not a valid continuation of the form '10XXXXXX'.
+ */
+ private static boolean isNotTrailingByte(byte b) {
+ return b > (byte) 0xBF;
+ }
+
+ /**
+ * Returns the actual value of the trailing byte (removes the prefix '10') for composition.
+ */
+ private static int trailingByteValue(byte b) {
+ return b & 0x3F;
+ }
+
+ private static char highSurrogate(int codePoint) {
+ return (char) ((MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10))
+ + (codePoint >>> 10));
+ }
+
+ private static char lowSurrogate(int codePoint) {
+ return (char) (MIN_LOW_SURROGATE + (codePoint & 0x3ff));
+ }
+ }
+
+ private Utf8() {}
}
diff --git a/java/core/src/main/java/com/google/protobuf/WireFormat.java b/java/core/src/main/java/com/google/protobuf/WireFormat.java
index 8dbe1ae3..8b837ee5 100644
--- a/java/core/src/main/java/com/google/protobuf/WireFormat.java
+++ b/java/core/src/main/java/com/google/protobuf/WireFormat.java
@@ -47,6 +47,12 @@ public final class WireFormat {
// Do not allow instantiation.
private WireFormat() {}
+ static final int FIXED32_SIZE = 4;
+ static final int FIXED64_SIZE = 8;
+ static final int MAX_VARINT32_SIZE = 5;
+ static final int MAX_VARINT64_SIZE = 10;
+ static final int MAX_VARINT_SIZE = 10;
+
public static final int WIRETYPE_VARINT = 0;
public static final int WIRETYPE_FIXED64 = 1;
public static final int WIRETYPE_LENGTH_DELIMITED = 2;
@@ -116,16 +122,24 @@ public final class WireFormat {
FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ),
BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ),
STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED) {
- public boolean isPackable() { return false; }
+ @Override
+ public boolean isPackable() {
+ return false; }
},
GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP ) {
- public boolean isPackable() { return false; }
+ @Override
+ public boolean isPackable() {
+ return false; }
},
MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED) {
- public boolean isPackable() { return false; }
+ @Override
+ public boolean isPackable() {
+ return false; }
},
BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED) {
- public boolean isPackable() { return false; }
+ @Override
+ public boolean isPackable() {
+ return false; }
},
UINT32 (JavaType.INT , WIRETYPE_VARINT ),
ENUM (JavaType.ENUM , WIRETYPE_VARINT ),
@@ -170,18 +184,21 @@ public final class WireFormat {
enum Utf8Validation {
/** Eagerly parses to String; silently accepts invalid UTF8 bytes. */
LOOSE {
+ @Override
Object readString(CodedInputStream input) throws IOException {
return input.readString();
}
},
/** Eagerly parses to String; throws an IOException on invalid bytes. */
STRICT {
+ @Override
Object readString(CodedInputStream input) throws IOException {
return input.readStringRequireUtf8();
}
},
/** Keep data as ByteString; validation/conversion to String is lazy. */
LAZY {
+ @Override
Object readString(CodedInputStream input) throws IOException {
return input.readBytes();
}
diff --git a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java
index d964ef59..cb2d34eb 100644
--- a/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/AbstractMessageTest.java
@@ -30,6 +30,9 @@
package com.google.protobuf;
+import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
+import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
+
import com.google.protobuf.Descriptors.FieldDescriptor;
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import protobuf_unittest.UnittestProto;
@@ -40,10 +43,8 @@ import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestRequiredForeign;
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
-
-import junit.framework.TestCase;
-
import java.util.Map;
+import junit.framework.TestCase;
/**
* Unit test for {@link AbstractMessage}.
@@ -65,35 +66,44 @@ public class AbstractMessageTest extends TestCase {
this.wrappedMessage = wrappedMessage;
}
+ @Override
public Descriptors.Descriptor getDescriptorForType() {
return wrappedMessage.getDescriptorForType();
}
+ @Override
public AbstractMessageWrapper getDefaultInstanceForType() {
return new AbstractMessageWrapper(
wrappedMessage.getDefaultInstanceForType());
}
+ @Override
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
return wrappedMessage.getAllFields();
}
+ @Override
public boolean hasField(Descriptors.FieldDescriptor field) {
return wrappedMessage.hasField(field);
}
+ @Override
public Object getField(Descriptors.FieldDescriptor field) {
return wrappedMessage.getField(field);
}
+ @Override
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
return wrappedMessage.getRepeatedFieldCount(field);
}
- public Object getRepeatedField(
- Descriptors.FieldDescriptor field, int index) {
+ @Override
+ public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) {
return wrappedMessage.getRepeatedField(field, index);
}
+ @Override
public UnknownFieldSet getUnknownFields() {
return wrappedMessage.getUnknownFields();
}
+ @Override
public Builder newBuilderForType() {
return new Builder(wrappedMessage.newBuilderForType());
}
+ @Override
public Builder toBuilder() {
return new Builder(wrappedMessage.toBuilder());
}
@@ -105,65 +115,80 @@ public class AbstractMessageTest extends TestCase {
this.wrappedBuilder = wrappedBuilder;
}
+ @Override
public AbstractMessageWrapper build() {
return new AbstractMessageWrapper(wrappedBuilder.build());
}
+ @Override
public AbstractMessageWrapper buildPartial() {
return new AbstractMessageWrapper(wrappedBuilder.buildPartial());
}
+ @Override
public Builder clone() {
return new Builder(wrappedBuilder.clone());
}
+ @Override
public boolean isInitialized() {
return clone().buildPartial().isInitialized();
}
+ @Override
public Descriptors.Descriptor getDescriptorForType() {
return wrappedBuilder.getDescriptorForType();
}
+ @Override
public AbstractMessageWrapper getDefaultInstanceForType() {
return new AbstractMessageWrapper(
wrappedBuilder.getDefaultInstanceForType());
}
+ @Override
public Map<Descriptors.FieldDescriptor, Object> getAllFields() {
return wrappedBuilder.getAllFields();
}
+ @Override
public Builder newBuilderForField(Descriptors.FieldDescriptor field) {
return new Builder(wrappedBuilder.newBuilderForField(field));
}
+ @Override
public boolean hasField(Descriptors.FieldDescriptor field) {
return wrappedBuilder.hasField(field);
}
+ @Override
public Object getField(Descriptors.FieldDescriptor field) {
return wrappedBuilder.getField(field);
}
+ @Override
public Builder setField(Descriptors.FieldDescriptor field, Object value) {
wrappedBuilder.setField(field, value);
return this;
}
+ @Override
public Builder clearField(Descriptors.FieldDescriptor field) {
wrappedBuilder.clearField(field);
return this;
}
+ @Override
public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) {
return wrappedBuilder.getRepeatedFieldCount(field);
}
- public Object getRepeatedField(
- Descriptors.FieldDescriptor field, int index) {
+ @Override
+ public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) {
return wrappedBuilder.getRepeatedField(field, index);
}
- public Builder setRepeatedField(Descriptors.FieldDescriptor field,
- int index, Object value) {
+ @Override
+ public Builder setRepeatedField(Descriptors.FieldDescriptor field, int index, Object value) {
wrappedBuilder.setRepeatedField(field, index, value);
return this;
}
- public Builder addRepeatedField(
- Descriptors.FieldDescriptor field, Object value) {
+ @Override
+ public Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value) {
wrappedBuilder.addRepeatedField(field, value);
return this;
}
+ @Override
public UnknownFieldSet getUnknownFields() {
return wrappedBuilder.getUnknownFields();
}
+ @Override
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
wrappedBuilder.setUnknownFields(unknownFields);
return this;
@@ -173,6 +198,7 @@ public class AbstractMessageTest extends TestCase {
return wrappedBuilder.getFieldBuilder(field);
}
}
+ @Override
public Parser<? extends Message> getParserForType() {
return wrappedMessage.getParserForType();
}
@@ -323,11 +349,6 @@ public class AbstractMessageTest extends TestCase {
// -----------------------------------------------------------------
// Tests for isInitialized().
- private static final TestRequired TEST_REQUIRED_UNINITIALIZED =
- TestRequired.getDefaultInstance();
- private static final TestRequired TEST_REQUIRED_INITIALIZED =
- TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
-
public void testIsInitialized() throws Exception {
TestRequired.Builder builder = TestRequired.newBuilder();
AbstractMessageWrapper.Builder abstractBuilder =
@@ -357,7 +378,7 @@ public class AbstractMessageTest extends TestCase {
builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED);
assertFalse(abstractBuilder.isInitialized());
assertEquals(
- "optional_message.a, optional_message.b, optional_message.c",
+ "optional_message.b, optional_message.c",
abstractBuilder.getInitializationErrorString());
builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED);
@@ -367,7 +388,7 @@ public class AbstractMessageTest extends TestCase {
builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED);
assertFalse(abstractBuilder.isInitialized());
assertEquals(
- "repeated_message[0].a, repeated_message[0].b, repeated_message[0].c",
+ "repeated_message[0].b, repeated_message[0].c",
abstractBuilder.getInitializationErrorString());
builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED);
@@ -468,7 +489,6 @@ public class AbstractMessageTest extends TestCase {
checkEqualsIsConsistent(eUnknownFields, eUnknownFields2);
}
-
/**
* Asserts that the given proto has symmetric equals and hashCode methods.
*/
diff --git a/java/core/src/test/java/com/google/protobuf/AnyTest.java b/java/core/src/test/java/com/google/protobuf/AnyTest.java
index e169f69d..cf91ed91 100644
--- a/java/core/src/test/java/com/google/protobuf/AnyTest.java
+++ b/java/core/src/test/java/com/google/protobuf/AnyTest.java
@@ -75,6 +75,51 @@ public class AnyTest extends TestCase {
}
}
+ public void testCustomTypeUrls() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ TestUtil.setAllFields(builder);
+ TestAllTypes message = builder.build();
+
+ TestAny container = TestAny.newBuilder()
+ .setValue(Any.pack(message, "xxx.com")).build();
+
+ assertEquals(
+ "xxx.com/" + TestAllTypes.getDescriptor().getFullName(),
+ container.getValue().getTypeUrl());
+
+ assertTrue(container.getValue().is(TestAllTypes.class));
+ assertFalse(container.getValue().is(TestAny.class));
+
+ TestAllTypes result = container.getValue().unpack(TestAllTypes.class);
+ TestUtil.assertAllFieldsSet(result);
+
+ container = TestAny.newBuilder()
+ .setValue(Any.pack(message, "yyy.com/")).build();
+
+ assertEquals(
+ "yyy.com/" + TestAllTypes.getDescriptor().getFullName(),
+ container.getValue().getTypeUrl());
+
+ assertTrue(container.getValue().is(TestAllTypes.class));
+ assertFalse(container.getValue().is(TestAny.class));
+
+ result = container.getValue().unpack(TestAllTypes.class);
+ TestUtil.assertAllFieldsSet(result);
+
+ container = TestAny.newBuilder()
+ .setValue(Any.pack(message, "")).build();
+
+ assertEquals(
+ "/" + TestAllTypes.getDescriptor().getFullName(),
+ container.getValue().getTypeUrl());
+
+ assertTrue(container.getValue().is(TestAllTypes.class));
+ assertFalse(container.getValue().is(TestAny.class));
+
+ result = container.getValue().unpack(TestAllTypes.class);
+ TestUtil.assertAllFieldsSet(result);
+ }
+
public void testCachedUnpackResult() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestUtil.setAllFields(builder);
diff --git a/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
index b8ad1fe4..4906763c 100644
--- a/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java
@@ -32,38 +32,39 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
+import com.google.protobuf.Internal.BooleanList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import junit.framework.TestCase;
/**
* Tests for {@link BooleanArrayList}.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
public class BooleanArrayListTest extends TestCase {
-
- private static final BooleanArrayList UNARY_LIST = newImmutableBooleanArrayList(true);
+
+ private static final BooleanArrayList UNARY_LIST =
+ newImmutableBooleanArrayList(true);
private static final BooleanArrayList TERTIARY_LIST =
- newImmutableBooleanArrayList(true, true, false);
-
+ newImmutableBooleanArrayList(true, false, true);
+
private BooleanArrayList list;
-
+
@Override
protected void setUp() throws Exception {
list = new BooleanArrayList();
}
-
+
public void testEmptyListReturnsSameInstance() {
assertSame(BooleanArrayList.emptyList(), BooleanArrayList.emptyList());
}
-
+
public void testEmptyListIsImmutable() {
assertImmutable(BooleanArrayList.emptyList());
}
-
+
public void testMakeImmutable() {
list.addBoolean(true);
list.addBoolean(false);
@@ -72,30 +73,16 @@ public class BooleanArrayListTest extends TestCase {
list.makeImmutable();
assertImmutable(list);
}
-
- public void testCopyConstructor() {
- BooleanArrayList copy = new BooleanArrayList(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new BooleanArrayList(BooleanArrayList.emptyList());
- assertEquals(BooleanArrayList.emptyList(), copy);
-
- copy = new BooleanArrayList(asList(false, false, true));
- assertEquals(asList(false, false, true), copy);
-
- copy = new BooleanArrayList(Collections.<Boolean>emptyList());
- assertEquals(BooleanArrayList.emptyList(), copy);
- }
-
+
public void testModificationWithIteration() {
- list.addAll(asList(true, false, false, true));
+ list.addAll(asList(true, false, true, false));
Iterator<Boolean> iterator = list.iterator();
assertEquals(4, list.size());
assertEquals(true, (boolean) list.get(0));
assertEquals(true, (boolean) iterator.next());
list.set(0, true);
assertEquals(false, (boolean) iterator.next());
-
+
list.remove(0);
try {
iterator.next();
@@ -103,7 +90,7 @@ public class BooleanArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
-
+
iterator = list.iterator();
list.add(0, false);
try {
@@ -113,19 +100,19 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGet() {
assertEquals(true, (boolean) TERTIARY_LIST.get(0));
- assertEquals(true, (boolean) TERTIARY_LIST.get(1));
- assertEquals(false, (boolean) TERTIARY_LIST.get(2));
-
+ assertEquals(false, (boolean) TERTIARY_LIST.get(1));
+ assertEquals(true, (boolean) TERTIARY_LIST.get(2));
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -133,19 +120,19 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
-
- public void testGetInt() {
+
+ public void testGetBoolean() {
assertEquals(true, TERTIARY_LIST.getBoolean(0));
- assertEquals(true, TERTIARY_LIST.getBoolean(1));
- assertEquals(false, TERTIARY_LIST.getBoolean(2));
-
+ assertEquals(false, TERTIARY_LIST.getBoolean(1));
+ assertEquals(true, TERTIARY_LIST.getBoolean(2));
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -153,7 +140,7 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSize() {
assertEquals(0, BooleanArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
@@ -164,26 +151,26 @@ public class BooleanArrayListTest extends TestCase {
list.addBoolean(false);
list.addBoolean(false);
assertEquals(4, list.size());
-
+
list.remove(0);
assertEquals(3, list.size());
-
+
list.add(true);
assertEquals(4, list.size());
}
-
+
public void testSet() {
list.addBoolean(false);
list.addBoolean(false);
-
+
assertEquals(false, (boolean) list.set(0, true));
assertEquals(true, list.getBoolean(0));
assertEquals(false, (boolean) list.set(1, false));
assertEquals(false, list.getBoolean(1));
-
+
try {
- list.set(-1, true);
+ list.set(-1, false);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
@@ -196,17 +183,17 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
-
- public void testSetInt() {
+
+ public void testSetBoolean() {
list.addBoolean(true);
list.addBoolean(true);
-
+
assertEquals(true, list.setBoolean(0, false));
assertEquals(false, list.getBoolean(0));
assertEquals(true, list.setBoolean(1, false));
assertEquals(false, list.getBoolean(1));
-
+
try {
list.setBoolean(-1, false);
fail();
@@ -215,76 +202,78 @@ public class BooleanArrayListTest extends TestCase {
}
try {
- list.setBoolean(2, true);
+ list.setBoolean(2, false);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
public void testAdd() {
assertEquals(0, list.size());
- assertTrue(list.add(true));
- assertEquals(asList(true), list);
-
assertTrue(list.add(false));
+ assertEquals(asList(false), list);
+
+ assertTrue(list.add(true));
list.add(0, false);
- assertEquals(asList(false, true, false), list);
-
- list.add(0, false);
+ assertEquals(asList(false, false, true), list);
+
list.add(0, true);
+ list.add(0, false);
// Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) {
- list.add(true);
+ list.add(i % 2 == 0);
}
- assertEquals(asList(true, false, false, true, false, true, true, true, true, true, true), list);
-
+ assertEquals(
+ asList(false, true, false, false, true, true, false, true, false, true, false),
+ list);
+
try {
- list.add(-1, false);
+ list.add(-1, true);
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.add(4, true);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
- public void testAddInt() {
- assertEquals(0, list.size());
- list.addBoolean(true);
- assertEquals(asList(true), list);
+ public void testAddBoolean() {
+ assertEquals(0, list.size());
list.addBoolean(false);
- assertEquals(asList(true, false), list);
+ assertEquals(asList(false), list);
+
+ list.addBoolean(true);
+ assertEquals(asList(false, true), list);
}
-
+
public void testAddAll() {
assertEquals(0, list.size());
- assertTrue(list.addAll(Collections.singleton(false)));
+ assertTrue(list.addAll(Collections.singleton(true)));
assertEquals(1, list.size());
- assertEquals(false, (boolean) list.get(0));
- assertEquals(false, list.getBoolean(0));
-
- assertTrue(list.addAll(asList(true, false, false, false, true)));
- assertEquals(asList(false, true, false, false, false, true), list);
-
+ assertEquals(true, (boolean) list.get(0));
+ assertEquals(true, list.getBoolean(0));
+
+ assertTrue(list.addAll(asList(false, true, false, true, false)));
+ assertEquals(asList(true, false, true, false, true, false), list);
+
assertTrue(list.addAll(TERTIARY_LIST));
- assertEquals(asList(false, true, false, false, false, true, true, true, false), list);
+ assertEquals(asList(true, false, true, false, true, false, true, false, true), list);
assertFalse(list.addAll(Collections.<Boolean>emptyList()));
assertFalse(list.addAll(BooleanArrayList.emptyList()));
}
-
+
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(true, (boolean) list.remove(0));
- assertEquals(asList(true, false), list);
+ assertEquals(asList(false, true), list);
assertTrue(list.remove(Boolean.TRUE));
assertEquals(asList(false), list);
@@ -294,92 +283,107 @@ public class BooleanArrayListTest extends TestCase {
assertEquals(false, (boolean) list.remove(0));
assertEquals(asList(), list);
-
+
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
+ public void testRemoveEndOfCapacity() {
+ BooleanList toRemove = BooleanArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addBoolean(true);
+ toRemove.remove(0);
+ assertEquals(0, toRemove.size());
+ }
+
+ public void testSublistRemoveEndOfCapacity() {
+ BooleanList toRemove = BooleanArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addBoolean(true);
+ toRemove.subList(0, 1).clear();
+ assertEquals(0, toRemove.size());
+ }
+
private void assertImmutable(BooleanArrayList list) {
+
try {
- list.add(false);
+ list.add(true);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.add(0, true);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
- list.addAll(Collections.singletonList(false));
+ list.addAll(Collections.singletonList(true));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(new BooleanArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.singleton(true));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
- list.addBoolean(true);
+ list.addBoolean(false);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.clear();
fail();
@@ -393,63 +397,63 @@ public class BooleanArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.<Boolean>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
- list.retainAll(Collections.singleton(Boolean.TRUE));
+ list.removeAll(Collections.singleton(Boolean.TRUE));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
- list.set(0, true);
+ list.set(0, false);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.setBoolean(0, false);
fail();
@@ -457,7 +461,7 @@ public class BooleanArrayListTest extends TestCase {
// expected
}
}
-
+
private static BooleanArrayList newImmutableBooleanArrayList(boolean... elements) {
BooleanArrayList list = new BooleanArrayList();
for (boolean element : elements) {
diff --git a/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java
index 7a8a0a5e..db10ee74 100644
--- a/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java
@@ -37,7 +37,6 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
-
/**
* This class tests {@link BoundedByteString}, which extends {@link LiteralByteString},
* by inheriting the tests from {@link LiteralByteStringTest}. The only method which
diff --git a/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java b/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java
new file mode 100644
index 00000000..6b1cfe78
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java
@@ -0,0 +1,80 @@
+// 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 java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link ByteBufferWriter}.
+ */
+public class ByteBufferWriterTest extends TestCase {
+
+ public void testHeapBuffer() throws IOException {
+ // Test a small and large buffer.
+ testWrite(ByteBuffer.allocate(100));
+ testWrite(ByteBuffer.allocate(1024 * 100));
+ }
+
+ public void testDirectBuffer() throws IOException {
+ // Test a small and large buffer.
+ testWrite(ByteBuffer.allocateDirect(100));
+ testWrite(ByteBuffer.allocateDirect(1024 * 100));
+ }
+
+ private void testWrite(ByteBuffer buffer) throws IOException {
+ fillRandom(buffer);
+ ByteArrayOutputStream os = new ByteArrayOutputStream(buffer.remaining());
+ ByteBufferWriter.write(buffer, os);
+ assertEquals(0, buffer.position());
+ assertTrue(Arrays.equals(toArray(buffer), os.toByteArray()));
+ }
+
+ private void fillRandom(ByteBuffer buf) {
+ byte[] bytes = new byte[buf.remaining()];
+ new Random().nextBytes(bytes);
+ buf.put(bytes);
+ buf.flip();
+ return;
+ }
+
+ private byte[] toArray(ByteBuffer buf) {
+ int originalPosition = buf.position();
+ byte[] bytes = new byte[buf.remaining()];
+ buf.get(bytes);
+ buf.position(originalPosition);
+ return bytes;
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
index 5267c160..be71f1f5 100644
--- a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
@@ -31,14 +31,12 @@
package com.google.protobuf;
import com.google.protobuf.ByteString.Output;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -47,6 +45,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
+import junit.framework.TestCase;
/**
* Test methods with implementations in {@link ByteString}, plus do some top-level "integration"
@@ -757,4 +756,17 @@ public class ByteStringTest extends TestCase {
assertEquals((byte) 2, result[dataSize - dataSize / 2]);
assertEquals((byte) 2, result[dataSize - 1]);
}
+
+ /**
+ * Tests ByteString uses Arrays based byte copier when running under Hotstop VM.
+ */
+ public void testByteArrayCopier() throws Exception {
+ Field field = ByteString.class.getDeclaredField("byteArrayCopier");
+ field.setAccessible(true);
+ Object byteArrayCopier = field.get(null);
+ assertNotNull(byteArrayCopier);
+ assertTrue(
+ byteArrayCopier.toString(),
+ byteArrayCopier.getClass().getSimpleName().endsWith("ArraysByteArrayCopier"));
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java
index 3d6381c9..50b87ae3 100644
--- a/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java
+++ b/java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java
@@ -34,6 +34,7 @@ import proto2_test_check_utf8.TestCheckUtf8.BytesWrapper;
import proto2_test_check_utf8.TestCheckUtf8.StringWrapper;
import proto2_test_check_utf8_size.TestCheckUtf8Size.BytesWrapperSize;
import proto2_test_check_utf8_size.TestCheckUtf8Size.StringWrapperSize;
+import java.io.ByteArrayInputStream;
import junit.framework.TestCase;
/**
@@ -89,14 +90,9 @@ public class CheckUtf8Test extends TestCase {
}
public void testParseRequiredStringWithBadUtf8() throws Exception {
- ByteString serialized =
- BytesWrapper.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
- try {
- StringWrapper.parser().parseFrom(serialized);
- fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
- } catch (InvalidProtocolBufferException exception) {
- assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
- }
+ byte[] serialized =
+ BytesWrapper.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteArray();
+ assertParseBadUtf8(StringWrapper.getDefaultInstance(), serialized);
}
public void testBuildRequiredStringWithBadUtf8Size() throws Exception {
@@ -127,14 +123,36 @@ public class CheckUtf8Test extends TestCase {
}
public void testParseRequiredStringWithBadUtf8Size() throws Exception {
- ByteString serialized =
- BytesWrapperSize.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
+ byte[] serialized =
+ BytesWrapperSize.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteArray();
+ assertParseBadUtf8(StringWrapperSize.getDefaultInstance(), serialized);
+ }
+
+ private void assertParseBadUtf8(MessageLite defaultInstance, byte[] data) throws Exception {
+ // Check combinations of (parser vs. builder) x (byte[] vs. InputStream)
try {
- StringWrapperSize.parser().parseFrom(serialized);
+ defaultInstance.getParserForType().parseFrom(data);
+ fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
+ } catch (InvalidProtocolBufferException exception) {
+ assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
+ }
+ try {
+ defaultInstance.newBuilderForType().mergeFrom(data);
+ fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
+ } catch (InvalidProtocolBufferException exception) {
+ assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
+ }
+ try {
+ defaultInstance.getParserForType().parseFrom(new ByteArrayInputStream(data));
+ fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
+ } catch (InvalidProtocolBufferException exception) {
+ assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
+ }
+ try {
+ defaultInstance.newBuilderForType().mergeFrom(new ByteArrayInputStream(data));
fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
}
}
-
}
diff --git a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
index 18d8142c..5ea6b79c 100644
--- a/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
+++ b/java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java
@@ -35,15 +35,15 @@ import protobuf_unittest.UnittestProto.Int32Message;
import protobuf_unittest.UnittestProto.Int64Message;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestRecursiveMessage;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import junit.framework.TestCase;
/**
* Unit test for {@link CodedInputStream}.
@@ -51,10 +51,84 @@ import java.nio.ByteBuffer;
* @author kenton@google.com Kenton Varda
*/
public class CodedInputStreamTest extends TestCase {
+
+ private static final int DEFAULT_BLOCK_SIZE = 4096;
+
+ private enum InputType {
+ ARRAY {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(data);
+ }
+ },
+ NIO_HEAP {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(ByteBuffer.wrap(data));
+ }
+ },
+ NIO_DIRECT {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+ buffer.put(data);
+ buffer.flip();
+ return CodedInputStream.newInstance(buffer);
+ }
+ },
+ STREAM {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ return CodedInputStream.newInstance(new SmallBlockInputStream(data, blockSize));
+ }
+ },
+ ITER_DIRECT {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ if (blockSize > DEFAULT_BLOCK_SIZE) {
+ blockSize = DEFAULT_BLOCK_SIZE;
+ }
+ ArrayList <ByteBuffer> input = new ArrayList <ByteBuffer>();
+ for (int i = 0; i < data.length; i += blockSize) {
+ int rl = Math.min(blockSize, data.length - i);
+ ByteBuffer rb = ByteBuffer.allocateDirect(rl);
+ rb.put(data, i, rl);
+ rb.flip();
+ input.add(rb);
+ }
+ return CodedInputStream.newInstance(input);
+ }
+ },
+ STREAM_ITER_DIRECT {
+ @Override
+ CodedInputStream newDecoder(byte[] data, int blockSize) {
+ if (blockSize > DEFAULT_BLOCK_SIZE) {
+ blockSize = DEFAULT_BLOCK_SIZE;
+ }
+ ArrayList <ByteBuffer> input = new ArrayList <ByteBuffer>();
+ for (int i = 0; i < data.length; i += blockSize) {
+ int rl = Math.min(blockSize, data.length - i);
+ ByteBuffer rb = ByteBuffer.allocateDirect(rl);
+ rb.put(data, i, rl);
+ rb.flip();
+ input.add(rb);
+ }
+ return CodedInputStream.newInstance(new IterableByteBufferInputStream(input));
+ }
+ };
+
+
+
+ CodedInputStream newDecoder(byte[] data) {
+ return newDecoder(data, data.length);
+ }
+
+ abstract CodedInputStream newDecoder(byte[] data, int blockSize);
+ }
+
/**
- * Helper to construct a byte array from a bunch of bytes. The inputs are
- * actually ints so that I can use hex notation and not get stupid errors
- * about precision.
+ * Helper to construct a byte array from a bunch of bytes. The inputs are actually ints so that I
+ * can use hex notation and not get stupid errors about precision.
*/
private byte[] bytes(int... bytesAsInts) {
byte[] bytes = new byte[bytesAsInts.length];
@@ -65,79 +139,58 @@ public class CodedInputStreamTest extends TestCase {
}
/**
- * An InputStream which limits the number of bytes it reads at a time.
- * We use this to make sure that CodedInputStream doesn't screw up when
- * reading in small blocks.
+ * An InputStream which limits the number of bytes it reads at a time. We use this to make sure
+ * that CodedInputStream doesn't screw up when reading in small blocks.
*/
private static final class SmallBlockInputStream extends FilterInputStream {
private final int blockSize;
public SmallBlockInputStream(byte[] data, int blockSize) {
- this(new ByteArrayInputStream(data), blockSize);
- }
-
- public SmallBlockInputStream(InputStream in, int blockSize) {
- super(in);
+ super(new ByteArrayInputStream(data));
this.blockSize = blockSize;
}
+ @Override
public int read(byte[] b) throws IOException {
return super.read(b, 0, Math.min(b.length, blockSize));
}
+ @Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, Math.min(len, blockSize));
}
}
- private void assertDataConsumed(byte[] data, CodedInputStream input)
+ private void assertDataConsumed(String msg, byte[] data, CodedInputStream input)
throws IOException {
- assertEquals(data.length, input.getTotalBytesRead());
- assertTrue(input.isAtEnd());
+ assertEquals(msg, data.length, input.getTotalBytesRead());
+ assertTrue(msg, input.isAtEnd());
}
/**
- * Parses the given bytes using readRawVarint32() and readRawVarint64() and
- * checks that the result matches the given value.
+ * Parses the given bytes using readRawVarint32() and readRawVarint64() and checks that the result
+ * matches the given value.
*/
private void assertReadVarint(byte[] data, long value) throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(data);
- assertEquals((int) value, input.readRawVarint32());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(data);
- assertEquals(value, input.readRawVarint64());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(data);
- assertEquals(value, input.readRawVarint64SlowPath());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(data);
- assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
- assertDataConsumed(data, input);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertEquals((int) value, input.readRawVarint32());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertEquals(value, input.readRawVarint64());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertEquals(value, input.readRawVarint64SlowPath());
- assertDataConsumed(data, input);
-
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertTrue(input.skipField(WireFormat.WIRETYPE_VARINT));
- assertDataConsumed(data, input);
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), (int) value, input.readRawVarint32());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawVarint64());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawVarint64SlowPath());
+ assertDataConsumed(inputType.name(), data, input);
+
+ input = inputType.newDecoder(data, blockSize);
+ assertTrue(inputType.name(), input.skipField(WireFormat.WIRETYPE_VARINT));
+ assertDataConsumed(inputType.name(), data, input);
+ }
}
// Try reading direct from an InputStream. We want to verify that it
@@ -151,35 +204,26 @@ public class CodedInputStreamTest extends TestCase {
}
/**
- * Parses the given bytes using readRawVarint32() and readRawVarint64() and
- * expects them to fail with an InvalidProtocolBufferException whose
- * description matches the given one.
+ * Parses the given bytes using readRawVarint32() and readRawVarint64() and expects them to fail
+ * with an InvalidProtocolBufferException whose description matches the given one.
*/
- private void assertReadVarintFailure(
- InvalidProtocolBufferException expected, byte[] data)
+ private void assertReadVarintFailure(InvalidProtocolBufferException expected, byte[] data)
throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(data);
- try {
- input.readRawVarint32();
- fail("Should have thrown an exception.");
- } catch (InvalidProtocolBufferException e) {
- assertEquals(expected.getMessage(), e.getMessage());
- }
-
- input = CodedInputStream.newInstance(data);
- try {
- input.readRawVarint64();
- fail("Should have thrown an exception.");
- } catch (InvalidProtocolBufferException e) {
- assertEquals(expected.getMessage(), e.getMessage());
- }
-
- input = CodedInputStream.newInstance(data);
- try {
- input.readRawVarint64SlowPath();
- fail("Should have thrown an exception.");
- } catch (InvalidProtocolBufferException e) {
- assertEquals(expected.getMessage(), e.getMessage());
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.readRawVarint32();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(inputType.name(), expected.getMessage(), e.getMessage());
+ }
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.readRawVarint64();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(inputType.name(), expected.getMessage(), e.getMessage());
+ }
}
// Make sure we get the same error when reading direct from an InputStream.
@@ -199,72 +243,74 @@ public class CodedInputStreamTest extends TestCase {
// 14882
assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
// 2961488830
- assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
+ assertReadVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28));
// 64-bit
// 7256456126
- assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
+ assertReadVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28));
// 41256202580718336
assertReadVarint(
- bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+ bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+ (0x00 << 0)
+ | (0x66 << 7)
+ | (0x6b << 14)
+ | (0x1c << 21)
+ | (0x43L << 28)
+ | (0x49L << 35)
+ | (0x24L << 42)
+ | (0x49L << 49));
// 11964378330978735131
assertReadVarint(
- bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
- (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
- (0x05L << 49) | (0x26L << 56) | (0x01L << 63));
+ bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+ (0x1b << 0)
+ | (0x28 << 7)
+ | (0x79 << 14)
+ | (0x42 << 21)
+ | (0x3bL << 28)
+ | (0x56L << 35)
+ | (0x00L << 42)
+ | (0x05L << 49)
+ | (0x26L << 56)
+ | (0x01L << 63));
// Failures
assertReadVarintFailure(
- InvalidProtocolBufferException.malformedVarint(),
- bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
- 0x00));
- assertReadVarintFailure(
- InvalidProtocolBufferException.truncatedMessage(),
- bytes(0x80));
+ InvalidProtocolBufferException.malformedVarint(),
+ bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00));
+ assertReadVarintFailure(InvalidProtocolBufferException.truncatedMessage(), bytes(0x80));
}
/**
- * Parses the given bytes using readRawLittleEndian32() and checks
- * that the result matches the given value.
+ * Parses the given bytes using readRawLittleEndian32() and checks that the result matches the
+ * given value.
*/
- private void assertReadLittleEndian32(byte[] data, int value)
- throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(data);
- assertEquals(value, input.readRawLittleEndian32());
- assertTrue(input.isAtEnd());
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertEquals(value, input.readRawLittleEndian32());
- assertTrue(input.isAtEnd());
+ private void assertReadLittleEndian32(byte[] data, int value) throws Exception {
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawLittleEndian32());
+ assertTrue(inputType.name(), input.isAtEnd());
+ }
}
}
/**
- * Parses the given bytes using readRawLittleEndian64() and checks
- * that the result matches the given value.
+ * Parses the given bytes using readRawLittleEndian64() and checks that the result matches the
+ * given value.
*/
- private void assertReadLittleEndian64(byte[] data, long value)
- throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(data);
- assertEquals(value, input.readRawLittleEndian64());
- assertTrue(input.isAtEnd());
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- input = CodedInputStream.newInstance(
- new SmallBlockInputStream(data, blockSize));
- assertEquals(value, input.readRawLittleEndian64());
- assertTrue(input.isAtEnd());
+ private void assertReadLittleEndian64(byte[] data, long value) throws Exception {
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ CodedInputStream input = inputType.newDecoder(data, blockSize);
+ assertEquals(inputType.name(), value, input.readRawLittleEndian64());
+ assertTrue(inputType.name(), input.isAtEnd());
+ }
}
}
@@ -274,40 +320,32 @@ public class CodedInputStreamTest extends TestCase {
assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
assertReadLittleEndian64(
- bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
- 0x123456789abcdef0L);
+ bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L);
assertReadLittleEndian64(
- bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
- 0x9abcdef012345678L);
+ bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L);
}
/** Test decodeZigZag32() and decodeZigZag64(). */
public void testDecodeZigZag() throws Exception {
- assertEquals( 0, CodedInputStream.decodeZigZag32(0));
+ assertEquals(0, CodedInputStream.decodeZigZag32(0));
assertEquals(-1, CodedInputStream.decodeZigZag32(1));
- assertEquals( 1, CodedInputStream.decodeZigZag32(2));
+ assertEquals(1, CodedInputStream.decodeZigZag32(2));
assertEquals(-2, CodedInputStream.decodeZigZag32(3));
assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE));
assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF));
assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE));
assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF));
- assertEquals( 0, CodedInputStream.decodeZigZag64(0));
+ assertEquals(0, CodedInputStream.decodeZigZag64(0));
assertEquals(-1, CodedInputStream.decodeZigZag64(1));
- assertEquals( 1, CodedInputStream.decodeZigZag64(2));
+ assertEquals(1, CodedInputStream.decodeZigZag64(2));
assertEquals(-2, CodedInputStream.decodeZigZag64(3));
- assertEquals(0x000000003FFFFFFFL,
- CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
- assertEquals(0xFFFFFFFFC0000000L,
- CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
- assertEquals(0x000000007FFFFFFFL,
- CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
- assertEquals(0xFFFFFFFF80000000L,
- CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
- assertEquals(0x7FFFFFFFFFFFFFFFL,
- CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
- assertEquals(0x8000000000000000L,
- CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
+ assertEquals(0x000000003FFFFFFFL, CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL));
+ assertEquals(0xFFFFFFFFC0000000L, CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL));
+ assertEquals(0x000000007FFFFFFFL, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL));
+ assertEquals(0xFFFFFFFF80000000L, CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL));
+ assertEquals(0x7FFFFFFFFFFFFFFFL, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL));
+ assertEquals(0x8000000000000000L, CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL));
}
/** Tests reading and parsing a whole message with every field type. */
@@ -317,14 +355,12 @@ public class CodedInputStreamTest extends TestCase {
byte[] rawBytes = message.toByteArray();
assertEquals(rawBytes.length, message.getSerializedSize());
- TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
- TestUtil.assertAllFieldsSet(message2);
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
- message2 = TestAllTypes.parseFrom(
- new SmallBlockInputStream(rawBytes, blockSize));
- TestUtil.assertAllFieldsSet(message2);
+ for (InputType inputType : InputType.values()) {
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
+ TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(rawBytes, blockSize));
+ TestUtil.assertAllFieldsSet(message2);
+ }
}
}
@@ -333,57 +369,65 @@ public class CodedInputStreamTest extends TestCase {
TestAllTypes message = TestUtil.getAllSet();
byte[] rawBytes = message.toByteArray();
- // Create two parallel inputs. Parse one as unknown fields while using
- // skipField() to skip each field on the other. Expect the same tags.
- CodedInputStream input1 = CodedInputStream.newInstance(rawBytes);
- CodedInputStream input2 = CodedInputStream.newInstance(rawBytes);
+ InputType[] inputTypes = InputType.values();
+ CodedInputStream[] inputs = new CodedInputStream[inputTypes.length];
+ for (int i = 0; i < inputs.length; ++i) {
+ inputs[i] = inputTypes[i].newDecoder(rawBytes);
+ }
UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder();
while (true) {
+ CodedInputStream input1 = inputs[0];
int tag = input1.readTag();
- assertEquals(tag, input2.readTag());
+ // Ensure that the rest match.
+ for (int i = 1; i < inputs.length; ++i) {
+ assertEquals(inputTypes[i].name(), tag, inputs[i].readTag());
+ }
if (tag == 0) {
break;
}
unknownFields.mergeFieldFrom(tag, input1);
- input2.skipField(tag);
+ // Skip the field for the rest of the inputs.
+ for (int i = 1; i < inputs.length; ++i) {
+ inputs[i].skipField(tag);
+ }
}
}
/**
- * Test that a bug in skipRawBytes() has been fixed: if the skip skips
- * exactly up to a limit, this should not break things.
+ * Test that a bug in skipRawBytes() has been fixed: if the skip skips exactly up to a limit, this
+ * should not break things.
*/
public void testSkipRawBytesBug() throws Exception {
- byte[] rawBytes = new byte[] { 1, 2 };
- CodedInputStream input = CodedInputStream.newInstance(rawBytes);
-
- int limit = input.pushLimit(1);
- input.skipRawBytes(1);
- input.popLimit(limit);
- assertEquals(2, input.readRawByte());
+ byte[] rawBytes = new byte[] {1, 2};
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawBytes);
+ int limit = input.pushLimit(1);
+ input.skipRawBytes(1);
+ input.popLimit(limit);
+ assertEquals(inputType.name(), 2, input.readRawByte());
+ }
}
/**
- * Test that a bug in skipRawBytes() has been fixed: if the skip skips
- * past the end of a buffer with a limit that has been set past the end of
- * that buffer, this should not break things.
+ * Test that a bug in skipRawBytes() has been fixed: if the skip skips past the end of a buffer
+ * with a limit that has been set past the end of that buffer, this should not break things.
*/
public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception {
- byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 };
- CodedInputStream input = CodedInputStream.newInstance(
- new SmallBlockInputStream(rawBytes, 3));
-
- int limit = input.pushLimit(4);
- // In order to expose the bug we need to read at least one byte to prime the
- // buffer inside the CodedInputStream.
- assertEquals(1, input.readRawByte());
- // Skip to the end of the limit.
- input.skipRawBytes(3);
- assertTrue(input.isAtEnd());
- input.popLimit(limit);
- assertEquals(5, input.readRawByte());
+ byte[] rawBytes = new byte[] {1, 2, 3, 4, 5};
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawBytes);
+ int limit = input.pushLimit(4);
+ // In order to expose the bug we need to read at least one byte to prime the
+ // buffer inside the CodedInputStream.
+ assertEquals(inputType.name(), 1, input.readRawByte());
+ // Skip to the end of the limit.
+ input.skipRawBytes(3);
+ assertTrue(inputType.name(), input.isAtEnd());
+ input.popLimit(limit);
+ assertEquals(inputType.name(), 5, input.readRawByte());
+ }
}
public void testReadHugeBlob() throws Exception {
@@ -399,19 +443,22 @@ public class CodedInputStreamTest extends TestCase {
builder.setOptionalBytes(ByteString.copyFrom(blob));
TestAllTypes message = builder.build();
- // Serialize and parse it. Make sure to parse from an InputStream, not
- // directly from a ByteString, so that CodedInputStream uses buffered
- // reading.
- TestAllTypes message2 =
- TestAllTypes.parseFrom(message.toByteString().newInput());
-
- assertEquals(message.getOptionalBytes(), message2.getOptionalBytes());
-
- // Make sure all the other fields were parsed correctly.
- TestAllTypes message3 = TestAllTypes.newBuilder(message2)
- .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
- .build();
- TestUtil.assertAllFieldsSet(message3);
+ byte[] data = message.toByteArray();
+ for (InputType inputType : InputType.values()) {
+ // Serialize and parse it. Make sure to parse from an InputStream, not
+ // directly from a ByteString, so that CodedInputStream uses buffered
+ // reading.
+ TestAllTypes message2 = TestAllTypes.parseFrom(inputType.newDecoder(data));
+
+ assertEquals(inputType.name(), message.getOptionalBytes(), message2.getOptionalBytes());
+
+ // Make sure all the other fields were parsed correctly.
+ TestAllTypes message3 =
+ TestAllTypes.newBuilder(message2)
+ .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes())
+ .build();
+ TestUtil.assertAllFieldsSet(message3);
+ }
}
public void testReadMaliciouslyLargeBlob() throws Exception {
@@ -421,17 +468,95 @@ public class CodedInputStreamTest extends TestCase {
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(tag);
output.writeRawVarint32(0x7FFFFFFF);
- output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
+ output.writeRawBytes(new byte[32]); // Pad with a few random bytes.
output.flush();
- CodedInputStream input = rawOutput.toByteString().newCodedInput();
- assertEquals(tag, input.readTag());
+ byte[] data = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(data);
+ assertEquals(tag, input.readTag());
+ try {
+ input.readBytes();
+ fail(inputType.name() + ": Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
+ }
+ }
+
+ /**
+ * Test we can do messages that are up to CodedInputStream#DEFAULT_SIZE_LIMIT
+ * in size (2G or Integer#MAX_SIZE).
+ * @throws IOException
+ */
+ public void testParseMessagesCloseTo2G() throws IOException {
+ byte[] serializedMessage = getBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2G limit?
+ int count = Integer.MAX_VALUE / serializedMessage.length;
+ // Now make an inputstream that will fake a near 2G message of messages
+ // returning our big serialized message 'count' times.
+ InputStream is = new RepeatingInputStream(serializedMessage, count);
+ // Parse should succeed!
+ TestAllTypes.parseFrom(is);
+ }
+ /**
+ * Test there is an exception if a message exceeds
+ * CodedInputStream#DEFAULT_SIZE_LIMIT in size (2G or Integer#MAX_SIZE).
+ * @throws IOException
+ */
+ public void testParseMessagesOver2G() throws IOException {
+ byte[] serializedMessage = getBigSerializedMessage();
+ // How many of these big messages do we need to take us near our 2G limit?
+ int count = Integer.MAX_VALUE / serializedMessage.length;
+ // Now add one to take us over the limit
+ count++;
+ // Now make an inputstream that will fake a near 2G message of messages
+ // returning our big serialized message 'count' times.
+ InputStream is = new RepeatingInputStream(serializedMessage, count);
try {
- input.readBytes();
+ TestAllTypes.parseFrom(is);
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException e) {
- // success.
+ assertTrue(e.getMessage().contains("too large"));
+ }
+ }
+
+ /*
+ * @return A serialized big message.
+ */
+ private static byte[] getBigSerializedMessage() {
+ byte[] value = new byte[16 * 1024 * 1024];
+ ByteString bsValue = ByteString.wrap(value);
+ return TestAllTypes.newBuilder().setOptionalBytes(bsValue).build().toByteArray();
+ }
+
+ /*
+ * An input stream that repeats a byte arrays' content a number of times.
+ * Simulates really large input without consuming loads of memory. Used above
+ * to test the parsing behavior when the input size exceeds 2G or close to it.
+ */
+ private static class RepeatingInputStream extends InputStream {
+ private final byte[] serializedMessage;
+ private final int count;
+ private int index = 0;
+ private int offset = 0;
+
+ RepeatingInputStream(byte[] serializedMessage, int count) {
+ this.serializedMessage = serializedMessage;
+ this.count = count;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (this.offset == this.serializedMessage.length) {
+ this.index++;
+ this.offset = 0;
+ }
+ if (this.index == this.count) {
+ return -1;
+ }
+ return this.serializedMessage[offset++];
}
}
@@ -439,54 +564,55 @@ public class CodedInputStreamTest extends TestCase {
if (depth == 0) {
return TestRecursiveMessage.newBuilder().setI(5).build();
} else {
- return TestRecursiveMessage.newBuilder()
- .setA(makeRecursiveMessage(depth - 1)).build();
+ return TestRecursiveMessage.newBuilder().setA(makeRecursiveMessage(depth - 1)).build();
}
}
- private void assertMessageDepth(TestRecursiveMessage message, int depth) {
+ private void assertMessageDepth(String msg, TestRecursiveMessage message, int depth) {
if (depth == 0) {
- assertFalse(message.hasA());
- assertEquals(5, message.getI());
+ assertFalse(msg, message.hasA());
+ assertEquals(msg, 5, message.getI());
} else {
- assertTrue(message.hasA());
- assertMessageDepth(message.getA(), depth - 1);
+ assertTrue(msg, message.hasA());
+ assertMessageDepth(msg, message.getA(), depth - 1);
}
}
public void testMaliciousRecursion() throws Exception {
- ByteString data100 = makeRecursiveMessage(100).toByteString();
- ByteString data101 = makeRecursiveMessage(101).toByteString();
+ byte[] data100 = makeRecursiveMessage(100).toByteArray();
+ byte[] data101 = makeRecursiveMessage(101).toByteArray();
- assertMessageDepth(TestRecursiveMessage.parseFrom(data100), 100);
+ for (InputType inputType : InputType.values()) {
+ assertMessageDepth(
+ inputType.name(), TestRecursiveMessage.parseFrom(inputType.newDecoder(data100)), 100);
- try {
- TestRecursiveMessage.parseFrom(data101);
- fail("Should have thrown an exception!");
- } catch (InvalidProtocolBufferException e) {
- // success.
- }
+ try {
+ TestRecursiveMessage.parseFrom(inputType.newDecoder(data101));
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
- CodedInputStream input = data100.newCodedInput();
- input.setRecursionLimit(8);
- try {
- TestRecursiveMessage.parseFrom(input);
- fail("Should have thrown an exception!");
- } catch (InvalidProtocolBufferException e) {
- // success.
+ CodedInputStream input = inputType.newDecoder(data100);
+ input.setRecursionLimit(8);
+ try {
+ TestRecursiveMessage.parseFrom(input);
+ fail(inputType.name() + ": Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException e) {
+ // success.
+ }
}
}
private void checkSizeLimitExceeded(InvalidProtocolBufferException e) {
- assertEquals(
- InvalidProtocolBufferException.sizeLimitExceeded().getMessage(),
- e.getMessage());
+ assertEquals(InvalidProtocolBufferException.sizeLimitExceeded().getMessage(), e.getMessage());
}
public void testSizeLimit() throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(
- new SmallBlockInputStream(
- TestUtil.getAllSet().toByteString().newInput(), 16));
+ // NOTE: Size limit only applies to the stream-backed CIS.
+ CodedInputStream input =
+ CodedInputStream.newInstance(
+ new SmallBlockInputStream(TestUtil.getAllSet().toByteArray(), 16));
input.setSizeLimit(16);
try {
@@ -498,8 +624,9 @@ public class CodedInputStreamTest extends TestCase {
}
public void testResetSizeCounter() throws Exception {
- CodedInputStream input = CodedInputStream.newInstance(
- new SmallBlockInputStream(new byte[256], 8));
+ // NOTE: Size limit only applies to the stream-backed CIS.
+ CodedInputStream input =
+ CodedInputStream.newInstance(new SmallBlockInputStream(new byte[256], 8));
input.setSizeLimit(16);
input.readRawBytes(16);
assertEquals(16, input.getTotalBytesRead());
@@ -513,7 +640,7 @@ public class CodedInputStreamTest extends TestCase {
input.resetSizeCounter();
assertEquals(0, input.getTotalBytesRead());
- input.readRawByte(); // No exception thrown.
+ input.readRawByte(); // No exception thrown.
input.resetSizeCounter();
assertEquals(0, input.getTotalBytesRead());
input.readRawBytes(16);
@@ -521,20 +648,96 @@ public class CodedInputStreamTest extends TestCase {
input.resetSizeCounter();
try {
- input.readRawBytes(17); // Hits limit again.
+ input.readRawBytes(17); // Hits limit again.
fail("Should have thrown an exception!");
} catch (InvalidProtocolBufferException expected) {
checkSizeLimitExceeded(expected);
}
}
+
+ public void testRefillBufferWithCorrectSize() throws Exception {
+ // NOTE: refillBuffer only applies to the stream-backed CIS.
+ byte[] bytes = "123456789".getBytes("UTF-8");
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.writeRawByte(4);
+ output.flush();
+
+ // Input is two string with length 9 and one raw byte.
+ byte[] rawInput = rawOutput.toByteArray();
+ for (int inputStreamBufferLength = 8;
+ inputStreamBufferLength <= rawInput.length + 1; inputStreamBufferLength++) {
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(rawInput), inputStreamBufferLength);
+ input.setSizeLimit(rawInput.length - 1);
+ input.readString();
+ input.readString();
+ try {
+ input.readRawByte(); // Hits limit.
+ fail("Should have thrown an exception!");
+ } catch (InvalidProtocolBufferException expected) {
+ checkSizeLimitExceeded(expected);
+ }
+ }
+ }
+
+ public void testIsAtEnd() throws Exception {
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(new byte[5]));
+ try {
+ for (int i = 0; i < 5; i++) {
+ assertEquals(false, input.isAtEnd());
+ input.readRawByte();
+ }
+ assertEquals(true, input.isAtEnd());
+ } catch (Exception e) {
+ fail("Catch exception in the testIsAtEnd");
+ }
+ }
+
+ public void testCurrentLimitExceeded() throws Exception {
+ byte[] bytes = "123456789".getBytes("UTF-8");
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteArray();
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(rawInput));
+ // The length of the whole rawInput
+ input.setSizeLimit(11);
+ // Some number that is smaller than the rawInput's length
+ // but larger than 2
+ input.pushLimit(5);
+ try {
+ input.readString();
+ fail("Should have thrown an exception");
+ } catch (InvalidProtocolBufferException expected) {
+ assertEquals(expected.getMessage(),
+ InvalidProtocolBufferException.truncatedMessage().getMessage());
+ }
+ }
public void testSizeLimitMultipleMessages() throws Exception {
+ // NOTE: Size limit only applies to the stream-backed CIS.
byte[] bytes = new byte[256];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) i;
}
- CodedInputStream input = CodedInputStream.newInstance(
- new SmallBlockInputStream(bytes, 7));
+ CodedInputStream input = CodedInputStream.newInstance(new SmallBlockInputStream(bytes, 7));
input.setSizeLimit(16);
for (int i = 0; i < 256 / 16; i++) {
byte[] message = input.readRawBytes(16);
@@ -547,10 +750,61 @@ public class CodedInputStreamTest extends TestCase {
}
}
+ public void testReadString() throws Exception {
+ String lorem = "Lorem ipsum dolor sit amet ";
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 4096; i += lorem.length()) {
+ builder.append(lorem);
+ }
+ lorem = builder.toString().substring(0, 4096);
+ byte[] bytes = lorem.getBytes("UTF-8");
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readString();
+ assertEquals(inputType.name(), lorem, text);
+ }
+ }
+
+ public void testReadStringRequireUtf8() throws Exception {
+ String lorem = "Lorem ipsum dolor sit amet ";
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 4096; i += lorem.length()) {
+ builder.append(lorem);
+ }
+ lorem = builder.toString().substring(0, 4096);
+ byte[] bytes = lorem.getBytes("UTF-8");
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
+
+ int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ output.writeRawVarint32(tag);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readStringRequireUtf8();
+ assertEquals(inputType.name(), lorem, text);
+ }
+ }
+
/**
- * Tests that if we readString invalid UTF-8 bytes, no exception
- * is thrown. Instead, the invalid bytes are replaced with the Unicode
- * "replacement character" U+FFFD.
+ * Tests that if we readString invalid UTF-8 bytes, no exception is thrown. Instead, the invalid
+ * bytes are replaced with the Unicode "replacement character" U+FFFD.
*/
public void testReadStringInvalidUtf8() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
@@ -559,18 +813,21 @@ public class CodedInputStreamTest extends TestCase {
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(tag);
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[] { (byte) 0x80 });
+ output.writeRawBytes(new byte[] {(byte) 0x80});
output.flush();
- CodedInputStream input = rawOutput.toByteString().newCodedInput();
- assertEquals(tag, input.readTag());
- String text = input.readString();
- assertEquals(0xfffd, text.charAt(0));
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(inputType.name(), tag, input.readTag());
+ String text = input.readString();
+ assertEquals(inputType.name(), 0xfffd, text.charAt(0));
+ }
}
/**
- * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an
- * InvalidProtocolBufferException is thrown.
+ * Tests that if we readStringRequireUtf8 invalid UTF-8 bytes, an InvalidProtocolBufferException
+ * is thrown.
*/
public void testReadStringRequireUtf8InvalidUtf8() throws Exception {
ByteString.Output rawOutput = ByteString.newOutput();
@@ -579,16 +836,20 @@ public class CodedInputStreamTest extends TestCase {
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(tag);
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[] { (byte) 0x80 });
+ output.writeRawBytes(new byte[] {(byte) 0x80});
output.flush();
- CodedInputStream input = rawOutput.toByteString().newCodedInput();
- assertEquals(tag, input.readTag());
- try {
- input.readStringRequireUtf8();
- fail("Expected invalid UTF-8 exception.");
- } catch (InvalidProtocolBufferException exception) {
- assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream input = inputType.newDecoder(rawInput);
+ assertEquals(tag, input.readTag());
+ try {
+ input.readStringRequireUtf8();
+ fail(inputType.name() + ": Expected invalid UTF-8 exception.");
+ } catch (InvalidProtocolBufferException exception) {
+ assertEquals(
+ inputType.name(), "Protocol message had invalid UTF-8.", exception.getMessage());
+ }
}
}
@@ -608,13 +869,17 @@ public class CodedInputStreamTest extends TestCase {
public void testInvalidTag() throws Exception {
// Any tag number which corresponds to field number zero is invalid and
// should throw InvalidProtocolBufferException.
- for (int i = 0; i < 8; i++) {
- try {
- CodedInputStream.newInstance(bytes(i)).readTag();
- fail("Should have thrown an exception.");
- } catch (InvalidProtocolBufferException e) {
- assertEquals(InvalidProtocolBufferException.invalidTag().getMessage(),
- e.getMessage());
+ for (InputType inputType : InputType.values()) {
+ for (int i = 0; i < 8; i++) {
+ try {
+ inputType.newDecoder(bytes(i)).readTag();
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ assertEquals(
+ inputType.name(),
+ InvalidProtocolBufferException.invalidTag().getMessage(),
+ e.getMessage());
+ }
}
}
}
@@ -626,10 +891,10 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[] { (byte) 23 });
+ output.writeRawBytes(new byte[] {(byte) 23});
// Another one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[] { (byte) 45 });
+ output.writeRawBytes(new byte[] {(byte) 45});
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
@@ -639,20 +904,70 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawBytes(bytes);
output.flush();
- CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
-
- byte[] result = inputStream.readByteArray();
- assertEquals(0, result.length);
- result = inputStream.readByteArray();
- assertEquals(1, result.length);
- assertEquals((byte) 23, result[0]);
- result = inputStream.readByteArray();
- assertEquals(1, result.length);
- assertEquals((byte) 45, result[0]);
- result = inputStream.readByteArray();
- assertEquals(bytesLength, result.length);
- assertEquals((byte) 67, result[0]);
- assertEquals((byte) 89, result[bytesLength - 1]);
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(rawInput);
+
+ byte[] result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 0, result.length);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 1, result.length);
+ assertEquals(inputType.name(), (byte) 23, result[0]);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), 1, result.length);
+ assertEquals(inputType.name(), (byte) 45, result[0]);
+ result = inputStream.readByteArray();
+ assertEquals(inputType.name(), bytesLength, result.length);
+ assertEquals(inputType.name(), (byte) 67, result[0]);
+ assertEquals(inputType.name(), (byte) 89, result[bytesLength - 1]);
+ }
+ }
+
+ public void testReadLargeByteStringFromInputStream() throws Exception {
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (i & 0xFF);
+ }
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+ byte[] data = rawOutput.toByteString().toByteArray();
+
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(data) {
+ @Override
+ public synchronized int available() {
+ return 0;
+ }
+ });
+ ByteString result = input.readBytes();
+ assertEquals(ByteString.copyFrom(bytes), result);
+ }
+
+ public void testReadLargeByteArrayFromInputStream() throws Exception {
+ byte[] bytes = new byte[1024 * 1024];
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] = (byte) (i & 0xFF);
+ }
+ ByteString.Output rawOutput = ByteString.newOutput();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
+ output.writeRawVarint32(bytes.length);
+ output.writeRawBytes(bytes);
+ output.flush();
+ byte[] data = rawOutput.toByteString().toByteArray();
+
+ CodedInputStream input = CodedInputStream.newInstance(
+ new ByteArrayInputStream(data) {
+ @Override
+ public synchronized int available() {
+ return 0;
+ }
+ });
+ byte[] result = input.readByteArray();
+ assertTrue(Arrays.equals(bytes, result));
}
public void testReadByteBuffer() throws Exception {
@@ -662,10 +977,10 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[]{(byte) 23});
+ output.writeRawBytes(new byte[] {(byte) 23});
// Another one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[]{(byte) 45});
+ output.writeRawBytes(new byte[] {(byte) 45});
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
@@ -675,21 +990,25 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawBytes(bytes);
output.flush();
- CodedInputStream inputStream = rawOutput.toByteString().newCodedInput();
-
- ByteBuffer result = inputStream.readByteBuffer();
- assertEquals(0, result.capacity());
- result = inputStream.readByteBuffer();
- assertEquals(1, result.capacity());
- assertEquals((byte) 23, result.get());
- result = inputStream.readByteBuffer();
- assertEquals(1, result.capacity());
- assertEquals((byte) 45, result.get());
- result = inputStream.readByteBuffer();
- assertEquals(bytesLength, result.capacity());
- assertEquals((byte) 67, result.get());
- result.position(bytesLength - 1);
- assertEquals((byte) 89, result.get());
+
+ byte[] rawInput = rawOutput.toByteString().toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(rawInput);
+
+ ByteBuffer result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+ }
}
public void testReadByteBufferAliasing() throws Exception {
@@ -699,10 +1018,10 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawVarint32(0);
// One one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[]{(byte) 23});
+ output.writeRawBytes(new byte[] {(byte) 23});
// Another one-byte bytes field
output.writeRawVarint32(1);
- output.writeRawBytes(new byte[]{(byte) 45});
+ output.writeRawBytes(new byte[] {(byte) 45});
// A bytes field large enough that won't fit into the 4K buffer.
final int bytesLength = 16 * 1024;
byte[] bytes = new byte[bytesLength];
@@ -711,59 +1030,107 @@ public class CodedInputStreamTest extends TestCase {
output.writeRawVarint32(bytesLength);
output.writeRawBytes(bytes);
output.flush();
+
byte[] data = byteArrayStream.toByteArray();
- // Without aliasing
- CodedInputStream inputStream = CodedInputStream.newInstance(data);
- ByteBuffer result = inputStream.readByteBuffer();
- assertEquals(0, result.capacity());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() != data);
- assertEquals(1, result.capacity());
- assertEquals((byte) 23, result.get());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() != data);
- assertEquals(1, result.capacity());
- assertEquals((byte) 45, result.get());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() != data);
- assertEquals(bytesLength, result.capacity());
- assertEquals((byte) 67, result.get());
- result.position(bytesLength - 1);
- assertEquals((byte) 89, result.get());
-
- // Enable aliasing
- inputStream = CodedInputStream.newInstance(data);
- inputStream.enableAliasing(true);
- result = inputStream.readByteBuffer();
- assertEquals(0, result.capacity());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() == data);
- assertEquals(1, result.capacity());
- assertEquals((byte) 23, result.get());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() == data);
- assertEquals(1, result.capacity());
- assertEquals((byte) 45, result.get());
- result = inputStream.readByteBuffer();
- assertTrue(result.array() == data);
- assertEquals(bytesLength, result.capacity());
- assertEquals((byte) 67, result.get());
- result.position(bytesLength - 1);
- assertEquals((byte) 89, result.get());
+ for (InputType inputType : InputType.values()) {
+ if (inputType == InputType.STREAM
+ || inputType == InputType.STREAM_ITER_DIRECT
+ || inputType == InputType.ITER_DIRECT) {
+ // Aliasing doesn't apply to stream-backed CIS.
+ continue;
+ }
+
+ // Without aliasing
+ CodedInputStream inputStream = inputType.newDecoder(data);
+ ByteBuffer result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ assertTrue(inputType.name(), result.array() != data);
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+
+ // Enable aliasing
+ inputStream = inputType.newDecoder(data, data.length);
+ inputStream.enableAliasing(true);
+ result = inputStream.readByteBuffer();
+ assertEquals(inputType.name(), 0, result.capacity());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 23, result.get());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), 1, result.capacity());
+ assertEquals(inputType.name(), (byte) 45, result.get());
+ result = inputStream.readByteBuffer();
+ if (result.hasArray()) {
+ assertTrue(inputType.name(), result.array() == data);
+ }
+ assertEquals(inputType.name(), bytesLength, result.capacity());
+ assertEquals(inputType.name(), (byte) 67, result.get());
+ result.position(bytesLength - 1);
+ assertEquals(inputType.name(), (byte) 89, result.get());
+ }
}
public void testCompatibleTypes() throws Exception {
long data = 0x100000000L;
Int64Message message = Int64Message.newBuilder().setData(data).build();
- ByteString serialized = message.toByteString();
+ byte[] serialized = message.toByteArray();
+ for (InputType inputType : InputType.values()) {
+ CodedInputStream inputStream = inputType.newDecoder(serialized);
+
+ // Test int64(long) is compatible with bool(boolean)
+ BoolMessage msg2 = BoolMessage.parseFrom(inputStream);
+ assertTrue(msg2.getData());
+
+ // Test int64(long) is compatible with int32(int)
+ inputStream = inputType.newDecoder(serialized);
+ Int32Message msg3 = Int32Message.parseFrom(inputStream);
+ assertEquals((int) data, msg3.getData());
+ }
+ }
- // Test int64(long) is compatible with bool(boolean)
- BoolMessage msg2 = BoolMessage.parseFrom(serialized);
- assertTrue(msg2.getData());
+ public void testSkipInvalidVarint_FastPath() throws Exception {
+ // Fast path: We have >= 10 bytes available. Ensure we properly recognize a non-ending varint.
+ byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0};
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ // Expected
+ }
+ }
+ }
- // Test int64(long) is compatible with int32(int)
- Int32Message msg3 = Int32Message.parseFrom(serialized);
- assertEquals((int) data, msg3.getData());
+ public void testSkipInvalidVarint_SlowPath() throws Exception {
+ // Slow path: < 10 bytes available. Ensure we properly recognize a non-ending varint.
+ byte[] data = new byte[] {-1, -1, -1, -1, -1, -1, -1, -1, -1};
+ for (InputType inputType : InputType.values()) {
+ try {
+ CodedInputStream input = inputType.newDecoder(data);
+ input.skipField(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT));
+ fail(inputType.name() + ": Should have thrown an exception.");
+ } catch (InvalidProtocolBufferException e) {
+ // Expected
+ }
+ }
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
index 6018ea55..78f415c2 100644
--- a/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
+++ b/java/core/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
@@ -30,19 +30,18 @@
package com.google.protobuf;
+import com.google.protobuf.CodedOutputStream.OutOfSpaceException;
import protobuf_unittest.UnittestProto.SparseEnumMessage;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestSparseEnum;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import junit.framework.TestCase;
/**
* Unit test for {@link CodedOutputStream}.
@@ -50,118 +49,191 @@ import java.util.List;
* @author kenton@google.com Kenton Varda
*/
public class CodedOutputStreamTest extends TestCase {
- /**
- * Helper to construct a byte array from a bunch of bytes. The inputs are
- * actually ints so that I can use hex notation and not get stupid errors
- * about precision.
- */
- private byte[] bytes(int... bytesAsInts) {
- byte[] bytes = new byte[bytesAsInts.length];
- for (int i = 0; i < bytesAsInts.length; i++) {
- bytes[i] = (byte) bytesAsInts[i];
- }
- return bytes;
+ private interface Coder {
+ CodedOutputStream stream();
+
+ byte[] toByteArray();
+
+ OutputType getOutputType();
}
- /** Arrays.asList() does not work with arrays of primitives. :( */
- private List<Byte> toList(byte[] bytes) {
- List<Byte> result = new ArrayList<Byte>();
- for (byte b : bytes) {
- result.add(b);
+ private static final class OutputStreamCoder implements Coder {
+ private final CodedOutputStream stream;
+ private final ByteArrayOutputStream output;
+
+ OutputStreamCoder(int size) {
+ output = new ByteArrayOutputStream();
+ stream = CodedOutputStream.newInstance(output, size);
+ }
+
+ @Override
+ public CodedOutputStream stream() {
+ return stream;
+ }
+
+ @Override
+ public byte[] toByteArray() {
+ return output.toByteArray();
+ }
+
+ @Override
+ public OutputType getOutputType() {
+ return OutputType.STREAM;
}
- return result;
}
- private void assertEqualBytes(byte[] a, byte[] b) {
- assertEquals(toList(a), toList(b));
+ private static final class ArrayCoder implements Coder {
+ private final CodedOutputStream stream;
+ private final byte[] bytes;
+
+ ArrayCoder(int size) {
+ bytes = new byte[size];
+ stream = CodedOutputStream.newInstance(bytes);
+ }
+
+ @Override
+ public CodedOutputStream stream() {
+ return stream;
+ }
+
+ @Override
+ public byte[] toByteArray() {
+ return Arrays.copyOf(bytes, stream.getTotalBytesWritten());
+ }
+
+ @Override
+ public OutputType getOutputType() {
+ return OutputType.ARRAY;
+ }
}
- /**
- * Writes the given value using writeRawVarint32() and writeRawVarint64() and
- * checks that the result matches the given bytes.
- */
- private void assertWriteVarint(byte[] data, long value) throws Exception {
- // Only test 32-bit write if the value fits into an int.
- if (value == (int) value) {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawVarint32((int) value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
+ private static final class NioHeapCoder implements Coder {
+ private final CodedOutputStream stream;
+ private final ByteBuffer buffer;
+ private final int initialPosition;
- // Also try computing size.
- assertEquals(data.length,
- CodedOutputStream.computeRawVarint32Size((int) value));
+ NioHeapCoder(int size) {
+ this(size, 0);
}
- {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawVarint64(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
+ NioHeapCoder(int size, int initialPosition) {
+ this.initialPosition = initialPosition;
+ buffer = ByteBuffer.allocate(size);
+ buffer.position(initialPosition);
+ stream = CodedOutputStream.newInstance(buffer);
+ }
- // Also try computing size.
- assertEquals(data.length,
- CodedOutputStream.computeRawVarint64Size(value));
+ @Override
+ public CodedOutputStream stream() {
+ return stream;
}
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- // Only test 32-bit write if the value fits into an int.
- if (value == (int) value) {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output =
- CodedOutputStream.newInstance(rawOutput, blockSize);
- output.writeRawVarint32((int) value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
- }
+ @Override
+ public byte[] toByteArray() {
+ ByteBuffer dup = buffer.duplicate();
+ dup.position(initialPosition);
+ dup.limit(buffer.position());
- {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output =
- CodedOutputStream.newInstance(rawOutput, blockSize);
- output.writeRawVarint64(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
- }
+ byte[] bytes = new byte[dup.remaining()];
+ dup.get(bytes);
+ return bytes;
+ }
+
+ @Override
+ public OutputType getOutputType() {
+ return OutputType.NIO_HEAP;
}
}
- private void assertVarintRoundTrip(long value) throws Exception {
- {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawVarint64(value);
- output.flush();
- byte[] bytes = rawOutput.toByteArray();
- assertEquals(bytes.length, CodedOutputStream.computeRawVarint64Size(value));
- CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
- assertEquals(value, input.readRawVarint64());
+ private static final class NioDirectCoder implements Coder {
+ private final int initialPosition;
+ private final CodedOutputStream stream;
+ private final ByteBuffer buffer;
+ private final boolean unsafe;
+
+ NioDirectCoder(int size, boolean unsafe) {
+ this(size, 0, unsafe);
}
- if (value == (int) value) {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawVarint32((int) value);
- output.flush();
- byte[] bytes = rawOutput.toByteArray();
- assertEquals(bytes.length, CodedOutputStream.computeRawVarint32Size((int) value));
- CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
- assertEquals(value, input.readRawVarint32());
+ NioDirectCoder(int size, int initialPosition, boolean unsafe) {
+ this.unsafe = unsafe;
+ this.initialPosition = initialPosition;
+ buffer = ByteBuffer.allocateDirect(size);
+ buffer.position(initialPosition);
+ stream =
+ unsafe
+ ? CodedOutputStream.newUnsafeInstance(buffer)
+ : CodedOutputStream.newSafeInstance(buffer);
+ }
+
+ @Override
+ public CodedOutputStream stream() {
+ return stream;
}
+
+ @Override
+ public byte[] toByteArray() {
+ ByteBuffer dup = buffer.duplicate();
+ dup.position(initialPosition);
+ dup.limit(buffer.position());
+
+ byte[] bytes = new byte[dup.remaining()];
+ dup.get(bytes);
+ return bytes;
+ }
+
+ @Override
+ public OutputType getOutputType() {
+ return unsafe ? OutputType.NIO_DIRECT_SAFE : OutputType.NIO_DIRECT_UNSAFE;
+ }
+ }
+
+ private enum OutputType {
+ ARRAY() {
+ @Override
+ Coder newCoder(int size) {
+ return new ArrayCoder(size);
+ }
+ },
+ NIO_HEAP() {
+ @Override
+ Coder newCoder(int size) {
+ return new NioHeapCoder(size);
+ }
+ },
+ NIO_DIRECT_SAFE() {
+ @Override
+ Coder newCoder(int size) {
+ return new NioDirectCoder(size, false);
+ }
+ },
+ NIO_DIRECT_UNSAFE() {
+ @Override
+ Coder newCoder(int size) {
+ return new NioDirectCoder(size, true);
+ }
+ },
+ STREAM() {
+ @Override
+ Coder newCoder(int size) {
+ return new OutputStreamCoder(size);
+ }
+ };
+
+ abstract Coder newCoder(int size);
}
/** Checks that invariants are maintained for varint round trip input and output. */
public void testVarintRoundTrips() throws Exception {
- assertVarintRoundTrip(0L);
- for (int bits = 0; bits < 64; bits++) {
- long value = 1L << bits;
- assertVarintRoundTrip(value);
- assertVarintRoundTrip(value + 1);
- assertVarintRoundTrip(value - 1);
- assertVarintRoundTrip(-value);
+ for (OutputType outputType : OutputType.values()) {
+ assertVarintRoundTrip(outputType, 0L);
+ for (int bits = 0; bits < 64; bits++) {
+ long value = 1L << bits;
+ assertVarintRoundTrip(outputType, value);
+ assertVarintRoundTrip(outputType, value + 1);
+ assertVarintRoundTrip(outputType, value - 1);
+ assertVarintRoundTrip(outputType, -value);
+ }
}
}
@@ -173,70 +245,25 @@ public class CodedOutputStreamTest extends TestCase {
// 14882
assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7));
// 2961488830
- assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x0bL << 28));
+ assertWriteVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x0bL << 28));
// 64-bit
// 7256456126
- assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
- (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
- (0x1bL << 28));
+ assertWriteVarint(
+ bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b),
+ (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | (0x1bL << 28));
// 41256202580718336
assertWriteVarint(
- bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
- (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
- (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
+ bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
+ (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | (0x43L << 28) | (0x49L << 35)
+ | (0x24L << 42) | (0x49L << 49));
// 11964378330978735131
assertWriteVarint(
- bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
- (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
- (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) |
- (0x05L << 49) | (0x26L << 56) | (0x01L << 63));
- }
-
- /**
- * Parses the given bytes using writeRawLittleEndian32() and checks
- * that the result matches the given value.
- */
- private void assertWriteLittleEndian32(byte[] data, int value)
- throws Exception {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawLittleEndian32(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- rawOutput = new ByteArrayOutputStream();
- output = CodedOutputStream.newInstance(rawOutput, blockSize);
- output.writeRawLittleEndian32(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
- }
- }
-
- /**
- * Parses the given bytes using writeRawLittleEndian64() and checks
- * that the result matches the given value.
- */
- private void assertWriteLittleEndian64(byte[] data, long value)
- throws Exception {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output = CodedOutputStream.newInstance(rawOutput);
- output.writeRawLittleEndian64(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
-
- // Try different block sizes.
- for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
- rawOutput = new ByteArrayOutputStream();
- output = CodedOutputStream.newInstance(rawOutput, blockSize);
- output.writeRawLittleEndian64(value);
- output.flush();
- assertEqualBytes(data, rawOutput.toByteArray());
- }
+ bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
+ (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | (0x3bL << 28) | (0x56L << 35)
+ | (0x00L << 42) | (0x05L << 49) | (0x26L << 56) | (0x01L << 63));
}
/** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */
@@ -245,141 +272,139 @@ public class CodedOutputStreamTest extends TestCase {
assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
assertWriteLittleEndian64(
- bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
- 0x123456789abcdef0L);
+ bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), 0x123456789abcdef0L);
assertWriteLittleEndian64(
- bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
- 0x9abcdef012345678L);
+ bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678L);
}
/** Test encodeZigZag32() and encodeZigZag64(). */
public void testEncodeZigZag() throws Exception {
- assertEquals(0, CodedOutputStream.encodeZigZag32( 0));
+ assertEquals(0, CodedOutputStream.encodeZigZag32(0));
assertEquals(1, CodedOutputStream.encodeZigZag32(-1));
- assertEquals(2, CodedOutputStream.encodeZigZag32( 1));
+ assertEquals(2, CodedOutputStream.encodeZigZag32(1));
assertEquals(3, CodedOutputStream.encodeZigZag32(-2));
assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF));
assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000));
assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF));
assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000));
- assertEquals(0, CodedOutputStream.encodeZigZag64( 0));
+ assertEquals(0, CodedOutputStream.encodeZigZag64(0));
assertEquals(1, CodedOutputStream.encodeZigZag64(-1));
- assertEquals(2, CodedOutputStream.encodeZigZag64( 1));
+ assertEquals(2, CodedOutputStream.encodeZigZag64(1));
assertEquals(3, CodedOutputStream.encodeZigZag64(-2));
- assertEquals(0x000000007FFFFFFEL,
- CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL));
- assertEquals(0x000000007FFFFFFFL,
- CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L));
- assertEquals(0x00000000FFFFFFFEL,
- CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL));
- assertEquals(0x00000000FFFFFFFFL,
- CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L));
- assertEquals(0xFFFFFFFFFFFFFFFEL,
- CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL));
- assertEquals(0xFFFFFFFFFFFFFFFFL,
- CodedOutputStream.encodeZigZag64(0x8000000000000000L));
+ assertEquals(0x000000007FFFFFFEL, CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL));
+ assertEquals(0x000000007FFFFFFFL, CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L));
+ assertEquals(0x00000000FFFFFFFEL, CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL));
+ assertEquals(0x00000000FFFFFFFFL, CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L));
+ assertEquals(0xFFFFFFFFFFFFFFFEL, CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL));
+ assertEquals(0xFFFFFFFFFFFFFFFFL, CodedOutputStream.encodeZigZag64(0x8000000000000000L));
// Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1)
// were chosen semi-randomly via keyboard bashing.
- assertEquals(0,
- CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0)));
- assertEquals(1,
- CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1)));
- assertEquals(-1,
- CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1)));
- assertEquals(14927,
- CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927)));
- assertEquals(-3612,
- CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612)));
-
- assertEquals(0,
- CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0)));
- assertEquals(1,
- CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1)));
- assertEquals(-1,
- CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1)));
- assertEquals(14927,
- CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927)));
- assertEquals(-3612,
- CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612)));
-
- assertEquals(856912304801416L,
- CodedOutputStream.encodeZigZag64(
- CodedInputStream.decodeZigZag64(
- 856912304801416L)));
- assertEquals(-75123905439571256L,
- CodedOutputStream.encodeZigZag64(
- CodedInputStream.decodeZigZag64(
- -75123905439571256L)));
+ assertEquals(0, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0)));
+ assertEquals(1, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1)));
+ assertEquals(-1, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1)));
+ assertEquals(14927, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927)));
+ assertEquals(-3612, CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612)));
+
+ assertEquals(0, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0)));
+ assertEquals(1, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1)));
+ assertEquals(-1, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1)));
+ assertEquals(14927, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927)));
+ assertEquals(-3612, CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612)));
+
+ assertEquals(
+ 856912304801416L,
+ CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(856912304801416L)));
+ assertEquals(
+ -75123905439571256L,
+ CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-75123905439571256L)));
}
/** Tests writing a whole message with every field type. */
public void testWriteWholeMessage() throws Exception {
+ final byte[] expectedBytes = TestUtil.getGoldenMessage().toByteArray();
TestAllTypes message = TestUtil.getAllSet();
- byte[] rawBytes = message.toByteArray();
- assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes);
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(message.getSerializedSize());
+ message.writeTo(coder.stream());
+ coder.stream().flush();
+ byte[] rawBytes = coder.toByteArray();
+ assertEqualBytes(outputType, expectedBytes, rawBytes);
+ }
// Try different block sizes.
for (int blockSize = 1; blockSize < 256; blockSize *= 2) {
- ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
- CodedOutputStream output =
- CodedOutputStream.newInstance(rawOutput, blockSize);
- message.writeTo(output);
- output.flush();
- assertEqualBytes(rawBytes, rawOutput.toByteArray());
+ Coder coder = OutputType.STREAM.newCoder(blockSize);
+ message.writeTo(coder.stream());
+ coder.stream().flush();
+ assertEqualBytes(OutputType.STREAM, expectedBytes, coder.toByteArray());
}
}
- /** Tests writing a whole message with every packed field type. Ensures the
- * wire format of packed fields is compatible with C++. */
+ /**
+ * Tests writing a whole message with every packed field type. Ensures the
+ * wire format of packed fields is compatible with C++.
+ */
public void testWriteWholePackedFieldsMessage() throws Exception {
+ byte[] expectedBytes = TestUtil.getGoldenPackedFieldsMessage().toByteArray();
TestPackedTypes message = TestUtil.getPackedSet();
- byte[] rawBytes = message.toByteArray();
- assertEqualBytes(TestUtil.getGoldenPackedFieldsMessage().toByteArray(),
- rawBytes);
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(message.getSerializedSize());
+ message.writeTo(coder.stream());
+ coder.stream().flush();
+ byte[] rawBytes = coder.toByteArray();
+ assertEqualBytes(outputType, expectedBytes, rawBytes);
+ }
}
- /** Test writing a message containing a negative enum value. This used to
+ /**
+ * Test writing a message containing a negative enum value. This used to
* fail because the size was not properly computed as a sign-extended varint.
*/
public void testWriteMessageWithNegativeEnumValue() throws Exception {
- SparseEnumMessage message = SparseEnumMessage.newBuilder()
- .setSparseEnum(TestSparseEnum.SPARSE_E) .build();
+ SparseEnumMessage message =
+ SparseEnumMessage.newBuilder().setSparseEnum(TestSparseEnum.SPARSE_E).build();
assertTrue(message.getSparseEnum().getNumber() < 0);
- byte[] rawBytes = message.toByteArray();
- SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes);
- assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum());
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(message.getSerializedSize());
+ message.writeTo(coder.stream());
+ coder.stream().flush();
+ byte[] rawBytes = coder.toByteArray();
+ SparseEnumMessage message2 = SparseEnumMessage.parseFrom(rawBytes);
+ assertEquals(TestSparseEnum.SPARSE_E, message2.getSparseEnum());
+ }
}
/** Test getTotalBytesWritten() */
public void testGetTotalBytesWritten() throws Exception {
- final int BUFFER_SIZE = 4 * 1024;
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(BUFFER_SIZE);
- CodedOutputStream codedStream = CodedOutputStream.newInstance(outputStream);
+ Coder coder = OutputType.STREAM.newCoder(4 * 1024);
+
+ // Write some some bytes (more than the buffer can hold) and verify that totalWritten
+ // is correct.
byte[] value = "abcde".getBytes(Internal.UTF_8);
for (int i = 0; i < 1024; ++i) {
- codedStream.writeRawBytes(value, 0, value.length);
+ coder.stream().writeRawBytes(value, 0, value.length);
}
+ assertEquals(value.length * 1024, coder.stream().getTotalBytesWritten());
+
+ // Now write an encoded string.
String string =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
// Ensure we take the slower fast path.
- assertTrue(CodedOutputStream.computeRawVarint32Size(string.length())
- != CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR));
-
- codedStream.writeStringNoTag(string);
+ assertTrue(CodedOutputStream.computeUInt32SizeNoTag(string.length())
+ != CodedOutputStream.computeUInt32SizeNoTag(string.length() * Utf8.MAX_BYTES_PER_CHAR));
+
+ coder.stream().writeStringNoTag(string);
+ coder.stream().flush();
int stringSize = CodedOutputStream.computeStringSizeNoTag(string);
-
- // Make sure we have written more bytes than the buffer could hold. This is
- // to make the test complete.
- assertTrue(codedStream.getTotalBytesWritten() > BUFFER_SIZE);
-
+
// Verify that the total bytes written is correct
- assertEquals((value.length * 1024) + stringSize, codedStream.getTotalBytesWritten());
+ assertEquals((value.length * 1024) + stringSize, coder.stream().getTotalBytesWritten());
}
-
+
// TODO(dweis): Write a comprehensive test suite for CodedOutputStream that covers more than just
// this case.
public void testWriteStringNoTag_fastpath() throws Exception {
@@ -390,14 +415,16 @@ public class CodedOutputStreamTest extends TestCase {
string += threeBytesPer;
}
// These checks ensure we will tickle the slower fast path.
- assertEquals(1, CodedOutputStream.computeRawVarint32Size(string.length()));
+ assertEquals(1, CodedOutputStream.computeUInt32SizeNoTag(string.length()));
assertEquals(
- 2, CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR));
+ 2, CodedOutputStream.computeUInt32SizeNoTag(string.length() * Utf8.MAX_BYTES_PER_CHAR));
assertEquals(bufferSize, string.length() * Utf8.MAX_BYTES_PER_CHAR);
-
- CodedOutputStream output =
- CodedOutputStream.newInstance(ByteBuffer.allocate(bufferSize), bufferSize);
- output.writeStringNoTag(string);
+
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(bufferSize + 2);
+ coder.stream().writeStringNoTag(string);
+ coder.stream().flush();
+ }
}
public void testWriteToByteBuffer() throws Exception {
@@ -461,86 +488,308 @@ public class CodedOutputStreamTest extends TestCase {
public void testWriteByteArrayWithOffsets() throws Exception {
byte[] fullArray = bytes(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
- byte[] destination = new byte[4];
- CodedOutputStream codedStream = CodedOutputStream.newInstance(destination);
- codedStream.writeByteArrayNoTag(fullArray, 2, 2);
- assertEqualBytes(bytes(0x02, 0x33, 0x44, 0x00), destination);
- assertEquals(3, codedStream.getTotalBytesWritten());
+ for (OutputType type : new OutputType[] {OutputType.ARRAY}) {
+ Coder coder = type.newCoder(4);
+ coder.stream().writeByteArrayNoTag(fullArray, 2, 2);
+ assertEqualBytes(type, bytes(0x02, 0x33, 0x44), coder.toByteArray());
+ assertEquals(3, coder.stream().getTotalBytesWritten());
+ }
+ }
+
+ public void testSerializeUtf8_MultipleSmallWrites() throws Exception {
+ final String source = "abcdefghijklmnopqrstuvwxyz";
+
+ // Generate the expected output if the source string is written 2 bytes at a time.
+ ByteArrayOutputStream expectedBytesStream = new ByteArrayOutputStream();
+ for (int pos = 0; pos < source.length(); pos += 2) {
+ String substr = source.substring(pos, pos + 2);
+ expectedBytesStream.write(2);
+ expectedBytesStream.write(substr.getBytes(Internal.UTF_8));
+ }
+ final byte[] expectedBytes = expectedBytesStream.toByteArray();
+
+ // For each output type, write the source string 2 bytes at a time and verify the output.
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(expectedBytes.length);
+ for (int pos = 0; pos < source.length(); pos += 2) {
+ String substr = source.substring(pos, pos + 2);
+ coder.stream().writeStringNoTag(substr);
+ }
+ coder.stream().flush();
+ assertEqualBytes(outputType, expectedBytes, coder.toByteArray());
+ }
}
-
+
public void testSerializeInvalidUtf8() throws Exception {
- String[] invalidStrings = new String[] {
- newString(Character.MIN_HIGH_SURROGATE),
- "foobar" + newString(Character.MIN_HIGH_SURROGATE),
- newString(Character.MIN_LOW_SURROGATE),
+ String[] invalidStrings = new String[] {newString(Character.MIN_HIGH_SURROGATE),
+ "foobar" + newString(Character.MIN_HIGH_SURROGATE), newString(Character.MIN_LOW_SURROGATE),
"foobar" + newString(Character.MIN_LOW_SURROGATE),
- newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE)
- };
-
+ newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE)};
+
CodedOutputStream outputWithStream = CodedOutputStream.newInstance(new ByteArrayOutputStream());
CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[10000]);
+ CodedOutputStream outputWithByteBuffer =
+ CodedOutputStream.newInstance(ByteBuffer.allocate(10000));
for (String s : invalidStrings) {
// TODO(dweis): These should all fail; instead they are corrupting data.
CodedOutputStream.computeStringSizeNoTag(s);
outputWithStream.writeStringNoTag(s);
outputWithArray.writeStringNoTag(s);
+ outputWithByteBuffer.writeStringNoTag(s);
}
}
-
- private static String newString(char... chars) {
- return new String(chars);
+
+ // TODO(nathanmittler): This test can be deleted once we properly throw IOException while
+ // encoding invalid UTF-8 strings.
+ public void testSerializeInvalidUtf8FollowedByOutOfSpace() throws Exception {
+ final int notEnoughBytes = 4;
+ CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[notEnoughBytes]);
+ CodedOutputStream outputWithByteBuffer =
+ CodedOutputStream.newInstance(ByteBuffer.allocate(notEnoughBytes));
+
+ String invalidString = newString(Character.MIN_HIGH_SURROGATE, 'f', 'o', 'o', 'b', 'a', 'r');
+ try {
+ outputWithArray.writeStringNoTag(invalidString);
+ fail("Expected OutOfSpaceException");
+ } catch (OutOfSpaceException e) {
+ assertTrue(e.getCause() instanceof IndexOutOfBoundsException);
+ }
+ try {
+ outputWithByteBuffer.writeStringNoTag(invalidString);
+ fail("Expected OutOfSpaceException");
+ } catch (OutOfSpaceException e) {
+ assertTrue(e.getCause() instanceof IndexOutOfBoundsException);
+ }
}
/** Regression test for https://github.com/google/protobuf/issues/292 */
public void testCorrectExceptionThrowWhenEncodingStringsWithoutEnoughSpace() throws Exception {
String testCase = "Foooooooo";
- assertEquals(CodedOutputStream.computeRawVarint32Size(testCase.length()),
- CodedOutputStream.computeRawVarint32Size(testCase.length() * 3));
+ assertEquals(
+ CodedOutputStream.computeUInt32SizeNoTag(testCase.length()),
+ CodedOutputStream.computeUInt32SizeNoTag(testCase.length() * 3));
assertEquals(11, CodedOutputStream.computeStringSize(1, testCase));
// Tag is one byte, varint describing string length is 1 byte, string length is 9 bytes.
// An array of size 1 will cause a failure when trying to write the varint.
- for (int i = 0; i < 11; i++) {
- CodedOutputStream output = CodedOutputStream.newInstance(new byte[i]);
- try {
- output.writeString(1, testCase);
- fail("Should have thrown an out of space exception");
- } catch (CodedOutputStream.OutOfSpaceException expected) {}
+ for (OutputType outputType :
+ new OutputType[] {
+ OutputType.ARRAY,
+ OutputType.NIO_HEAP,
+ OutputType.NIO_DIRECT_SAFE,
+ OutputType.NIO_DIRECT_UNSAFE
+ }) {
+ for (int i = 0; i < 11; i++) {
+ Coder coder = outputType.newCoder(i);
+ try {
+ coder.stream().writeString(1, testCase);
+ fail("Should have thrown an out of space exception");
+ } catch (CodedOutputStream.OutOfSpaceException expected) {
+ }
+ }
}
}
-
+
public void testDifferentStringLengths() throws Exception {
// Test string serialization roundtrip using strings of the following lengths,
// with ASCII and Unicode characters requiring different UTF-8 byte counts per
// char, hence causing the length delimiter varint to sometimes require more
// bytes for the Unicode strings than the ASCII string of the same length.
int[] lengths = new int[] {
- 0,
- 1,
- (1 << 4) - 1, // 1 byte for ASCII and Unicode
- (1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode
- (1 << 11) - 1, // 2 bytes for ASCII and Unicode
- (1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode
- (1 << 17) - 1, // 3 bytes for ASCII and Unicode
+ 0,
+ 1,
+ (1 << 4) - 1, // 1 byte for ASCII and Unicode
+ (1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode
+ (1 << 11) - 1, // 2 bytes for ASCII and Unicode
+ (1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode
+ (1 << 17) - 1,
+ // 3 bytes for ASCII and Unicode
};
- for (int i : lengths) {
- testEncodingOfString('q', i); // 1 byte per char
- testEncodingOfString('\u07FF', i); // 2 bytes per char
- testEncodingOfString('\u0981', i); // 3 bytes per char
+ for (OutputType outputType : OutputType.values()) {
+ for (int i : lengths) {
+ testEncodingOfString(outputType, 'q', i); // 1 byte per char
+ testEncodingOfString(outputType, '\u07FF', i); // 2 bytes per char
+ testEncodingOfString(outputType, '\u0981', i); // 3 bytes per char
+ }
+ }
+ }
+
+ public void testNioEncodersWithInitialOffsets() throws Exception {
+ String value = "abc";
+ for (Coder coder :
+ new Coder[] {
+ new NioHeapCoder(10, 2), new NioDirectCoder(10, 2, false), new NioDirectCoder(10, 2, true)
+ }) {
+ coder.stream().writeStringNoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(coder.getOutputType(), new byte[] {3, 'a', 'b', 'c'}, coder.toByteArray());
+ }
+ }
+
+ /**
+ * Parses the given bytes using writeRawLittleEndian32() and checks
+ * that the result matches the given value.
+ */
+ private static void assertWriteLittleEndian32(byte[] data, int value) throws Exception {
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(data.length);
+ coder.stream().writeFixed32NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(outputType, data, coder.toByteArray());
+ }
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ Coder coder = OutputType.STREAM.newCoder(blockSize);
+ coder.stream().writeFixed32NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(OutputType.STREAM, data, coder.toByteArray());
+ }
+ }
+
+ /**
+ * Parses the given bytes using writeRawLittleEndian64() and checks
+ * that the result matches the given value.
+ */
+ private static void assertWriteLittleEndian64(byte[] data, long value) throws Exception {
+ for (OutputType outputType : OutputType.values()) {
+ Coder coder = outputType.newCoder(data.length);
+ coder.stream().writeFixed64NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(outputType, data, coder.toByteArray());
+ }
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ Coder coder = OutputType.STREAM.newCoder(blockSize);
+ coder.stream().writeFixed64NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(OutputType.STREAM, data, coder.toByteArray());
}
}
- private void testEncodingOfString(char c, int length) throws Exception {
+ private static String newString(char... chars) {
+ return new String(chars);
+ }
+
+ private static void testEncodingOfString(OutputType outputType, char c, int length)
+ throws Exception {
String fullString = fullString(c, length);
- TestAllTypes testAllTypes = TestAllTypes.newBuilder()
- .setOptionalString(fullString)
- .build();
+ TestAllTypes testAllTypes = TestAllTypes.newBuilder().setOptionalString(fullString).build();
+ Coder coder = outputType.newCoder(testAllTypes.getSerializedSize());
+ testAllTypes.writeTo(coder.stream());
+ coder.stream().flush();
assertEquals(
- fullString, TestAllTypes.parseFrom(testAllTypes.toByteArray()).getOptionalString());
+ "OuputType: " + outputType,
+ fullString,
+ TestAllTypes.parseFrom(coder.toByteArray()).getOptionalString());
}
- private String fullString(char c, int length) {
+ private static String fullString(char c, int length) {
char[] result = new char[length];
Arrays.fill(result, c);
return new String(result);
}
+
+ /**
+ * Helper to construct a byte array from a bunch of bytes. The inputs are
+ * actually ints so that I can use hex notation and not get stupid errors
+ * about precision.
+ */
+ private static byte[] bytes(int... bytesAsInts) {
+ byte[] bytes = new byte[bytesAsInts.length];
+ for (int i = 0; i < bytesAsInts.length; i++) {
+ bytes[i] = (byte) bytesAsInts[i];
+ }
+ return bytes;
+ }
+
+ /** Arrays.asList() does not work with arrays of primitives. :( */
+ private static List<Byte> toList(byte[] bytes) {
+ List<Byte> result = new ArrayList<Byte>();
+ for (byte b : bytes) {
+ result.add(b);
+ }
+ return result;
+ }
+
+ private static void assertEqualBytes(OutputType outputType, byte[] a, byte[] b) {
+ assertEquals(outputType.name(), toList(a), toList(b));
+ }
+
+ /**
+ * Writes the given value using writeRawVarint32() and writeRawVarint64() and
+ * checks that the result matches the given bytes.
+ */
+ private static void assertWriteVarint(byte[] data, long value) throws Exception {
+ for (OutputType outputType : OutputType.values()) {
+ // Only test 32-bit write if the value fits into an int.
+ if (value == (int) value) {
+ Coder coder = outputType.newCoder(10);
+ coder.stream().writeUInt32NoTag((int) value);
+ coder.stream().flush();
+ assertEqualBytes(outputType, data, coder.toByteArray());
+
+ // Also try computing size.
+ assertEquals(data.length, CodedOutputStream.computeUInt32SizeNoTag((int) value));
+ }
+
+ {
+ Coder coder = outputType.newCoder(10);
+ coder.stream().writeUInt64NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(outputType, data, coder.toByteArray());
+
+ // Also try computing size.
+ assertEquals(data.length, CodedOutputStream.computeUInt64SizeNoTag(value));
+ }
+ }
+
+ // Try different block sizes.
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) {
+ // Only test 32-bit write if the value fits into an int.
+ if (value == (int) value) {
+ Coder coder = OutputType.STREAM.newCoder(blockSize);
+ coder.stream().writeUInt64NoTag((int) value);
+ coder.stream().flush();
+ assertEqualBytes(OutputType.STREAM, data, coder.toByteArray());
+
+ ByteArrayOutputStream rawOutput = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, blockSize);
+ output.writeUInt32NoTag((int) value);
+ output.flush();
+ assertEqualBytes(OutputType.STREAM, data, rawOutput.toByteArray());
+ }
+
+ {
+ Coder coder = OutputType.STREAM.newCoder(blockSize);
+ coder.stream().writeUInt64NoTag(value);
+ coder.stream().flush();
+ assertEqualBytes(OutputType.STREAM, data, coder.toByteArray());
+ }
+ }
+ }
+
+ private static void assertVarintRoundTrip(OutputType outputType, long value) throws Exception {
+ {
+ Coder coder = outputType.newCoder(10);
+ coder.stream().writeUInt64NoTag(value);
+ coder.stream().flush();
+ byte[] bytes = coder.toByteArray();
+ assertEquals(
+ outputType.name(), bytes.length, CodedOutputStream.computeUInt64SizeNoTag(value));
+ CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
+ assertEquals(outputType.name(), value, input.readRawVarint64());
+ }
+
+ if (value == (int) value) {
+ Coder coder = outputType.newCoder(10);
+ coder.stream().writeUInt32NoTag((int) value);
+ coder.stream().flush();
+ byte[] bytes = coder.toByteArray();
+ assertEquals(
+ outputType.name(), bytes.length, CodedOutputStream.computeUInt32SizeNoTag((int) value));
+ CodedInputStream input = CodedInputStream.newInstance(new ByteArrayInputStream(bytes));
+ assertEquals(outputType.name(), value, input.readRawVarint32());
+ }
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java b/java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java
new file mode 100644
index 00000000..359d4d74
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java
@@ -0,0 +1,325 @@
+package com.google.protobuf;
+
+import com.google.protobuf.Utf8.Processor;
+import com.google.protobuf.Utf8.SafeProcessor;
+import com.google.protobuf.Utf8.UnsafeProcessor;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+import junit.framework.TestCase;
+
+public class DecodeUtf8Test extends TestCase {
+ private static Logger logger = Logger.getLogger(DecodeUtf8Test.class.getName());
+
+ private static final Processor SAFE_PROCESSOR = new SafeProcessor();
+ private static final Processor UNSAFE_PROCESSOR = new UnsafeProcessor();
+
+ public void testRoundTripAllValidChars() throws Exception {
+ for (int i = Character.MIN_CODE_POINT; i < Character.MAX_CODE_POINT; i++) {
+ if (i < Character.MIN_SURROGATE || i > Character.MAX_SURROGATE) {
+ String str = new String(Character.toChars(i));
+ assertRoundTrips(str);
+ }
+ }
+ }
+
+ // Test all 1, 2, 3 invalid byte combinations. Valid ones would have been covered above.
+
+ public void testOneByte() throws Exception {
+ int valid = 0;
+ for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+ ByteString bs = ByteString.copyFrom(new byte[] { (byte) i });
+ if (!bs.isValidUtf8()) {
+ assertInvalid(bs.toByteArray());
+ } else {
+ valid++;
+ }
+ }
+ assertEquals(IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, valid);
+ }
+
+ public void testTwoBytes() throws Exception {
+ int valid = 0;
+ for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+ for (int j = Byte.MIN_VALUE; j <= Byte.MAX_VALUE; j++) {
+ ByteString bs = ByteString.copyFrom(new byte[]{(byte) i, (byte) j});
+ if (!bs.isValidUtf8()) {
+ assertInvalid(bs.toByteArray());
+ } else {
+ valid++;
+ }
+ }
+ }
+ assertEquals(IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT, valid);
+ }
+
+ public void testThreeBytes() throws Exception {
+ // Travis' OOM killer doesn't like this test
+ if (System.getenv("TRAVIS") == null) {
+ int count = 0;
+ int valid = 0;
+ for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+ for (int j = Byte.MIN_VALUE; j <= Byte.MAX_VALUE; j++) {
+ for (int k = Byte.MIN_VALUE; k <= Byte.MAX_VALUE; k++) {
+ byte[] bytes = new byte[]{(byte) i, (byte) j, (byte) k};
+ ByteString bs = ByteString.copyFrom(bytes);
+ if (!bs.isValidUtf8()) {
+ assertInvalid(bytes);
+ } else {
+ valid++;
+ }
+ count++;
+ if (count % 1000000L == 0) {
+ logger.info("Processed " + (count / 1000000L) + " million characters");
+ }
+ }
+ }
+ }
+ assertEquals(IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT, valid);
+ }
+ }
+
+ /**
+ * Tests that round tripping of a sample of four byte permutations work.
+ */
+ public void testInvalid_4BytesSamples() throws Exception {
+ // Bad trailing bytes
+ assertInvalid(0xF0, 0xA4, 0xAD, 0x7F);
+ assertInvalid(0xF0, 0xA4, 0xAD, 0xC0);
+
+ // Special cases for byte2
+ assertInvalid(0xF0, 0x8F, 0xAD, 0xA2);
+ assertInvalid(0xF4, 0x90, 0xAD, 0xA2);
+ }
+
+ public void testRealStrings() throws Exception {
+ // English
+ assertRoundTrips("The quick brown fox jumps over the lazy dog");
+ // German
+ assertRoundTrips("Quizdeltagerne spiste jordb\u00e6r med fl\u00f8de, mens cirkusklovnen");
+ // Japanese
+ assertRoundTrips(
+ "\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3061\u308a\u306c\u308b\u3092");
+ // Hebrew
+ assertRoundTrips(
+ "\u05d3\u05d2 \u05e1\u05e7\u05e8\u05df \u05e9\u05d8 \u05d1\u05d9\u05dd "
+ + "\u05de\u05d0\u05d5\u05db\u05d6\u05d1 \u05d5\u05dc\u05e4\u05ea\u05e2"
+ + " \u05de\u05e6\u05d0 \u05dc\u05d5 \u05d7\u05d1\u05e8\u05d4 "
+ + "\u05d0\u05d9\u05da \u05d4\u05e7\u05dc\u05d9\u05d8\u05d4");
+ // Thai
+ assertRoundTrips(
+ " \u0e08\u0e07\u0e1d\u0e48\u0e32\u0e1f\u0e31\u0e19\u0e1e\u0e31\u0e12"
+ + "\u0e19\u0e32\u0e27\u0e34\u0e0a\u0e32\u0e01\u0e32\u0e23");
+ // Chinese
+ assertRoundTrips(
+ "\u8fd4\u56de\u94fe\u4e2d\u7684\u4e0b\u4e00\u4e2a\u4ee3\u7406\u9879\u9009\u62e9\u5668");
+ // Chinese with 4-byte chars
+ assertRoundTrips("\uD841\uDF0E\uD841\uDF31\uD841\uDF79\uD843\uDC53\uD843\uDC78"
+ + "\uD843\uDC96\uD843\uDCCF\uD843\uDCD5\uD843\uDD15\uD843\uDD7C\uD843\uDD7F"
+ + "\uD843\uDE0E\uD843\uDE0F\uD843\uDE77\uD843\uDE9D\uD843\uDEA2");
+ // Mixed
+ assertRoundTrips(
+ "The quick brown \u3044\u308d\u306f\u306b\u307b\u3078\u8fd4\u56de\u94fe"
+ + "\u4e2d\u7684\u4e0b\u4e00");
+ }
+
+ public void testOverlong() throws Exception {
+ assertInvalid(0xc0, 0xaf);
+ assertInvalid(0xe0, 0x80, 0xaf);
+ assertInvalid(0xf0, 0x80, 0x80, 0xaf);
+
+ // Max overlong
+ assertInvalid(0xc1, 0xbf);
+ assertInvalid(0xe0, 0x9f, 0xbf);
+ assertInvalid(0xf0 ,0x8f, 0xbf, 0xbf);
+
+ // null overlong
+ assertInvalid(0xc0, 0x80);
+ assertInvalid(0xe0, 0x80, 0x80);
+ assertInvalid(0xf0, 0x80, 0x80, 0x80);
+ }
+
+ public void testIllegalCodepoints() throws Exception {
+ // Single surrogate
+ assertInvalid(0xed, 0xa0, 0x80);
+ assertInvalid(0xed, 0xad, 0xbf);
+ assertInvalid(0xed, 0xae, 0x80);
+ assertInvalid(0xed, 0xaf, 0xbf);
+ assertInvalid(0xed, 0xb0, 0x80);
+ assertInvalid(0xed, 0xbe, 0x80);
+ assertInvalid(0xed, 0xbf, 0xbf);
+
+ // Paired surrogates
+ assertInvalid(0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80);
+ assertInvalid(0xed, 0xa0, 0x80, 0xed, 0xbf, 0xbf);
+ assertInvalid(0xed, 0xad, 0xbf, 0xed, 0xb0, 0x80);
+ assertInvalid(0xed, 0xad, 0xbf, 0xed, 0xbf, 0xbf);
+ assertInvalid(0xed, 0xae, 0x80, 0xed, 0xb0, 0x80);
+ assertInvalid(0xed, 0xae, 0x80, 0xed, 0xbf, 0xbf);
+ assertInvalid(0xed, 0xaf, 0xbf, 0xed, 0xb0, 0x80);
+ assertInvalid(0xed, 0xaf, 0xbf, 0xed, 0xbf, 0xbf);
+ }
+
+ public void testBufferSlice() throws Exception {
+ String str = "The quick brown fox jumps over the lazy dog";
+ assertRoundTrips(str, 10, 4);
+ assertRoundTrips(str, str.length(), 0);
+ }
+
+ public void testInvalidBufferSlice() throws Exception {
+ byte[] bytes = "The quick brown fox jumps over the lazy dog".getBytes(Internal.UTF_8);
+ assertInvalidSlice(bytes, bytes.length - 3, 4);
+ assertInvalidSlice(bytes, bytes.length, 1);
+ assertInvalidSlice(bytes, bytes.length + 1, 0);
+ assertInvalidSlice(bytes, 0, bytes.length + 1);
+ }
+
+ private void assertInvalid(int... bytesAsInt) throws Exception {
+ byte[] bytes = new byte[bytesAsInt.length];
+ for (int i = 0; i < bytesAsInt.length; i++) {
+ bytes[i] = (byte) bytesAsInt[i];
+ }
+ assertInvalid(bytes);
+ }
+
+ private void assertInvalid(byte[] bytes) throws Exception {
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(bytes, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(bytes, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+
+ ByteBuffer direct = ByteBuffer.allocateDirect(bytes.length);
+ direct.put(bytes);
+ direct.flip();
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(direct, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(direct, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+
+ ByteBuffer heap = ByteBuffer.allocate(bytes.length);
+ heap.put(bytes);
+ heap.flip();
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(heap, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(heap, 0, bytes.length);
+ fail();
+ } catch (InvalidProtocolBufferException e) {
+ // Expected.
+ }
+ }
+
+ private void assertInvalidSlice(byte[] bytes, int index, int size) throws Exception {
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(bytes, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(bytes, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+
+ ByteBuffer direct = ByteBuffer.allocateDirect(bytes.length);
+ direct.put(bytes);
+ direct.flip();
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(direct, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(direct, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+
+ ByteBuffer heap = ByteBuffer.allocate(bytes.length);
+ heap.put(bytes);
+ heap.flip();
+ try {
+ UNSAFE_PROCESSOR.decodeUtf8(heap, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+ try {
+ SAFE_PROCESSOR.decodeUtf8(heap, index, size);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // Expected.
+ }
+ }
+
+ private void assertRoundTrips(String str) throws Exception {
+ assertRoundTrips(str, 0, -1);
+ }
+
+ private void assertRoundTrips(String str, int index, int size) throws Exception {
+ byte[] bytes = str.getBytes(Internal.UTF_8);
+ if (size == -1) {
+ size = bytes.length;
+ }
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ UNSAFE_PROCESSOR.decodeUtf8(bytes, index, size));
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ SAFE_PROCESSOR.decodeUtf8(bytes, index, size));
+
+ ByteBuffer direct = ByteBuffer.allocateDirect(bytes.length);
+ direct.put(bytes);
+ direct.flip();
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ UNSAFE_PROCESSOR.decodeUtf8(direct, index, size));
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ SAFE_PROCESSOR.decodeUtf8(direct, index, size));
+
+ ByteBuffer heap = ByteBuffer.allocate(bytes.length);
+ heap.put(bytes);
+ heap.flip();
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ UNSAFE_PROCESSOR.decodeUtf8(heap, index, size));
+ assertDecode(new String(bytes, index, size, Internal.UTF_8),
+ SAFE_PROCESSOR.decodeUtf8(heap, index, size));
+ }
+
+ private void assertDecode(String expected, String actual) {
+ if (!expected.equals(actual)) {
+ fail("Failure: Expected (" + codepoints(expected) + ") Actual (" + codepoints(actual) + ")");
+ }
+ }
+
+ private List<String> codepoints(String str) {
+ List<String> codepoints = new ArrayList<String>();
+ for (int i = 0; i < str.length(); i++) {
+ codepoints.add(Long.toHexString(str.charAt(i)));
+ }
+ return codepoints;
+ }
+
+}
diff --git a/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
index e7905f79..9c0997c4 100644
--- a/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DeprecatedFieldTest.java
@@ -31,29 +31,28 @@
package com.google.protobuf;
import protobuf_unittest.UnittestProto.TestDeprecatedFields;
-
-import junit.framework.TestCase;
-
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
+import junit.framework.TestCase;
+
/**
* Test field deprecation
- *
+ *
* @author birdo@google.com (Roberto Scaramuzzi)
*/
public class DeprecatedFieldTest extends TestCase {
private String[] deprecatedGetterNames = {
"hasDeprecatedInt32",
"getDeprecatedInt32"};
-
+
private String[] deprecatedBuilderGetterNames = {
"hasDeprecatedInt32",
"getDeprecatedInt32",
"clearDeprecatedInt32"};
-
+
private String[] deprecatedBuilderSetterNames = {
- "setDeprecatedInt32"};
-
+ "setDeprecatedInt32"};
+
public void testDeprecatedField() throws Exception {
Class<?> deprecatedFields = TestDeprecatedFields.class;
Class<?> deprecatedFieldsBuilder = TestDeprecatedFields.Builder.class;
@@ -73,7 +72,15 @@ public class DeprecatedFieldTest extends TestCase {
isDeprecated(method));
}
}
-
+
+ public void testDeprecatedFieldInOneof() throws Exception {
+ Class<?> oneofCase = TestDeprecatedFields.OneofFieldsCase.class;
+ String name = "DEPRECATED_INT32_IN_ONEOF";
+ java.lang.reflect.Field enumValue = oneofCase.getField(name);
+ assertTrue("Enum value " + name + " should be deprecated.",
+ isDeprecated(enumValue));
+ }
+
private boolean isDeprecated(AnnotatedElement annotated) {
return annotated.isAnnotationPresent(Deprecated.class);
}
diff --git a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
index 82ff34af..b60cd620 100644
--- a/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DescriptorsTest.java
@@ -55,16 +55,15 @@ import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
+import protobuf_unittest.UnittestProto.TestJsonName;
import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestReservedFields;
import protobuf_unittest.UnittestProto.TestService;
-
-import junit.framework.TestCase;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import junit.framework.TestCase;
/**
* Unit test for {@link Descriptors}.
@@ -383,6 +382,14 @@ public class DescriptorsTest extends TestCase {
assertEquals(Long.valueOf(8765432109L),
field.getOptions().getExtension(UnittestCustomOptions.fieldOpt1));
+ OneofDescriptor oneof = descriptor.getOneofs().get(0);
+ assertNotNull(oneof);
+
+ assertTrue(
+ oneof.getOptions().hasExtension(UnittestCustomOptions.oneofOpt1));
+ assertEquals(Integer.valueOf(-99),
+ oneof.getOptions().getExtension(UnittestCustomOptions.oneofOpt1));
+
EnumDescriptor enumType =
UnittestCustomOptions.TestMessageWithCustomOptions.AnEnum.getDescriptor();
@@ -573,6 +580,42 @@ public class DescriptorsTest extends TestCase {
}
}
+ public void testUnknownFieldsDenied() throws Exception {
+ FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
+ .setName("foo.proto")
+ .addMessageType(DescriptorProto.newBuilder()
+ .setName("Foo")
+ .addField(FieldDescriptorProto.newBuilder()
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setTypeName("Bar")
+ .setName("bar")
+ .setNumber(1)))
+ .build();
+
+ try {
+ Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]);
+ fail("DescriptorValidationException expected");
+ } catch (DescriptorValidationException e) {
+ assertTrue(e.getMessage().indexOf("Bar") != -1);
+ assertTrue(e.getMessage().indexOf("is not defined") != -1);
+ }
+ }
+
+ public void testUnknownFieldsAllowed() throws Exception {
+ FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
+ .setName("foo.proto")
+ .addDependency("bar.proto")
+ .addMessageType(DescriptorProto.newBuilder()
+ .setName("Foo")
+ .addField(FieldDescriptorProto.newBuilder()
+ .setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
+ .setTypeName("Bar")
+ .setName("bar")
+ .setNumber(1)))
+ .build();
+ Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true);
+ }
+
public void testHiddenDependency() throws Exception {
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
.setName("bar.proto")
@@ -762,4 +805,15 @@ public class DescriptorsTest extends TestCase {
Descriptors.FileDescriptor.buildFrom(
fileDescriptorProto, new FileDescriptor[0]);
}
+
+ public void testFieldJsonName() throws Exception {
+ Descriptor d = TestJsonName.getDescriptor();
+ assertEquals(6, d.getFields().size());
+ assertEquals("fieldName1", d.getFields().get(0).getJsonName());
+ assertEquals("fieldName2", d.getFields().get(1).getJsonName());
+ assertEquals("FieldName3", d.getFields().get(2).getJsonName());
+ assertEquals("FieldName4", d.getFields().get(3).getJsonName());
+ assertEquals("FIELDNAME5", d.getFields().get(4).getJsonName());
+ assertEquals("@type", d.getFields().get(5).getJsonName());
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java b/java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java
new file mode 100644
index 00000000..0f09a51b
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/DiscardUnknownFieldsTest.java
@@ -0,0 +1,157 @@
+// 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 org.junit.Assert.assertEquals;
+
+import protobuf_unittest.UnittestProto;
+import proto3_unittest.UnittestProto3;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for discard or preserve unknown fields. */
+@RunWith(JUnit4.class)
+public class DiscardUnknownFieldsTest {
+ @Test
+ public void testProto2() throws Exception {
+ testProto2Message(
+ UnittestProto.TestEmptyMessage.getDefaultInstance());
+ testProto2Message(
+ UnittestProto.TestEmptyMessageWithExtensions.getDefaultInstance());
+ testProto2Message(
+ DynamicMessage.getDefaultInstance(UnittestProto.TestEmptyMessage.getDescriptor()));
+ testProto2Message(
+ DynamicMessage.getDefaultInstance(
+ UnittestProto.TestEmptyMessageWithExtensions.getDescriptor()));
+ }
+
+ @Test
+ public void testProto3() throws Exception {
+ testProto3Message(UnittestProto3.TestEmptyMessage.getDefaultInstance());
+ testProto3Message(
+ DynamicMessage.getDefaultInstance(UnittestProto3.TestEmptyMessage.getDescriptor()));
+ }
+
+ private static void testProto2Message(Message message) throws Exception {
+ assertUnknownFieldsDefaultPreserved(message);
+ assertUnknownFieldsExplicitlyDiscarded(message);
+ assertReuseCodedInputStreamPreserve(message);
+ assertUnknownFieldsInUnknownFieldSetArePreserve(message);
+ }
+
+ private static void testProto3Message(Message message) throws Exception {
+ CodedInputStream.setProto3KeepUnknownsByDefaultForTest();
+ assertUnknownFieldsDefaultPreserved(message);
+ assertUnknownFieldsExplicitlyDiscarded(message);
+ assertReuseCodedInputStreamPreserve(message);
+ assertUnknownFieldsInUnknownFieldSetArePreserve(message);
+ CodedInputStream.setProto3DiscardUnknownsByDefaultForTest();
+ assertUnknownFieldsDefaultDiscarded(message);
+ assertUnknownFieldsExplicitlyDiscarded(message);
+ assertUnknownFieldsInUnknownFieldSetAreDiscarded(message);
+ }
+
+ private static void assertReuseCodedInputStreamPreserve(Message message) throws Exception {
+ final int messageSize = payload.size();
+ byte[] copied = new byte[messageSize * 2];
+ payload.copyTo(copied, 0);
+ payload.copyTo(copied, messageSize);
+ CodedInputStream input = CodedInputStream.newInstance(copied);
+ {
+ // Use DiscardUnknownFieldsParser to parse the first payload.
+ int oldLimit = input.pushLimit(messageSize);
+ Message parsed = DiscardUnknownFieldsParser.wrap(message.getParserForType()).parseFrom(input);
+ assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+ input.popLimit(oldLimit);
+ }
+ {
+ // Use the normal parser to parse the remaining payload should have unknown fields preserved.
+ Message parsed = message.getParserForType().parseFrom(input);
+ assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+ }
+ }
+
+ /**
+ * {@link Message.Builder#setUnknownFields(UnknownFieldSet)} and {@link
+ * Message.Builder#mergeUnknownFields(UnknownFieldSet)} should preserve the unknown fields.
+ */
+ private static void assertUnknownFieldsInUnknownFieldSetArePreserve(Message message)
+ throws Exception {
+ UnknownFieldSet unknownFields = UnknownFieldSet.newBuilder().mergeFrom(payload).build();
+ Message built = message.newBuilderForType().setUnknownFields(unknownFields).build();
+ assertEquals(message.getClass().getName(), payload, built.toByteString());
+
+ }
+ /**
+ * {@link Message.Builder#setUnknownFields(UnknownFieldSet)} and {@link
+ * Message.Builder#mergeUnknownFields(UnknownFieldSet)} should discard the unknown fields.
+ */
+ private static void assertUnknownFieldsInUnknownFieldSetAreDiscarded(Message message)
+ throws Exception {
+ UnknownFieldSet unknownFields = UnknownFieldSet.newBuilder().mergeFrom(payload).build();
+ Message built = message.newBuilderForType().setUnknownFields(unknownFields).build();
+ assertEquals(message.getClass().getName(), 0, built.getSerializedSize());
+ }
+
+ private static void assertUnknownFieldsDefaultPreserved(MessageLite message) throws Exception {
+ {
+ MessageLite parsed = message.getParserForType().parseFrom(payload);
+ assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+ }
+
+ {
+ MessageLite parsed = message.newBuilderForType().mergeFrom(payload).build();
+ assertEquals(message.getClass().getName(), payload, parsed.toByteString());
+ }
+ }
+
+ private static void assertUnknownFieldsDefaultDiscarded(MessageLite message) throws Exception {
+ {
+ MessageLite parsed = message.getParserForType().parseFrom(payload);
+ assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+ }
+
+ {
+ MessageLite parsed = message.newBuilderForType().mergeFrom(payload).build();
+ assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+ }
+ }
+
+ private static void assertUnknownFieldsExplicitlyDiscarded(Message message) throws Exception {
+ Message parsed =
+ DiscardUnknownFieldsParser.wrap(message.getParserForType()).parseFrom(payload);
+ assertEquals(message.getClass().getName(), 0, parsed.getSerializedSize());
+ }
+
+ private static final ByteString payload =
+ TestUtilLite.getAllLiteSetBuilder().build().toByteString();
+}
diff --git a/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
index d3deaa07..923d7f43 100644
--- a/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java
@@ -32,61 +32,48 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
+import com.google.protobuf.Internal.DoubleList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import junit.framework.TestCase;
/**
* Tests for {@link DoubleArrayList}.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
public class DoubleArrayListTest extends TestCase {
-
- private static final DoubleArrayList UNARY_LIST = newImmutableDoubleArrayList(1);
+
+ private static final DoubleArrayList UNARY_LIST =
+ newImmutableDoubleArrayList(1);
private static final DoubleArrayList TERTIARY_LIST =
newImmutableDoubleArrayList(1, 2, 3);
-
+
private DoubleArrayList list;
-
+
@Override
protected void setUp() throws Exception {
list = new DoubleArrayList();
}
-
+
public void testEmptyListReturnsSameInstance() {
assertSame(DoubleArrayList.emptyList(), DoubleArrayList.emptyList());
}
-
+
public void testEmptyListIsImmutable() {
assertImmutable(DoubleArrayList.emptyList());
}
-
+
public void testMakeImmutable() {
- list.addDouble(2);
+ list.addDouble(3);
list.addDouble(4);
- list.addDouble(6);
- list.addDouble(8);
+ list.addDouble(5);
+ list.addDouble(7);
list.makeImmutable();
assertImmutable(list);
}
-
- public void testCopyConstructor() {
- DoubleArrayList copy = new DoubleArrayList(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new DoubleArrayList(DoubleArrayList.emptyList());
- assertEquals(DoubleArrayList.emptyList(), copy);
-
- copy = new DoubleArrayList(asList(1D, 2D, 3D));
- assertEquals(asList(1D, 2D, 3D), copy);
-
- copy = new DoubleArrayList(Collections.<Double>emptyList());
- assertEquals(DoubleArrayList.emptyList(), copy);
- }
-
+
public void testModificationWithIteration() {
list.addAll(asList(1D, 2D, 3D, 4D));
Iterator<Double> iterator = list.iterator();
@@ -95,7 +82,7 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1D, (double) iterator.next());
list.set(0, 1D);
assertEquals(2D, (double) iterator.next());
-
+
list.remove(0);
try {
iterator.next();
@@ -103,7 +90,7 @@ public class DoubleArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
-
+
iterator = list.iterator();
list.add(0, 0D);
try {
@@ -113,19 +100,19 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGet() {
assertEquals(1D, (double) TERTIARY_LIST.get(0));
assertEquals(2D, (double) TERTIARY_LIST.get(1));
assertEquals(3D, (double) TERTIARY_LIST.get(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -133,19 +120,19 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
- public void testGetInt() {
+
+ public void testGetDouble() {
assertEquals(1D, TERTIARY_LIST.getDouble(0));
assertEquals(2D, TERTIARY_LIST.getDouble(1));
assertEquals(3D, TERTIARY_LIST.getDouble(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -153,35 +140,35 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSize() {
assertEquals(0, DoubleArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size());
- list.addDouble(2);
+ list.addDouble(3);
list.addDouble(4);
list.addDouble(6);
list.addDouble(8);
assertEquals(4, list.size());
-
+
list.remove(0);
assertEquals(3, list.size());
-
- list.add(16D);
+
+ list.add(17D);
assertEquals(4, list.size());
}
-
+
public void testSet() {
list.addDouble(2);
list.addDouble(4);
-
- assertEquals(2D, (double) list.set(0, 0D));
- assertEquals(0D, list.getDouble(0));
+
+ assertEquals(2D, (double) list.set(0, 3D));
+ assertEquals(3D, list.getDouble(0));
assertEquals(4D, (double) list.set(1, 0D));
assertEquals(0D, list.getDouble(1));
-
+
try {
list.set(-1, 0D);
fail();
@@ -196,17 +183,17 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
- public void testSetInt() {
- list.addDouble(2);
- list.addDouble(4);
-
- assertEquals(2D, list.setDouble(0, 0));
+
+ public void testSetDouble() {
+ list.addDouble(1);
+ list.addDouble(3);
+
+ assertEquals(1D, list.setDouble(0, 0));
assertEquals(0D, list.getDouble(0));
- assertEquals(4D, list.setDouble(1, 0));
+ assertEquals(3D, list.setDouble(1, 0));
assertEquals(0D, list.getDouble(1));
-
+
try {
list.setDouble(-1, 0);
fail();
@@ -221,7 +208,7 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
+
public void testAdd() {
assertEquals(0, list.size());
@@ -231,29 +218,31 @@ public class DoubleArrayListTest extends TestCase {
assertTrue(list.add(3D));
list.add(0, 4D);
assertEquals(asList(4D, 2D, 3D), list);
-
+
list.add(0, 1D);
list.add(0, 0D);
// Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) {
list.add(Double.valueOf(5 + i));
}
- assertEquals(asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D), list);
-
+ assertEquals(
+ asList(0D, 1D, 4D, 2D, 3D, 5D, 6D, 7D, 8D, 9D, 10D),
+ list);
+
try {
list.add(-1, 5D);
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.add(4, 5D);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
- public void testAddInt() {
+
+ public void testAddDouble() {
assertEquals(0, list.size());
list.addDouble(2);
@@ -262,7 +251,7 @@ public class DoubleArrayListTest extends TestCase {
list.addDouble(3);
assertEquals(asList(2D, 3D), list);
}
-
+
public void testAddAll() {
assertEquals(0, list.size());
@@ -270,17 +259,17 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1D, (double) list.get(0));
assertEquals(1D, list.getDouble(0));
-
+
assertTrue(list.addAll(asList(2D, 3D, 4D, 5D, 6D)));
assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D), list);
-
+
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1D, 2D, 3D, 4D, 5D, 6D, 1D, 2D, 3D), list);
assertFalse(list.addAll(Collections.<Double>emptyList()));
assertFalse(list.addAll(DoubleArrayList.emptyList()));
}
-
+
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1D, (double) list.remove(0));
@@ -294,96 +283,110 @@ public class DoubleArrayListTest extends TestCase {
assertEquals(2D, (double) list.remove(0));
assertEquals(asList(), list);
-
+
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
+ public void testRemoveEndOfCapacity() {
+ DoubleList toRemove = DoubleArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addDouble(3);
+ toRemove.remove(0);
+ assertEquals(0, toRemove.size());
+ }
+
+ public void testSublistRemoveEndOfCapacity() {
+ DoubleList toRemove = DoubleArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addDouble(3);
+ toRemove.subList(0, 1).clear();
+ assertEquals(0, toRemove.size());
+ }
+
private void assertImmutable(DoubleArrayList list) {
if (list.contains(1D)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
-
+
try {
list.add(1D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.add(0, 1D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.singletonList(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(new DoubleArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addDouble(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.clear();
fail();
@@ -397,28 +400,28 @@ public class DoubleArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.<Double>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(UNARY_LIST);
fail();
@@ -432,28 +435,28 @@ public class DoubleArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.singleton(1D));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.set(0, 0D);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.setDouble(0, 0);
fail();
@@ -461,7 +464,7 @@ public class DoubleArrayListTest extends TestCase {
// expected
}
}
-
+
private static DoubleArrayList newImmutableDoubleArrayList(double... elements) {
DoubleArrayList list = new DoubleArrayList();
for (double element : elements) {
diff --git a/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
index 55144e7c..77d14f6b 100644
--- a/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java
@@ -33,14 +33,12 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
-
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestPackedTypes;
-
-import junit.framework.TestCase;
import java.util.Arrays;
+import junit.framework.TestCase;
/**
* Unit test for {@link DynamicMessage}. See also {@link MessageTest}, which
diff --git a/java/core/src/test/java/com/google/protobuf/EnumTest.java b/java/core/src/test/java/com/google/protobuf/EnumTest.java
new file mode 100644
index 00000000..14c7406b
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/EnumTest.java
@@ -0,0 +1,76 @@
+// 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 com.google.protobuf.UnittestLite.ForeignEnumLite;
+import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import protobuf_unittest.UnittestProto.ForeignEnum;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+
+import junit.framework.TestCase;
+
+public class EnumTest extends TestCase {
+
+ public void testForNumber() {
+ ForeignEnum e = ForeignEnum.forNumber(ForeignEnum.FOREIGN_BAR.getNumber());
+ assertEquals(ForeignEnum.FOREIGN_BAR, e);
+
+ e = ForeignEnum.forNumber(1000);
+ assertEquals(null, e);
+ }
+
+ public void testForNumber_oneof() {
+ TestAllTypes.OneofFieldCase e = TestAllTypes.OneofFieldCase.forNumber(
+ TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber());
+ assertEquals(TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE, e);
+
+ e = TestAllTypes.OneofFieldCase.forNumber(1000);
+ assertEquals(null, e);
+ }
+
+ public void testForNumberLite() {
+ ForeignEnumLite e = ForeignEnumLite.forNumber(ForeignEnumLite.FOREIGN_LITE_BAR.getNumber());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, e);
+
+ e = ForeignEnumLite.forNumber(1000);
+ assertEquals(null, e);
+ }
+
+ public void testForNumberLite_oneof() {
+ TestAllTypesLite.OneofFieldCase e = TestAllTypesLite.OneofFieldCase.forNumber(
+ TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber());
+ assertEquals(TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE, e);
+
+ e = TestAllTypesLite.OneofFieldCase.forNumber(1000);
+ assertEquals(null, e);
+ }
+}
+
diff --git a/java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java b/java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java
new file mode 100644
index 00000000..6157e589
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/ExtensionRegistryFactoryTest.java
@@ -0,0 +1,276 @@
+// 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 protobuf_unittest.NonNestedExtension;
+import protobuf_unittest.NonNestedExtensionLite;
+import java.lang.reflect.Method;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for {@link ExtensionRegistryFactory} and the {@link ExtensionRegistry} instances it
+ * creates.
+ *
+ * <p>This test simulates the runtime behaviour of the ExtensionRegistryFactory by delegating test
+ * definitions to two inner classes {@link InnerTest} and {@link InnerLiteTest}, the latter of
+ * which is executed using a custom ClassLoader, simulating the ProtoLite environment.
+ *
+ * <p>The test mechanism employed here is based on the pattern in
+ * {@code com.google.common.util.concurrent.AbstractFutureFallbackAtomicHelperTest}
+ */
+public class ExtensionRegistryFactoryTest extends TestCase {
+
+ // A classloader which blacklists some non-Lite classes.
+ private static final ClassLoader LITE_CLASS_LOADER = getLiteOnlyClassLoader();
+
+ /**
+ * Defines the set of test methods which will be run.
+ */
+ static interface RegistryTests {
+ void testCreate();
+ void testEmpty();
+ void testIsFullRegistry();
+ void testAdd();
+ void testAdd_immutable();
+ }
+
+ /**
+ * Test implementations for the non-Lite usage of ExtensionRegistryFactory.
+ */
+ public static class InnerTest implements RegistryTests {
+
+ @Override
+ public void testCreate() {
+ ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
+
+ assertEquals(registry.getClass(), ExtensionRegistry.class);
+ }
+
+ @Override
+ public void testEmpty() {
+ ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
+
+ assertEquals(emptyRegistry.getClass(), ExtensionRegistry.class);
+ assertEquals(emptyRegistry, ExtensionRegistry.EMPTY_REGISTRY);
+ }
+
+ @Override
+ public void testIsFullRegistry() {
+ ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
+ assertTrue(ExtensionRegistryFactory.isFullRegistry(registry));
+ }
+
+ @Override
+ public void testAdd() {
+ ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance();
+ NonNestedExtensionLite.registerAllExtensions(registry1);
+ registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
+
+ ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance();
+ NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
+ registry2.add(NonNestedExtension.nonNestedExtension);
+
+ ExtensionRegistry fullRegistry1 = (ExtensionRegistry) registry1;
+ ExtensionRegistry fullRegistry2 = (ExtensionRegistry) registry2;
+
+ assertTrue("Test is using a non-lite extension",
+ GeneratedMessageLite.GeneratedExtension.class.isAssignableFrom(
+ NonNestedExtensionLite.nonNestedExtensionLite.getClass()));
+ assertNull("Extension is not registered in masqueraded full registry",
+ fullRegistry1.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
+ GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
+ extension = registry1.findLiteExtensionByNumber(
+ NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
+ assertNotNull("Extension registered in lite registry", extension);
+
+ assertTrue("Test is using a non-lite extension",
+ GeneratedMessage.GeneratedExtension.class.isAssignableFrom(
+ NonNestedExtension.nonNestedExtension.getClass()));
+ assertNotNull("Extension is registered in masqueraded full registry",
+ fullRegistry2.findImmutableExtensionByName("protobuf_unittest.nonNestedExtension"));
+ }
+
+ @Override
+ public void testAdd_immutable() {
+ ExtensionRegistryLite registry1 = ExtensionRegistryLite.newInstance().getUnmodifiable();
+ try {
+ NonNestedExtensionLite.registerAllExtensions(registry1);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+ try {
+ registry1.add(NonNestedExtensionLite.nonNestedExtensionLite);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+
+ ExtensionRegistryLite registry2 = ExtensionRegistryLite.newInstance().getUnmodifiable();
+ try {
+ NonNestedExtension.registerAllExtensions((ExtensionRegistry) registry2);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ try {
+ registry2.add(NonNestedExtension.nonNestedExtension);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ }
+ }
+
+ /**
+ * Test implementations for the Lite usage of ExtensionRegistryFactory.
+ */
+ public static final class InnerLiteTest implements RegistryTests {
+
+ @Override
+ public void testCreate() {
+ ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
+
+ assertEquals(registry.getClass(), ExtensionRegistryLite.class);
+ }
+
+ @Override
+ public void testEmpty() {
+ ExtensionRegistryLite emptyRegistry = ExtensionRegistryFactory.createEmpty();
+
+ assertEquals(emptyRegistry.getClass(), ExtensionRegistryLite.class);
+ assertEquals(emptyRegistry, ExtensionRegistryLite.EMPTY_REGISTRY_LITE);
+ }
+
+ @Override
+ public void testIsFullRegistry() {
+ ExtensionRegistryLite registry = ExtensionRegistryFactory.create();
+ assertFalse(ExtensionRegistryFactory.isFullRegistry(registry));
+ }
+
+ @Override
+ public void testAdd() {
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ NonNestedExtensionLite.registerAllExtensions(registry);
+ GeneratedMessageLite.GeneratedExtension<NonNestedExtensionLite.MessageLiteToBeExtended, ?>
+ extension = registry.findLiteExtensionByNumber(
+ NonNestedExtensionLite.MessageLiteToBeExtended.getDefaultInstance(), 1);
+ assertNotNull("Extension is registered in Lite registry", extension);
+ }
+
+ @Override
+ public void testAdd_immutable() {
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance().getUnmodifiable();
+ try {
+ NonNestedExtensionLite.registerAllExtensions(registry);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+ }
+ }
+
+ /**
+ * Defines a suite of tests which the JUnit3 runner retrieves by reflection.
+ */
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ for (Method method : RegistryTests.class.getMethods()) {
+ suite.addTest(TestSuite.createTest(ExtensionRegistryFactoryTest.class, method.getName()));
+ }
+ return suite;
+ }
+
+ /**
+ * Sequentially runs first the Lite and then the non-Lite test variant via classloader
+ * manipulation.
+ */
+ @Override
+ public void runTest() throws Exception {
+ ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(LITE_CLASS_LOADER);
+ try {
+ runTestMethod(LITE_CLASS_LOADER, InnerLiteTest.class);
+ } finally {
+ Thread.currentThread().setContextClassLoader(storedClassLoader);
+ }
+ try {
+ runTestMethod(storedClassLoader, InnerTest.class);
+ } finally {
+ Thread.currentThread().setContextClassLoader(storedClassLoader);
+ }
+ }
+
+ private void runTestMethod(ClassLoader classLoader, Class<? extends RegistryTests> testClass)
+ throws Exception {
+ classLoader.loadClass(ExtensionRegistryFactory.class.getName());
+ Class<?> test = classLoader.loadClass(testClass.getName());
+ String testName = getName();
+ test.getMethod(testName).invoke(test.newInstance());
+ }
+
+ /**
+ * Constructs a custom ClassLoader blacklisting the classes which are inspected in the SUT
+ * to determine the Lite/non-Lite runtime.
+ */
+ private static ClassLoader getLiteOnlyClassLoader() {
+ ClassLoader testClassLoader = ExtensionRegistryFactoryTest.class.getClassLoader();
+ final Set<String> classNamesNotInLite =
+ Collections.unmodifiableSet(
+ new HashSet<String>(
+ Arrays.asList(
+ ExtensionRegistryFactory.FULL_REGISTRY_CLASS_NAME,
+ ExtensionRegistry.EXTENSION_CLASS_NAME)));
+
+ // Construct a URLClassLoader delegating to the system ClassLoader, and looking up classes
+ // in jar files based on the URLs already configured for this test's UrlClassLoader.
+ // Certain classes throw a ClassNotFoundException by design.
+ return new URLClassLoader(((URLClassLoader) testClassLoader).getURLs(),
+ ClassLoader.getSystemClassLoader()) {
+ @Override
+ public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ if (classNamesNotInLite.contains(name)) {
+ throw new ClassNotFoundException("Class deliberately blacklisted by test.");
+ }
+ Class<?> loadedClass = null;
+ try {
+ loadedClass = findLoadedClass(name);
+ if (loadedClass == null) {
+ loadedClass = findClass(name);
+ if (resolve) {
+ resolveClass(loadedClass);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ loadedClass = super.loadClass(name, resolve);
+ }
+ return loadedClass;
+ }
+ };
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
index eaeec0b8..42da5bb3 100644
--- a/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
+++ b/java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java
@@ -31,6 +31,8 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.EnumDescriptor;
+import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
import com.google.protobuf.FieldPresenceTestProto.TestOptionalFieldsOnly;
@@ -44,9 +46,9 @@ import junit.framework.TestCase;
* non-message fields.
*/
public class FieldPresenceTest extends TestCase {
- private static boolean hasMethod(Class clazz, String name) {
+ private static boolean hasMethod(Class<?> clazz, String name) {
try {
- if (clazz.getMethod(name, new Class[]{}) != null) {
+ if (clazz.getMethod(name) != null) {
return true;
} else {
return false;
@@ -56,90 +58,84 @@ public class FieldPresenceTest extends TestCase {
}
}
- private static boolean isHasMethodRemoved(
- Class classWithFieldPresence,
- Class classWithoutFieldPresence,
+ private static void assertHasMethodRemoved(
+ Class<?> classWithFieldPresence,
+ Class<?> classWithoutFieldPresence,
String camelName) {
- return hasMethod(classWithFieldPresence, "get" + camelName)
- && hasMethod(classWithFieldPresence, "has" + camelName)
- && hasMethod(classWithoutFieldPresence, "get" + camelName)
- && !hasMethod(classWithoutFieldPresence, "has" + camelName);
+ assertTrue(hasMethod(classWithFieldPresence, "get" + camelName));
+ assertTrue(hasMethod(classWithFieldPresence, "has" + camelName));
+ assertTrue(hasMethod(classWithoutFieldPresence, "get" + camelName));
+ assertFalse(hasMethod(classWithoutFieldPresence, "has" + camelName));
}
public void testHasMethod() {
// Optional non-message fields don't have a hasFoo() method generated.
- assertTrue(isHasMethodRemoved(
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OptionalInt32"));
- assertTrue(isHasMethodRemoved(
+ "OptionalInt32");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OptionalString"));
- assertTrue(isHasMethodRemoved(
+ "OptionalString");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OptionalBytes"));
- assertTrue(isHasMethodRemoved(
+ "OptionalBytes");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OptionalNestedEnum"));
+ "OptionalNestedEnum");
- assertTrue(isHasMethodRemoved(
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OptionalInt32"));
- assertTrue(isHasMethodRemoved(
+ "OptionalInt32");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OptionalString"));
- assertTrue(isHasMethodRemoved(
+ "OptionalString");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OptionalBytes"));
- assertTrue(isHasMethodRemoved(
+ "OptionalBytes");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OptionalNestedEnum"));
+ "OptionalNestedEnum");
// message fields still have the hasFoo() method generated.
assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage());
assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage());
- // oneof fields don't have hasFoo() methods (even for message types).
- assertTrue(isHasMethodRemoved(
+ // oneof fields don't have hasFoo() methods for non-message types.
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OneofUint32"));
- assertTrue(isHasMethodRemoved(
+ "OneofUint32");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OneofString"));
- assertTrue(isHasMethodRemoved(
+ "OneofString");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.class,
TestAllTypes.class,
- "OneofBytes"));
- assertTrue(isHasMethodRemoved(
- UnittestProto.TestAllTypes.class,
- TestAllTypes.class,
- "OneofNestedMessage"));
+ "OneofBytes");
+ assertFalse(TestAllTypes.newBuilder().build().hasOneofNestedMessage());
+ assertFalse(TestAllTypes.newBuilder().hasOneofNestedMessage());
- assertTrue(isHasMethodRemoved(
- UnittestProto.TestAllTypes.Builder.class,
- TestAllTypes.Builder.class,
- "OneofUint32"));
- assertTrue(isHasMethodRemoved(
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OneofString"));
- assertTrue(isHasMethodRemoved(
+ "OneofUint32");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OneofBytes"));
- assertTrue(isHasMethodRemoved(
+ "OneofString");
+ assertHasMethodRemoved(
UnittestProto.TestAllTypes.Builder.class,
TestAllTypes.Builder.class,
- "OneofNestedMessage"));
+ "OneofBytes");
}
public void testOneofEquals() throws Exception {
@@ -152,6 +148,26 @@ public class FieldPresenceTest extends TestCase {
assertFalse(message1.equals(message2));
}
+ public void testLazyField() throws Exception {
+ // Test default constructed message.
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ TestAllTypes message = builder.build();
+ assertFalse(message.hasOptionalLazyMessage());
+ assertEquals(0, message.getSerializedSize());
+ assertEquals(ByteString.EMPTY, message.toByteString());
+
+ // Set default instance to the field.
+ builder.setOptionalLazyMessage(TestAllTypes.NestedMessage.getDefaultInstance());
+ message = builder.build();
+ assertTrue(message.hasOptionalLazyMessage());
+ assertEquals(2, message.getSerializedSize());
+
+ // Test parse zero-length from wire sets the presence.
+ TestAllTypes parsed = TestAllTypes.parseFrom(message.toByteString());
+ assertTrue(parsed.hasOptionalLazyMessage());
+ assertEquals(message.getOptionalLazyMessage(), parsed.getOptionalLazyMessage());
+ }
+
public void testFieldPresence() {
// Optional non-message fields set to their default value are treated the
// same way as not set.
@@ -232,24 +248,72 @@ public class FieldPresenceTest extends TestCase {
assertTrue(message.hasField(optionalNestedEnumField));
assertEquals(4, message.getAllFields().size());
}
-
+
+ public void testFieldPresenceDynamicMessage() {
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ FieldDescriptor optionalInt32Field = descriptor.findFieldByName("optional_int32");
+ FieldDescriptor optionalStringField = descriptor.findFieldByName("optional_string");
+ FieldDescriptor optionalBytesField = descriptor.findFieldByName("optional_bytes");
+ FieldDescriptor optionalNestedEnumField = descriptor.findFieldByName("optional_nested_enum");
+ EnumDescriptor enumDescriptor = optionalNestedEnumField.getEnumType();
+ EnumValueDescriptor defaultEnumValueDescriptor = enumDescriptor.getValues().get(0);
+ EnumValueDescriptor nonDefaultEnumValueDescriptor = enumDescriptor.getValues().get(1);
+
+ DynamicMessage defaultInstance = DynamicMessage.getDefaultInstance(descriptor);
+ // Field not present.
+ DynamicMessage message = defaultInstance.newBuilderForType().build();
+ assertFalse(message.hasField(optionalInt32Field));
+ assertFalse(message.hasField(optionalStringField));
+ assertFalse(message.hasField(optionalBytesField));
+ assertFalse(message.hasField(optionalNestedEnumField));
+ assertEquals(0, message.getAllFields().size());
+
+ // Field set to non-default value is seen as present.
+ message =
+ defaultInstance
+ .newBuilderForType()
+ .setField(optionalInt32Field, 1)
+ .setField(optionalStringField, "x")
+ .setField(optionalBytesField, ByteString.copyFromUtf8("y"))
+ .setField(optionalNestedEnumField, nonDefaultEnumValueDescriptor)
+ .build();
+ assertTrue(message.hasField(optionalInt32Field));
+ assertTrue(message.hasField(optionalStringField));
+ assertTrue(message.hasField(optionalBytesField));
+ assertTrue(message.hasField(optionalNestedEnumField));
+ assertEquals(4, message.getAllFields().size());
+
+ // Field set to default value is seen as not present.
+ message = message.toBuilder()
+ .setField(optionalInt32Field, 0)
+ .setField(optionalStringField, "")
+ .setField(optionalBytesField, ByteString.EMPTY)
+ .setField(optionalNestedEnumField, defaultEnumValueDescriptor)
+ .build();
+ assertFalse(message.hasField(optionalInt32Field));
+ assertFalse(message.hasField(optionalStringField));
+ assertFalse(message.hasField(optionalBytesField));
+ assertFalse(message.hasField(optionalNestedEnumField));
+ assertEquals(0, message.getAllFields().size());
+ }
+
public void testMessageField() {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
assertFalse(builder.hasOptionalNestedMessage());
assertFalse(builder.build().hasOptionalNestedMessage());
-
+
TestAllTypes.NestedMessage.Builder nestedBuilder =
builder.getOptionalNestedMessageBuilder();
assertTrue(builder.hasOptionalNestedMessage());
assertTrue(builder.build().hasOptionalNestedMessage());
-
+
nestedBuilder.setValue(1);
assertEquals(1, builder.build().getOptionalNestedMessage().getValue());
-
+
builder.clearOptionalNestedMessage();
assertFalse(builder.hasOptionalNestedMessage());
assertFalse(builder.build().hasOptionalNestedMessage());
-
+
// Unlike non-message fields, if we set a message field to its default value (i.e.,
// default instance), the field should be seen as present.
builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
@@ -340,34 +404,4 @@ public class FieldPresenceTest extends TestCase {
assertTrue(builder.buildPartial().isInitialized());
}
-
- // Test that unknown fields are dropped.
- public void testUnknownFields() throws Exception {
- TestAllTypes.Builder builder = TestAllTypes.newBuilder();
- builder.setOptionalInt32(1234);
- builder.addRepeatedInt32(5678);
- TestAllTypes message = builder.build();
- ByteString data = message.toByteString();
-
- TestOptionalFieldsOnly optionalOnlyMessage =
- TestOptionalFieldsOnly.parseFrom(data);
- // UnknownFieldSet should be empty.
- assertEquals(
- 0, optionalOnlyMessage.getUnknownFields().toByteString().size());
- assertEquals(1234, optionalOnlyMessage.getOptionalInt32());
- message = TestAllTypes.parseFrom(optionalOnlyMessage.toByteString());
- assertEquals(1234, message.getOptionalInt32());
- // The repeated field is discarded because it's unknown to the optional-only
- // message.
- assertEquals(0, message.getRepeatedInt32Count());
-
- DynamicMessage dynamicOptionalOnlyMessage =
- DynamicMessage.getDefaultInstance(
- TestOptionalFieldsOnly.getDescriptor())
- .getParserForType().parseFrom(data);
- assertEquals(
- 0, dynamicOptionalOnlyMessage.getUnknownFields().toByteString().size());
- assertEquals(optionalOnlyMessage.toByteString(),
- dynamicOptionalOnlyMessage.toByteString());
- }
}
diff --git a/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
index a5e65424..903a79db 100644
--- a/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java
@@ -32,61 +32,48 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
+import com.google.protobuf.Internal.FloatList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import junit.framework.TestCase;
/**
* Tests for {@link FloatArrayList}.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
public class FloatArrayListTest extends TestCase {
-
- private static final FloatArrayList UNARY_LIST = newImmutableFloatArrayList(1);
+
+ private static final FloatArrayList UNARY_LIST =
+ newImmutableFloatArrayList(1);
private static final FloatArrayList TERTIARY_LIST =
newImmutableFloatArrayList(1, 2, 3);
-
+
private FloatArrayList list;
-
+
@Override
protected void setUp() throws Exception {
list = new FloatArrayList();
}
-
+
public void testEmptyListReturnsSameInstance() {
assertSame(FloatArrayList.emptyList(), FloatArrayList.emptyList());
}
-
+
public void testEmptyListIsImmutable() {
assertImmutable(FloatArrayList.emptyList());
}
-
+
public void testMakeImmutable() {
- list.addFloat(2);
+ list.addFloat(3);
list.addFloat(4);
- list.addFloat(6);
- list.addFloat(8);
+ list.addFloat(5);
+ list.addFloat(7);
list.makeImmutable();
assertImmutable(list);
}
-
- public void testCopyConstructor() {
- FloatArrayList copy = new FloatArrayList(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new FloatArrayList(FloatArrayList.emptyList());
- assertEquals(FloatArrayList.emptyList(), copy);
-
- copy = new FloatArrayList(asList(1F, 2F, 3F));
- assertEquals(asList(1F, 2F, 3F), copy);
-
- copy = new FloatArrayList(Collections.<Float>emptyList());
- assertEquals(FloatArrayList.emptyList(), copy);
- }
-
+
public void testModificationWithIteration() {
list.addAll(asList(1F, 2F, 3F, 4F));
Iterator<Float> iterator = list.iterator();
@@ -95,7 +82,7 @@ public class FloatArrayListTest extends TestCase {
assertEquals(1F, (float) iterator.next());
list.set(0, 1F);
assertEquals(2F, (float) iterator.next());
-
+
list.remove(0);
try {
iterator.next();
@@ -103,7 +90,7 @@ public class FloatArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
-
+
iterator = list.iterator();
list.add(0, 0F);
try {
@@ -113,19 +100,19 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGet() {
assertEquals(1F, (float) TERTIARY_LIST.get(0));
assertEquals(2F, (float) TERTIARY_LIST.get(1));
assertEquals(3F, (float) TERTIARY_LIST.get(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -133,19 +120,19 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGetFloat() {
assertEquals(1F, TERTIARY_LIST.getFloat(0));
assertEquals(2F, TERTIARY_LIST.getFloat(1));
assertEquals(3F, TERTIARY_LIST.getFloat(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -153,35 +140,35 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSize() {
assertEquals(0, FloatArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size());
- list.addFloat(2);
+ list.addFloat(3);
list.addFloat(4);
list.addFloat(6);
list.addFloat(8);
assertEquals(4, list.size());
-
+
list.remove(0);
assertEquals(3, list.size());
-
- list.add(16F);
+
+ list.add(17F);
assertEquals(4, list.size());
}
-
+
public void testSet() {
list.addFloat(2);
list.addFloat(4);
-
- assertEquals(2F, (float) list.set(0, 0F));
- assertEquals(0F, list.getFloat(0));
+
+ assertEquals(2F, (float) list.set(0, 3F));
+ assertEquals(3F, list.getFloat(0));
assertEquals(4F, (float) list.set(1, 0F));
assertEquals(0F, list.getFloat(1));
-
+
try {
list.set(-1, 0F);
fail();
@@ -196,17 +183,17 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSetFloat() {
- list.addFloat(2);
- list.addFloat(4);
-
- assertEquals(2F, list.setFloat(0, 0));
+ list.addFloat(1);
+ list.addFloat(3);
+
+ assertEquals(1F, list.setFloat(0, 0));
assertEquals(0F, list.getFloat(0));
- assertEquals(4F, list.setFloat(1, 0));
+ assertEquals(3F, list.setFloat(1, 0));
assertEquals(0F, list.getFloat(1));
-
+
try {
list.setFloat(-1, 0);
fail();
@@ -221,7 +208,7 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
+
public void testAdd() {
assertEquals(0, list.size());
@@ -231,28 +218,30 @@ public class FloatArrayListTest extends TestCase {
assertTrue(list.add(3F));
list.add(0, 4F);
assertEquals(asList(4F, 2F, 3F), list);
-
+
list.add(0, 1F);
list.add(0, 0F);
// Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) {
list.add(Float.valueOf(5 + i));
}
- assertEquals(asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F), list);
-
+ assertEquals(
+ asList(0F, 1F, 4F, 2F, 3F, 5F, 6F, 7F, 8F, 9F, 10F),
+ list);
+
try {
list.add(-1, 5F);
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.add(4, 5F);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
public void testAddFloat() {
assertEquals(0, list.size());
@@ -262,7 +251,7 @@ public class FloatArrayListTest extends TestCase {
list.addFloat(3);
assertEquals(asList(2F, 3F), list);
}
-
+
public void testAddAll() {
assertEquals(0, list.size());
@@ -270,17 +259,17 @@ public class FloatArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1F, (float) list.get(0));
assertEquals(1F, list.getFloat(0));
-
+
assertTrue(list.addAll(asList(2F, 3F, 4F, 5F, 6F)));
assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F), list);
-
+
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1F, 2F, 3F, 4F, 5F, 6F, 1F, 2F, 3F), list);
assertFalse(list.addAll(Collections.<Float>emptyList()));
assertFalse(list.addAll(FloatArrayList.emptyList()));
}
-
+
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1F, (float) list.remove(0));
@@ -294,96 +283,110 @@ public class FloatArrayListTest extends TestCase {
assertEquals(2F, (float) list.remove(0));
assertEquals(asList(), list);
-
+
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
+ public void testRemoveEndOfCapacity() {
+ FloatList toRemove = FloatArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addFloat(3);
+ toRemove.remove(0);
+ assertEquals(0, toRemove.size());
+ }
+
+ public void testSublistRemoveEndOfCapacity() {
+ FloatList toRemove = FloatArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addFloat(3);
+ toRemove.subList(0, 1).clear();
+ assertEquals(0, toRemove.size());
+ }
+
private void assertImmutable(FloatArrayList list) {
if (list.contains(1F)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
-
+
try {
list.add(1F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.add(0, 1F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.singletonList(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(new FloatArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addFloat(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.clear();
fail();
@@ -397,63 +400,63 @@ public class FloatArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.<Float>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.singleton(1F));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.set(0, 0F);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.setFloat(0, 0);
fail();
@@ -461,10 +464,10 @@ public class FloatArrayListTest extends TestCase {
// expected
}
}
-
- private static FloatArrayList newImmutableFloatArrayList(int... elements) {
+
+ private static FloatArrayList newImmutableFloatArrayList(float... elements) {
FloatArrayList list = new FloatArrayList();
- for (int element : elements) {
+ for (float element : elements) {
list.addFloat(element);
}
list.makeImmutable();
diff --git a/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
index a92ba374..b7eaebf5 100644
--- a/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
+++ b/java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java
@@ -41,7 +41,7 @@ package com.google.protobuf;
*/
public class ForceFieldBuildersPreRun implements Runnable {
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void run() {
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
}
diff --git a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 70812b95..c9ebe7f5 100644
--- a/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -32,19 +32,14 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
-import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
import com.google.protobuf.test.UnittestImport;
import protobuf_unittest.EnumWithNoOuter;
import protobuf_unittest.MessageWithNoOuter;
import protobuf_unittest.MultipleFilesTestProto;
import protobuf_unittest.NestedExtension.MyNestedExtension;
-import protobuf_unittest.NestedExtensionLite.MyNestedExtensionLite;
import protobuf_unittest.NonNestedExtension;
import protobuf_unittest.NonNestedExtension.MessageToBeExtended;
import protobuf_unittest.NonNestedExtension.MyNonNestedExtension;
-import protobuf_unittest.NonNestedExtensionLite;
-import protobuf_unittest.NonNestedExtensionLite.MessageLiteToBeExtended;
-import protobuf_unittest.NonNestedExtensionLite.MyNonNestedExtensionLite;
import protobuf_unittest.OuterClassNameTest2OuterClass;
import protobuf_unittest.OuterClassNameTest3OuterClass;
import protobuf_unittest.OuterClassNameTestOuterClass;
@@ -65,9 +60,6 @@ import protobuf_unittest.UnittestProto.TestExtremeDefaultValues;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
@@ -76,6 +68,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import junit.framework.TestCase;
/**
* Unit test for generated messages and generated code. See also
@@ -620,6 +613,21 @@ public class GeneratedMessageTest extends TestCase {
TestUtil.assertExtensionsClear(TestAllExtensions.newBuilder().build());
}
+ public void testUnsetRepeatedExtensionGetField() {
+ TestAllExtensions message = TestAllExtensions.getDefaultInstance();
+ Object value;
+
+ value = message.getField(UnittestProto.repeatedStringExtension.getDescriptor());
+ assertTrue(value instanceof List);
+ assertTrue(((List<?>) value).isEmpty());
+ assertIsUnmodifiable((List<?>) value);
+
+ value = message.getField(UnittestProto.repeatedNestedMessageExtension.getDescriptor());
+ assertTrue(value instanceof List);
+ assertTrue(((List<?>) value).isEmpty());
+ assertIsUnmodifiable((List<?>) value);
+ }
+
public void testExtensionReflectionGetters() throws Exception {
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
TestUtil.setAllExtensions(builder);
@@ -699,70 +707,6 @@ public class GeneratedMessageTest extends TestCase {
}
// =================================================================
- // Lite Extensions.
-
- // We test lite extensions directly because they have a separate
- // implementation from full extensions. In contrast, we do not test
- // lite fields directly since they are implemented exactly the same as
- // regular fields.
-
- public void testLiteExtensionMessageOrBuilder() throws Exception {
- TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
- TestUtil.setAllExtensions(builder);
- TestUtil.assertAllExtensionsSet(builder);
-
- TestAllExtensionsLite message = builder.build();
- TestUtil.assertAllExtensionsSet(message);
- }
-
- public void testLiteExtensionRepeatedSetters() throws Exception {
- TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
- TestUtil.setAllExtensions(builder);
- TestUtil.modifyRepeatedExtensions(builder);
- TestUtil.assertRepeatedExtensionsModified(builder);
-
- TestAllExtensionsLite message = builder.build();
- TestUtil.assertRepeatedExtensionsModified(message);
- }
-
- public void testLiteExtensionDefaults() throws Exception {
- TestUtil.assertExtensionsClear(TestAllExtensionsLite.getDefaultInstance());
- TestUtil.assertExtensionsClear(TestAllExtensionsLite.newBuilder().build());
- }
-
- public void testClearLiteExtension() throws Exception {
- // clearExtension() is not actually used in TestUtil, so try it manually.
- assertFalse(
- TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 1)
- .clearExtension(UnittestLite.optionalInt32ExtensionLite)
- .hasExtension(UnittestLite.optionalInt32ExtensionLite));
- assertEquals(0,
- TestAllExtensionsLite.newBuilder()
- .addExtension(UnittestLite.repeatedInt32ExtensionLite, 1)
- .clearExtension(UnittestLite.repeatedInt32ExtensionLite)
- .getExtensionCount(UnittestLite.repeatedInt32ExtensionLite));
- }
-
- public void testLiteExtensionCopy() throws Exception {
- TestAllExtensionsLite original = TestUtil.getAllLiteExtensionsSet();
- TestAllExtensionsLite copy =
- TestAllExtensionsLite.newBuilder(original).build();
- TestUtil.assertAllExtensionsSet(copy);
- }
-
- public void testLiteExtensionMergeFrom() throws Exception {
- TestAllExtensionsLite original =
- TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 1).build();
- TestAllExtensionsLite merged =
- TestAllExtensionsLite.newBuilder().mergeFrom(original).build();
- assertTrue(merged.hasExtension(UnittestLite.optionalInt32ExtensionLite));
- assertEquals(
- 1, (int) merged.getExtension(UnittestLite.optionalInt32ExtensionLite));
- }
-
- // =================================================================
// multiple_files_test
// Test that custom options of an file level enum are properly initialized.
@@ -910,15 +854,9 @@ public class GeneratedMessageTest extends TestCase {
}
public void testEnumValues() {
- assertEquals(
- TestAllTypes.NestedEnum.BAR.getNumber(),
- TestAllTypes.NestedEnum.BAR_VALUE);
- assertEquals(
- TestAllTypes.NestedEnum.BAZ.getNumber(),
- TestAllTypes.NestedEnum.BAZ_VALUE);
- assertEquals(
- TestAllTypes.NestedEnum.FOO.getNumber(),
- TestAllTypes.NestedEnum.FOO_VALUE);
+ assertEquals(TestAllTypes.NestedEnum.BAR_VALUE, TestAllTypes.NestedEnum.BAR.getNumber());
+ assertEquals(TestAllTypes.NestedEnum.BAZ_VALUE, TestAllTypes.NestedEnum.BAZ.getNumber());
+ assertEquals(TestAllTypes.NestedEnum.FOO_VALUE, TestAllTypes.NestedEnum.FOO.getNumber());
}
public void testNonNestedExtensionInitialization() {
@@ -935,16 +873,6 @@ public class GeneratedMessageTest extends TestCase {
MyNestedExtension.recursiveExtension.getDescriptor().getName());
}
- public void testNonNestedExtensionLiteInitialization() {
- assertTrue(NonNestedExtensionLite.nonNestedExtensionLite
- .getMessageDefaultInstance() instanceof MyNonNestedExtensionLite);
- }
-
- public void testNestedExtensionLiteInitialization() {
- assertTrue(MyNestedExtensionLite.recursiveExtensionLite
- .getMessageDefaultInstance() instanceof MessageLiteToBeExtended);
- }
-
public void testInvalidations() throws Exception {
GeneratedMessage.enableAlwaysUseFieldBuildersForTesting();
TestAllTypes.NestedMessage nestedMessage1 =
@@ -957,7 +885,7 @@ public class GeneratedMessageTest extends TestCase {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
TestAllTypes.Builder builder = (TestAllTypes.Builder)
- ((GeneratedMessage) TestAllTypes.getDefaultInstance()).
+ ((AbstractMessage) TestAllTypes.getDefaultInstance()).
newBuilderForType(mockParent);
builder.setOptionalInt32(1);
builder.setOptionalNestedEnum(TestAllTypes.NestedEnum.BAR);
@@ -1012,7 +940,7 @@ public class GeneratedMessageTest extends TestCase {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
TestAllExtensions.Builder builder = (TestAllExtensions.Builder)
- ((GeneratedMessage) TestAllExtensions.getDefaultInstance()).
+ ((AbstractMessage) TestAllExtensions.getDefaultInstance()).
newBuilderForType(mockParent);
builder.addExtension(UnittestProto.repeatedInt32Extension, 1);
@@ -1306,51 +1234,51 @@ public class GeneratedMessageTest extends TestCase {
assertFalse(builder.clearFooInt().hasFooInt());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooInt());
- assertEquals(message2.getFooInt(), 0);
+ assertEquals(0, message2.getFooInt());
}
// Enum
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
- assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.FOO);
+ assertEquals(TestOneof2.NestedEnum.FOO, builder.getFooEnum());
assertTrue(builder.setFooEnum(TestOneof2.NestedEnum.BAR).hasFooEnum());
- assertEquals(builder.getFooEnum(), TestOneof2.NestedEnum.BAR);
+ assertEquals(TestOneof2.NestedEnum.BAR, builder.getFooEnum());
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooEnum());
- assertEquals(message.getFooEnum(), TestOneof2.NestedEnum.BAR);
+ assertEquals(TestOneof2.NestedEnum.BAR, message.getFooEnum());
assertFalse(builder.clearFooEnum().hasFooEnum());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooEnum());
- assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.FOO);
+ assertEquals(TestOneof2.NestedEnum.FOO, message2.getFooEnum());
}
// String
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
- assertEquals(builder.getFooString(), "");
+ assertEquals("", builder.getFooString());
builder.setFooString("foo");
assertTrue(builder.hasFooString());
- assertEquals(builder.getFooString(), "foo");
+ assertEquals("foo", builder.getFooString());
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooString());
- assertEquals(message.getFooString(), "foo");
+ assertEquals("foo", message.getFooString());
assertEquals(message.getFooStringBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooString().hasFooString());
TestOneof2 message2 = builder.buildPartial();
assertFalse(message2.hasFooString());
- assertEquals(message2.getFooString(), "");
+ assertEquals("", message2.getFooString());
assertEquals(message2.getFooStringBytes(), TestUtil.toBytes(""));
// Get method should not change the oneof value.
builder.setFooInt(123);
- assertEquals(builder.getFooString(), "");
+ assertEquals("", builder.getFooString());
assertEquals(builder.getFooStringBytes(), TestUtil.toBytes(""));
assertEquals(123, builder.getFooInt());
message = builder.build();
- assertEquals(message.getFooString(), "");
+ assertEquals("", message.getFooString());
assertEquals(message.getFooStringBytes(), TestUtil.toBytes(""));
assertEquals(123, message.getFooInt());
}
@@ -1358,38 +1286,38 @@ public class GeneratedMessageTest extends TestCase {
// Cord
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
- assertEquals(builder.getFooCord(), "");
+ assertEquals("", builder.getFooCord());
builder.setFooCord("foo");
assertTrue(builder.hasFooCord());
- assertEquals(builder.getFooCord(), "foo");
+ assertEquals("foo", builder.getFooCord());
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooCord());
- assertEquals(message.getFooCord(), "foo");
+ assertEquals("foo", message.getFooCord());
assertEquals(message.getFooCordBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooCord().hasFooCord());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooCord());
- assertEquals(message2.getFooCord(), "");
+ assertEquals("", message2.getFooCord());
assertEquals(message2.getFooCordBytes(), TestUtil.toBytes(""));
}
// StringPiece
{
TestOneof2.Builder builder = TestOneof2.newBuilder();
- assertEquals(builder.getFooStringPiece(), "");
+ assertEquals("", builder.getFooStringPiece());
builder.setFooStringPiece("foo");
assertTrue(builder.hasFooStringPiece());
- assertEquals(builder.getFooStringPiece(), "foo");
+ assertEquals("foo", builder.getFooStringPiece());
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooStringPiece());
- assertEquals(message.getFooStringPiece(), "foo");
+ assertEquals("foo", message.getFooStringPiece());
assertEquals(message.getFooStringPieceBytes(), TestUtil.toBytes("foo"));
assertFalse(builder.clearFooStringPiece().hasFooStringPiece());
TestOneof2 message2 = builder.build();
assertFalse(message2.hasFooStringPiece());
- assertEquals(message2.getFooStringPiece(), "");
+ assertEquals("", message2.getFooStringPiece());
assertEquals(message2.getFooStringPieceBytes(), TestUtil.toBytes(""));
}
@@ -1397,20 +1325,20 @@ public class GeneratedMessageTest extends TestCase {
{
// set
TestOneof2.Builder builder = TestOneof2.newBuilder();
- assertEquals(builder.getFooMessage().getQuxInt(), 0);
+ assertEquals(0, builder.getFooMessage().getQuxInt());
builder.setFooMessage(
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build());
assertTrue(builder.hasFooMessage());
- assertEquals(builder.getFooMessage().getQuxInt(), 234);
+ assertEquals(234, builder.getFooMessage().getQuxInt());
TestOneof2 message = builder.buildPartial();
assertTrue(message.hasFooMessage());
- assertEquals(message.getFooMessage().getQuxInt(), 234);
+ assertEquals(234, message.getFooMessage().getQuxInt());
// clear
assertFalse(builder.clearFooMessage().hasFooString());
message = builder.build();
assertFalse(message.hasFooMessage());
- assertEquals(message.getFooMessage().getQuxInt(), 0);
+ assertEquals(0, message.getFooMessage().getQuxInt());
// nested builder
builder = TestOneof2.newBuilder();
@@ -1419,10 +1347,10 @@ public class GeneratedMessageTest extends TestCase {
assertFalse(builder.hasFooMessage());
builder.getFooMessageBuilder().setQuxInt(123);
assertTrue(builder.hasFooMessage());
- assertEquals(builder.getFooMessage().getQuxInt(), 123);
+ assertEquals(123, builder.getFooMessage().getQuxInt());
message = builder.build();
assertTrue(message.hasFooMessage());
- assertEquals(message.getFooMessage().getQuxInt(), 123);
+ assertEquals(123, message.getFooMessage().getQuxInt());
}
// LazyMessage is tested in LazyMessageLiteTest.java
@@ -1435,7 +1363,7 @@ public class GeneratedMessageTest extends TestCase {
TestOneof2 message = builder.setFooInt(123).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooInt());
- assertEquals(message2.getFooInt(), 123);
+ assertEquals(123, message2.getFooInt());
}
// String
@@ -1444,7 +1372,7 @@ public class GeneratedMessageTest extends TestCase {
TestOneof2 message = builder.setFooString("foo").build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooString());
- assertEquals(message2.getFooString(), "foo");
+ assertEquals("foo", message2.getFooString());
}
// Enum
@@ -1453,7 +1381,7 @@ public class GeneratedMessageTest extends TestCase {
TestOneof2 message = builder.setFooEnum(TestOneof2.NestedEnum.BAR).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooEnum());
- assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
+ assertEquals(TestOneof2.NestedEnum.BAR, message2.getFooEnum());
}
// Message
@@ -1463,7 +1391,7 @@ public class GeneratedMessageTest extends TestCase {
TestOneof2.NestedMessage.newBuilder().setQuxInt(234).build()).build();
TestOneof2 message2 = TestOneof2.newBuilder().mergeFrom(message).build();
assertTrue(message2.hasFooMessage());
- assertEquals(message2.getFooMessage().getQuxInt(), 234);
+ assertEquals(234, message2.getFooMessage().getQuxInt());
}
}
@@ -1475,7 +1403,7 @@ public class GeneratedMessageTest extends TestCase {
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooInt());
- assertEquals(message2.getFooInt(), 123);
+ assertEquals(123, message2.getFooInt());
}
// String
@@ -1485,7 +1413,7 @@ public class GeneratedMessageTest extends TestCase {
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooString());
- assertEquals(message2.getFooString(), "foo");
+ assertEquals("foo", message2.getFooString());
}
// Enum
@@ -1495,7 +1423,7 @@ public class GeneratedMessageTest extends TestCase {
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooEnum());
- assertEquals(message2.getFooEnum(), TestOneof2.NestedEnum.BAR);
+ assertEquals(TestOneof2.NestedEnum.BAR, message2.getFooEnum());
}
// Message
@@ -1506,7 +1434,7 @@ public class GeneratedMessageTest extends TestCase {
ByteString serialized = message.toByteString();
TestOneof2 message2 = TestOneof2.parseFrom(serialized);
assertTrue(message2.hasFooMessage());
- assertEquals(message2.getFooMessage().getQuxInt(), 234);
+ assertEquals(234, message2.getFooMessage().getQuxInt());
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
index 3733eb30..d8e97d4f 100644
--- a/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/IntArrayListTest.java
@@ -32,60 +32,47 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
+import com.google.protobuf.Internal.IntList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import junit.framework.TestCase;
/**
* Tests for {@link IntArrayList}.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
public class IntArrayListTest extends TestCase {
-
- private static final IntArrayList UNARY_LIST = newImmutableIntArrayList(1);
+
+ private static final IntArrayList UNARY_LIST =
+ newImmutableIntArrayList(1);
private static final IntArrayList TERTIARY_LIST =
newImmutableIntArrayList(1, 2, 3);
-
+
private IntArrayList list;
-
+
@Override
protected void setUp() throws Exception {
list = new IntArrayList();
}
-
+
public void testEmptyListReturnsSameInstance() {
assertSame(IntArrayList.emptyList(), IntArrayList.emptyList());
}
-
+
public void testEmptyListIsImmutable() {
assertImmutable(IntArrayList.emptyList());
}
-
+
public void testMakeImmutable() {
- list.addInt(2);
+ list.addInt(3);
list.addInt(4);
- list.addInt(6);
- list.addInt(8);
+ list.addInt(5);
+ list.addInt(7);
list.makeImmutable();
assertImmutable(list);
}
-
- public void testCopyConstructor() {
- IntArrayList copy = new IntArrayList(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new IntArrayList(IntArrayList.emptyList());
- assertEquals(IntArrayList.emptyList(), copy);
-
- copy = new IntArrayList(asList(1, 2, 3));
- assertEquals(asList(1, 2, 3), copy);
-
- copy = new IntArrayList(Collections.<Integer>emptyList());
- assertEquals(IntArrayList.emptyList(), copy);
- }
public void testModificationWithIteration() {
list.addAll(asList(1, 2, 3, 4));
@@ -95,7 +82,7 @@ public class IntArrayListTest extends TestCase {
assertEquals(1, (int) iterator.next());
list.set(0, 1);
assertEquals(2, (int) iterator.next());
-
+
list.remove(0);
try {
iterator.next();
@@ -113,19 +100,19 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGet() {
assertEquals(1, (int) TERTIARY_LIST.get(0));
assertEquals(2, (int) TERTIARY_LIST.get(1));
assertEquals(3, (int) TERTIARY_LIST.get(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -133,19 +120,19 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGetInt() {
assertEquals(1, TERTIARY_LIST.getInt(0));
assertEquals(2, TERTIARY_LIST.getInt(1));
assertEquals(3, TERTIARY_LIST.getInt(2));
-
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -153,35 +140,35 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSize() {
assertEquals(0, IntArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size());
- list.addInt(2);
+ list.addInt(3);
list.addInt(4);
list.addInt(6);
list.addInt(8);
assertEquals(4, list.size());
-
+
list.remove(0);
assertEquals(3, list.size());
-
- list.add(16);
+
+ list.add(17);
assertEquals(4, list.size());
}
-
+
public void testSet() {
list.addInt(2);
list.addInt(4);
-
- assertEquals(2, (int) list.set(0, 0));
- assertEquals(0, list.getInt(0));
+
+ assertEquals(2, (int) list.set(0, 3));
+ assertEquals(3, list.getInt(0));
assertEquals(4, (int) list.set(1, 0));
assertEquals(0, list.getInt(1));
-
+
try {
list.set(-1, 0);
fail();
@@ -196,17 +183,17 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSetInt() {
- list.addInt(2);
- list.addInt(4);
-
- assertEquals(2, list.setInt(0, 0));
+ list.addInt(1);
+ list.addInt(3);
+
+ assertEquals(1, list.setInt(0, 0));
assertEquals(0, list.getInt(0));
- assertEquals(4, list.setInt(1, 0));
+ assertEquals(3, list.setInt(1, 0));
assertEquals(0, list.getInt(1));
-
+
try {
list.setInt(-1, 0);
fail();
@@ -221,7 +208,7 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
public void testAdd() {
assertEquals(0, list.size());
@@ -231,28 +218,30 @@ public class IntArrayListTest extends TestCase {
assertTrue(list.add(3));
list.add(0, 4);
assertEquals(asList(4, 2, 3), list);
-
+
list.add(0, 1);
list.add(0, 0);
// Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) {
- list.add(5 + i);
+ list.add(Integer.valueOf(5 + i));
}
- assertEquals(asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10), list);
-
+ assertEquals(
+ asList(0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10),
+ list);
+
try {
list.add(-1, 5);
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.add(4, 5);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
public void testAddInt() {
assertEquals(0, list.size());
@@ -262,7 +251,7 @@ public class IntArrayListTest extends TestCase {
list.addInt(3);
assertEquals(asList(2, 3), list);
}
-
+
public void testAddAll() {
assertEquals(0, list.size());
@@ -270,17 +259,17 @@ public class IntArrayListTest extends TestCase {
assertEquals(1, list.size());
assertEquals(1, (int) list.get(0));
assertEquals(1, list.getInt(0));
-
+
assertTrue(list.addAll(asList(2, 3, 4, 5, 6)));
assertEquals(asList(1, 2, 3, 4, 5, 6), list);
-
+
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1, 2, 3, 4, 5, 6, 1, 2, 3), list);
assertFalse(list.addAll(Collections.<Integer>emptyList()));
assertFalse(list.addAll(IntArrayList.emptyList()));
}
-
+
public void testRemove() {
list.addAll(TERTIARY_LIST);
assertEquals(1, (int) list.remove(0));
@@ -294,96 +283,110 @@ public class IntArrayListTest extends TestCase {
assertEquals(2, (int) list.remove(0));
assertEquals(asList(), list);
-
+
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
+ public void testRemoveEndOfCapacity() {
+ IntList toRemove = IntArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addInt(3);
+ toRemove.remove(0);
+ assertEquals(0, toRemove.size());
+ }
+
+ public void testSublistRemoveEndOfCapacity() {
+ IntList toRemove = IntArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addInt(3);
+ toRemove.subList(0, 1).clear();
+ assertEquals(0, toRemove.size());
+ }
+
private void assertImmutable(IntArrayList list) {
if (list.contains(1)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
-
+
try {
list.add(1);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.add(0, 1);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.singletonList(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(new IntArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addInt(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.clear();
fail();
@@ -397,63 +400,63 @@ public class IntArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.<Integer>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.singleton(1));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.set(0, 0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.setInt(0, 0);
fail();
@@ -461,7 +464,7 @@ public class IntArrayListTest extends TestCase {
// expected
}
}
-
+
private static IntArrayList newImmutableIntArrayList(int... elements) {
IntArrayList list = new IntArrayList();
for (int element : elements) {
diff --git a/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java
index 8751baae..756049b4 100644
--- a/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java
+++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java
@@ -30,12 +30,18 @@
package com.google.protobuf;
+import static com.google.protobuf.IsValidUtf8TestUtil.DIRECT_NIO_FACTORY;
+import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT;
+import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT;
+import static com.google.protobuf.IsValidUtf8TestUtil.HEAP_NIO_FACTORY;
+import static com.google.protobuf.IsValidUtf8TestUtil.LITERAL_FACTORY;
+import static com.google.protobuf.IsValidUtf8TestUtil.testBytes;
+
+import com.google.protobuf.IsValidUtf8TestUtil.ByteStringFactory;
import com.google.protobuf.IsValidUtf8TestUtil.Shard;
import junit.framework.TestCase;
-import java.io.UnsupportedEncodingException;
-
/**
* Tests cases for {@link ByteString#isValidUtf8()}. This includes three
* brute force tests that actually test every permutation of one byte, two byte,
@@ -51,31 +57,33 @@ import java.io.UnsupportedEncodingException;
* @author martinrb@google.com (Martin Buchholz)
*/
public class IsValidUtf8Test extends TestCase {
-
/**
* Tests that round tripping of all two byte permutations work.
*/
- public void testIsValidUtf8_1Byte() throws UnsupportedEncodingException {
- IsValidUtf8TestUtil.testBytes(1,
- IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
+ public void testIsValidUtf8_1Byte() {
+ testBytes(LITERAL_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(HEAP_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(DIRECT_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
}
/**
* Tests that round tripping of all two byte permutations work.
*/
- public void testIsValidUtf8_2Bytes() throws UnsupportedEncodingException {
- IsValidUtf8TestUtil.testBytes(2,
- IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
+ public void testIsValidUtf8_2Bytes() {
+ testBytes(LITERAL_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(HEAP_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(DIRECT_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
}
/**
* Tests that round tripping of all three byte permutations work.
*/
- public void testIsValidUtf8_3Bytes() throws UnsupportedEncodingException {
+ public void testIsValidUtf8_3Bytes() {
// Travis' OOM killer doesn't like this test
if (System.getenv("TRAVIS") == null) {
- IsValidUtf8TestUtil.testBytes(3,
- IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(LITERAL_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(HEAP_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
+ testBytes(DIRECT_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
}
}
@@ -85,8 +93,7 @@ public class IsValidUtf8Test extends TestCase {
* {@link IsValidUtf8FourByteTest} is used for full coverage. This method
* tests specific four-byte cases.
*/
- public void testIsValidUtf8_4BytesSamples()
- throws UnsupportedEncodingException {
+ public void testIsValidUtf8_4BytesSamples() {
// Valid 4 byte.
assertValidUtf8(0xF0, 0xA4, 0xAD, 0xA2);
@@ -119,9 +126,7 @@ public class IsValidUtf8Test extends TestCase {
assertTrue(asBytes("\u024B62\u024B62").isValidUtf8());
// Mixed string
- assertTrue(
- asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62")
- .isValidUtf8());
+ assertTrue(asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62").isValidUtf8());
// Not a valid string
assertInvalidUtf8(-1, 0, -1, 0);
@@ -135,36 +140,35 @@ public class IsValidUtf8Test extends TestCase {
return realBytes;
}
- private ByteString toByteString(int... bytes) {
- return ByteString.copyFrom(toByteArray(bytes));
- }
-
- private void assertValidUtf8(int[] bytes, boolean not) {
+ private void assertValidUtf8(ByteStringFactory factory, int[] bytes, boolean not) {
byte[] realBytes = toByteArray(bytes);
assertTrue(not ^ Utf8.isValidUtf8(realBytes));
assertTrue(not ^ Utf8.isValidUtf8(realBytes, 0, bytes.length));
- ByteString lit = ByteString.copyFrom(realBytes);
- ByteString sub = lit.substring(0, bytes.length);
- assertTrue(not ^ lit.isValidUtf8());
+ ByteString leaf = factory.newByteString(realBytes);
+ ByteString sub = leaf.substring(0, bytes.length);
+ assertTrue(not ^ leaf.isValidUtf8());
assertTrue(not ^ sub.isValidUtf8());
ByteString[] ropes = {
- RopeByteString.newInstanceForTest(ByteString.EMPTY, lit),
- RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
- RopeByteString.newInstanceForTest(lit, ByteString.EMPTY),
- RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
- RopeByteString.newInstanceForTest(sub, lit)
- };
+ RopeByteString.newInstanceForTest(ByteString.EMPTY, leaf),
+ RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
+ RopeByteString.newInstanceForTest(leaf, ByteString.EMPTY),
+ RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
+ RopeByteString.newInstanceForTest(sub, leaf)};
for (ByteString rope : ropes) {
assertTrue(not ^ rope.isValidUtf8());
}
}
private void assertValidUtf8(int... bytes) {
- assertValidUtf8(bytes, false);
+ assertValidUtf8(LITERAL_FACTORY, bytes, false);
+ assertValidUtf8(HEAP_NIO_FACTORY, bytes, false);
+ assertValidUtf8(DIRECT_NIO_FACTORY, bytes, false);
}
private void assertInvalidUtf8(int... bytes) {
- assertValidUtf8(bytes, true);
+ assertValidUtf8(LITERAL_FACTORY, bytes, true);
+ assertValidUtf8(HEAP_NIO_FACTORY, bytes, true);
+ assertValidUtf8(DIRECT_NIO_FACTORY, bytes, true);
}
private static ByteString asBytes(String s) {
@@ -177,7 +181,6 @@ public class IsValidUtf8Test extends TestCase {
for (Shard shard : IsValidUtf8TestUtil.FOUR_BYTE_SHARDS) {
actual += shard.expected;
}
- assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT,
- actual);
+ assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT, actual);
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
index 321669f3..1bcf63e7 100644
--- a/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
+++ b/java/core/src/test/java/com/google/protobuf/IsValidUtf8TestUtil.java
@@ -30,9 +30,13 @@
package com.google.protobuf;
-import static junit.framework.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
-import java.io.UnsupportedEncodingException;
+import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetDecoder;
@@ -52,64 +56,105 @@ import java.util.logging.Logger;
* @author jonp@google.com (Jon Perlow)
* @author martinrb@google.com (Martin Buchholz)
*/
-class IsValidUtf8TestUtil {
- private static Logger logger = Logger.getLogger(
- IsValidUtf8TestUtil.class.getName());
+final class IsValidUtf8TestUtil {
+ private static Logger logger = Logger.getLogger(IsValidUtf8TestUtil.class.getName());
+
+ private IsValidUtf8TestUtil() {}
+
+ static interface ByteStringFactory {
+ ByteString newByteString(byte[] bytes);
+ }
+
+ static final ByteStringFactory LITERAL_FACTORY = new ByteStringFactory() {
+ @Override
+ public ByteString newByteString(byte[] bytes) {
+ return ByteString.wrap(bytes);
+ }
+ };
+
+ static final ByteStringFactory HEAP_NIO_FACTORY = new ByteStringFactory() {
+ @Override
+ public ByteString newByteString(byte[] bytes) {
+ return new NioByteString(ByteBuffer.wrap(bytes));
+ }
+ };
+
+ private static ThreadLocal<SoftReference<ByteBuffer>> directBuffer =
+ new ThreadLocal<SoftReference<ByteBuffer>>();
+
+ /**
+ * Factory for direct {@link ByteBuffer} instances. To reduce direct memory usage, this
+ * uses a thread local direct buffer. This means that each call will overwrite the buffer's
+ * contents from the previous call, so the calling code must be careful not to continue using
+ * a buffer returned from a previous invocation.
+ */
+ static final ByteStringFactory DIRECT_NIO_FACTORY = new ByteStringFactory() {
+ @Override
+ public ByteString newByteString(byte[] bytes) {
+ SoftReference<ByteBuffer> ref = directBuffer.get();
+ ByteBuffer buffer = ref == null ? null : ref.get();
+ if (buffer == null || buffer.capacity() < bytes.length) {
+ buffer = ByteBuffer.allocateDirect(bytes.length);
+ directBuffer.set(new SoftReference<ByteBuffer>(buffer));
+ }
+ buffer.clear();
+ buffer.put(bytes);
+ buffer.flip();
+ return new NioByteString(buffer);
+ }
+ };
// 128 - [chars 0x0000 to 0x007f]
- static long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
+ static final long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
// 128
- static long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT =
- ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
+ static final long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT = ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
// 1920 [chars 0x0080 to 0x07FF]
- static long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
+ static final long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
// 18,304
- static long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
+ static final long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
// Both bytes are one byte characters
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 2) +
// The possible number of two byte characters
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS;
// 2048
- static long THREE_BYTE_SURROGATES = 2 * 1024;
+ static final long THREE_BYTE_SURROGATES = 2 * 1024;
// 61,440 [chars 0x0800 to 0xFFFF, minus surrogates]
- static long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
+ static final long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
0xFFFF - 0x0800 + 1 - THREE_BYTE_SURROGATES;
// 2,650,112
- static long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
+ static final long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
// All one byte characters
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 3) +
// One two byte character and a one byte character
- 2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
- ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
- // Three byte characters
+ 2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
+ // Three byte characters
THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
// 1,048,576 [chars 0x10000L to 0x10FFFF]
- static long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
+ static final long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
// 289,571,839
- static long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
+ static final long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
// All one byte characters
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 4) +
// One and three byte characters
- 2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
- ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
+ 2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
// Two two byte characters
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS +
// Permutations of one and two byte characters
- 3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
- ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
- ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
+ 3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS
+ * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS
+ +
// Four byte characters
FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS;
- static class Shard {
+ static final class Shard {
final long index;
final long start;
final long lim;
@@ -138,7 +183,7 @@ class IsValidUtf8TestUtil {
// 97-111 are all 2342912
for (int i = 97; i <= 111; i++) {
- expected[i] = 2342912;
+ expected[i] = 2342912;
}
// 113-117 are all 1048576
@@ -158,22 +203,18 @@ class IsValidUtf8TestUtil {
return expected;
}
- static final List<Shard> FOUR_BYTE_SHARDS = generateFourByteShards(
- 128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
+ static final List<Shard> FOUR_BYTE_SHARDS =
+ generateFourByteShards(128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
- private static List<Shard> generateFourByteShards(
- int numShards, long[] expected) {
+ private static List<Shard> generateFourByteShards(int numShards, long[] expected) {
assertEquals(numShards, expected.length);
List<Shard> shards = new ArrayList<Shard>(numShards);
long LIM = 1L << 32;
long increment = LIM / numShards;
assertTrue(LIM % numShards == 0);
for (int i = 0; i < numShards; i++) {
- shards.add(new Shard(i,
- increment * i,
- increment * (i + 1),
- expected[i]));
+ shards.add(new Shard(i, increment * i, increment * (i + 1), expected[i]));
}
return shards;
}
@@ -182,12 +223,12 @@ class IsValidUtf8TestUtil {
* Helper to run the loop to test all the permutations for the number of bytes
* specified.
*
+ * @param factory the factory for {@link ByteString} instances.
* @param numBytes the number of bytes in the byte array
* @param expectedCount the expected number of roundtrippable permutations
*/
- static void testBytes(int numBytes, long expectedCount)
- throws UnsupportedEncodingException {
- testBytes(numBytes, expectedCount, 0, -1);
+ static void testBytes(ByteStringFactory factory, int numBytes, long expectedCount) {
+ testBytes(factory, numBytes, expectedCount, 0, -1);
}
/**
@@ -195,14 +236,15 @@ class IsValidUtf8TestUtil {
* specified. This overload is useful for debugging to get the loop to start
* at a certain character.
*
+ * @param factory the factory for {@link ByteString} instances.
* @param numBytes the number of bytes in the byte array
* @param expectedCount the expected number of roundtrippable permutations
* @param start the starting bytes encoded as a long as big-endian
* @param lim the limit of bytes to process encoded as a long as big-endian,
* or -1 to mean the max limit for numBytes
*/
- static void testBytes(int numBytes, long expectedCount, long start, long lim)
- throws UnsupportedEncodingException {
+ static void testBytes(
+ ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) {
Random rnd = new Random();
byte[] bytes = new byte[numBytes];
@@ -217,7 +259,7 @@ class IsValidUtf8TestUtil {
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
tmpByteChar = tmpByteChar >> 8;
}
- ByteString bs = ByteString.copyFrom(bytes);
+ ByteString bs = factory.newByteString(bytes);
boolean isRoundTrippable = bs.isValidUtf8();
String s = new String(bytes, Internal.UTF_8);
byte[] bytesReencoded = s.getBytes(Internal.UTF_8);
@@ -231,19 +273,29 @@ class IsValidUtf8TestUtil {
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes));
assertEquals(isRoundTrippable, Utf8.isValidUtf8(bytes, 0, numBytes));
+ try {
+ assertEquals(s, Utf8.decodeUtf8(bytes, 0, numBytes));
+ } catch (InvalidProtocolBufferException e) {
+ if (isRoundTrippable) {
+ System.out.println("Could not decode utf-8");
+ outputFailure(byteChar, bytes, bytesReencoded);
+ }
+ }
+
// Test partial sequences.
// Partition numBytes into three segments (not necessarily non-empty).
int i = rnd.nextInt(numBytes);
int j = rnd.nextInt(numBytes);
if (j < i) {
- int tmp = i; i = j; j = tmp;
+ int tmp = i;
+ i = j;
+ j = tmp;
}
int state1 = Utf8.partialIsValidUtf8(Utf8.COMPLETE, bytes, 0, i);
int state2 = Utf8.partialIsValidUtf8(state1, bytes, i, j);
int state3 = Utf8.partialIsValidUtf8(state2, bytes, j, numBytes);
if (isRoundTrippable != (state3 == Utf8.COMPLETE)) {
- System.out.printf("state=%04x %04x %04x i=%d j=%d%n",
- state1, state2, state3, i, j);
+ System.out.printf("state=%04x %04x %04x i=%d j=%d%n", state1, state2, state3, i, j);
outputFailure(byteChar, bytes, bytesReencoded);
}
assertEquals(isRoundTrippable, (state3 == Utf8.COMPLETE));
@@ -251,36 +303,24 @@ class IsValidUtf8TestUtil {
// Test ropes built out of small partial sequences
ByteString rope = RopeByteString.newInstanceForTest(
bs.substring(0, i),
- RopeByteString.newInstanceForTest(
- bs.substring(i, j),
- bs.substring(j, numBytes)));
+ RopeByteString.newInstanceForTest(bs.substring(i, j), bs.substring(j, numBytes)));
assertSame(RopeByteString.class, rope.getClass());
- ByteString[] byteStrings = { bs, bs.substring(0, numBytes), rope };
+ ByteString[] byteStrings = {bs, bs.substring(0, numBytes), rope};
for (ByteString x : byteStrings) {
- assertEquals(isRoundTrippable,
- x.isValidUtf8());
- assertEquals(state3,
- x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
-
- assertEquals(state1,
- x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
- assertEquals(state1,
- x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
- assertEquals(state2,
- x.partialIsValidUtf8(state1, i, j - i));
- assertEquals(state2,
- x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
- assertEquals(state3,
- x.partialIsValidUtf8(state2, j, numBytes - j));
- assertEquals(state3,
- x.substring(j, numBytes)
- .partialIsValidUtf8(state2, 0, numBytes - j));
+ assertEquals(isRoundTrippable, x.isValidUtf8());
+ assertEquals(state3, x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
+
+ assertEquals(state1, x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
+ assertEquals(state1, x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
+ assertEquals(state2, x.partialIsValidUtf8(state1, i, j - i));
+ assertEquals(state2, x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
+ assertEquals(state3, x.partialIsValidUtf8(state2, j, numBytes - j));
+ assertEquals(state3, x.substring(j, numBytes).partialIsValidUtf8(state2, 0, numBytes - j));
}
// ByteString reduplication should not affect its UTF-8 validity.
- ByteString ropeADope =
- RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
+ ByteString ropeADope = RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
assertEquals(isRoundTrippable, ropeADope.isValidUtf8());
if (isRoundTrippable) {
@@ -288,8 +328,7 @@ class IsValidUtf8TestUtil {
}
count++;
if (byteChar != 0 && byteChar % 1000000L == 0) {
- logger.info("Processed " + (byteChar / 1000000L) +
- " million characters");
+ logger.info("Processed " + (byteChar / 1000000L) + " million characters");
}
}
logger.info("Round tripped " + countRoundTripped + " of " + count);
@@ -303,25 +342,26 @@ class IsValidUtf8TestUtil {
* actual String class, it's possible for incompatibilities to develop
* (although unlikely).
*
+ * @param factory the factory for {@link ByteString} instances.
* @param numBytes the number of bytes in the byte array
* @param expectedCount the expected number of roundtrippable permutations
* @param start the starting bytes encoded as a long as big-endian
* @param lim the limit of bytes to process encoded as a long as big-endian,
* or -1 to mean the max limit for numBytes
*/
- void testBytesUsingByteBuffers(
- int numBytes, long expectedCount, long start, long lim)
- throws UnsupportedEncodingException {
- CharsetDecoder decoder = Internal.UTF_8.newDecoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
- CharsetEncoder encoder = Internal.UTF_8.newEncoder()
- .onMalformedInput(CodingErrorAction.REPLACE)
- .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ static void testBytesUsingByteBuffers(
+ ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) {
+ CharsetDecoder decoder =
+ Internal.UTF_8.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
+ CharsetEncoder encoder =
+ Internal.UTF_8.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE);
byte[] bytes = new byte[numBytes];
int maxChars = (int) (decoder.maxCharsPerByte() * numBytes) + 1;
- char[] charsDecoded =
- new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
+ char[] charsDecoded = new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
int maxBytes = (int) (encoder.maxBytesPerChar() * maxChars) + 1;
byte[] bytesReencoded = new byte[maxBytes];
@@ -347,7 +387,7 @@ class IsValidUtf8TestUtil {
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
tmpByteChar = tmpByteChar >> 8;
}
- boolean isRoundTrippable = ByteString.copyFrom(bytes).isValidUtf8();
+ boolean isRoundTrippable = factory.newByteString(bytes).isValidUtf8();
CoderResult result = decoder.decode(bb, cb, true);
assertFalse(result.isError());
result = decoder.flush(cb);
@@ -382,8 +422,7 @@ class IsValidUtf8TestUtil {
countRoundTripped++;
}
if (byteChar != 0 && byteChar % 1000000 == 0) {
- logger.info("Processed " + (byteChar / 1000000) +
- " million characters");
+ logger.info("Processed " + (byteChar / 1000000) + " million characters");
}
}
logger.info("Round tripped " + countRoundTripped + " of " + count);
@@ -394,10 +433,9 @@ class IsValidUtf8TestUtil {
outputFailure(byteChar, bytes, after, after.length);
}
- private static void outputFailure(long byteChar, byte[] bytes, byte[] after,
- int len) {
- fail("Failure: (" + Long.toHexString(byteChar) + ") " +
- toHexString(bytes) + " => " + toHexString(after, len));
+ private static void outputFailure(long byteChar, byte[] bytes, byte[] after, int len) {
+ fail("Failure: (" + Long.toHexString(byteChar) + ") " + toHexString(bytes) + " => "
+ + toHexString(after, len));
}
private static String toHexString(byte[] b) {
@@ -416,5 +454,4 @@ class IsValidUtf8TestUtil {
s.append("\"");
return s.toString();
}
-
}
diff --git a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
index 211b5697..813fe6bc 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyFieldLiteTest.java
@@ -31,11 +31,9 @@
package com.google.protobuf;
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
-import static protobuf_unittest.UnittestProto.optionalInt64Extension;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
-
import java.io.IOException;
import junit.framework.TestCase;
@@ -220,29 +218,6 @@ public class LazyFieldLiteTest extends TestCase {
assertEquals(messageWithExtensions, field.getValue(TestAllExtensions.getDefaultInstance()));
}
- public void testMergeMightLoseExtensions() throws Exception {
- // Test that we don't know about the extensions when parsing.
- TestAllExtensions message1 =
- TestAllExtensions.newBuilder().setExtension(optionalInt32Extension, 1).build();
- TestAllExtensions message2 =
- TestAllExtensions.newBuilder().setExtension(optionalInt64Extension, 2L).build();
-
- LazyFieldLite field = LazyFieldLite.fromValue(message1);
- field.merge(LazyFieldLite.fromValue(message2));
-
- // We lose the extensions from message 2 because we have to serialize it and then parse it
- // again, using the empty registry this time.
- TestAllExtensions value =
- (TestAllExtensions) field.getValue(TestAllExtensions.getDefaultInstance());
- assertTrue(value.hasExtension(optionalInt32Extension));
- assertEquals(Integer.valueOf(1), value.getExtension(optionalInt32Extension));
- assertFalse(value.hasExtension(optionalInt64Extension));
-
- // The field is still there, it is just unknown.
- assertTrue(value.getUnknownFields()
- .hasField(optionalInt64Extension.getDescriptor().getNumber()));
- }
-
// Help methods.
diff --git a/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java
index 2b900065..f27e8e51 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyFieldTest.java
@@ -32,8 +32,6 @@ package com.google.protobuf;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
-
-import java.io.IOException;
import junit.framework.TestCase;
/**
@@ -90,6 +88,7 @@ public class LazyFieldTest extends TestCase {
assertFalse(message.equals(lazyField.getValue()));
}
+ @SuppressWarnings("EqualsIncompatibleType") // LazyField.equals() is not symmetric
public void testEqualsObjectEx() throws Exception {
TestAllExtensions message = TestUtil.getAllExtensionsSet();
LazyField lazyField = createLazyFieldFromMessage(message);
diff --git a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
index afe0fffd..968ca206 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyMessageLiteTest.java
@@ -34,10 +34,8 @@ import protobuf_unittest.LazyFieldsLite.LazyExtension;
import protobuf_unittest.LazyFieldsLite.LazyInnerMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyMessageLite;
import protobuf_unittest.LazyFieldsLite.LazyNestedInnerMessageLite;
-
-import junit.framework.TestCase;
-
import java.util.ArrayList;
+import junit.framework.TestCase;
/**
* Unit test for messages with lazy fields.
@@ -103,6 +101,19 @@ public class LazyMessageLiteTest extends TestCase {
assertEquals(119, outer.getRepeatedInner(0).getNum());
assertEquals(122, outer.getRepeatedInner(1).getNum());
}
+
+ public void testRepeatedMutability() throws Exception {
+ LazyMessageLite outer = LazyMessageLite.newBuilder()
+ .addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(119))
+ .addRepeatedInner(LazyInnerMessageLite.newBuilder().setNum(122))
+ .build();
+
+ outer = LazyMessageLite.parseFrom(outer.toByteArray());
+ try {
+ outer.getRepeatedInnerList().set(1, null);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+ }
public void testAddAll() {
ArrayList<LazyInnerMessageLite> inners = new ArrayList<LazyInnerMessageLite>();
@@ -251,6 +262,23 @@ public class LazyMessageLiteTest extends TestCase {
assertEquals(42, merged.getOneofInner().getNumWithDefault());
}
+ // Regression test for b/28198805.
+ public void testMergeOneofMessages() throws Exception {
+ LazyInnerMessageLite inner = LazyInnerMessageLite.newBuilder().build();
+ LazyMessageLite outer = LazyMessageLite.newBuilder().setOneofInner(inner).build();
+ ByteString data1 = outer.toByteString();
+
+ // The following should not alter the content of the 'outer' message.
+ LazyMessageLite.Builder merged = LazyMessageLite.newBuilder().mergeFrom(outer);
+ LazyInnerMessageLite anotherInner = LazyInnerMessageLite.newBuilder().setNum(12345).build();
+ merged.setOneofInner(anotherInner);
+
+ // Check that the 'outer' stays the same.
+ ByteString data2 = outer.toByteString();
+ assertEquals(data1, data2);
+ assertEquals(0, outer.getOneofInner().getNum());
+ }
+
public void testSerialize() throws InvalidProtocolBufferException {
LazyNestedInnerMessageLite nested = LazyNestedInnerMessageLite.newBuilder()
.setNum(3)
diff --git a/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java b/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
index 0f42ac50..2fc3124d 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyStringArrayListTest.java
@@ -32,12 +32,12 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
import java.util.ArrayList;
+import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
+import junit.framework.TestCase;
/**
* Tests for {@link LazyStringArrayList}.
@@ -233,7 +233,7 @@ public class LazyStringArrayListTest extends TestCase {
}
try {
- list.addAllByteArray(asList(BYTE_STRING_A.toByteArray()));
+ list.addAllByteArray(Collections.singletonList(BYTE_STRING_A.toByteArray()));
fail();
} catch (UnsupportedOperationException e) {
// expected
@@ -281,6 +281,7 @@ public class LazyStringArrayListTest extends TestCase {
assertGenericListImmutable(byteArrayList, byteArrayList.get(0));
}
+ @SuppressWarnings("unchecked")
private static <T> void assertGenericListImmutable(List<T> list, T value) {
try {
list.add(value);
diff --git a/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
index 0ef414aa..006e4933 100644
--- a/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
@@ -32,10 +32,8 @@ package com.google.protobuf;
import protobuf_unittest.UnittestProto;
-
-import junit.framework.TestCase;
-
import java.io.IOException;
+import junit.framework.TestCase;
/**
* Tests to make sure the lazy conversion of UTF8-encoded byte arrays to
diff --git a/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
index 035917c8..4764ca1b 100644
--- a/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteEqualsAndHashTest.java
@@ -33,6 +33,8 @@ package com.google.protobuf;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
import junit.framework.TestCase;
@@ -53,7 +55,7 @@ public class LiteEqualsAndHashTest extends TestCase {
// correctly when linked only against the lite library.
// We do however do some basic testing to make sure that equals is actually
- // overriden to test for value equality rather than simple object equality.
+ // overridden to test for value equality rather than simple object equality.
// Check that two identical objs are equal.
Foo foo1a = Foo.newBuilder()
@@ -83,6 +85,16 @@ public class LiteEqualsAndHashTest extends TestCase {
assertFalse(bar.equals(barPrime));
}
+ public void testOneofEquals() throws Exception {
+ TestOneofEquals.Builder builder = TestOneofEquals.newBuilder();
+ TestOneofEquals message1 = builder.build();
+ // Set message2's name field to default value. The two messages should be different when we
+ // check with the oneof case.
+ builder.setName("");
+ TestOneofEquals message2 = builder.build();
+ assertFalse(message1.equals(message2));
+ }
+
public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException {
Foo fooWithOnlyValue = Foo.newBuilder()
.setValue(1)
@@ -105,4 +117,9 @@ public class LiteEqualsAndHashTest extends TestCase {
assertFalse(o1.equals(o2));
assertFalse(o1.hashCode() == o2.hashCode());
}
+
+ public void testRecursiveHashcode() {
+ // This tests that we don't infinite loop.
+ TestRecursiveOneof.getDefaultInstance().hashCode();
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/LiteTest.java b/java/core/src/test/java/com/google/protobuf/LiteTest.java
index b1f298ff..5ab80ca2 100644
--- a/java/core/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteTest.java
@@ -33,24 +33,37 @@ package com.google.protobuf;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
-import com.google.protobuf.UnittestLite;
+import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
+import com.google.protobuf.UnittestImportLite.ImportEnumLite;
+import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
import com.google.protobuf.UnittestLite.ForeignEnumLite;
import com.google.protobuf.UnittestLite.ForeignMessageLite;
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedEnum;
import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage;
import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase;
import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder;
+import com.google.protobuf.UnittestLite.TestHugeFieldNumbersLite;
import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
-
-import junit.framework.TestCase;
-
+import map_lite_test.MapTestProto.TestMap;
+import map_lite_test.MapTestProto.TestMap.MessageValue;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
+import java.lang.reflect.Field;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import junit.framework.TestCase;
/**
* Test lite runtime.
@@ -58,6 +71,7 @@ import java.io.ObjectOutputStream;
* @author kenton@google.com Kenton Varda
*/
public class LiteTest extends TestCase {
+ @Override
public void setUp() throws Exception {
// Test that nested extensions are initialized correctly even if the outer
// class has not been accessed directly. This was once a bug with lite
@@ -78,12 +92,11 @@ public class LiteTest extends TestCase {
// stuff to make sure the lite message is actually here and usable.
TestAllTypesLite message =
- TestAllTypesLite.newBuilder()
- .setOptionalInt32(123)
- .addRepeatedString("hello")
- .setOptionalNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
- .build();
+ TestAllTypesLite.newBuilder()
+ .setOptionalInt32(123)
+ .addRepeatedString("hello")
+ .setOptionalNestedMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
+ .build();
ByteString data = message.toByteString();
@@ -95,90 +108,97 @@ public class LiteTest extends TestCase {
assertEquals(7, message2.getOptionalNestedMessage().getBb());
}
+ public void testLite_unknownEnumAtListBoundary() throws Exception {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteStream);
+ for (int i = 0; i < AbstractProtobufList.DEFAULT_CAPACITY; i++) {
+ output.writeInt32(TestAllTypesLite.REPEATED_NESTED_ENUM_FIELD_NUMBER, 1);
+ }
+ // 0 is not a valid enum value for NestedEnum
+ output.writeInt32(TestAllTypesLite.REPEATED_NESTED_ENUM_FIELD_NUMBER, 0);
+ output.flush();
+ // This tests a bug we had once with removal right at the boundary of the array. It would throw
+ // at runtime so no need to assert.
+ TestAllTypesLite.parseFrom(new ByteArrayInputStream(byteStream.toByteArray()));
+ }
+
public void testLiteExtensions() throws Exception {
// TODO(kenton): Unlike other features of the lite library, extensions are
// implemented completely differently from the regular library. We
// should probably test them more thoroughly.
TestAllExtensionsLite message =
- TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
- .addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
- .setExtension(UnittestLite.optionalNestedEnumExtensionLite,
- TestAllTypesLite.NestedEnum.BAZ)
- .setExtension(UnittestLite.optionalNestedMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
- .build();
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
+ .setExtension(
+ UnittestLite.optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ)
+ .setExtension(
+ UnittestLite.optionalNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
+ .build();
// Test copying a message, since coping extensions actually does use a
// different code path between lite and regular libraries, and as of this
// writing, parsing hasn't been implemented yet.
TestAllExtensionsLite message2 = message.toBuilder().build();
- assertEquals(123, (int) message2.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
- assertEquals(1, message2.getExtensionCount(
- UnittestLite.repeatedStringExtensionLite));
- assertEquals(1, message2.getExtension(
- UnittestLite.repeatedStringExtensionLite).size());
- assertEquals("hello", message2.getExtension(
- UnittestLite.repeatedStringExtensionLite, 0));
- assertEquals(TestAllTypesLite.NestedEnum.BAZ, message2.getExtension(
- UnittestLite.optionalNestedEnumExtensionLite));
- assertEquals(7, message2.getExtension(
- UnittestLite.optionalNestedMessageExtensionLite).getBb());
+ assertEquals(123, (int) message2.getExtension(UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(1, message2.getExtensionCount(UnittestLite.repeatedStringExtensionLite));
+ assertEquals(1, message2.getExtension(UnittestLite.repeatedStringExtensionLite).size());
+ assertEquals("hello", message2.getExtension(UnittestLite.repeatedStringExtensionLite, 0));
+ assertEquals(
+ TestAllTypesLite.NestedEnum.BAZ,
+ message2.getExtension(UnittestLite.optionalNestedEnumExtensionLite));
+ assertEquals(7, message2.getExtension(UnittestLite.optionalNestedMessageExtensionLite).getBb());
}
- public void testSerialize() throws Exception {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- TestAllTypesLite expected =
- TestAllTypesLite.newBuilder()
- .setOptionalInt32(123)
- .addRepeatedString("hello")
- .setOptionalNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
- .build();
- ObjectOutputStream out = new ObjectOutputStream(baos);
- try {
- out.writeObject(expected);
- } finally {
- out.close();
- }
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ObjectInputStream in = new ObjectInputStream(bais);
- TestAllTypesLite actual = (TestAllTypesLite) in.readObject();
- assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32());
- assertEquals(expected.getRepeatedStringCount(),
- actual.getRepeatedStringCount());
- assertEquals(expected.getRepeatedString(0),
- actual.getRepeatedString(0));
- assertEquals(expected.getOptionalNestedMessage().getBb(),
- actual.getOptionalNestedMessage().getBb());
- }
-
public void testClone() {
- TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder()
- .setOptionalInt32(123);
- assertEquals(
- expected.getOptionalInt32(), expected.clone().getOptionalInt32());
-
- TestAllExtensionsLite.Builder expected2 = TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalInt32ExtensionLite, 123);
+ TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder().setOptionalInt32(123);
+ assertEquals(expected.getOptionalInt32(), expected.clone().getOptionalInt32());
+
+ TestAllExtensionsLite.Builder expected2 =
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123);
assertEquals(
expected2.getExtension(UnittestLite.optionalInt32ExtensionLite),
expected2.clone().getExtension(UnittestLite.optionalInt32ExtensionLite));
}
-
+
public void testAddAll() {
try {
- TestAllTypesLite.newBuilder()
- .addAllRepeatedBytes(null);
+ TestAllTypesLite.newBuilder().addAllRepeatedBytes(null);
fail();
} catch (NullPointerException e) {
// expected.
}
}
-
+
+ public void testMemoization() throws Exception {
+ TestAllExtensionsLite message = TestUtilLite.getAllLiteExtensionsSet();
+
+ // Test serialized size is memoized
+ message.memoizedSerializedSize = -1;
+ int size = message.getSerializedSize();
+ assertTrue(size > 0);
+ assertEquals(size, message.memoizedSerializedSize);
+
+ // Test hashCode is memoized
+ assertEquals(0, message.memoizedHashCode);
+ int hashCode = message.hashCode();
+ assertTrue(hashCode != 0);
+ assertEquals(hashCode, message.memoizedHashCode);
+
+ // Test isInitialized is memoized
+ Field memo = message.getClass().getDeclaredField("memoizedIsInitialized");
+ memo.setAccessible(true);
+ memo.set(message, (byte) -1);
+ boolean initialized = message.isInitialized();
+ assertTrue(initialized);
+ // We have to cast to Byte first. Casting to byte causes a type error
+ assertEquals(1, ((Byte) memo.get(message)).intValue());
+ }
+
public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
// Since builders are implemented as a thin wrapper around a message
// instance, we attempt to verify that we can't cause the builder to modify
@@ -202,13 +222,11 @@ public class LiteTest extends TestCase {
assertEquals(ByteString.EMPTY, message.getOptionalBytes());
assertEquals(ByteString.copyFromUtf8("hi"), builder.getOptionalBytes());
messageAfterBuild = builder.build();
- assertEquals(
- ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
assertEquals(ByteString.EMPTY, message.getOptionalBytes());
builder.clearOptionalBytes();
assertEquals(ByteString.EMPTY, builder.getOptionalBytes());
- assertEquals(
- ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getOptionalBytes());
message = builder.build();
builder.setOptionalCord("hi");
@@ -226,27 +244,23 @@ public class LiteTest extends TestCase {
assertEquals(ByteString.EMPTY, message.getOptionalCordBytes());
assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalCordBytes());
messageAfterBuild = builder.build();
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalCordBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalCordBytes());
assertEquals(ByteString.EMPTY, message.getOptionalCordBytes());
builder.clearOptionalCord();
assertEquals(ByteString.EMPTY, builder.getOptionalCordBytes());
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalCordBytes());
-
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalCordBytes());
+
message = builder.build();
builder.setOptionalDouble(1);
- assertEquals(0D, message.getOptionalDouble());
- assertEquals(1D, builder.getOptionalDouble());
+ assertEquals(0D, message.getOptionalDouble(), 0.0);
+ assertEquals(1D, builder.getOptionalDouble(), 0.0);
messageAfterBuild = builder.build();
- assertEquals(1D, messageAfterBuild.getOptionalDouble());
- assertEquals(0D, message.getOptionalDouble());
+ assertEquals(1D, messageAfterBuild.getOptionalDouble(), 0.0);
+ assertEquals(0D, message.getOptionalDouble(), 0.0);
builder.clearOptionalDouble();
- assertEquals(0D, builder.getOptionalDouble());
- assertEquals(1D, messageAfterBuild.getOptionalDouble());
-
+ assertEquals(0D, builder.getOptionalDouble(), 0.0);
+ assertEquals(1D, messageAfterBuild.getOptionalDouble(), 0.0);
+
message = builder.build();
builder.setOptionalFixed32(1);
assertEquals(0, message.getOptionalFixed32());
@@ -257,7 +271,7 @@ public class LiteTest extends TestCase {
builder.clearOptionalFixed32();
assertEquals(0, builder.getOptionalFixed32());
assertEquals(1, messageAfterBuild.getOptionalFixed32());
-
+
message = builder.build();
builder.setOptionalFixed64(1);
assertEquals(0L, message.getOptionalFixed64());
@@ -271,110 +285,73 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.setOptionalFloat(1);
- assertEquals(0F, message.getOptionalFloat());
- assertEquals(1F, builder.getOptionalFloat());
+ assertEquals(0F, message.getOptionalFloat(), 0.0f);
+ assertEquals(1F, builder.getOptionalFloat(), 0.0f);
messageAfterBuild = builder.build();
- assertEquals(1F, messageAfterBuild.getOptionalFloat());
- assertEquals(0F, message.getOptionalFloat());
+ assertEquals(1F, messageAfterBuild.getOptionalFloat(), 0.0f);
+ assertEquals(0F, message.getOptionalFloat(), 0.0f);
builder.clearOptionalFloat();
- assertEquals(0F, builder.getOptionalFloat());
- assertEquals(1F, messageAfterBuild.getOptionalFloat());
+ assertEquals(0F, builder.getOptionalFloat(), 0.0f);
+ assertEquals(1F, messageAfterBuild.getOptionalFloat(), 0.0f);
message = builder.build();
builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_BAR, builder.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, builder.getOptionalForeignEnum());
messageAfterBuild = builder.build();
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_BAR,
- messageAfterBuild.getOptionalForeignEnum());
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, messageAfterBuild.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, message.getOptionalForeignEnum());
builder.clearOptionalForeignEnum();
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_FOO, builder.getOptionalForeignEnum());
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_BAR,
- messageAfterBuild.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, builder.getOptionalForeignEnum());
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, messageAfterBuild.getOptionalForeignEnum());
message = builder.build();
- ForeignMessageLite foreignMessage = ForeignMessageLite.newBuilder()
- .setC(1)
- .build();
+ ForeignMessageLite foreignMessage = ForeignMessageLite.newBuilder().setC(1).build();
builder.setOptionalForeignMessage(foreignMessage);
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- message.getOptionalForeignMessage());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage());
assertEquals(foreignMessage, builder.getOptionalForeignMessage());
messageAfterBuild = builder.build();
assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage());
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- message.getOptionalForeignMessage());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage());
builder.clearOptionalForeignMessage();
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- builder.getOptionalForeignMessage());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), builder.getOptionalForeignMessage());
assertEquals(foreignMessage, messageAfterBuild.getOptionalForeignMessage());
message = builder.build();
- ForeignMessageLite.Builder foreignMessageBuilder =
- ForeignMessageLite.newBuilder()
- .setC(3);
+ ForeignMessageLite.Builder foreignMessageBuilder = ForeignMessageLite.newBuilder().setC(3);
builder.setOptionalForeignMessage(foreignMessageBuilder);
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- message.getOptionalForeignMessage());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on foreignMessage.
- assertEquals(3, builder.getOptionalForeignMessage().getC());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage());
+ assertEquals(foreignMessageBuilder.build(), builder.getOptionalForeignMessage());
messageAfterBuild = builder.build();
- assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC());
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- message.getOptionalForeignMessage());
+ assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getOptionalForeignMessage());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), message.getOptionalForeignMessage());
builder.clearOptionalForeignMessage();
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- builder.getOptionalForeignMessage());
- assertEquals(3, messageAfterBuild.getOptionalForeignMessage().getC());
+ assertEquals(ForeignMessageLite.getDefaultInstance(), builder.getOptionalForeignMessage());
+ assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getOptionalForeignMessage());
message = builder.build();
- OptionalGroup optionalGroup = OptionalGroup.newBuilder()
- .setA(1)
- .build();
+ OptionalGroup optionalGroup = OptionalGroup.newBuilder().setA(1).build();
builder.setOptionalGroup(optionalGroup);
- assertEquals(
- OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ assertEquals(OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
assertEquals(optionalGroup, builder.getOptionalGroup());
messageAfterBuild = builder.build();
assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup());
- assertEquals(
- OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ assertEquals(OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
builder.clearOptionalGroup();
- assertEquals(
- OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
+ assertEquals(OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
assertEquals(optionalGroup, messageAfterBuild.getOptionalGroup());
message = builder.build();
- OptionalGroup.Builder optionalGroupBuilder = OptionalGroup.newBuilder()
- .setA(3);
+ OptionalGroup.Builder optionalGroupBuilder = OptionalGroup.newBuilder().setA(3);
builder.setOptionalGroup(optionalGroupBuilder);
- assertEquals(
- OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on optionalGroup.
- assertEquals(3, builder.getOptionalGroup().getA());
+ assertEquals(OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ assertEquals(optionalGroupBuilder.build(), builder.getOptionalGroup());
messageAfterBuild = builder.build();
- assertEquals(3, messageAfterBuild.getOptionalGroup().getA());
- assertEquals(
- OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
+ assertEquals(optionalGroupBuilder.build(), messageAfterBuild.getOptionalGroup());
+ assertEquals(OptionalGroup.getDefaultInstance(), message.getOptionalGroup());
builder.clearOptionalGroup();
- assertEquals(
- OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
- assertEquals(3, messageAfterBuild.getOptionalGroup().getA());
+ assertEquals(OptionalGroup.getDefaultInstance(), builder.getOptionalGroup());
+ assertEquals(optionalGroupBuilder.build(), messageAfterBuild.getOptionalGroup());
message = builder.build();
builder.setOptionalInt32(1);
@@ -397,45 +374,30 @@ public class LiteTest extends TestCase {
builder.clearOptionalInt64();
assertEquals(0L, builder.getOptionalInt64());
assertEquals(1L, messageAfterBuild.getOptionalInt64());
-
+
message = builder.build();
- NestedMessage nestedMessage = NestedMessage.newBuilder()
- .setBb(1)
- .build();
+ NestedMessage nestedMessage = NestedMessage.newBuilder().setBb(1).build();
builder.setOptionalLazyMessage(nestedMessage);
- assertEquals(
- NestedMessage.getDefaultInstance(),
- message.getOptionalLazyMessage());
+ assertEquals(NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage());
assertEquals(nestedMessage, builder.getOptionalLazyMessage());
messageAfterBuild = builder.build();
assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage());
- assertEquals(
- NestedMessage.getDefaultInstance(),
- message.getOptionalLazyMessage());
+ assertEquals(NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage());
builder.clearOptionalLazyMessage();
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
assertEquals(nestedMessage, messageAfterBuild.getOptionalLazyMessage());
message = builder.build();
- NestedMessage.Builder nestedMessageBuilder =
- NestedMessage.newBuilder()
- .setBb(3);
+ NestedMessage.Builder nestedMessageBuilder = NestedMessage.newBuilder().setBb(3);
builder.setOptionalLazyMessage(nestedMessageBuilder);
- assertEquals(
- NestedMessage.getDefaultInstance(),
- message.getOptionalLazyMessage());
- // LITE_RUNTIME doesn't implement equals so we compare on a property.
- assertEquals(3, builder.getOptionalLazyMessage().getBb());
+ assertEquals(NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage());
+ assertEquals(nestedMessageBuilder.build(), builder.getOptionalLazyMessage());
messageAfterBuild = builder.build();
- assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb());
- assertEquals(
- NestedMessage.getDefaultInstance(),
- message.getOptionalLazyMessage());
+ assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getOptionalLazyMessage());
+ assertEquals(NestedMessage.getDefaultInstance(), message.getOptionalLazyMessage());
builder.clearOptionalLazyMessage();
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
- assertEquals(3, messageAfterBuild.getOptionalLazyMessage().getBb());
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getOptionalLazyMessage());
+ assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getOptionalLazyMessage());
message = builder.build();
builder.setOptionalSfixed32(1);
@@ -494,19 +456,14 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.setOptionalStringBytes(ByteString.copyFromUtf8("no"));
assertEquals(ByteString.EMPTY, message.getOptionalStringBytes());
- assertEquals(
- ByteString.copyFromUtf8("no"), builder.getOptionalStringBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalStringBytes());
messageAfterBuild = builder.build();
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalStringBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalStringBytes());
assertEquals(ByteString.EMPTY, message.getOptionalStringBytes());
builder.clearOptionalString();
assertEquals(ByteString.EMPTY, builder.getOptionalStringBytes());
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalStringBytes());
-
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalStringBytes());
+
message = builder.build();
builder.setOptionalStringPiece("hi");
assertEquals("", message.getOptionalStringPiece());
@@ -521,18 +478,13 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.setOptionalStringPieceBytes(ByteString.copyFromUtf8("no"));
assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes());
- assertEquals(
- ByteString.copyFromUtf8("no"), builder.getOptionalStringPieceBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), builder.getOptionalStringPieceBytes());
messageAfterBuild = builder.build();
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalStringPieceBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalStringPieceBytes());
assertEquals(ByteString.EMPTY, message.getOptionalStringPieceBytes());
builder.clearOptionalStringPiece();
assertEquals(ByteString.EMPTY, builder.getOptionalStringPieceBytes());
- assertEquals(
- ByteString.copyFromUtf8("no"),
- messageAfterBuild.getOptionalStringPieceBytes());
+ assertEquals(ByteString.copyFromUtf8("no"), messageAfterBuild.getOptionalStringPieceBytes());
message = builder.build();
builder.setOptionalUint32(1);
@@ -569,16 +521,13 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addAllRepeatedBytes(singletonList(ByteString.copyFromUtf8("hi")));
assertEquals(emptyList(), message.getRepeatedBytesList());
- assertEquals(
- singletonList(ByteString.copyFromUtf8("hi")),
- builder.getRepeatedBytesList());
+ assertEquals(singletonList(ByteString.copyFromUtf8("hi")), builder.getRepeatedBytesList());
assertEquals(emptyList(), message.getRepeatedBytesList());
messageAfterBuild = builder.build();
builder.clearRepeatedBytes();
assertEquals(emptyList(), builder.getRepeatedBytesList());
assertEquals(
- singletonList(ByteString.copyFromUtf8("hi")),
- messageAfterBuild.getRepeatedBytesList());
+ singletonList(ByteString.copyFromUtf8("hi")), messageAfterBuild.getRepeatedBytesList());
message = builder.build();
builder.addAllRepeatedCord(singletonList("hi"));
@@ -631,12 +580,10 @@ public class LiteTest extends TestCase {
assertEquals(singletonList(1F), messageAfterBuild.getRepeatedFloatList());
message = builder.build();
- builder.addAllRepeatedForeignEnum(
- singletonList(ForeignEnumLite.FOREIGN_LITE_BAR));
+ builder.addAllRepeatedForeignEnum(singletonList(ForeignEnumLite.FOREIGN_LITE_BAR));
assertEquals(emptyList(), message.getRepeatedForeignEnumList());
assertEquals(
- singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
- builder.getRepeatedForeignEnumList());
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), builder.getRepeatedForeignEnumList());
assertEquals(emptyList(), message.getRepeatedForeignEnumList());
messageAfterBuild = builder.build();
builder.clearRepeatedForeignEnum();
@@ -648,23 +595,17 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addAllRepeatedForeignMessage(singletonList(foreignMessage));
assertEquals(emptyList(), message.getRepeatedForeignMessageList());
- assertEquals(
- singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
+ assertEquals(singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
assertEquals(emptyList(), message.getRepeatedForeignMessageList());
messageAfterBuild = builder.build();
builder.clearRepeatedForeignMessage();
assertEquals(emptyList(), builder.getRepeatedForeignMessageList());
- assertEquals(
- singletonList(foreignMessage),
- messageAfterBuild.getRepeatedForeignMessageList());
+ assertEquals(singletonList(foreignMessage), messageAfterBuild.getRepeatedForeignMessageList());
message = builder.build();
- builder.addAllRepeatedGroup(
- singletonList(RepeatedGroup.getDefaultInstance()));
+ builder.addAllRepeatedGroup(singletonList(RepeatedGroup.getDefaultInstance()));
assertEquals(emptyList(), message.getRepeatedGroupList());
- assertEquals(
- singletonList(RepeatedGroup.getDefaultInstance()),
- builder.getRepeatedGroupList());
+ assertEquals(singletonList(RepeatedGroup.getDefaultInstance()), builder.getRepeatedGroupList());
assertEquals(emptyList(), message.getRepeatedGroupList());
messageAfterBuild = builder.build();
builder.clearRepeatedGroup();
@@ -696,15 +637,12 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addAllRepeatedLazyMessage(singletonList(nestedMessage));
assertEquals(emptyList(), message.getRepeatedLazyMessageList());
- assertEquals(
- singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
+ assertEquals(singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
assertEquals(emptyList(), message.getRepeatedLazyMessageList());
messageAfterBuild = builder.build();
builder.clearRepeatedLazyMessage();
assertEquals(emptyList(), builder.getRepeatedLazyMessageList());
- assertEquals(
- singletonList(nestedMessage),
- messageAfterBuild.getRepeatedLazyMessageList());
+ assertEquals(singletonList(nestedMessage), messageAfterBuild.getRepeatedLazyMessageList());
message = builder.build();
builder.addAllRepeatedSfixed32(singletonList(1));
@@ -724,8 +662,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedSfixed64();
assertEquals(emptyList(), builder.getRepeatedSfixed64List());
- assertEquals(
- singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
message = builder.build();
builder.addAllRepeatedSint32(singletonList(1));
@@ -755,8 +692,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedString();
assertEquals(emptyList(), builder.getRepeatedStringList());
- assertEquals(
- singletonList("hi"), messageAfterBuild.getRepeatedStringList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedStringList());
message = builder.build();
builder.addAllRepeatedStringPiece(singletonList("hi"));
@@ -766,8 +702,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedStringPiece();
assertEquals(emptyList(), builder.getRepeatedStringPieceList());
- assertEquals(
- singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
message = builder.build();
builder.addAllRepeatedUint32(singletonList(1));
@@ -802,16 +737,13 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addRepeatedBytes(ByteString.copyFromUtf8("hi"));
assertEquals(emptyList(), message.getRepeatedBytesList());
- assertEquals(
- singletonList(ByteString.copyFromUtf8("hi")),
- builder.getRepeatedBytesList());
+ assertEquals(singletonList(ByteString.copyFromUtf8("hi")), builder.getRepeatedBytesList());
assertEquals(emptyList(), message.getRepeatedBytesList());
messageAfterBuild = builder.build();
builder.clearRepeatedBytes();
assertEquals(emptyList(), builder.getRepeatedBytesList());
assertEquals(
- singletonList(ByteString.copyFromUtf8("hi")),
- messageAfterBuild.getRepeatedBytesList());
+ singletonList(ByteString.copyFromUtf8("hi")), messageAfterBuild.getRepeatedBytesList());
message = builder.build();
builder.addRepeatedCord("hi");
@@ -867,8 +799,7 @@ public class LiteTest extends TestCase {
builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
assertEquals(emptyList(), message.getRepeatedForeignEnumList());
assertEquals(
- singletonList(ForeignEnumLite.FOREIGN_LITE_BAR),
- builder.getRepeatedForeignEnumList());
+ singletonList(ForeignEnumLite.FOREIGN_LITE_BAR), builder.getRepeatedForeignEnumList());
assertEquals(emptyList(), message.getRepeatedForeignEnumList());
messageAfterBuild = builder.build();
builder.clearRepeatedForeignEnum();
@@ -880,22 +811,17 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addRepeatedForeignMessage(foreignMessage);
assertEquals(emptyList(), message.getRepeatedForeignMessageList());
- assertEquals(
- singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
+ assertEquals(singletonList(foreignMessage), builder.getRepeatedForeignMessageList());
assertEquals(emptyList(), message.getRepeatedForeignMessageList());
messageAfterBuild = builder.build();
builder.removeRepeatedForeignMessage(0);
assertEquals(emptyList(), builder.getRepeatedForeignMessageList());
- assertEquals(
- singletonList(foreignMessage),
- messageAfterBuild.getRepeatedForeignMessageList());
+ assertEquals(singletonList(foreignMessage), messageAfterBuild.getRepeatedForeignMessageList());
message = builder.build();
builder.addRepeatedGroup(RepeatedGroup.getDefaultInstance());
assertEquals(emptyList(), message.getRepeatedGroupList());
- assertEquals(
- singletonList(RepeatedGroup.getDefaultInstance()),
- builder.getRepeatedGroupList());
+ assertEquals(singletonList(RepeatedGroup.getDefaultInstance()), builder.getRepeatedGroupList());
assertEquals(emptyList(), message.getRepeatedGroupList());
messageAfterBuild = builder.build();
builder.removeRepeatedGroup(0);
@@ -927,15 +853,12 @@ public class LiteTest extends TestCase {
message = builder.build();
builder.addRepeatedLazyMessage(nestedMessage);
assertEquals(emptyList(), message.getRepeatedLazyMessageList());
- assertEquals(
- singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
+ assertEquals(singletonList(nestedMessage), builder.getRepeatedLazyMessageList());
assertEquals(emptyList(), message.getRepeatedLazyMessageList());
messageAfterBuild = builder.build();
builder.removeRepeatedLazyMessage(0);
assertEquals(emptyList(), builder.getRepeatedLazyMessageList());
- assertEquals(
- singletonList(nestedMessage),
- messageAfterBuild.getRepeatedLazyMessageList());
+ assertEquals(singletonList(nestedMessage), messageAfterBuild.getRepeatedLazyMessageList());
message = builder.build();
builder.addRepeatedSfixed32(1);
@@ -955,8 +878,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedSfixed64();
assertEquals(emptyList(), builder.getRepeatedSfixed64List());
- assertEquals(
- singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
+ assertEquals(singletonList(1L), messageAfterBuild.getRepeatedSfixed64List());
message = builder.build();
builder.addRepeatedSint32(1);
@@ -986,8 +908,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedString();
assertEquals(emptyList(), builder.getRepeatedStringList());
- assertEquals(
- singletonList("hi"), messageAfterBuild.getRepeatedStringList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedStringList());
message = builder.build();
builder.addRepeatedStringPiece("hi");
@@ -997,8 +918,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
builder.clearRepeatedStringPiece();
assertEquals(emptyList(), builder.getRepeatedStringPieceList());
- assertEquals(
- singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
+ assertEquals(singletonList("hi"), messageAfterBuild.getRepeatedStringPieceList());
message = builder.build();
builder.addRepeatedUint32(1);
@@ -1019,7 +939,7 @@ public class LiteTest extends TestCase {
builder.clearRepeatedUint64();
assertEquals(emptyList(), builder.getRepeatedUint64List());
assertEquals(singletonList(1L), messageAfterBuild.getRepeatedUint64List());
-
+
message = builder.build();
builder.addRepeatedBool(true);
messageAfterBuild = builder.build();
@@ -1028,14 +948,13 @@ public class LiteTest extends TestCase {
assertEquals(true, messageAfterBuild.getRepeatedBool(0));
assertEquals(false, builder.getRepeatedBool(0));
builder.clearRepeatedBool();
-
+
message = builder.build();
builder.addRepeatedBytes(ByteString.copyFromUtf8("hi"));
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedBytesCount());
builder.setRepeatedBytes(0, ByteString.EMPTY);
- assertEquals(
- ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedBytes(0));
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedBytes(0));
assertEquals(ByteString.EMPTY, builder.getRepeatedBytes(0));
builder.clearRepeatedBytes();
@@ -1053,8 +972,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedCordCount());
builder.setRepeatedCord(0, "");
- assertEquals(
- ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedCordBytes(0));
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedCordBytes(0));
assertEquals(ByteString.EMPTY, builder.getRepeatedCordBytes(0));
builder.clearRepeatedCord();
@@ -1063,8 +981,8 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedDoubleCount());
builder.setRepeatedDouble(0, 0D);
- assertEquals(1D, messageAfterBuild.getRepeatedDouble(0));
- assertEquals(0D, builder.getRepeatedDouble(0));
+ assertEquals(1D, messageAfterBuild.getRepeatedDouble(0), 0.0);
+ assertEquals(0D, builder.getRepeatedDouble(0), 0.0);
builder.clearRepeatedDouble();
message = builder.build();
@@ -1090,8 +1008,8 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedFloatCount());
builder.setRepeatedFloat(0, 0F);
- assertEquals(1F, messageAfterBuild.getRepeatedFloat(0));
- assertEquals(0F, builder.getRepeatedFloat(0));
+ assertEquals(1F, messageAfterBuild.getRepeatedFloat(0), 0.0f);
+ assertEquals(0F, builder.getRepeatedFloat(0), 0.0f);
builder.clearRepeatedFloat();
message = builder.build();
@@ -1099,37 +1017,26 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedForeignEnumCount());
builder.setRepeatedForeignEnum(0, ForeignEnumLite.FOREIGN_LITE_FOO);
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_BAR,
- messageAfterBuild.getRepeatedForeignEnum(0));
- assertEquals(
- ForeignEnumLite.FOREIGN_LITE_FOO, builder.getRepeatedForeignEnum(0));
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, messageAfterBuild.getRepeatedForeignEnum(0));
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, builder.getRepeatedForeignEnum(0));
builder.clearRepeatedForeignEnum();
message = builder.build();
builder.addRepeatedForeignMessage(foreignMessage);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedForeignMessageCount());
- builder.setRepeatedForeignMessage(
- 0, ForeignMessageLite.getDefaultInstance());
- assertEquals(
- foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- builder.getRepeatedForeignMessage(0));
+ builder.setRepeatedForeignMessage(0, ForeignMessageLite.getDefaultInstance());
+ assertEquals(foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
+ assertEquals(ForeignMessageLite.getDefaultInstance(), builder.getRepeatedForeignMessage(0));
builder.clearRepeatedForeignMessage();
-
+
message = builder.build();
builder.addRepeatedForeignMessage(foreignMessageBuilder);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedForeignMessageCount());
- builder.setRepeatedForeignMessage(
- 0, ForeignMessageLite.getDefaultInstance());
- // LITE_RUNTIME doesn't implement equals so we compare on a property.
- assertEquals(3, messageAfterBuild.getRepeatedForeignMessage(0).getC());
- assertEquals(
- ForeignMessageLite.getDefaultInstance(),
- builder.getRepeatedForeignMessage(0));
+ builder.setRepeatedForeignMessage(0, ForeignMessageLite.getDefaultInstance());
+ assertEquals(foreignMessageBuilder.build(), messageAfterBuild.getRepeatedForeignMessage(0));
+ assertEquals(ForeignMessageLite.getDefaultInstance(), builder.getRepeatedForeignMessage(0));
builder.clearRepeatedForeignMessage();
message = builder.build();
@@ -1137,59 +1044,46 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedForeignMessageCount());
builder.setRepeatedForeignMessage(0, foreignMessageBuilder);
- assertEquals(
- foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
- // LITE_RUNTIME doesn't implement equals so we compare on a property.
- assertEquals(3, builder.getRepeatedForeignMessage(0).getC());
+ assertEquals(foreignMessage, messageAfterBuild.getRepeatedForeignMessage(0));
+ assertEquals(foreignMessageBuilder.build(), builder.getRepeatedForeignMessage(0));
builder.clearRepeatedForeignMessage();
message = builder.build();
- RepeatedGroup repeatedGroup = RepeatedGroup.newBuilder()
- .setA(1)
- .build();
+ RepeatedGroup repeatedGroup = RepeatedGroup.newBuilder().setA(1).build();
builder.addRepeatedGroup(repeatedGroup);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedGroupCount());
builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0));
- assertEquals(
- RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ assertEquals(RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
builder.clearRepeatedGroup();
-
+
message = builder.build();
builder.addRepeatedGroup(0, repeatedGroup);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedGroupCount());
builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
assertEquals(repeatedGroup, messageAfterBuild.getRepeatedGroup(0));
- assertEquals(
- RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ assertEquals(RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
builder.clearRepeatedGroup();
-
+
message = builder.build();
- RepeatedGroup.Builder repeatedGroupBuilder = RepeatedGroup.newBuilder()
- .setA(3);
+ RepeatedGroup.Builder repeatedGroupBuilder = RepeatedGroup.newBuilder().setA(3);
builder.addRepeatedGroup(repeatedGroupBuilder);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedGroupCount());
builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on repeatedGroup.
- assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA());
- assertEquals(
- RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ assertEquals(repeatedGroupBuilder.build(), messageAfterBuild.getRepeatedGroup(0));
+ assertEquals(RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
builder.clearRepeatedGroup();
-
+
message = builder.build();
builder.addRepeatedGroup(0, repeatedGroupBuilder);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedGroupCount());
builder.setRepeatedGroup(0, RepeatedGroup.getDefaultInstance());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on repeatedGroup.
- assertEquals(3, messageAfterBuild.getRepeatedGroup(0).getA());
- assertEquals(
- RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
+ assertEquals(repeatedGroupBuilder.build(), messageAfterBuild.getRepeatedGroup(0));
+ assertEquals(RepeatedGroup.getDefaultInstance(), builder.getRepeatedGroup(0));
builder.clearRepeatedGroup();
message = builder.build();
@@ -1209,49 +1103,41 @@ public class LiteTest extends TestCase {
assertEquals(1L, messageAfterBuild.getRepeatedInt64(0));
assertEquals(0L, builder.getRepeatedInt64(0));
builder.clearRepeatedInt64();
-
+
message = builder.build();
builder.addRepeatedLazyMessage(nestedMessage);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedLazyMessageCount());
builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0));
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
builder.clearRepeatedLazyMessage();
-
+
message = builder.build();
builder.addRepeatedLazyMessage(0, nestedMessage);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedLazyMessageCount());
builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
assertEquals(nestedMessage, messageAfterBuild.getRepeatedLazyMessage(0));
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
builder.clearRepeatedLazyMessage();
-
+
message = builder.build();
builder.addRepeatedLazyMessage(nestedMessageBuilder);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedLazyMessageCount());
builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on repeatedGroup.
- assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb());
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getRepeatedLazyMessage(0));
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
builder.clearRepeatedLazyMessage();
-
+
message = builder.build();
builder.addRepeatedLazyMessage(0, nestedMessageBuilder);
messageAfterBuild = builder.build();
assertEquals(0, message.getRepeatedLazyMessageCount());
builder.setRepeatedLazyMessage(0, NestedMessage.getDefaultInstance());
- // LITE_RUNTIME doesn't implement equals so we compare on a property and
- // ensure the property isn't set on repeatedGroup.
- assertEquals(3, messageAfterBuild.getRepeatedLazyMessage(0).getBb());
- assertEquals(
- NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
+ assertEquals(nestedMessageBuilder.build(), messageAfterBuild.getRepeatedLazyMessage(0));
+ assertEquals(NestedMessage.getDefaultInstance(), builder.getRepeatedLazyMessage(0));
builder.clearRepeatedLazyMessage();
message = builder.build();
@@ -1304,9 +1190,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0L, message.getRepeatedStringCount());
builder.setRepeatedString(0, "");
- assertEquals(
- ByteString.copyFromUtf8("hi"),
- messageAfterBuild.getRepeatedStringBytes(0));
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedStringBytes(0));
assertEquals(ByteString.EMPTY, builder.getRepeatedStringBytes(0));
builder.clearRepeatedString();
@@ -1324,9 +1208,7 @@ public class LiteTest extends TestCase {
messageAfterBuild = builder.build();
assertEquals(0L, message.getRepeatedStringPieceCount());
builder.setRepeatedStringPiece(0, "");
- assertEquals(
- ByteString.copyFromUtf8("hi"),
- messageAfterBuild.getRepeatedStringPieceBytes(0));
+ assertEquals(ByteString.copyFromUtf8("hi"), messageAfterBuild.getRepeatedStringPieceBytes(0));
assertEquals(ByteString.EMPTY, builder.getRepeatedStringPieceBytes(0));
builder.clearRepeatedStringPiece();
@@ -1350,18 +1232,14 @@ public class LiteTest extends TestCase {
message = builder.build();
assertEquals(0, message.getSerializedSize());
- builder.mergeFrom(TestAllTypesLite.newBuilder()
- .setOptionalBool(true)
- .build());
+ builder.mergeFrom(TestAllTypesLite.newBuilder().setOptionalBool(true).build());
assertEquals(0, message.getSerializedSize());
assertEquals(true, builder.build().getOptionalBool());
builder.clearOptionalBool();
message = builder.build();
assertEquals(0, message.getSerializedSize());
- builder.mergeFrom(TestAllTypesLite.newBuilder()
- .setOptionalBool(true)
- .build());
+ builder.mergeFrom(TestAllTypesLite.newBuilder().setOptionalBool(true).build());
assertEquals(0, message.getSerializedSize());
assertEquals(true, builder.build().getOptionalBool());
builder.clear();
@@ -1371,92 +1249,1133 @@ public class LiteTest extends TestCase {
assertEquals(0, message.getSerializedSize());
builder.mergeOptionalForeignMessage(foreignMessage);
assertEquals(0, message.getSerializedSize());
- assertEquals(
- foreignMessage.getC(),
- builder.build().getOptionalForeignMessage().getC());
+ assertEquals(foreignMessage.getC(), builder.build().getOptionalForeignMessage().getC());
builder.clearOptionalForeignMessage();
message = builder.build();
assertEquals(0, message.getSerializedSize());
builder.mergeOptionalLazyMessage(nestedMessage);
assertEquals(0, message.getSerializedSize());
- assertEquals(
- nestedMessage.getBb(),
- builder.build().getOptionalLazyMessage().getBb());
+ assertEquals(nestedMessage.getBb(), builder.build().getOptionalLazyMessage().getBb());
builder.clearOptionalLazyMessage();
-
+
message = builder.build();
builder.setOneofString("hi");
- assertEquals(
- OneofFieldCase.ONEOFFIELD_NOT_SET, message.getOneofFieldCase());
+ assertEquals(OneofFieldCase.ONEOFFIELD_NOT_SET, message.getOneofFieldCase());
assertEquals(OneofFieldCase.ONEOF_STRING, builder.getOneofFieldCase());
assertEquals("hi", builder.getOneofString());
messageAfterBuild = builder.build();
- assertEquals(
- OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
+ assertEquals(OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
assertEquals("hi", messageAfterBuild.getOneofString());
builder.setOneofUint32(1);
- assertEquals(
- OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
+ assertEquals(OneofFieldCase.ONEOF_STRING, messageAfterBuild.getOneofFieldCase());
assertEquals("hi", messageAfterBuild.getOneofString());
assertEquals(OneofFieldCase.ONEOF_UINT32, builder.getOneofFieldCase());
assertEquals(1, builder.getOneofUint32());
TestAllTypesLiteOrBuilder messageOrBuilder = builder;
assertEquals(OneofFieldCase.ONEOF_UINT32, messageOrBuilder.getOneofFieldCase());
-
- TestAllExtensionsLite.Builder extendableMessageBuilder =
- TestAllExtensionsLite.newBuilder();
+
+ TestAllExtensionsLite.Builder extendableMessageBuilder = TestAllExtensionsLite.newBuilder();
TestAllExtensionsLite extendableMessage = extendableMessageBuilder.build();
- extendableMessageBuilder.setExtension(
- UnittestLite.optionalInt32ExtensionLite, 1);
- assertFalse(extendableMessage.hasExtension(
- UnittestLite.optionalInt32ExtensionLite));
+ extendableMessageBuilder.setExtension(UnittestLite.optionalInt32ExtensionLite, 1);
+ assertFalse(extendableMessage.hasExtension(UnittestLite.optionalInt32ExtensionLite));
extendableMessage = extendableMessageBuilder.build();
assertEquals(
- 1, (int) extendableMessageBuilder.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
- assertEquals(
- 1, (int) extendableMessage.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
- extendableMessageBuilder.setExtension(
- UnittestLite.optionalInt32ExtensionLite, 3);
- assertEquals(
- 3, (int) extendableMessageBuilder.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
+ 1, (int) extendableMessageBuilder.getExtension(UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(1, (int) extendableMessage.getExtension(UnittestLite.optionalInt32ExtensionLite));
+ extendableMessageBuilder.setExtension(UnittestLite.optionalInt32ExtensionLite, 3);
assertEquals(
- 1, (int) extendableMessage.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
+ 3, (int) extendableMessageBuilder.getExtension(UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(1, (int) extendableMessage.getExtension(UnittestLite.optionalInt32ExtensionLite));
extendableMessage = extendableMessageBuilder.build();
assertEquals(
- 3, (int) extendableMessageBuilder.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
- assertEquals(
- 3, (int) extendableMessage.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
-
+ 3, (int) extendableMessageBuilder.getExtension(UnittestLite.optionalInt32ExtensionLite));
+ assertEquals(3, (int) extendableMessage.getExtension(UnittestLite.optionalInt32ExtensionLite));
+
// No extension registry, so it should be in unknown fields.
- extendableMessage =
- TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray());
- assertFalse(extendableMessage.hasExtension(
- UnittestLite.optionalInt32ExtensionLite));
-
+ extendableMessage = TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray());
+ assertFalse(extendableMessage.hasExtension(UnittestLite.optionalInt32ExtensionLite));
+
extendableMessageBuilder = extendableMessage.toBuilder();
- extendableMessageBuilder.mergeFrom(TestAllExtensionsLite.newBuilder()
- .setExtension(UnittestLite.optionalFixed32ExtensionLite, 11)
- .build());
-
+ extendableMessageBuilder.mergeFrom(
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalFixed32ExtensionLite, 11)
+ .build());
+
extendableMessage = extendableMessageBuilder.build();
ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
UnittestLite.registerAllExtensions(registry);
- extendableMessage = TestAllExtensionsLite.parseFrom(
- extendableMessage.toByteArray(), registry);
-
+ extendableMessage = TestAllExtensionsLite.parseFrom(extendableMessage.toByteArray(), registry);
+
// The unknown field was preserved.
+ assertEquals(3, (int) extendableMessage.getExtension(UnittestLite.optionalInt32ExtensionLite));
assertEquals(
- 3, (int) extendableMessage.getExtension(
- UnittestLite.optionalInt32ExtensionLite));
+ 11, (int) extendableMessage.getExtension(UnittestLite.optionalFixed32ExtensionLite));
+ }
+
+ public void testBuilderMergeFromNull() throws Exception {
+ try {
+ TestAllTypesLite.newBuilder().mergeFrom((TestAllTypesLite) null);
+ fail("Expected exception");
+ } catch (NullPointerException e) {
+ // Pass.
+ }
+ }
+
+ // Builder.mergeFrom() should keep existing extensions.
+ public void testBuilderMergeFromWithExtensions() throws Exception {
+ TestAllExtensionsLite message =
+ TestAllExtensionsLite.newBuilder()
+ .addExtension(UnittestLite.repeatedInt32ExtensionLite, 12)
+ .build();
+
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ UnittestLite.registerAllExtensions(registry);
+
+ TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
+ builder.mergeFrom(message.toByteArray(), registry);
+ builder.mergeFrom(message.toByteArray(), registry);
+ TestAllExtensionsLite result = builder.build();
+ assertEquals(2, result.getExtensionCount(UnittestLite.repeatedInt32ExtensionLite));
+ assertEquals(12, result.getExtension(UnittestLite.repeatedInt32ExtensionLite, 0).intValue());
+ assertEquals(12, result.getExtension(UnittestLite.repeatedInt32ExtensionLite, 1).intValue());
+ }
+
+ // Builder.mergeFrom() should keep existing unknown fields.
+ public void testBuilderMergeFromWithUnknownFields() throws Exception {
+ TestAllTypesLite message = TestAllTypesLite.newBuilder().addRepeatedInt32(1).build();
+
+ NestedMessage.Builder builder = NestedMessage.newBuilder();
+ builder.mergeFrom(message.toByteArray());
+ builder.mergeFrom(message.toByteArray());
+ NestedMessage result = builder.build();
+ assertEquals(message.getSerializedSize() * 2, result.getSerializedSize());
+ }
+
+ public void testToStringDefaultInstance() throws Exception {
+ assertToStringEquals("", TestAllTypesLite.getDefaultInstance());
+ }
+
+ public void testToStringScalarFieldsSuffixedWithList() throws Exception {
+ assertToStringEquals(
+ "deceptively_named_list: 7",
+ TestAllTypesLite.newBuilder().setDeceptivelyNamedList(7).build());
+ }
+
+ public void testToStringPrimitives() throws Exception {
+ TestAllTypesLite proto =
+ TestAllTypesLite.newBuilder()
+ .setOptionalInt32(1)
+ .setOptionalInt64(9223372036854775807L)
+ .build();
+ assertToStringEquals("optional_int32: 1\noptional_int64: 9223372036854775807", proto);
+
+ proto =
+ TestAllTypesLite.newBuilder()
+ .setOptionalBool(true)
+ .setOptionalNestedEnum(TestAllTypesLite.NestedEnum.BAZ)
+ .build();
+ assertToStringEquals("optional_bool: true\noptional_nested_enum: BAZ", proto);
+
+ proto = TestAllTypesLite.newBuilder().setOptionalFloat(2.72f).setOptionalDouble(3.14).build();
+ assertToStringEquals("optional_double: 3.14\noptional_float: 2.72", proto);
+ }
+
+ public void testToStringStringFields() throws Exception {
+ TestAllTypesLite proto =
+ TestAllTypesLite.newBuilder().setOptionalString("foo\"bar\nbaz\\").build();
+ assertToStringEquals("optional_string: \"foo\\\"bar\\nbaz\\\\\"", proto);
+
+ proto = TestAllTypesLite.newBuilder().setOptionalString("\u6587").build();
+ assertToStringEquals("optional_string: \"\\346\\226\\207\"", proto);
+ }
+
+ public void testToStringNestedMessage() throws Exception {
+ TestAllTypesLite proto =
+ TestAllTypesLite.newBuilder()
+ .setOptionalNestedMessage(TestAllTypesLite.NestedMessage.getDefaultInstance())
+ .build();
+ assertToStringEquals("optional_nested_message {\n}", proto);
+
+ proto =
+ TestAllTypesLite.newBuilder()
+ .setOptionalNestedMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
+ .build();
+ assertToStringEquals("optional_nested_message {\n bb: 7\n}", proto);
+ }
+
+ public void testToStringRepeatedFields() throws Exception {
+ TestAllTypesLite proto =
+ TestAllTypesLite.newBuilder()
+ .addRepeatedInt32(32)
+ .addRepeatedInt32(32)
+ .addRepeatedInt64(64)
+ .build();
+ assertToStringEquals("repeated_int32: 32\nrepeated_int32: 32\nrepeated_int64: 64", proto);
+
+ proto =
+ TestAllTypesLite.newBuilder()
+ .addRepeatedLazyMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
+ .addRepeatedLazyMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(8))
+ .build();
+ assertToStringEquals(
+ "repeated_lazy_message {\n bb: 7\n}\nrepeated_lazy_message {\n bb: 8\n}", proto);
+ }
+
+ public void testToStringForeignFields() throws Exception {
+ TestAllTypesLite proto =
+ TestAllTypesLite.newBuilder()
+ .setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR)
+ .setOptionalForeignMessage(ForeignMessageLite.newBuilder().setC(3))
+ .build();
+ assertToStringEquals(
+ "optional_foreign_enum: FOREIGN_LITE_BAR\noptional_foreign_message {\n c: 3\n}", proto);
+ }
+
+ public void testToStringExtensions() throws Exception {
+ TestAllExtensionsLite message =
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "spam")
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "eggs")
+ .setExtension(
+ UnittestLite.optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ)
+ .setExtension(
+ UnittestLite.optionalNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
+ .build();
+ assertToStringEquals(
+ "[1]: 123\n[18] {\n bb: 7\n}\n[21]: 3\n[44]: \"spam\"\n[44]: \"eggs\"", message);
+ }
+
+ public void testToStringUnknownFields() throws Exception {
+ TestAllExtensionsLite messageWithExtensions =
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "spam")
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "eggs")
+ .setExtension(
+ UnittestLite.optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ)
+ .setExtension(
+ UnittestLite.optionalNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
+ .build();
+ TestAllExtensionsLite messageWithUnknownFields =
+ TestAllExtensionsLite.parseFrom(messageWithExtensions.toByteArray());
+ assertToStringEquals(
+ "1: 123\n18: \"\\b\\a\"\n21: 3\n44: \"spam\"\n44: \"eggs\"", messageWithUnknownFields);
+ }
+
+ public void testToStringLazyMessage() throws Exception {
+ TestAllTypesLite message =
+ TestAllTypesLite.newBuilder()
+ .setOptionalLazyMessage(NestedMessage.newBuilder().setBb(1).build())
+ .build();
+ assertToStringEquals("optional_lazy_message {\n bb: 1\n}", message);
+ }
+
+ public void testToStringGroup() throws Exception {
+ TestAllTypesLite message =
+ TestAllTypesLite.newBuilder()
+ .setOptionalGroup(OptionalGroup.newBuilder().setA(1).build())
+ .build();
+ assertToStringEquals("optional_group {\n a: 1\n}", message);
+ }
+
+ public void testToStringOneof() throws Exception {
+ TestAllTypesLite message = TestAllTypesLite.newBuilder().setOneofString("hello").build();
+ assertToStringEquals("oneof_string: \"hello\"", message);
+ }
+
+ public void testToStringMapFields() throws Exception {
+ TestMap message1 =
+ TestMap.newBuilder()
+ .putInt32ToStringField(1, "alpha")
+ .putInt32ToStringField(2, "beta")
+ .build();
+ assertToStringEquals(
+ "int32_to_string_field {\n"
+ + " key: 1\n"
+ + " value: \"alpha\"\n"
+ + "}\n"
+ + "int32_to_string_field {\n"
+ + " key: 2\n"
+ + " value: \"beta\"\n"
+ + "}",
+ message1);
+
+ TestMap message2 =
+ TestMap.newBuilder()
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(10).build())
+ .putInt32ToMessageField(2, MessageValue.newBuilder().setValue(20).build())
+ .build();
+ assertToStringEquals(
+ "int32_to_message_field {\n"
+ + " key: 1\n"
+ + " value {\n"
+ + " value: 10\n"
+ + " }\n"
+ + "}\n"
+ + "int32_to_message_field {\n"
+ + " key: 2\n"
+ + " value {\n"
+ + " value: 20\n"
+ + " }\n"
+ + "}",
+ message2);
+ }
+
+ // Asserts that the toString() representation of the message matches the expected. This verifies
+ // the first line starts with a comment; but, does not factor in said comment as part of the
+ // comparison as it contains unstable addresses.
+ private static void assertToStringEquals(String expected, MessageLite message) {
+ String toString = message.toString();
+ assertEquals('#', toString.charAt(0));
+ if (toString.indexOf("\n") >= 0) {
+ toString = toString.substring(toString.indexOf("\n") + 1);
+ } else {
+ toString = "";
+ }
+ assertEquals(expected, toString);
+ }
+
+ public void testParseLazy() throws Exception {
+ ByteString bb =
+ TestAllTypesLite.newBuilder()
+ .setOptionalLazyMessage(NestedMessage.newBuilder().setBb(11).build())
+ .build()
+ .toByteString();
+ ByteString cc =
+ TestAllTypesLite.newBuilder()
+ .setOptionalLazyMessage(NestedMessage.newBuilder().setCc(22).build())
+ .build()
+ .toByteString();
+
+ ByteString concat = bb.concat(cc);
+ TestAllTypesLite message = TestAllTypesLite.parseFrom(concat);
+
+ assertEquals(11, message.getOptionalLazyMessage().getBb());
+ assertEquals(22L, message.getOptionalLazyMessage().getCc());
+ }
+
+ public void testParseLazy_oneOf() throws Exception {
+ ByteString bb =
+ TestAllTypesLite.newBuilder()
+ .setOneofLazyNestedMessage(NestedMessage.newBuilder().setBb(11).build())
+ .build()
+ .toByteString();
+ ByteString cc =
+ TestAllTypesLite.newBuilder()
+ .setOneofLazyNestedMessage(NestedMessage.newBuilder().setCc(22).build())
+ .build()
+ .toByteString();
+
+ ByteString concat = bb.concat(cc);
+ TestAllTypesLite message = TestAllTypesLite.parseFrom(concat);
+
+ assertEquals(11, message.getOneofLazyNestedMessage().getBb());
+ assertEquals(22L, message.getOneofLazyNestedMessage().getCc());
+ }
+
+ public void testMergeFromStream_repeatedField() throws Exception {
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder().addRepeatedString("hello");
+ builder.mergeFrom(CodedInputStream.newInstance(builder.build().toByteArray()));
+
+ assertEquals(2, builder.getRepeatedStringCount());
+ }
+
+ public void testMergeFromStream_invalidBytes() throws Exception {
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder().setDefaultBool(true);
+ try {
+ builder.mergeFrom(CodedInputStream.newInstance("Invalid bytes".getBytes(Internal.UTF_8)));
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ }
+ }
+
+ public void testMergeFrom_sanity() throws Exception {
+ TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
+ byte[] bytes = one.toByteArray();
+ TestAllTypesLite two = TestAllTypesLite.parseFrom(bytes);
+
+ one = one.toBuilder().mergeFrom(one).build();
+ two = two.toBuilder().mergeFrom(bytes).build();
+ assertEquals(one, two);
+ assertEquals(two, one);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+
+ public void testMergeFromNoLazyFieldSharing() throws Exception {
+ TestAllTypesLite.Builder sourceBuilder =
+ TestAllTypesLite.newBuilder()
+ .setOptionalLazyMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(1));
+ TestAllTypesLite.Builder targetBuilder =
+ TestAllTypesLite.newBuilder().mergeFrom(sourceBuilder.build());
+ assertEquals(1, sourceBuilder.getOptionalLazyMessage().getBb());
+ // now change the sourceBuilder, and target value shouldn't be affected.
+ sourceBuilder.setOptionalLazyMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(2));
+ assertEquals(1, targetBuilder.getOptionalLazyMessage().getBb());
+ }
+
+ public void testEquals_notEqual() throws Exception {
+ TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
+ byte[] bytes = one.toByteArray();
+ TestAllTypesLite two = one.toBuilder().mergeFrom(one).mergeFrom(bytes).build();
+
+ assertFalse(one.equals(two));
+ assertFalse(two.equals(one));
+
+ assertFalse(one.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(one));
+
+ TestAllTypesLite oneFieldSet = TestAllTypesLite.newBuilder().setDefaultBool(true).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultCord("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultCordBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultDouble(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultFixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultFixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultFloat(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR)
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().setDefaultImportEnum(ImportEnumLite.IMPORT_LITE_BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultInt32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultInt64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultNestedEnum(NestedEnum.BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultSfixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultSfixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultSint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultSint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultString("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultStringBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultStringPiece("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().setDefaultStringPieceBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultUint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setDefaultUint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedBool(true).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedCord("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedCordBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedDouble(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedFixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedFixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedFloat(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR)
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().addRepeatedImportEnum(ImportEnumLite.IMPORT_LITE_BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedInt32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedInt64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedNestedEnum(NestedEnum.BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedSfixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedSfixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedSint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedSint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedString("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedStringBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedStringPiece("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().addRepeatedStringPieceBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedUint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().addRepeatedUint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalBool(true).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalCord("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalCordBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalDouble(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalFixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalFixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalFloat(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR)
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().setOptionalImportEnum(ImportEnumLite.IMPORT_LITE_BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalInt32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalInt64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalNestedEnum(NestedEnum.BAR).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalSfixed32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalSfixed64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalSint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalSint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalString("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalStringBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalStringPiece("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().setOptionalStringPieceBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalUint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOptionalUint64(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOneofBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOneofLazyNestedMessage(NestedMessage.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOneofNestedMessage(NestedMessage.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOneofString("").build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOneofStringBytes(ByteString.EMPTY).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet = TestAllTypesLite.newBuilder().setOneofUint32(0).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOptionalForeignMessage(ForeignMessageLite.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder().setOptionalGroup(OptionalGroup.getDefaultInstance()).build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOptionalPublicImportMessage(PublicImportMessageLite.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .setOptionalLazyMessage(NestedMessage.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+ oneFieldSet =
+ TestAllTypesLite.newBuilder()
+ .addRepeatedLazyMessage(NestedMessage.getDefaultInstance())
+ .build();
+ assertFalse(oneFieldSet.equals(TestAllTypesLite.getDefaultInstance()));
+ assertFalse(TestAllTypesLite.getDefaultInstance().equals(oneFieldSet));
+ }
+
+ public void testEquals() throws Exception {
+ // Check that two identical objs are equal.
+ Foo foo1a = Foo.newBuilder().setValue(1).addBar(Bar.newBuilder().setName("foo1")).build();
+ Foo foo1b = Foo.newBuilder().setValue(1).addBar(Bar.newBuilder().setName("foo1")).build();
+ Foo foo2 = Foo.newBuilder().setValue(1).addBar(Bar.newBuilder().setName("foo2")).build();
+
+ // Check that equals is doing value rather than object equality.
+ assertEquals(foo1a, foo1b);
+ assertEquals(foo1a.hashCode(), foo1b.hashCode());
+
+ // Check that a diffeent object is not equal.
+ assertFalse(foo1a.equals(foo2));
+
+ // Check that two objects which have different types but the same field values are not
+ // considered to be equal.
+ Bar bar = Bar.newBuilder().setName("bar").build();
+ BarPrime barPrime = BarPrime.newBuilder().setName("bar").build();
+ assertFalse(bar.equals(barPrime));
+ }
+
+ public void testEqualsAndHashCodeForTrickySchemaTypes() {
+ Foo foo1 = Foo.getDefaultInstance();
+ Foo foo2 = Foo.newBuilder().setSint64(1).build();
+ Foo foo3 = Foo.newBuilder().putMyMap("key", "value2").build();
+ Foo foo4 = Foo.newBuilder().setMyGroup(Foo.MyGroup.newBuilder().setValue(4).build()).build();
+
+ assertEqualsAndHashCodeAreFalse(foo1, foo2);
+ assertEqualsAndHashCodeAreFalse(foo1, foo3);
+ assertEqualsAndHashCodeAreFalse(foo1, foo4);
+ }
+
+ public void testOneofEquals() throws Exception {
+ TestOneofEquals.Builder builder = TestOneofEquals.newBuilder();
+ TestOneofEquals message1 = builder.build();
+ // Set message2's name field to default value. The two messages should be different when we
+ // check with the oneof case.
+ builder.setName("");
+ TestOneofEquals message2 = builder.build();
+ assertFalse(message1.equals(message2));
+ }
+
+ public void testEquals_sanity() throws Exception {
+ TestAllTypesLite one = TestUtilLite.getAllLiteSetBuilder().build();
+ TestAllTypesLite two = TestAllTypesLite.parseFrom(one.toByteArray());
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+
assertEquals(
- 11, (int) extendableMessage.getExtension(
- UnittestLite.optionalFixed32ExtensionLite));
+ one.toBuilder().mergeFrom(two).build(),
+ two.toBuilder().mergeFrom(two.toByteArray()).build());
+ }
+
+ public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException {
+ Foo fooWithOnlyValue = Foo.newBuilder().setValue(1).build();
+
+ Foo fooWithValueAndExtension =
+ fooWithOnlyValue
+ .toBuilder()
+ .setValue(1)
+ .setExtension(Bar.fooExt, Bar.newBuilder().setName("name").build())
+ .build();
+
+ Foo fooWithValueAndUnknownFields = Foo.parseFrom(fooWithValueAndExtension.toByteArray());
+
+ assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndUnknownFields);
+ assertEqualsAndHashCodeAreFalse(fooWithValueAndExtension, fooWithValueAndUnknownFields);
+ }
+
+ public void testEqualsAndHashCodeWithExtensions() throws InvalidProtocolBufferException {
+ Foo fooWithOnlyValue = Foo.newBuilder().setValue(1).build();
+
+ Foo fooWithValueAndExtension =
+ fooWithOnlyValue
+ .toBuilder()
+ .setValue(1)
+ .setExtension(Bar.fooExt, Bar.newBuilder().setName("name").build())
+ .build();
+
+ assertEqualsAndHashCodeAreFalse(fooWithOnlyValue, fooWithValueAndExtension);
+ }
+
+ // Test to ensure we avoid a class cast exception with oneofs.
+ public void testEquals_oneOfMessages() {
+ TestAllTypesLite mine = TestAllTypesLite.newBuilder().setOneofString("Hello").build();
+
+ TestAllTypesLite other =
+ TestAllTypesLite.newBuilder()
+ .setOneofNestedMessage(NestedMessage.getDefaultInstance())
+ .build();
+
+ assertFalse(mine.equals(other));
+ assertFalse(other.equals(mine));
+ }
+
+ public void testHugeFieldNumbers() throws InvalidProtocolBufferException {
+ TestHugeFieldNumbersLite message =
+ TestHugeFieldNumbersLite.newBuilder()
+ .setOptionalInt32(1)
+ .addRepeatedInt32(2)
+ .setOptionalEnum(ForeignEnumLite.FOREIGN_LITE_FOO)
+ .setOptionalString("xyz")
+ .setOptionalMessage(ForeignMessageLite.newBuilder().setC(3).build())
+ .build();
+
+ TestHugeFieldNumbersLite parsedMessage =
+ TestHugeFieldNumbersLite.parseFrom(message.toByteArray());
+ assertEquals(1, parsedMessage.getOptionalInt32());
+ assertEquals(2, parsedMessage.getRepeatedInt32(0));
+ assertEquals(ForeignEnumLite.FOREIGN_LITE_FOO, parsedMessage.getOptionalEnum());
+ assertEquals("xyz", parsedMessage.getOptionalString());
+ assertEquals(3, parsedMessage.getOptionalMessage().getC());
+ }
+
+ private void assertEqualsAndHashCodeAreFalse(Object o1, Object o2) {
+ assertFalse(o1.equals(o2));
+ assertFalse(o1.hashCode() == o2.hashCode());
+ }
+
+ public void testRecursiveHashcode() {
+ // This tests that we don't infinite loop.
+ TestRecursiveOneof.getDefaultInstance().hashCode();
+ }
+
+ public void testParseFromByteBuffer() throws Exception {
+ TestAllTypesLite message =
+ TestAllTypesLite.newBuilder()
+ .setOptionalInt32(123)
+ .addRepeatedString("hello")
+ .setOptionalNestedMessage(TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
+ .build();
+
+ TestAllTypesLite copy =
+ TestAllTypesLite.parseFrom(message.toByteString().asReadOnlyByteBuffer());
+
+ assertEquals(message, copy);
+ }
+
+ public void testParseFromByteBufferThrows() {
+ try {
+ TestAllTypesLite.parseFrom(ByteBuffer.wrap(new byte[] {0x5}));
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ }
+
+ TestAllTypesLite message =
+ TestAllTypesLite.newBuilder().setOptionalInt32(123).addRepeatedString("hello").build();
+
+ ByteBuffer buffer = ByteBuffer.wrap(message.toByteArray(), 0, message.getSerializedSize() - 1);
+ try {
+ TestAllTypesLite.parseFrom(buffer);
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ assertEquals(
+ TestAllTypesLite.newBuilder().setOptionalInt32(123).build(),
+ expected.getUnfinishedMessage());
+ }
+ }
+
+ public void testParseFromByteBuffer_extensions() throws Exception {
+ TestAllExtensionsLite message =
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
+ .setExtension(
+ UnittestLite.optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ)
+ .setExtension(
+ UnittestLite.optionalNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
+ .build();
+
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ UnittestLite.registerAllExtensions(registry);
+
+ TestAllExtensionsLite copy =
+ TestAllExtensionsLite.parseFrom(message.toByteString().asReadOnlyByteBuffer(), registry);
+
+ assertEquals(message, copy);
+ }
+
+ public void testParseFromByteBufferThrows_extensions() {
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ UnittestLite.registerAllExtensions(registry);
+ try {
+ TestAllExtensionsLite.parseFrom(ByteBuffer.wrap(new byte[] {0x5}), registry);
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ }
+
+ TestAllExtensionsLite message =
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .addExtension(UnittestLite.repeatedStringExtensionLite, "hello")
+ .build();
+
+ ByteBuffer buffer = ByteBuffer.wrap(message.toByteArray(), 0, message.getSerializedSize() - 1);
+ try {
+ TestAllExtensionsLite.parseFrom(buffer, registry);
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ assertEquals(
+ TestAllExtensionsLite.newBuilder()
+ .setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
+ .build(),
+ expected.getUnfinishedMessage());
+ }
+ }
+
+ // Make sure we haven't screwed up the code generation for packing fields by default.
+ public void testPackedSerialization() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ builder.addRepeatedInt32(4321);
+ builder.addRepeatedNestedEnum(TestAllTypes.NestedEnum.BAZ);
+ TestAllTypes message = builder.build();
+
+ CodedInputStream in = CodedInputStream.newInstance(message.toByteArray());
+
+ while (!in.isAtEnd()) {
+ int tag = in.readTag();
+ assertEquals(WireFormat.WIRETYPE_LENGTH_DELIMITED, WireFormat.getTagWireType(tag));
+ in.skipField(tag);
+ }
+ }
+
+ public void testAddAllIteratesOnce() {
+ TestAllTypesLite.newBuilder()
+ .addAllRepeatedBool(new OneTimeIterableList(false))
+ .addAllRepeatedInt32(new OneTimeIterableList(0))
+ .addAllRepeatedInt64(new OneTimeIterableList(0L))
+ .addAllRepeatedFloat(new OneTimeIterableList(0f))
+ .addAllRepeatedDouble(new OneTimeIterableList(0d))
+ .addAllRepeatedBytes(new OneTimeIterableList(ByteString.EMPTY))
+ .addAllRepeatedString(new OneTimeIterableList(""))
+ .addAllRepeatedNestedMessage(new OneTimeIterableList(NestedMessage.getDefaultInstance()))
+ .addAllRepeatedBool(new OneTimeIterable(false))
+ .addAllRepeatedInt32(new OneTimeIterable(0))
+ .addAllRepeatedInt64(new OneTimeIterable(0L))
+ .addAllRepeatedFloat(new OneTimeIterable(0f))
+ .addAllRepeatedDouble(new OneTimeIterable(0d))
+ .addAllRepeatedBytes(new OneTimeIterable(ByteString.EMPTY))
+ .addAllRepeatedString(new OneTimeIterable(""))
+ .addAllRepeatedNestedMessage(new OneTimeIterable(NestedMessage.getDefaultInstance()))
+ .build();
+ }
+
+ public void testAddAllIteratesOnce_throwsOnNull() {
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
+ try {
+ builder.addAllRepeatedBool(new OneTimeIterableList(true, false, (Boolean) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 2 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedBoolCount());
+ }
+
+ try {
+ builder.addAllRepeatedBool(new OneTimeIterable(true, false, (Boolean) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 2 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedBoolCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedBool(new OneTimeIterableList((Boolean) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedBoolCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedInt32(new OneTimeIterableList((Integer) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedInt32Count());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedInt64(new OneTimeIterableList((Long) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedInt64Count());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedFloat(new OneTimeIterableList((Float) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedFloatCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedDouble(new OneTimeIterableList((Double) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedDoubleCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedBytes(new OneTimeIterableList((ByteString) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedBytesCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedString(new OneTimeIterableList("", "", (String) null, ""));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 2 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedStringCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedString(new OneTimeIterable("", "", (String) null, ""));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 2 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedStringCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedString(new OneTimeIterableList((String) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedStringCount());
+ }
+
+ try {
+ builder = TestAllTypesLite.newBuilder();
+ builder.addAllRepeatedNestedMessage(new OneTimeIterableList((NestedMessage) null));
+ fail();
+ } catch (NullPointerException expected) {
+ assertEquals("Element at index 0 is null.", expected.getMessage());
+ assertEquals(0, builder.getRepeatedNestedMessageCount());
+ }
+ }
+
+ private static final class OneTimeIterableList<T> extends ArrayList<T> {
+ private boolean wasIterated = false;
+
+ OneTimeIterableList(T... contents) {
+ addAll(Arrays.asList(contents));
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ if (wasIterated) {
+ fail();
+ }
+ wasIterated = true;
+ return super.iterator();
+ }
+ }
+
+ private static final class OneTimeIterable<T> implements Iterable<T> {
+ private final List<T> list;
+ private boolean wasIterated = false;
+
+ OneTimeIterable(T... contents) {
+ list = Arrays.asList(contents);
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ if (wasIterated) {
+ fail();
+ }
+ wasIterated = true;
+ return list.iterator();
+ }
+ }
+
+ public void testNullExtensionRegistry() throws Exception {
+ try {
+ TestAllTypesLite.parseFrom(new byte[] {}, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java
index 68b55ceb..eac47448 100644
--- a/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java
@@ -30,8 +30,6 @@
package com.google.protobuf;
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
@@ -45,11 +43,12 @@ import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
+import junit.framework.TestCase;
/**
- * Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}.
- * This class is designed to be extended for testing extensions of {@link LiteralByteString}
- * such as {@link BoundedByteString}, see {@link BoundedByteStringTest}.
+ * Test {@code LiteralByteString} by setting up a reference string in {@link #setUp()}.
+ * This class is designed to be extended for testing extensions of {@code LiteralByteString}
+ * such as {@code BoundedByteString}, see {@link BoundedByteStringTest}.
*
* @author carlanton@google.com (Carl Haverl)
*/
@@ -304,25 +303,75 @@ public class LiteralByteStringTest extends TestCase {
Arrays.equals(referenceBytes, roundTripBytes));
}
- public void testWriteTo_mutating() throws IOException {
+ public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException {
OutputStream os = new OutputStream() {
@Override
public void write(byte[] b, int off, int len) {
- for (int x = 0; x < len; ++x) {
- b[off + x] = (byte) 0;
- }
+ Arrays.fill(b, off, off + len, (byte) 0);
}
@Override
public void write(int b) {
- // Purposefully left blank.
+ throw new UnsupportedOperationException();
}
};
stringUnderTest.writeTo(os);
- byte[] newBytes = stringUnderTest.toByteArray();
assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array",
- Arrays.equals(referenceBytes, newBytes));
+ Arrays.equals(referenceBytes, stringUnderTest.toByteArray()));
+ }
+
+ public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException {
+ OutputStream os = new OutputStream() {
+ @Override
+ public void write(byte[] b, int off, int len) {
+ Arrays.fill(b, off, off + len, (byte) 0);
+ }
+
+ @Override
+ public void write(int b) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ stringUnderTest.writeToInternal(os, 0, stringUnderTest.size());
+ byte[] allZeros = new byte[stringUnderTest.size()];
+ assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array",
+ Arrays.equals(allZeros, stringUnderTest.toByteArray()));
+ }
+
+ public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException {
+ ByteOutput out = new ByteOutput() {
+ @Override
+ public void write(byte value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ Arrays.fill(value, offset, offset + length, (byte) 0);
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ stringUnderTest.writeTo(out);
+ byte[] allZeros = new byte[stringUnderTest.size()];
+ assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array",
+ Arrays.equals(allZeros, stringUnderTest.toByteArray()));
}
public void testNewOutput() throws IOException {
diff --git a/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
index 1bd094f7..e50c7d1e 100644
--- a/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/LongArrayListTest.java
@@ -32,70 +32,57 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
+import com.google.protobuf.Internal.LongList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
+import junit.framework.TestCase;
/**
* Tests for {@link LongArrayList}.
- *
+ *
* @author dweis@google.com (Daniel Weis)
*/
public class LongArrayListTest extends TestCase {
-
- private static final LongArrayList UNARY_LIST = newImmutableLongArrayList(1);
+
+ private static final LongArrayList UNARY_LIST =
+ newImmutableLongArrayList(1);
private static final LongArrayList TERTIARY_LIST =
newImmutableLongArrayList(1, 2, 3);
-
+
private LongArrayList list;
-
+
@Override
protected void setUp() throws Exception {
list = new LongArrayList();
}
-
+
public void testEmptyListReturnsSameInstance() {
assertSame(LongArrayList.emptyList(), LongArrayList.emptyList());
}
-
+
public void testEmptyListIsImmutable() {
assertImmutable(LongArrayList.emptyList());
}
-
+
public void testMakeImmutable() {
- list.addLong(2);
+ list.addLong(3);
list.addLong(4);
- list.addLong(6);
- list.addLong(8);
+ list.addLong(5);
+ list.addLong(7);
list.makeImmutable();
assertImmutable(list);
}
-
- public void testCopyConstructor() {
- LongArrayList copy = new LongArrayList(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new LongArrayList(LongArrayList.emptyList());
- assertEquals(LongArrayList.emptyList(), copy);
-
- copy = new LongArrayList(asList(1L, 2L, 3L));
- assertEquals(asList(1L, 2L, 3L), copy);
-
- copy = new LongArrayList(Collections.<Long>emptyList());
- assertEquals(LongArrayList.emptyList(), copy);
- }
-
+
public void testModificationWithIteration() {
list.addAll(asList(1L, 2L, 3L, 4L));
Iterator<Long> iterator = list.iterator();
assertEquals(4, list.size());
- assertEquals(1, (long) list.get(0));
- assertEquals(1, (long) iterator.next());
+ assertEquals(1L, (long) list.get(0));
+ assertEquals(1L, (long) iterator.next());
list.set(0, 1L);
- assertEquals(2, (long) iterator.next());
-
+ assertEquals(2L, (long) iterator.next());
+
list.remove(0);
try {
iterator.next();
@@ -103,7 +90,7 @@ public class LongArrayListTest extends TestCase {
} catch (ConcurrentModificationException e) {
// expected
}
-
+
iterator = list.iterator();
list.add(0, 0L);
try {
@@ -113,19 +100,19 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGet() {
- assertEquals(1, (long) TERTIARY_LIST.get(0));
- assertEquals(2, (long) TERTIARY_LIST.get(1));
- assertEquals(3, (long) TERTIARY_LIST.get(2));
-
+ assertEquals(1L, (long) TERTIARY_LIST.get(0));
+ assertEquals(2L, (long) TERTIARY_LIST.get(1));
+ assertEquals(3L, (long) TERTIARY_LIST.get(2));
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -133,19 +120,19 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
public void testGetLong() {
- assertEquals(1, TERTIARY_LIST.getLong(0));
- assertEquals(2, TERTIARY_LIST.getLong(1));
- assertEquals(3, TERTIARY_LIST.getLong(2));
-
+ assertEquals(1L, TERTIARY_LIST.getLong(0));
+ assertEquals(2L, TERTIARY_LIST.getLong(1));
+ assertEquals(3L, TERTIARY_LIST.getLong(2));
+
try {
TERTIARY_LIST.get(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
TERTIARY_LIST.get(3);
fail();
@@ -153,35 +140,35 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSize() {
assertEquals(0, LongArrayList.emptyList().size());
assertEquals(1, UNARY_LIST.size());
assertEquals(3, TERTIARY_LIST.size());
- list.addLong(2);
+ list.addLong(3);
list.addLong(4);
list.addLong(6);
list.addLong(8);
assertEquals(4, list.size());
-
+
list.remove(0);
assertEquals(3, list.size());
-
- list.add(16L);
+
+ list.add(17L);
assertEquals(4, list.size());
}
-
+
public void testSet() {
list.addLong(2);
list.addLong(4);
-
- assertEquals(2, (long) list.set(0, 0L));
- assertEquals(0, list.getLong(0));
- assertEquals(4, (long) list.set(1, 0L));
- assertEquals(0, list.getLong(1));
-
+ assertEquals(2L, (long) list.set(0, 3L));
+ assertEquals(3L, list.getLong(0));
+
+ assertEquals(4L, (long) list.set(1, 0L));
+ assertEquals(0L, list.getLong(1));
+
try {
list.set(-1, 0L);
fail();
@@ -196,17 +183,17 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
public void testSetLong() {
- list.addLong(2);
- list.addLong(4);
-
- assertEquals(2, list.setLong(0, 0));
- assertEquals(0, list.getLong(0));
+ list.addLong(1);
+ list.addLong(3);
+
+ assertEquals(1L, list.setLong(0, 0));
+ assertEquals(0L, list.getLong(0));
+
+ assertEquals(3L, list.setLong(1, 0));
+ assertEquals(0L, list.getLong(1));
- assertEquals(4, list.setLong(1, 0));
- assertEquals(0, list.getLong(1));
-
try {
list.setLong(-1, 0);
fail();
@@ -221,7 +208,7 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
public void testAdd() {
assertEquals(0, list.size());
@@ -231,28 +218,30 @@ public class LongArrayListTest extends TestCase {
assertTrue(list.add(3L));
list.add(0, 4L);
assertEquals(asList(4L, 2L, 3L), list);
-
+
list.add(0, 1L);
list.add(0, 0L);
// Force a resize by getting up to 11 elements.
for (int i = 0; i < 6; i++) {
list.add(Long.valueOf(5 + i));
}
- assertEquals(asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L), list);
-
+ assertEquals(
+ asList(0L, 1L, 4L, 2L, 3L, 5L, 6L, 7L, 8L, 9L, 10L),
+ list);
+
try {
list.add(-1, 5L);
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.add(4, 5L);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
public void testAddLong() {
assertEquals(0, list.size());
@@ -262,128 +251,142 @@ public class LongArrayListTest extends TestCase {
list.addLong(3);
assertEquals(asList(2L, 3L), list);
}
-
+
public void testAddAll() {
assertEquals(0, list.size());
assertTrue(list.addAll(Collections.singleton(1L)));
assertEquals(1, list.size());
- assertEquals(1, (long) list.get(0));
- assertEquals(1, list.getLong(0));
-
+ assertEquals(1L, (long) list.get(0));
+ assertEquals(1L, list.getLong(0));
+
assertTrue(list.addAll(asList(2L, 3L, 4L, 5L, 6L)));
assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L), list);
-
+
assertTrue(list.addAll(TERTIARY_LIST));
assertEquals(asList(1L, 2L, 3L, 4L, 5L, 6L, 1L, 2L, 3L), list);
assertFalse(list.addAll(Collections.<Long>emptyList()));
assertFalse(list.addAll(LongArrayList.emptyList()));
}
-
+
public void testRemove() {
list.addAll(TERTIARY_LIST);
- assertEquals(1, (long) list.remove(0));
+ assertEquals(1L, (long) list.remove(0));
assertEquals(asList(2L, 3L), list);
- assertTrue(list.remove(3L));
+ assertTrue(list.remove(Long.valueOf(3)));
assertEquals(asList(2L), list);
- assertFalse(list.remove(3L));
+ assertFalse(list.remove(Long.valueOf(3)));
assertEquals(asList(2L), list);
- assertEquals(2, (long) list.remove(0));
+ assertEquals(2L, (long) list.remove(0));
assertEquals(asList(), list);
-
+
try {
list.remove(-1);
fail();
} catch (IndexOutOfBoundsException e) {
// expected
}
-
+
try {
list.remove(0);
} catch (IndexOutOfBoundsException e) {
// expected
}
}
-
+
+ public void testRemoveEndOfCapacity() {
+ LongList toRemove = LongArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addLong(3);
+ toRemove.remove(0);
+ assertEquals(0, toRemove.size());
+ }
+
+ public void testSublistRemoveEndOfCapacity() {
+ LongList toRemove = LongArrayList.emptyList().mutableCopyWithCapacity(1);
+ toRemove.addLong(3);
+ toRemove.subList(0, 1).clear();
+ assertEquals(0, toRemove.size());
+ }
+
private void assertImmutable(LongArrayList list) {
if (list.contains(1L)) {
throw new RuntimeException("Cannot test the immutability of lists that contain 1.");
}
-
+
try {
list.add(1L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.add(0, 1L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(Collections.singletonList(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(new LongArrayList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addAll(0, Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.addLong(0);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.clear();
fail();
@@ -397,63 +400,63 @@ public class LongArrayListTest extends TestCase {
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.remove(new Object());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.removeAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.<Long>emptyList());
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(Collections.singleton(1L));
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.retainAll(UNARY_LIST);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.set(0, 0L);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
-
+
try {
list.setLong(0, 0);
fail();
@@ -461,7 +464,7 @@ public class LongArrayListTest extends TestCase {
// expected
}
}
-
+
private static LongArrayList newImmutableLongArrayList(long... elements) {
LongArrayList list = new LongArrayList();
for (long element : elements) {
diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
index 3d8c9bc4..da9195f9 100644
--- a/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
@@ -30,48 +30,56 @@
package com.google.protobuf;
+import map_lite_test.MapForProto2TestProto.BizarroTestMap;
import map_lite_test.MapForProto2TestProto.TestMap;
import map_lite_test.MapForProto2TestProto.TestMap.MessageValue;
+import map_lite_test.MapForProto2TestProto.TestMapOrBuilder;
import map_lite_test.MapForProto2TestProto.TestUnknownEnumValue;
-
-import junit.framework.TestCase;
-
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import junit.framework.TestCase;
/**
* Unit tests for map fields.
*/
-public class MapForProto2LiteTest extends TestCase {
+public final class MapForProto2LiteTest extends TestCase {
+
private void setMapValues(TestMap.Builder builder) {
- builder.getMutableInt32ToInt32Field().put(1, 11);
- builder.getMutableInt32ToInt32Field().put(2, 22);
- builder.getMutableInt32ToInt32Field().put(3, 33);
-
- builder.getMutableInt32ToStringField().put(1, "11");
- builder.getMutableInt32ToStringField().put(2, "22");
- builder.getMutableInt32ToStringField().put(3, "33");
-
- builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
- builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
- builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
- builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
- builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
- builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
- builder.getMutableInt32ToMessageField().put(
- 1, MessageValue.newBuilder().setValue(11).build());
- builder.getMutableInt32ToMessageField().put(
- 2, MessageValue.newBuilder().setValue(22).build());
- builder.getMutableInt32ToMessageField().put(
- 3, MessageValue.newBuilder().setValue(33).build());
-
- builder.getMutableStringToInt32Field().put("1", 11);
- builder.getMutableStringToInt32Field().put("2", 22);
- builder.getMutableStringToInt32Field().put("3", 33);
+ builder
+ .putInt32ToInt32Field(1, 11)
+ .putInt32ToInt32Field(2, 22)
+ .putInt32ToInt32Field(3, 33)
+
+ .putInt32ToStringField(1, "11")
+ .putInt32ToStringField(2, "22")
+ .putInt32ToStringField(3, "33")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("11"))
+ .putInt32ToBytesField(2, TestUtil.toBytes("22"))
+ .putInt32ToBytesField(3, TestUtil.toBytes("33"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.FOO)
+ .putInt32ToEnumField(2, TestMap.EnumValue.BAR)
+ .putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
+ .putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
+
+ .putStringToInt32Field("1", 11)
+ .putStringToInt32Field("2", 22)
+ .putStringToInt32Field("3", 33);
+ }
+
+ public void testSetMapValues() {
+ TestMap.Builder mapBuilder = TestMap.newBuilder();
+ setMapValues(mapBuilder);
+ TestMap map = mapBuilder.build();
+ assertMapValuesSet(map);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
@@ -94,22 +102,22 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals("11", message.getInt32ToStringField().get(1));
assertEquals("22", message.getInt32ToStringField().get(2));
assertEquals("33", message.getInt32ToStringField().get(3));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
@@ -117,31 +125,42 @@ public class MapForProto2LiteTest extends TestCase {
}
private void updateMapValues(TestMap.Builder builder) {
- builder.getMutableInt32ToInt32Field().put(1, 111);
- builder.getMutableInt32ToInt32Field().remove(2);
- builder.getMutableInt32ToInt32Field().put(4, 44);
-
- builder.getMutableInt32ToStringField().put(1, "111");
- builder.getMutableInt32ToStringField().remove(2);
- builder.getMutableInt32ToStringField().put(4, "44");
-
- builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
- builder.getMutableInt32ToBytesField().remove(2);
- builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
- builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
- builder.getMutableInt32ToEnumField().remove(2);
- builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
- builder.getMutableInt32ToMessageField().put(
- 1, MessageValue.newBuilder().setValue(111).build());
- builder.getMutableInt32ToMessageField().remove(2);
- builder.getMutableInt32ToMessageField().put(
- 4, MessageValue.newBuilder().setValue(44).build());
-
- builder.getMutableStringToInt32Field().put("1", 111);
- builder.getMutableStringToInt32Field().remove("2");
- builder.getMutableStringToInt32Field().put("4", 44);
+ builder
+ .putInt32ToInt32Field(1, 111)
+ .removeInt32ToInt32Field(2)
+ .putInt32ToInt32Field(4, 44)
+
+ .putInt32ToStringField(1, "111")
+ .removeInt32ToStringField(2)
+ .putInt32ToStringField(4, "44")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("111"))
+ .removeInt32ToBytesField(2)
+ .putInt32ToBytesField(4, TestUtil.toBytes("44"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.BAR)
+ .removeInt32ToEnumField(2)
+ .putInt32ToEnumField(4, TestMap.EnumValue.QUX)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
+ .removeInt32ToMessageField(2)
+ .putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
+
+ .putStringToInt32Field("1", 111)
+ .removeStringToInt32Field("2")
+ .putStringToInt32Field("4", 44);
+ }
+
+ public void testUpdateMapValues() {
+ TestMap.Builder mapBuilder = TestMap.newBuilder();
+ setMapValues(mapBuilder);
+ TestMap map = mapBuilder.build();
+ assertMapValuesSet(map);
+
+ mapBuilder = map.toBuilder();
+ updateMapValues(mapBuilder);
+ map = mapBuilder.build();
+ assertMapValuesUpdated(map);
}
private void assertMapValuesUpdated(TestMap message) {
@@ -154,188 +173,149 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals("111", message.getInt32ToStringField().get(1));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals("44", message.getInt32ToStringField().get(4));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
- private void assertMapValuesCleared(TestMap message) {
- assertEquals(0, message.getInt32ToInt32Field().size());
- assertEquals(0, message.getInt32ToStringField().size());
- assertEquals(0, message.getInt32ToBytesField().size());
- assertEquals(0, message.getInt32ToEnumField().size());
- assertEquals(0, message.getInt32ToMessageField().size());
- assertEquals(0, message.getStringToInt32Field().size());
+ private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
}
public void testSanityCopyOnWrite() throws InvalidProtocolBufferException {
// Since builders are implemented as a thin wrapper around a message
// instance, we attempt to verify that we can't cause the builder to modify
// a produced message.
-
+
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
- Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
- intMap.put(1, 2);
+ builder.putInt32ToInt32Field(1, 2);
assertTrue(message.getInt32ToInt32Field().isEmpty());
message = builder.build();
- try {
- intMap.put(2, 3);
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
- builder.getMutableInt32ToInt32Field().put(2, 3);
+ builder.putInt32ToInt32Field(2, 3);
assertEquals(newMap(1, 2), message.getInt32ToInt32Field());
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
}
-
- public void testMutableMapLifecycle() {
+
+ public void testGetMapIsImmutable() {
TestMap.Builder builder = TestMap.newBuilder();
- Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
- intMap.put(1, 2);
- assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+
+ setMapValues(builder);
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+ }
+
+ private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
+ assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
+ assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
+ assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
+ assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
+ assertImmutable(
+ testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
+ assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
+ }
+
+ private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
try {
- intMap.put(2, 3);
+ map.put(key, value);
fail();
} catch (UnsupportedOperationException e) {
// expected
}
+ if (!map.isEmpty()) {
+ try {
+ map.entrySet().remove(map.entrySet().iterator().next());
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+ }
+
+ public void testMutableMapLifecycle() {
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2);
+ assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
- builder.getMutableInt32ToInt32Field().put(2, 3);
+ builder.putInt32ToInt32Field(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
- Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
- enumMap.put(1, TestMap.EnumValue.BAR);
+ builder.putInt32ToEnumField(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
- try {
- enumMap.put(2, TestMap.EnumValue.FOO);
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.getInt32ToEnumField());
- builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.FOO);
+ builder.putInt32ToEnumField(2, TestMap.EnumValue.FOO);
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
-
- Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
- stringMap.put(1, "1");
+
+ builder.putInt32ToStringField(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
- try {
- stringMap.put(2, "2");
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
- builder.getMutableInt32ToStringField().put(2, "2");
- assertEquals(
- newMap(1, "1", 2, "2"),
- builder.getInt32ToStringField());
-
- Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
- messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
+ builder.putInt32ToStringField(2, "2");
+ assertEquals(newMap(1, "1", 2, "2"), builder.getInt32ToStringField());
+
+ builder.putInt32ToMessageField(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.build().getInt32ToMessageField());
- try {
- messageMap.put(2, TestMap.MessageValue.getDefaultInstance());
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
- builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ builder.putInt32ToMessageField(2, TestMap.MessageValue.getDefaultInstance());
assertEquals(
newMap(1, TestMap.MessageValue.getDefaultInstance(),
2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
}
- public void testMutableMapLifecycle_collections() {
- TestMap.Builder builder = TestMap.newBuilder();
- Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
- intMap.put(1, 2);
- assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
- try {
- intMap.remove(2);
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- try {
- intMap.entrySet().remove(new Object());
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- try {
- intMap.entrySet().iterator().remove();
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- try {
- intMap.keySet().remove(new Object());
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- try {
- intMap.values().remove(new Object());
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- try {
- intMap.values().iterator().remove();
- fail();
- } catch (UnsupportedOperationException e) {
- // expected
- }
- assertEquals(newMap(1, 2), intMap);
- assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
- assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
- }
-
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
-
+
builder = message.toBuilder();
setMapValues(builder);
message = builder.build();
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
+ assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
@@ -344,12 +324,52 @@ public class MapForProto2LiteTest extends TestCase {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build();
+ assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
assertMapValuesSet(destination.build());
}
+ public void testPutChecksNullKeysAndValues() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToMessageField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putStringToInt32Field(null, 1);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+ }
+
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
@@ -357,14 +377,14 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
message = builder.build();
@@ -372,12 +392,61 @@ public class MapForProto2LiteTest extends TestCase {
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
-
+
+ private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
+ bizarroMap.writeTo(output);
+ output.flush();
+ return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
+ }
+
+ public void testParseError() throws Exception {
+ ByteString bytes = TestUtil.toBytes("SOME BYTES");
+ String stringKey = "a string key";
+
+ TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToInt32Field(5, bytes)
+ .build());
+ assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToStringField(stringKey, 5)
+ .build());
+ assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToBytesField(stringKey, 5)
+ .build());
+ assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToEnumField(stringKey, bytes)
+ .build());
+ assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
+
+ try {
+ tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToMessageField(stringKey, bytes)
+ .build());
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
+ map = (TestMap) expected.getUnfinishedMessage();
+ assertTrue(map.getInt32ToMessageField().isEmpty());
+ }
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putStringToInt32Field(stringKey, bytes)
+ .build());
+ assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
+ }
+
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
-
+
TestMap.Builder other = TestMap.newBuilder();
other.mergeFrom(message);
assertMapValuesSet(other.build());
@@ -386,26 +455,26 @@ public class MapForProto2LiteTest extends TestCase {
public void testEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
-
+
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
- TestMap.Builder b1 = TestMap.newBuilder();
- b1.getMutableInt32ToInt32Field().put(1, 2);
- b1.getMutableInt32ToInt32Field().put(3, 4);
- b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap.Builder b1 = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
-
- TestMap.Builder b2 = TestMap.newBuilder();
- b2.getMutableInt32ToInt32Field().put(5, 6);
- b2.getMutableInt32ToInt32Field().put(1, 2);
- b2.getMutableInt32ToInt32Field().put(3, 4);
+
+ TestMap.Builder b2 = TestMap.newBuilder()
+ .putInt32ToInt32Field(5, 6)
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
-
+
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
-
+
// Make sure we did compare map fields.
- b2.getMutableInt32ToInt32Field().put(1, 0);
+ b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
@@ -413,10 +482,9 @@ public class MapForProto2LiteTest extends TestCase {
}
public void testUnknownEnumValues() throws Exception {
- TestUnknownEnumValue.Builder builder =
- TestUnknownEnumValue.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 1);
- builder.getMutableInt32ToInt32Field().put(2, 54321);
+ TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.newBuilder()
+ .putInt32ToInt32Field(1, 1)
+ .putInt32ToInt32Field(2, 54321);
ByteString data = builder.build().toByteString();
TestMap message = TestMap.parseFrom(data);
@@ -432,7 +500,6 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue());
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
}
-
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
@@ -442,17 +509,288 @@ public class MapForProto2LiteTest extends TestCase {
assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet()));
}
-
+
private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
return map;
}
-
+
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
map.put(key2, value2);
return map;
}
+
+ public void testGetMap() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ TestMap message = builder.build();
+ assertEquals(
+ message.getStringToInt32Field(),
+ message.getStringToInt32FieldMap());
+ assertEquals(
+ message.getInt32ToBytesField(),
+ message.getInt32ToBytesFieldMap());
+ assertEquals(
+ message.getInt32ToEnumField(),
+ message.getInt32ToEnumFieldMap());
+ assertEquals(
+ message.getInt32ToMessageField(),
+ message.getInt32ToMessageFieldMap());
+ }
+
+ public void testContains() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ assertMapContainsSetValues(builder);
+ assertMapContainsSetValues(builder.build());
+ }
+
+ private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
+ assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
+
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
+ assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
+ }
+
+ public void testCount() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+
+ setMapValues(builder);
+ assertMapCounts(3, builder);
+
+ TestMap message = builder.build();
+ assertMapCounts(3, message);
+
+ builder = message.toBuilder().putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ assertEquals(4, builder.build().getInt32ToInt32FieldCount());
+
+ // already present - should be unchanged
+ builder.putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ }
+
+ private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
+ }
+
+ public void testGetOrDefault() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValues(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
+ assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
+ assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
+ assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testGetOrThrow() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValues(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testPut() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.putInt32ToInt32Field(1, 11);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+
+ builder.putInt32ToStringField(1, "a");
+ assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putStringToInt32Field("a", 1);
+ assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
+ try {
+ builder.putStringToInt32Field(null, -1);
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testRemove() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValues(builder);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToInt32Field(1);
+ assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
+ }
+
+ assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToStringField(1);
+ assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToBytesField(1);
+ assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToEnumField(1);
+ assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
+ }
+
+ assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
+ for (int times = 0; times < 2; times++) {
+ builder.removeStringToInt32Field("1");
+ assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
+ }
+
+ try {
+ builder.removeStringToInt32Field(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
index 1fa3cbdb..bcfd927c 100644
--- a/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
+++ b/java/core/src/test/java/com/google/protobuf/MapForProto2Test.java
@@ -31,53 +31,99 @@
package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import map_test.MapForProto2TestProto.BizarroTestMap;
+import map_test.MapForProto2TestProto.ReservedAsMapField;
+import map_test.MapForProto2TestProto.ReservedAsMapFieldWithEnumValue;
import map_test.MapForProto2TestProto.TestMap;
import map_test.MapForProto2TestProto.TestMap.MessageValue;
import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields;
+import map_test.MapForProto2TestProto.TestMapOrBuilder;
import map_test.MapForProto2TestProto.TestRecursiveMap;
import map_test.MapForProto2TestProto.TestUnknownEnumValue;
-
-import junit.framework.TestCase;
-
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import junit.framework.TestCase;
/**
* Unit tests for map fields in proto2 protos.
*/
public class MapForProto2Test extends TestCase {
- private void setMapValues(TestMap.Builder builder) {
+
+ private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
-
+ //
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
}
+ private void setMapValuesUsingAccessors(TestMap.Builder builder) {
+ builder
+ .putInt32ToInt32Field(1, 11)
+ .putInt32ToInt32Field(2, 22)
+ .putInt32ToInt32Field(3, 33)
+
+ .putInt32ToStringField(1, "11")
+ .putInt32ToStringField(2, "22")
+ .putInt32ToStringField(3, "33")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("11"))
+ .putInt32ToBytesField(2, TestUtil.toBytes("22"))
+ .putInt32ToBytesField(3, TestUtil.toBytes("33"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.FOO)
+ .putInt32ToEnumField(2, TestMap.EnumValue.BAR)
+ .putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
+ .putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
+
+ .putStringToInt32Field("1", 11)
+ .putStringToInt32Field("2", 22)
+ .putStringToInt32Field("3", 33);
+ }
+
+ public void testSetMapValues() {
+ TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
+ setMapValuesUsingMutableMap(usingMutableMapBuilder);
+ TestMap usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesSet(usingMutableMap);
+
+ TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(usingAccessorsBuilder);
+ TestMap usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesSet(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ }
+
private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@@ -88,7 +134,7 @@ public class MapForProto2Test extends TestCase {
.putAllStringToInt32Field(source.getStringToInt32Field());
}
- private void assertMapValuesSet(TestMap message) {
+ private void assertMapValuesSet(TestMapOrBuilder message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
assertEquals(22, message.getInt32ToInt32Field().get(2).intValue());
@@ -98,56 +144,109 @@ public class MapForProto2Test extends TestCase {
assertEquals("11", message.getInt32ToStringField().get(1));
assertEquals("22", message.getInt32ToStringField().get(2));
assertEquals("33", message.getInt32ToStringField().get(3));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("11"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("22"), message.getInt32ToBytesField().get(2));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.FOO, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(2));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(11, message.getInt32ToMessageField().get(1).getValue());
assertEquals(22, message.getInt32ToMessageField().get(2).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(11, message.getStringToInt32Field().get("1").intValue());
assertEquals(22, message.getStringToInt32Field().get("2").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
}
- private void updateMapValues(TestMap.Builder builder) {
+ private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
-
+ //
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
}
+ private void updateMapValuesUsingAccessors(TestMap.Builder builder) {
+ builder
+ .putInt32ToInt32Field(1, 111)
+ .removeInt32ToInt32Field(2)
+ .putInt32ToInt32Field(4, 44)
+
+ .putInt32ToStringField(1, "111")
+ .removeInt32ToStringField(2)
+ .putInt32ToStringField(4, "44")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("111"))
+ .removeInt32ToBytesField(2)
+ .putInt32ToBytesField(4, TestUtil.toBytes("44"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.BAR)
+ .removeInt32ToEnumField(2)
+ .putInt32ToEnumField(4, TestMap.EnumValue.QUX)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
+ .removeInt32ToMessageField(2)
+ .putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
+
+ .putStringToInt32Field("1", 111)
+ .removeStringToInt32Field("2")
+ .putStringToInt32Field("4", 44);
+ }
+
+ public void testUpdateMapValues() {
+ TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
+ setMapValuesUsingMutableMap(usingMutableMapBuilder);
+ TestMap usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesSet(usingMutableMap);
+
+ TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(usingAccessorsBuilder);
+ TestMap usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesSet(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ //
+ usingMutableMapBuilder = usingMutableMap.toBuilder();
+ updateMapValuesUsingMutableMap(usingMutableMapBuilder);
+ usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesUpdated(usingMutableMap);
+
+ usingAccessorsBuilder = usingAccessors.toBuilder();
+ updateMapValuesUsingAccessors(usingAccessorsBuilder);
+ usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesUpdated(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ }
+
private void assertMapValuesUpdated(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@@ -158,37 +257,72 @@ public class MapForProto2Test extends TestCase {
assertEquals("111", message.getInt32ToStringField().get(1));
assertEquals("33", message.getInt32ToStringField().get(3));
assertEquals("44", message.getInt32ToStringField().get(4));
-
+
assertEquals(3, message.getInt32ToBytesField().size());
assertEquals(TestUtil.toBytes("111"), message.getInt32ToBytesField().get(1));
assertEquals(TestUtil.toBytes("33"), message.getInt32ToBytesField().get(3));
assertEquals(TestUtil.toBytes("44"), message.getInt32ToBytesField().get(4));
-
+
assertEquals(3, message.getInt32ToEnumField().size());
assertEquals(TestMap.EnumValue.BAR, message.getInt32ToEnumField().get(1));
assertEquals(TestMap.EnumValue.BAZ, message.getInt32ToEnumField().get(3));
assertEquals(TestMap.EnumValue.QUX, message.getInt32ToEnumField().get(4));
-
+
assertEquals(3, message.getInt32ToMessageField().size());
assertEquals(111, message.getInt32ToMessageField().get(1).getValue());
assertEquals(33, message.getInt32ToMessageField().get(3).getValue());
assertEquals(44, message.getInt32ToMessageField().get(4).getValue());
-
+
assertEquals(3, message.getStringToInt32Field().size());
assertEquals(111, message.getStringToInt32Field().get("1").intValue());
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
- private void assertMapValuesCleared(TestMap message) {
- assertEquals(0, message.getInt32ToInt32Field().size());
- assertEquals(0, message.getInt32ToStringField().size());
- assertEquals(0, message.getInt32ToBytesField().size());
- assertEquals(0, message.getInt32ToEnumField().size());
- assertEquals(0, message.getInt32ToMessageField().size());
- assertEquals(0, message.getStringToInt32Field().size());
+ private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
+ }
+
+ public void testGetMapIsImmutable() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+
+ setMapValuesUsingAccessors(builder);
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+ }
+
+ private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
+ assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
+ assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
+ assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
+ assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
+ assertImmutable(
+ testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
+ assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
+ }
+
+ private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
+ try {
+ map.put(key, value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
}
-
+
public void testMutableMapLifecycle() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@@ -203,7 +337,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.getMutableInt32ToInt32Field().put(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
-
+ //
Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
@@ -218,7 +352,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
-
+ //
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@@ -233,7 +367,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
-
+ //
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@@ -252,7 +386,7 @@ public class MapForProto2Test extends TestCase {
2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
}
-
+ //
public void testMutableMapLifecycle_collections() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@@ -299,52 +433,96 @@ public class MapForProto2Test extends TestCase {
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
}
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
-
+
builder = message.toBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
- updateMapValues(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
+ assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
- setMapValues(sourceBuilder);
+ setMapValuesUsingAccessors(sourceBuilder);
TestMap source = sourceBuilder.build();
+ assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
assertMapValuesSet(destination.build());
+
+ assertEquals(3, destination.getInt32ToEnumFieldCount());
+ }
+
+ public void testPutChecksNullKeysAndValues() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToMessageField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putStringToInt32Field(null, 1);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
}
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
-
+
builder = message.toBuilder();
- updateMapValues(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
-
+
builder = message.toBuilder();
builder.clear();
message = builder.build();
@@ -352,12 +530,61 @@ public class MapForProto2Test extends TestCase {
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
-
+
+ private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
+ bizarroMap.writeTo(output);
+ output.flush();
+ return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
+ }
+
+ public void testParseError() throws Exception {
+ ByteString bytes = TestUtil.toBytes("SOME BYTES");
+ String stringKey = "a string key";
+
+ TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToInt32Field(5, bytes)
+ .build());
+ assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToStringField(stringKey, 5)
+ .build());
+ assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToBytesField(stringKey, 5)
+ .build());
+ assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToEnumField(stringKey, bytes)
+ .build());
+ assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
+
+ try {
+ tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToMessageField(stringKey, bytes)
+ .build());
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
+ map = (TestMap) expected.getUnfinishedMessage();
+ assertTrue(map.getInt32ToMessageField().isEmpty());
+ }
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putStringToInt32Field(stringKey, bytes)
+ .build());
+ assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
+ }
+
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
-
+
TestMap.Builder other = TestMap.newBuilder();
other.mergeFrom(message);
assertMapValuesSet(other.build());
@@ -366,51 +593,51 @@ public class MapForProto2Test extends TestCase {
public void testEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
-
+
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
TestMap.Builder b1 = TestMap.newBuilder();
- b1.getMutableInt32ToInt32Field().put(1, 2);
- b1.getMutableInt32ToInt32Field().put(3, 4);
- b1.getMutableInt32ToInt32Field().put(5, 6);
+ b1.putInt32ToInt32Field(1, 2);
+ b1.putInt32ToInt32Field(3, 4);
+ b1.putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
-
+
TestMap.Builder b2 = TestMap.newBuilder();
- b2.getMutableInt32ToInt32Field().put(5, 6);
- b2.getMutableInt32ToInt32Field().put(1, 2);
- b2.getMutableInt32ToInt32Field().put(3, 4);
+ b2.putInt32ToInt32Field(5, 6);
+ b2.putInt32ToInt32Field(1, 2);
+ b2.putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
-
+
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
-
+
// Make sure we did compare map fields.
- b2.getMutableInt32ToInt32Field().put(1, 0);
+ b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
}
-
-
+
+
// The following methods are used to test reflection API.
-
+
private static FieldDescriptor f(String name) {
return TestMap.getDescriptor().findFieldByName(name);
}
-
+
private static Object getFieldValue(Message mapEntry, String name) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
return mapEntry.getField(field);
}
-
+
private static Message.Builder setFieldValue(
Message.Builder mapEntry, String name, Object value) {
FieldDescriptor field = mapEntry.getDescriptorForType().findFieldByName(name);
mapEntry.setField(field, value);
return mapEntry;
}
-
+
private static void assertHasMapValues(Message message, String name, Map<?, ?> values) {
FieldDescriptor field = f(name);
for (Object entry : (List<?>) message.getField(field)) {
@@ -429,7 +656,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(value, values.get(key));
}
}
-
+
private static <KeyType, ValueType>
Message newMapEntry(Message.Builder builder, String name, KeyType key, ValueType value) {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
@@ -440,7 +667,7 @@ public class MapForProto2Test extends TestCase {
entryBuilder.setField(valueField, value);
return entryBuilder.build();
}
-
+
private static void setMapValues(Message.Builder builder, String name, Map<?, ?> values) {
List<Message> entryList = new ArrayList<Message>();
for (Map.Entry<?, ?> entry : values.entrySet()) {
@@ -449,9 +676,8 @@ public class MapForProto2Test extends TestCase {
FieldDescriptor field = builder.getDescriptorForType().findFieldByName(name);
builder.setField(field, entryList);
}
-
- private static <KeyType, ValueType>
- Map<KeyType, ValueType> mapForValues(
+
+ private static <KeyType, ValueType> Map<KeyType, ValueType> mapForValues(
KeyType key1, ValueType value1, KeyType key2, ValueType value2) {
Map<KeyType, ValueType> map = new HashMap<KeyType, ValueType>();
map.put(key1, value1);
@@ -461,13 +687,11 @@ public class MapForProto2Test extends TestCase {
public void testReflectionApi() throws Exception {
// In reflection API, map fields are just repeated message fields.
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 2);
- builder.getMutableInt32ToInt32Field().put(3, 4);
- builder.getMutableInt32ToMessageField().put(
- 11, MessageValue.newBuilder().setValue(22).build());
- builder.getMutableInt32ToMessageField().put(
- 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToMessageField(11, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(33, MessageValue.newBuilder().setValue(44).build());
TestMap message = builder.build();
// Test getField(), getRepeatedFieldCount(), getRepeatedField().
@@ -477,14 +701,14 @@ public class MapForProto2Test extends TestCase {
mapForValues(
11, MessageValue.newBuilder().setValue(22).build(),
33, MessageValue.newBuilder().setValue(44).build()));
-
+
// Test clearField()
builder.clearField(f("int32_to_int32_field"));
builder.clearField(f("int32_to_message_field"));
message = builder.build();
assertEquals(0, message.getInt32ToInt32Field().size());
assertEquals(0, message.getInt32ToMessageField().size());
-
+
// Test setField()
setMapValues(builder, "int32_to_int32_field",
mapForValues(11, 22, 33, 44));
@@ -497,7 +721,7 @@ public class MapForProto2Test extends TestCase {
assertEquals(44, message.getInt32ToInt32Field().get(33).intValue());
assertEquals(222, message.getInt32ToMessageField().get(111).getValue());
assertEquals(444, message.getInt32ToMessageField().get(333).getValue());
-
+
// Test addRepeatedField
builder.addRepeatedField(f("int32_to_int32_field"),
newMapEntry(builder, "int32_to_int32_field", 55, 66));
@@ -517,7 +741,7 @@ public class MapForProto2Test extends TestCase {
message = builder.build();
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
assertEquals(555, message.getInt32ToMessageField().get(555).getValue());
-
+
// Test setRepeatedField
for (int i = 0; i < builder.getRepeatedFieldCount(f("int32_to_int32_field")); i++) {
Message mapEntry = (Message) builder.getRepeatedField(f("int32_to_int32_field"), i);
@@ -534,35 +758,54 @@ public class MapForProto2Test extends TestCase {
assertEquals(33, message.getInt32ToInt32Field().get(44).intValue());
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
}
-
+
+ // See additional coverage in TextFormatTest.java.
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
-
+
String textData = TextFormat.printToString(message);
-
+
builder = TestMap.newBuilder();
TextFormat.merge(textData, builder);
message = builder.build();
-
+
assertMapValuesSet(message);
}
-
+
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
-
+
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
Message dynamicMessage = dynamicDefaultInstance
.newBuilderForType().mergeFrom(message.toByteString()).build();
-
+
assertEquals(message, dynamicMessage);
assertEquals(message.hashCode(), dynamicMessage.hashCode());
}
-
+
+ // Check that DynamicMessage handles map field serialization the same way as generated code
+ // regarding unset key and value field in a map entry.
+ public void testDynamicMessageUnsetKeyAndValue() throws Exception {
+ FieldDescriptor field = f("int32_to_int32_field");
+
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ Message.Builder builder = dynamicDefaultInstance.newBuilderForType();
+ // Add an entry without key and value.
+ builder.addRepeatedField(field, builder.newBuilderForField(field).build());
+ Message message = builder.build();
+ ByteString bytes = message.toByteString();
+ // Parse it back to the same generated type.
+ Message generatedMessage = TestMap.parseFrom(bytes);
+ // Assert the serialized bytes are equivalent.
+ assertEquals(generatedMessage.toByteString(), bytes);
+ }
+
public void testReflectionEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
@@ -571,22 +814,22 @@ public class MapForProto2Test extends TestCase {
Message dynamicDefaultInstance =
DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
FieldDescriptor field = f("int32_to_int32_field");
-
+
Message.Builder b1 = dynamicDefaultInstance.newBuilderForType();
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 1, 2));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 3, 4));
b1.addRepeatedField(field, newMapEntry(b1, "int32_to_int32_field", 5, 6));
Message m1 = b1.build();
-
+
Message.Builder b2 = dynamicDefaultInstance.newBuilderForType();
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 5, 6));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 1, 2));
b2.addRepeatedField(field, newMapEntry(b2, "int32_to_int32_field", 3, 4));
Message m2 = b2.build();
-
+
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
-
+
// Make sure we did compare map fields.
b2.setRepeatedField(field, 0, newMapEntry(b1, "int32_to_int32_field", 0, 0));
m2 = b2.build();
@@ -594,12 +837,11 @@ public class MapForProto2Test extends TestCase {
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
// to be different.
}
-
+
public void testUnknownEnumValues() throws Exception {
- TestUnknownEnumValue.Builder builder =
- TestUnknownEnumValue.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 1);
- builder.getMutableInt32ToInt32Field().put(2, 54321);
+ TestUnknownEnumValue.Builder builder = TestUnknownEnumValue.newBuilder()
+ .putInt32ToInt32Field(1, 1)
+ .putInt32ToInt32Field(2, 54321);
ByteString data = builder.build().toByteString();
TestMap message = TestMap.parseFrom(data);
@@ -618,26 +860,21 @@ public class MapForProto2Test extends TestCase {
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
}
-
public void testRequiredMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableRequiredMessageMap().put(0,
- MessageWithRequiredFields.newBuilder().buildPartial());
+ builder.putRequiredMessageMap(0, MessageWithRequiredFields.newBuilder().buildPartial());
TestMap message = builder.buildPartial();
assertFalse(message.isInitialized());
- builder.getMutableRequiredMessageMap().put(0,
- MessageWithRequiredFields.newBuilder().setValue(1).build());
+ builder.putRequiredMessageMap(0, MessageWithRequiredFields.newBuilder().setValue(1).build());
message = builder.build();
assertTrue(message.isInitialized());
}
public void testRecursiveMap() throws Exception {
TestRecursiveMap.Builder builder = TestRecursiveMap.newBuilder();
- builder.getMutableRecursiveMapField().put(
- 1, TestRecursiveMap.newBuilder().setValue(2).build());
- builder.getMutableRecursiveMapField().put(
- 3, TestRecursiveMap.newBuilder().setValue(4).build());
+ builder.putRecursiveMapField(1, TestRecursiveMap.newBuilder().setValue(2).build());
+ builder.putRecursiveMapField(3, TestRecursiveMap.newBuilder().setValue(4).build());
ByteString data = builder.build().toByteString();
TestRecursiveMap message = TestRecursiveMap.parseFrom(data);
@@ -647,13 +884,266 @@ public class MapForProto2Test extends TestCase {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet()));
}
+ public void testContains() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ assertMapContainsSetValues(builder);
+ assertMapContainsSetValues(builder.build());
+ }
+
+ private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
+ assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
+
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
+ assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
+ }
+
+ public void testCount() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+
+ setMapValuesUsingAccessors(builder);
+ assertMapCounts(3, builder);
+
+ TestMap message = builder.build();
+ assertMapCounts(3, message);
+
+ builder = message.toBuilder().putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ assertEquals(4, builder.build().getInt32ToInt32FieldCount());
+
+ // already present - should be unchanged
+ builder.putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ }
+
+ private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
+ }
+
+ public void testGetOrDefault() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValuesUsingAccessors(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
+ assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
+ assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
+ assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testGetOrThrow() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValuesUsingAccessors(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testPut() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.putInt32ToInt32Field(1, 11);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+
+ builder.putInt32ToStringField(1, "a");
+ assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putStringToInt32Field("a", 1);
+ assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
+ try {
+ builder.putStringToInt32Field(null, -1);
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testRemove() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToInt32Field(1);
+ assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
+ }
+
+ assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToStringField(1);
+ assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToBytesField(1);
+ assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToEnumField(1);
+ assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
+ }
+
+ assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
+ for (int times = 0; times < 2; times++) {
+ builder.removeStringToInt32Field("1");
+ assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
+ }
+
+ try {
+ builder.removeStringToInt32Field(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
// Regression test for b/20494788
public void testMapInitializationOrder() throws Exception {
assertEquals("RedactAllTypes", map_test.RedactAllTypes
@@ -661,24 +1151,47 @@ public class MapForProto2Test extends TestCase {
map_test.Message1.Builder builder =
map_test.Message1.newBuilder();
- builder.getMutableMapField().put("key", true);
+ builder.putMapField("key", true);
map_test.Message1 message = builder.build();
Message mapEntry = (Message) message.getRepeatedField(
message.getDescriptorForType().findFieldByName("map_field"), 0);
assertEquals(2, mapEntry.getAllFields().size());
}
-
+
+ public void testReservedWordsFieldNames() {
+ ReservedAsMapField.newBuilder().build();
+ ReservedAsMapFieldWithEnumValue.newBuilder().build();
+ }
+
private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
return map;
}
-
+
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
map.put(key2, value2);
return map;
}
-}
+ public void testGetMap() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ assertMapValuesSet(builder);
+ TestMap message = builder.build();
+ assertEquals(
+ message.getStringToInt32Field(),
+ message.getStringToInt32FieldMap());
+ assertEquals(
+ message.getInt32ToBytesField(),
+ message.getInt32ToBytesFieldMap());
+ assertEquals(
+ message.getInt32ToEnumField(),
+ message.getInt32ToEnumFieldMap());
+ assertEquals(
+ message.getInt32ToMessageField(),
+ message.getInt32ToMessageFieldMap());
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/MapTest.java b/java/core/src/test/java/com/google/protobuf/MapTest.java
index 0e5c1284..58efce92 100644
--- a/java/core/src/test/java/com/google/protobuf/MapTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MapTest.java
@@ -30,55 +30,102 @@
package com.google.protobuf;
+import static org.junit.Assert.assertArrayEquals;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
+import map_test.MapTestProto.BizarroTestMap;
+import map_test.MapTestProto.ReservedAsMapField;
+import map_test.MapTestProto.ReservedAsMapFieldWithEnumValue;
import map_test.MapTestProto.TestMap;
import map_test.MapTestProto.TestMap.MessageValue;
+import map_test.MapTestProto.TestMapOrBuilder;
import map_test.MapTestProto.TestOnChangeEventPropagation;
-
-import junit.framework.TestCase;
-
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import junit.framework.TestCase;
/**
* Unit tests for map fields.
*/
public class MapTest extends TestCase {
- private void setMapValues(TestMap.Builder builder) {
+
+ private void setMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 11);
builder.getMutableInt32ToInt32Field().put(2, 22);
builder.getMutableInt32ToInt32Field().put(3, 33);
-
+ //
builder.getMutableInt32ToStringField().put(1, "11");
builder.getMutableInt32ToStringField().put(2, "22");
builder.getMutableInt32ToStringField().put(3, "33");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("11"));
builder.getMutableInt32ToBytesField().put(2, TestUtil.toBytes("22"));
builder.getMutableInt32ToBytesField().put(3, TestUtil.toBytes("33"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.FOO);
builder.getMutableInt32ToEnumField().put(2, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().put(3, TestMap.EnumValue.BAZ);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(11).build());
builder.getMutableInt32ToMessageField().put(
2, MessageValue.newBuilder().setValue(22).build());
builder.getMutableInt32ToMessageField().put(
3, MessageValue.newBuilder().setValue(33).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 11);
builder.getMutableStringToInt32Field().put("2", 22);
builder.getMutableStringToInt32Field().put("3", 33);
}
+ private void setMapValuesUsingAccessors(TestMap.Builder builder) {
+ builder
+ .putInt32ToInt32Field(1, 11)
+ .putInt32ToInt32Field(2, 22)
+ .putInt32ToInt32Field(3, 33)
+
+ .putInt32ToStringField(1, "11")
+ .putInt32ToStringField(2, "22")
+ .putInt32ToStringField(3, "33")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("11"))
+ .putInt32ToBytesField(2, TestUtil.toBytes("22"))
+ .putInt32ToBytesField(3, TestUtil.toBytes("33"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.FOO)
+ .putInt32ToEnumField(2, TestMap.EnumValue.BAR)
+ .putInt32ToEnumField(3, TestMap.EnumValue.BAZ)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(11).build())
+ .putInt32ToMessageField(2, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(3, MessageValue.newBuilder().setValue(33).build())
+
+ .putStringToInt32Field("1", 11)
+ .putStringToInt32Field("2", 22)
+ .putStringToInt32Field("3", 33);
+ }
+
+ public void testSetMapValues() {
+ TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
+ setMapValuesUsingMutableMap(usingMutableMapBuilder);
+ TestMap usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesSet(usingMutableMap);
+
+ TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(usingAccessorsBuilder);
+ TestMap usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesSet(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ }
+
private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
@@ -121,34 +168,87 @@ public class MapTest extends TestCase {
assertEquals(33, message.getStringToInt32Field().get("3").intValue());
}
- private void updateMapValues(TestMap.Builder builder) {
+ private void updateMapValuesUsingMutableMap(TestMap.Builder builder) {
builder.getMutableInt32ToInt32Field().put(1, 111);
builder.getMutableInt32ToInt32Field().remove(2);
builder.getMutableInt32ToInt32Field().put(4, 44);
-
+ //
builder.getMutableInt32ToStringField().put(1, "111");
builder.getMutableInt32ToStringField().remove(2);
builder.getMutableInt32ToStringField().put(4, "44");
-
+ //
builder.getMutableInt32ToBytesField().put(1, TestUtil.toBytes("111"));
builder.getMutableInt32ToBytesField().remove(2);
builder.getMutableInt32ToBytesField().put(4, TestUtil.toBytes("44"));
-
+ //
builder.getMutableInt32ToEnumField().put(1, TestMap.EnumValue.BAR);
builder.getMutableInt32ToEnumField().remove(2);
builder.getMutableInt32ToEnumField().put(4, TestMap.EnumValue.QUX);
-
+ //
builder.getMutableInt32ToMessageField().put(
1, MessageValue.newBuilder().setValue(111).build());
builder.getMutableInt32ToMessageField().remove(2);
builder.getMutableInt32ToMessageField().put(
4, MessageValue.newBuilder().setValue(44).build());
-
+ //
builder.getMutableStringToInt32Field().put("1", 111);
builder.getMutableStringToInt32Field().remove("2");
builder.getMutableStringToInt32Field().put("4", 44);
}
+ private void updateMapValuesUsingAccessors(TestMap.Builder builder) {
+ builder
+ .putInt32ToInt32Field(1, 111)
+ .removeInt32ToInt32Field(2)
+ .putInt32ToInt32Field(4, 44)
+
+ .putInt32ToStringField(1, "111")
+ .removeInt32ToStringField(2)
+ .putInt32ToStringField(4, "44")
+
+ .putInt32ToBytesField(1, TestUtil.toBytes("111"))
+ .removeInt32ToBytesField(2)
+ .putInt32ToBytesField(4, TestUtil.toBytes("44"))
+
+ .putInt32ToEnumField(1, TestMap.EnumValue.BAR)
+ .removeInt32ToEnumField(2)
+ .putInt32ToEnumField(4, TestMap.EnumValue.QUX)
+
+ .putInt32ToMessageField(1, MessageValue.newBuilder().setValue(111).build())
+ .removeInt32ToMessageField(2)
+ .putInt32ToMessageField(4, MessageValue.newBuilder().setValue(44).build())
+
+ .putStringToInt32Field("1", 111)
+ .removeStringToInt32Field("2")
+ .putStringToInt32Field("4", 44);
+ }
+
+ public void testUpdateMapValues() {
+ TestMap.Builder usingMutableMapBuilder = TestMap.newBuilder();
+ setMapValuesUsingMutableMap(usingMutableMapBuilder);
+ TestMap usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesSet(usingMutableMap);
+
+ TestMap.Builder usingAccessorsBuilder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(usingAccessorsBuilder);
+ TestMap usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesSet(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ //
+ usingMutableMapBuilder = usingMutableMap.toBuilder();
+ updateMapValuesUsingMutableMap(usingMutableMapBuilder);
+ usingMutableMap = usingMutableMapBuilder.build();
+ assertMapValuesUpdated(usingMutableMap);
+
+ usingAccessorsBuilder = usingAccessors.toBuilder();
+ updateMapValuesUsingAccessors(usingAccessorsBuilder);
+ usingAccessors = usingAccessorsBuilder.build();
+ assertMapValuesUpdated(usingAccessors);
+
+ assertEquals(usingAccessors, usingMutableMap);
+ }
+
private void assertMapValuesUpdated(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(111, message.getInt32ToInt32Field().get(1).intValue());
@@ -181,15 +281,50 @@ public class MapTest extends TestCase {
assertEquals(44, message.getStringToInt32Field().get("4").intValue());
}
- private void assertMapValuesCleared(TestMap message) {
- assertEquals(0, message.getInt32ToInt32Field().size());
- assertEquals(0, message.getInt32ToStringField().size());
- assertEquals(0, message.getInt32ToBytesField().size());
- assertEquals(0, message.getInt32ToEnumField().size());
- assertEquals(0, message.getInt32ToMessageField().size());
- assertEquals(0, message.getStringToInt32Field().size());
+ private void assertMapValuesCleared(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageField().size());
+ assertEquals(0, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(0, testMapOrBuilder.getStringToInt32Field().size());
+ assertEquals(0, testMapOrBuilder.getStringToInt32FieldCount());
}
-
+
+ public void testGetMapIsImmutable() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+
+ setMapValuesUsingAccessors(builder);
+ assertMapsAreImmutable(builder);
+ assertMapsAreImmutable(builder.build());
+ }
+
+ private void assertMapsAreImmutable(TestMapOrBuilder testMapOrBuilder) {
+ assertImmutable(testMapOrBuilder.getInt32ToInt32Field(), 1, 2);
+ assertImmutable(testMapOrBuilder.getInt32ToStringField(), 1, "2");
+ assertImmutable(testMapOrBuilder.getInt32ToBytesField(), 1, TestUtil.toBytes("2"));
+ assertImmutable(testMapOrBuilder.getInt32ToEnumField(), 1, TestMap.EnumValue.FOO);
+ assertImmutable(
+ testMapOrBuilder.getInt32ToMessageField(), 1, MessageValue.getDefaultInstance());
+ assertImmutable(testMapOrBuilder.getStringToInt32Field(), "1", 2);
+ }
+
+ private <K, V> void assertImmutable(Map<K, V> map, K key, V value) {
+ try {
+ map.put(key, value);
+ fail();
+ } catch (UnsupportedOperationException e) {
+ // expected
+ }
+ }
+
public void testMutableMapLifecycle() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@@ -204,7 +339,7 @@ public class MapTest extends TestCase {
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
builder.getMutableInt32ToInt32Field().put(2, 3);
assertEquals(newMap(1, 2, 2, 3), builder.getInt32ToInt32Field());
-
+ //
Map<Integer, TestMap.EnumValue> enumMap = builder.getMutableInt32ToEnumField();
enumMap.put(1, TestMap.EnumValue.BAR);
assertEquals(newMap(1, TestMap.EnumValue.BAR), builder.build().getInt32ToEnumField());
@@ -219,7 +354,7 @@ public class MapTest extends TestCase {
assertEquals(
newMap(1, TestMap.EnumValue.BAR, 2, TestMap.EnumValue.FOO),
builder.getInt32ToEnumField());
-
+ //
Map<Integer, String> stringMap = builder.getMutableInt32ToStringField();
stringMap.put(1, "1");
assertEquals(newMap(1, "1"), builder.build().getInt32ToStringField());
@@ -230,11 +365,11 @@ public class MapTest extends TestCase {
// expected
}
assertEquals(newMap(1, "1"), builder.getInt32ToStringField());
- builder.getMutableInt32ToStringField().put(2, "2");
+ builder.putInt32ToStringField(2, "2");
assertEquals(
newMap(1, "1", 2, "2"),
builder.getInt32ToStringField());
-
+ //
Map<Integer, TestMap.MessageValue> messageMap = builder.getMutableInt32ToMessageField();
messageMap.put(1, TestMap.MessageValue.getDefaultInstance());
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
@@ -247,13 +382,13 @@ public class MapTest extends TestCase {
}
assertEquals(newMap(1, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
- builder.getMutableInt32ToMessageField().put(2, TestMap.MessageValue.getDefaultInstance());
+ builder.putInt32ToMessageField(2, TestMap.MessageValue.getDefaultInstance());
assertEquals(
newMap(1, TestMap.MessageValue.getDefaultInstance(),
2, TestMap.MessageValue.getDefaultInstance()),
builder.getInt32ToMessageField());
}
-
+ //
public void testMutableMapLifecycle_collections() {
TestMap.Builder builder = TestMap.newBuilder();
Map<Integer, Integer> intMap = builder.getMutableInt32ToInt32Field();
@@ -299,32 +434,35 @@ public class MapTest extends TestCase {
assertEquals(newMap(1, 2), builder.getInt32ToInt32Field());
assertEquals(newMap(1, 2), builder.build().getInt32ToInt32Field());
}
-
+
+
public void testGettersAndSetters() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
TestMap message = builder.build();
assertMapValuesCleared(message);
builder = message.toBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValues(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
+ assertMapValuesCleared(builder);
message = builder.build();
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
- setMapValues(sourceBuilder);
+ setMapValuesUsingAccessors(sourceBuilder);
TestMap source = sourceBuilder.build();
+ assertMapValuesSet(source);
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
@@ -332,31 +470,84 @@ public class MapTest extends TestCase {
}
public void testPutAllForUnknownEnumValues() throws Exception {
- TestMap.Builder sourceBuilder = TestMap.newBuilder();
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(0, 0);
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(1, 1);
- sourceBuilder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
- TestMap source = sourceBuilder.build();
+ TestMap source = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000)) // unknown value.
+ .build();
- TestMap.Builder destinationBuilder = TestMap.newBuilder();
- destinationBuilder.putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue());
- TestMap destination = destinationBuilder.build();
+ TestMap destination = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue())
+ .build();
assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue());
assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue());
+ assertEquals(3, destination.getInt32ToEnumFieldCount());
+ }
+
+ public void testPutForUnknownEnumValues() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToEnumFieldValue(0, 0)
+ .putInt32ToEnumFieldValue(1, 1)
+ .putInt32ToEnumFieldValue(2, 1000); // unknown value.
+ TestMap message = builder.build();
+ assertEquals(0, message.getInt32ToEnumFieldValueOrThrow(0));
+ assertEquals(1, message.getInt32ToEnumFieldValueOrThrow(1));
+ assertEquals(1000, message.getInt32ToEnumFieldValueOrThrow(2));
+ assertEquals(3, message.getInt32ToEnumFieldCount());
+ }
+
+ public void testPutChecksNullKeysAndValues() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putInt32ToMessageField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
+
+ try {
+ builder.putStringToInt32Field(null, 1);
+ fail();
+ } catch (NullPointerException e) {
+ // expected.
+ }
}
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
- updateMapValues(builder);
+ updateMapValuesUsingAccessors(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.parser().parseFrom(message.toByteString());
@@ -370,9 +561,58 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message);
}
+ private TestMap tryParseTestMap(BizarroTestMap bizarroMap) throws IOException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ CodedOutputStream output = CodedOutputStream.newInstance(byteArrayOutputStream);
+ bizarroMap.writeTo(output);
+ output.flush();
+ return TestMap.parser().parseFrom(ByteString.copyFrom(byteArrayOutputStream.toByteArray()));
+ }
+
+ public void testParseError() throws Exception {
+ ByteString bytes = TestUtil.toBytes("SOME BYTES");
+ String stringKey = "a string key";
+
+ TestMap map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToInt32Field(5, bytes)
+ .build());
+ assertEquals(0, map.getInt32ToInt32FieldOrDefault(5, -1));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToStringField(stringKey, 5)
+ .build());
+ assertEquals("", map.getInt32ToStringFieldOrDefault(0, null));
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToBytesField(stringKey, 5)
+ .build());
+ assertEquals(map.getInt32ToBytesFieldOrDefault(0, null), ByteString.EMPTY);
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToEnumField(stringKey, bytes)
+ .build());
+ assertEquals(TestMap.EnumValue.FOO, map.getInt32ToEnumFieldOrDefault(0, null));
+
+ try {
+ tryParseTestMap(BizarroTestMap.newBuilder()
+ .putInt32ToMessageField(stringKey, bytes)
+ .build());
+ fail();
+ } catch (InvalidProtocolBufferException expected) {
+ assertTrue(expected.getUnfinishedMessage() instanceof TestMap);
+ map = (TestMap) expected.getUnfinishedMessage();
+ assertTrue(map.getInt32ToMessageField().isEmpty());
+ }
+
+ map = tryParseTestMap(BizarroTestMap.newBuilder()
+ .putStringToInt32Field(stringKey, bytes)
+ .build());
+ assertEquals(0, map.getStringToInt32FieldOrDefault(stringKey, -1));
+ }
+
public void testMergeFrom() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
TestMap.Builder other = TestMap.newBuilder();
@@ -386,23 +626,23 @@ public class MapTest extends TestCase {
// We can't control the order of elements in a HashMap. The best we can do
// here is to add elements in different order.
- TestMap.Builder b1 = TestMap.newBuilder();
- b1.getMutableInt32ToInt32Field().put(1, 2);
- b1.getMutableInt32ToInt32Field().put(3, 4);
- b1.getMutableInt32ToInt32Field().put(5, 6);
+ TestMap.Builder b1 = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToInt32Field(5, 6);
TestMap m1 = b1.build();
- TestMap.Builder b2 = TestMap.newBuilder();
- b2.getMutableInt32ToInt32Field().put(5, 6);
- b2.getMutableInt32ToInt32Field().put(1, 2);
- b2.getMutableInt32ToInt32Field().put(3, 4);
+ TestMap.Builder b2 = TestMap.newBuilder()
+ .putInt32ToInt32Field(5, 6)
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4);
TestMap m2 = b2.build();
assertEquals(m1, m2);
assertEquals(m1.hashCode(), m2.hashCode());
// Make sure we did compare map fields.
- b2.getMutableInt32ToInt32Field().put(1, 0);
+ b2.putInt32ToInt32Field(1, 0);
m2 = b2.build();
assertFalse(m1.equals(m2));
// Don't check m1.hashCode() != m2.hashCode() because it's not guaranteed
@@ -410,7 +650,7 @@ public class MapTest extends TestCase {
// Regression test for b/18549190: if a map is a subset of the other map,
// equals() should return false.
- b2.getMutableInt32ToInt32Field().remove(1);
+ b2.removeInt32ToInt32Field(1);
m2 = b2.build();
assertFalse(m1.equals(m2));
assertFalse(m2.equals(m1));
@@ -419,20 +659,19 @@ public class MapTest extends TestCase {
public void testNestedBuilderOnChangeEventPropagation() {
TestOnChangeEventPropagation.Builder parent =
TestOnChangeEventPropagation.newBuilder();
- parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 2);
+ parent.getOptionalMessageBuilder().putInt32ToInt32Field(1, 2);
TestOnChangeEventPropagation message = parent.build();
assertEquals(2, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
// Make a change using nested builder.
- parent.getOptionalMessageBuilder().getMutableInt32ToInt32Field().put(1, 3);
+ parent.getOptionalMessageBuilder().putInt32ToInt32Field(1, 3);
// Should be able to observe the change.
message = parent.build();
assertEquals(3, message.getOptionalMessage().getInt32ToInt32Field().get(1).intValue());
// Make another change using mergeFrom()
- TestMap.Builder other = TestMap.newBuilder();
- other.getMutableInt32ToInt32Field().put(1, 4);
+ TestMap.Builder other = TestMap.newBuilder().putInt32ToInt32Field(1, 4);
parent.getOptionalMessageBuilder().mergeFrom(other.build());
// Should be able to observe the change.
@@ -455,8 +694,7 @@ public class MapTest extends TestCase {
TestMap.Builder testMapBuilder = parentBuilder.getOptionalMessageBuilder();
// Create a map entry message.
- TestMap.Builder entryBuilder = TestMap.newBuilder();
- entryBuilder.getMutableInt32ToInt32Field().put(1, 1);
+ TestMap.Builder entryBuilder = TestMap.newBuilder().putInt32ToInt32Field(1, 1);
// Put the entry into the nested builder.
testMapBuilder.addRepeatedField(
@@ -467,7 +705,7 @@ public class MapTest extends TestCase {
assertEquals(1, message.getOptionalMessage().getInt32ToInt32Field().size());
// Change the entry value.
- entryBuilder.getMutableInt32ToInt32Field().put(1, 4);
+ entryBuilder.putInt32ToInt32Field(1, 4);
testMapBuilder = parentBuilder.getOptionalMessageBuilder();
testMapBuilder.setRepeatedField(
intMapField, 0, entryBuilder.getRepeatedField(intMapField, 0));
@@ -554,13 +792,11 @@ public class MapTest extends TestCase {
public void testReflectionApi() throws Exception {
// In reflection API, map fields are just repeated message fields.
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToInt32Field().put(1, 2);
- builder.getMutableInt32ToInt32Field().put(3, 4);
- builder.getMutableInt32ToMessageField().put(
- 11, MessageValue.newBuilder().setValue(22).build());
- builder.getMutableInt32ToMessageField().put(
- 33, MessageValue.newBuilder().setValue(44).build());
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putInt32ToInt32Field(1, 2)
+ .putInt32ToInt32Field(3, 4)
+ .putInt32ToMessageField(11, MessageValue.newBuilder().setValue(22).build())
+ .putInt32ToMessageField(33, MessageValue.newBuilder().setValue(44).build());
TestMap message = builder.build();
// Test getField(), getRepeatedFieldCount(), getRepeatedField().
@@ -628,9 +864,10 @@ public class MapTest extends TestCase {
assertEquals(55, message.getInt32ToInt32Field().get(55).intValue());
}
+ // See additional coverage in TextFormatTest.java.
public void testTextFormat() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
String textData = TextFormat.printToString(message);
@@ -644,7 +881,7 @@ public class MapTest extends TestCase {
public void testDynamicMessage() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
Message dynamicDefaultInstance =
@@ -656,6 +893,24 @@ public class MapTest extends TestCase {
assertEquals(message.hashCode(), dynamicMessage.hashCode());
}
+ // Check that DynamicMessage handles map field serialization the same way as generated code
+ // regarding unset key and value field in a map entry.
+ public void testDynamicMessageUnsetKeyAndValue() throws Exception {
+ FieldDescriptor field = f("int32_to_int32_field");
+
+ Message dynamicDefaultInstance =
+ DynamicMessage.getDefaultInstance(TestMap.getDescriptor());
+ Message.Builder builder = dynamicDefaultInstance.newBuilderForType();
+ // Add an entry without key and value.
+ builder.addRepeatedField(field, builder.newBuilderForField(field).build());
+ Message message = builder.build();
+ ByteString bytes = message.toByteString();
+ // Parse it back to the same generated type.
+ Message generatedMessage = TestMap.parseFrom(bytes);
+ // Assert the serialized bytes are equivalent.
+ assertEquals(generatedMessage.toByteString(), bytes);
+ }
+
public void testReflectionEqualsAndHashCode() throws Exception {
// Test that generated equals() and hashCode() will disregard the order
// of map entries when comparing/hashing map fields.
@@ -689,10 +944,11 @@ public class MapTest extends TestCase {
}
public void testUnknownEnumValues() throws Exception {
- TestMap.Builder builder = TestMap.newBuilder();
- builder.getMutableInt32ToEnumFieldValue().put(0, 0);
- builder.getMutableInt32ToEnumFieldValue().put(1, 1);
- builder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000)); // unknown value.
TestMap message = builder.build();
assertEquals(TestMap.EnumValue.FOO,
@@ -715,7 +971,7 @@ public class MapTest extends TestCase {
assertEquals(1000, builder.getInt32ToEnumFieldValue().get(2).intValue());
// hashCode()/equals() should take unknown enum values into account.
- builder.getMutableInt32ToEnumFieldValue().put(2, 1001);
+ builder.putAllInt32ToEnumFieldValue(newMap(2, 1001));
TestMap message2 = builder.build();
assertFalse(message.hashCode() == message2.hashCode());
assertFalse(message.equals(message2));
@@ -729,15 +985,13 @@ public class MapTest extends TestCase {
EnumDescriptor enumDescriptor = TestMap.EnumValue.getDescriptor();
FieldDescriptor field = descriptor.findFieldByName("int32_to_enum_field");
- Map<Integer, Integer> data = new HashMap<Integer, Integer>();
- data.put(0, 0);
- data.put(1, 1);
- data.put(2, 1000); // unknown value.
+ Map<Integer, Integer> data = newMap(
+ 0, 0,
+ 1, 1,
+ 2, 1000); // unknown value
- TestMap.Builder builder = TestMap.newBuilder();
- for (Map.Entry<Integer, Integer> entry : data.entrySet()) {
- builder.getMutableInt32ToEnumFieldValue().put(entry.getKey(), entry.getValue());
- }
+ TestMap.Builder builder = TestMap.newBuilder()
+ .putAllInt32ToEnumFieldValue(data);
// Try to read unknown enum values using reflection API.
for (int i = 0; i < builder.getRepeatedFieldCount(field); i++) {
@@ -761,23 +1015,528 @@ public class MapTest extends TestCase {
public void testIterationOrder() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
- setMapValues(builder);
+ setMapValuesUsingAccessors(builder);
TestMap message = builder.build();
assertEquals(Arrays.asList("1", "2", "3"),
new ArrayList<String>(message.getStringToInt32Field().keySet()));
}
-
+
+ public void testGetMap() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ TestMap message = builder.build();
+ assertEquals(
+ message.getStringToInt32Field(),
+ message.getStringToInt32FieldMap());
+ assertEquals(
+ message.getInt32ToBytesField(),
+ message.getInt32ToBytesFieldMap());
+ assertEquals(
+ message.getInt32ToEnumField(),
+ message.getInt32ToEnumFieldMap());
+ assertEquals(
+ message.getInt32ToEnumFieldValue(),
+ message.getInt32ToEnumFieldValueMap());
+ assertEquals(
+ message.getInt32ToMessageField(),
+ message.getInt32ToMessageFieldMap());
+ }
+
+ public void testContains() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ assertMapContainsSetValues(builder);
+ assertMapContainsSetValues(builder.build());
+ }
+
+ private void assertMapContainsSetValues(TestMapOrBuilder testMapOrBuilder) {
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(1));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(2));
+ assertTrue(testMapOrBuilder.containsInt32ToInt32Field(3));
+ assertFalse(testMapOrBuilder.containsInt32ToInt32Field(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToStringField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToStringField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToBytesField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToBytesField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToEnumField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToEnumField(-1));
+
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(1));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(2));
+ assertTrue(testMapOrBuilder.containsInt32ToMessageField(3));
+ assertFalse(testMapOrBuilder.containsInt32ToMessageField(-1));
+
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("1"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("2"));
+ assertTrue(testMapOrBuilder.containsStringToInt32Field("3"));
+ assertFalse(testMapOrBuilder.containsStringToInt32Field("-1"));
+ }
+
+ public void testCount() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+
+ setMapValuesUsingAccessors(builder);
+ assertMapCounts(3, builder);
+
+ TestMap message = builder.build();
+ assertMapCounts(3, message);
+
+ builder = message.toBuilder().putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ assertEquals(4, builder.build().getInt32ToInt32FieldCount());
+
+ // already present - should be unchanged
+ builder.putInt32ToInt32Field(4, 44);
+ assertEquals(4, builder.getInt32ToInt32FieldCount());
+ }
+
+ private void assertMapCounts(int expectedCount, TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToInt32FieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToStringFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToBytesFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToEnumFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getInt32ToMessageFieldCount());
+ assertEquals(expectedCount, testMapOrBuilder.getStringToInt32FieldCount());
+ }
+
+ public void testGetOrDefault() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValuesUsingAccessors(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrDefault(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(1, -11));
+ assertEquals(-11, testMapOrBuilder.getInt32ToInt32FieldOrDefault(-1, -11));
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrDefault(1, "-11"));
+ assertNull("-11", testMapOrBuilder.getInt32ToStringFieldOrDefault(-1, null));
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToBytesFieldOrDefault(-1, null));
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToEnumFieldOrDefault(-1, null));
+
+ assertEquals(
+ TestMap.EnumValue.BAR.getNumber(),
+ (int) testMapOrBuilder.getInt32ToEnumFieldValueOrDefault(2, -1));
+ assertEquals(-1, testMapOrBuilder.getInt32ToEnumFieldValueOrDefault(-1000, -1));
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrDefault(1, null));
+ assertNull(testMapOrBuilder.getInt32ToMessageFieldOrDefault(-1, null));
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrDefault("1", -11));
+ assertEquals(-11, testMapOrBuilder.getStringToInt32FieldOrDefault("-1", -11));
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrDefault(null, -11);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testGetOrThrow() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ assertMapCounts(0, builder);
+ setMapValuesUsingAccessors(builder);
+ doTestGetOrDefault(builder);
+ doTestGetOrDefault(builder.build());
+ }
+
+ public void doTestGetOrThrow(TestMapOrBuilder testMapOrBuilder) {
+ assertEquals(11, testMapOrBuilder.getInt32ToInt32FieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToInt32FieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals("11", testMapOrBuilder.getInt32ToStringFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToStringFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestUtil.toBytes("11"), testMapOrBuilder.getInt32ToBytesFieldOrThrow(1));
+
+ try {
+ testMapOrBuilder.getInt32ToBytesFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, testMapOrBuilder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToEnumFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(
+ TestMap.EnumValue.BAR.getNumber(), testMapOrBuilder.getInt32ToEnumFieldValueOrThrow(2));
+ try {
+ testMapOrBuilder.getInt32ToEnumFieldValueOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(MessageValue.newBuilder().setValue(11).build(),
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(1));
+ try {
+ testMapOrBuilder.getInt32ToMessageFieldOrThrow(-1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(11, testMapOrBuilder.getStringToInt32FieldOrThrow("1"));
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow("-1");
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ testMapOrBuilder.getStringToInt32FieldOrThrow(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testPut() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ builder.putInt32ToInt32Field(1, 11);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+
+ builder.putInt32ToStringField(1, "a");
+ assertEquals("a", builder.getInt32ToStringFieldOrThrow(1));
+ try {
+ builder.putInt32ToStringField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToBytesField(1, TestUtil.toBytes("11"));
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ try {
+ builder.putInt32ToBytesField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToEnumField(1, TestMap.EnumValue.FOO);
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ try {
+ builder.putInt32ToEnumField(1, null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+
+ builder.putInt32ToEnumFieldValue(1, TestMap.EnumValue.BAR.getNumber());
+ assertEquals(
+ TestMap.EnumValue.BAR.getNumber(), builder.getInt32ToEnumFieldValueOrThrow(1));
+ builder.putInt32ToEnumFieldValue(1, -1);
+ assertEquals(-1, builder.getInt32ToEnumFieldValueOrThrow(1));
+ assertEquals(TestMap.EnumValue.UNRECOGNIZED, builder.getInt32ToEnumFieldOrThrow(1));
+
+ builder.putStringToInt32Field("a", 1);
+ assertEquals(1, builder.getStringToInt32FieldOrThrow("a"));
+ try {
+ builder.putStringToInt32Field(null, -1);
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testRemove() {
+ TestMap.Builder builder = TestMap.newBuilder();
+ setMapValuesUsingAccessors(builder);
+ assertEquals(11, builder.getInt32ToInt32FieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToInt32Field(1);
+ assertEquals(-1, builder.getInt32ToInt32FieldOrDefault(1, -1));
+ }
+
+ assertEquals("11", builder.getInt32ToStringFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToStringField(1);
+ assertNull(builder.getInt32ToStringFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestUtil.toBytes("11"), builder.getInt32ToBytesFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToBytesField(1);
+ assertNull(builder.getInt32ToBytesFieldOrDefault(1, null));
+ }
+
+ assertEquals(TestMap.EnumValue.FOO, builder.getInt32ToEnumFieldOrThrow(1));
+ for (int times = 0; times < 2; times++) {
+ builder.removeInt32ToEnumField(1);
+ assertNull(builder.getInt32ToEnumFieldOrDefault(1, null));
+ }
+
+ assertEquals(11, builder.getStringToInt32FieldOrThrow("1"));
+ for (int times = 0; times < 2; times++) {
+ builder.removeStringToInt32Field("1");
+ assertEquals(-1, builder.getStringToInt32FieldOrDefault("1", -1));
+ }
+
+ try {
+ builder.removeStringToInt32Field(null);
+ fail();
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testReservedWordsFieldNames() {
+ ReservedAsMapField.newBuilder().build();
+ ReservedAsMapFieldWithEnumValue.newBuilder().build();
+ }
+
+ public void testDeterministicSerialziation() throws Exception {
+ TestMap.Builder builder = TestMap.newBuilder();
+ // int32->int32
+ builder.putInt32ToInt32Field(5, 1);
+ builder.putInt32ToInt32Field(1, 1);
+ builder.putInt32ToInt32Field(4, 1);
+ builder.putInt32ToInt32Field(-2, 1);
+ builder.putInt32ToInt32Field(0, 1);
+
+ // uint32->int32
+ builder.putUint32ToInt32Field(5, 1);
+ builder.putUint32ToInt32Field(1, 1);
+ builder.putUint32ToInt32Field(4, 1);
+ builder.putUint32ToInt32Field(-2, 1);
+ builder.putUint32ToInt32Field(0, 1);
+
+ // int64->int32
+ builder.putInt64ToInt32Field(5L, 1);
+ builder.putInt64ToInt32Field(1L, 1);
+ builder.putInt64ToInt32Field(4L, 1);
+ builder.putInt64ToInt32Field(-2L, 1);
+ builder.putInt64ToInt32Field(0L, 1);
+
+ // string->int32
+ builder.putStringToInt32Field("baz", 1);
+ builder.putStringToInt32Field("foo", 1);
+ builder.putStringToInt32Field("bar", 1);
+ builder.putStringToInt32Field("", 1);
+ builder.putStringToInt32Field("hello", 1);
+ builder.putStringToInt32Field("world", 1);
+
+ TestMap message = builder.build();
+ byte[] serialized = new byte[message.getSerializedSize()];
+ CodedOutputStream output = CodedOutputStream.newInstance(serialized);
+ output.useDeterministicSerialization();
+ message.writeTo(output);
+ output.flush();
+
+ CodedInputStream input = CodedInputStream.newInstance(serialized);
+ List<Integer> int32Keys = new ArrayList<Integer>();
+ List<Integer> uint32Keys = new ArrayList<Integer>();
+ List<Long> int64Keys = new ArrayList<Long>();
+ List<String> stringKeys = new ArrayList<String>();
+ int tag;
+ while (true) {
+ tag = input.readTag();
+ if (tag == 0) {
+ break;
+ }
+ int length = input.readRawVarint32();
+ int oldLimit = input.pushLimit(length);
+ switch (WireFormat.getTagFieldNumber(tag)) {
+ case TestMap.STRING_TO_INT32_FIELD_FIELD_NUMBER:
+ stringKeys.add(readMapStringKey(input));
+ break;
+ case TestMap.INT32_TO_INT32_FIELD_FIELD_NUMBER:
+ int32Keys.add(readMapIntegerKey(input));
+ break;
+ case TestMap.UINT32_TO_INT32_FIELD_FIELD_NUMBER:
+ uint32Keys.add(readMapIntegerKey(input));
+ break;
+ case TestMap.INT64_TO_INT32_FIELD_FIELD_NUMBER:
+ int64Keys.add(readMapLongKey(input));
+ break;
+ default:
+ fail("Unexpected fields.");
+ }
+ input.popLimit(oldLimit);
+ }
+ assertEquals(
+ Arrays.asList(-2, 0, 1, 4, 5),
+ int32Keys);
+ assertEquals(
+ Arrays.asList(-2, 0, 1, 4, 5),
+ uint32Keys);
+ assertEquals(
+ Arrays.asList(-2L, 0L, 1L, 4L, 5L),
+ int64Keys);
+ assertEquals(
+ Arrays.asList("", "bar", "baz", "foo", "hello", "world"),
+ stringKeys);
+ }
+
+ public void testInitFromPartialDynamicMessage() {
+ FieldDescriptor fieldDescriptor =
+ TestMap.getDescriptor().findFieldByNumber(TestMap.INT32_TO_MESSAGE_FIELD_FIELD_NUMBER);
+ Descriptor mapEntryType = fieldDescriptor.getMessageType();
+ FieldDescriptor keyField = mapEntryType.findFieldByNumber(1);
+ FieldDescriptor valueField = mapEntryType.findFieldByNumber(2);
+ DynamicMessage dynamicMessage =
+ DynamicMessage.newBuilder(TestMap.getDescriptor())
+ .addRepeatedField(
+ fieldDescriptor,
+ DynamicMessage.newBuilder(mapEntryType)
+ .setField(keyField, 10)
+ .setField(valueField, TestMap.MessageValue.newBuilder().setValue(10).build())
+ .build())
+ .build();
+ TestMap message = TestMap.newBuilder().mergeFrom(dynamicMessage).build();
+ assertEquals(
+ TestMap.MessageValue.newBuilder().setValue(10).build(),
+ message.getInt32ToMessageFieldMap().get(10));
+ }
+
+ public void testInitFromFullyDynamicMessage() {
+ FieldDescriptor fieldDescriptor =
+ TestMap.getDescriptor().findFieldByNumber(TestMap.INT32_TO_MESSAGE_FIELD_FIELD_NUMBER);
+ Descriptor mapEntryType = fieldDescriptor.getMessageType();
+ FieldDescriptor keyField = mapEntryType.findFieldByNumber(1);
+ FieldDescriptor valueField = mapEntryType.findFieldByNumber(2);
+ DynamicMessage dynamicMessage =
+ DynamicMessage.newBuilder(TestMap.getDescriptor())
+ .addRepeatedField(
+ fieldDescriptor,
+ DynamicMessage.newBuilder(mapEntryType)
+ .setField(keyField, 10)
+ .setField(
+ valueField,
+ DynamicMessage.newBuilder(TestMap.MessageValue.getDescriptor())
+ .setField(
+ TestMap.MessageValue.getDescriptor().findFieldByName("value"), 10)
+ .build())
+ .build())
+ .build();
+ TestMap message = TestMap.newBuilder().mergeFrom(dynamicMessage).build();
+ assertEquals(
+ TestMap.MessageValue.newBuilder().setValue(10).build(),
+ message.getInt32ToMessageFieldMap().get(10));
+ }
+
+ private int readMapIntegerKey(CodedInputStream input) throws IOException {
+ int tag = input.readTag();
+ assertEquals(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT), tag);
+ int ret = input.readInt32();
+ // skip the value field.
+ input.skipField(input.readTag());
+ assertTrue(input.isAtEnd());
+ return ret;
+ }
+
+ private long readMapLongKey(CodedInputStream input) throws IOException {
+ int tag = input.readTag();
+ assertEquals(WireFormat.makeTag(1, WireFormat.WIRETYPE_VARINT), tag);
+ long ret = input.readInt64();
+ // skip the value field.
+ input.skipField(input.readTag());
+ assertTrue(input.isAtEnd());
+ return ret;
+ }
+
+ private String readMapStringKey(CodedInputStream input) throws IOException {
+ int tag = input.readTag();
+ assertEquals(WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED), tag);
+ String ret = input.readString();
+ // skip the value field.
+ input.skipField(input.readTag());
+ assertTrue(input.isAtEnd());
+ return ret;
+ }
+
private static <K, V> Map<K, V> newMap(K key1, V value1) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
return map;
}
-
+
private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2) {
Map<K, V> map = new HashMap<K, V>();
map.put(key1, value1);
map.put(key2, value2);
return map;
}
+
+ private static <K, V> Map<K, V> newMap(K key1, V value1, K key2, V value2, K key3, V value3) {
+ Map<K, V> map = new HashMap<K, V>();
+ map.put(key1, value1);
+ map.put(key2, value2);
+ map.put(key3, value3);
+ return map;
+ }
+
+ public void testMap_withNulls() {
+ TestMap.Builder builder = TestMap.newBuilder();
+
+ try {
+ builder.putStringToInt32Field(null, 3);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ builder.putAllStringToInt32Field(newMap(null, 3, "hi", 4));
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ builder.putInt32ToMessageField(3, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ builder.putAllInt32ToMessageField(
+ MapTest.<Integer, MessageValue>newMap(4, null, 5, null));
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ try {
+ builder.putAllInt32ToMessageField(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ assertArrayEquals(new byte[0], builder.build().toByteArray());
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/MessageTest.java b/java/core/src/test/java/com/google/protobuf/MessageTest.java
index abcd3a1d..4fc8f78e 100644
--- a/java/core/src/test/java/com/google/protobuf/MessageTest.java
+++ b/java/core/src/test/java/com/google/protobuf/MessageTest.java
@@ -30,15 +30,13 @@
package com.google.protobuf;
-import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestRequiredForeign;
-import protobuf_unittest.UnittestProto.ForeignMessage;
-
-import junit.framework.TestCase;
-
import java.util.List;
+import junit.framework.TestCase;
/**
* Misc. unit tests for message operations that apply to both generated
@@ -76,6 +74,14 @@ public class MessageTest extends TestCase {
"repeated_string: \"qux\"\n" +
"repeated_string: \"bar\"\n";
+ public void testParsingWithNullExtensionRegistry() throws Exception {
+ try {
+ TestAllTypes.parseFrom(new byte[] {}, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
public void testMergeFrom() throws Exception {
TestAllTypes result =
TestAllTypes.newBuilder(MERGE_DEST)
@@ -323,8 +329,10 @@ public class MessageTest extends TestCase {
assertTrue(result.getField(result.getDescriptorForType()
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
- assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
- .findFieldByName("repeated_foreign_message")), 0);
+ assertEquals(
+ 0,
+ result.getRepeatedFieldCount(
+ result.getDescriptorForType().findFieldByName("repeated_foreign_message")));
}
/** Test reading repeated message from DynamicMessage. */
@@ -347,7 +355,9 @@ public class MessageTest extends TestCase {
assertTrue(result.getField(result.getDescriptorForType()
.findFieldByName("repeated_foreign_message")) instanceof List<?>);
- assertEquals(result.getRepeatedFieldCount(result.getDescriptorForType()
- .findFieldByName("repeated_foreign_message")), 2);
+ assertEquals(
+ 2,
+ result.getRepeatedFieldCount(
+ result.getDescriptorForType().findFieldByName("repeated_foreign_message")));
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
index 23653126..03ed65a5 100644
--- a/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
+++ b/java/core/src/test/java/com/google/protobuf/NestedBuildersTest.java
@@ -32,11 +32,9 @@ package com.google.protobuf;
import protobuf_unittest.Vehicle;
import protobuf_unittest.Wheel;
-
-import junit.framework.TestCase;
-
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
+import junit.framework.TestCase;
/**
* Test cases that exercise end-to-end use cases involving
diff --git a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java
index e40a3662..c388bd05 100644
--- a/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java
+++ b/java/core/src/test/java/com/google/protobuf/NioByteStringTest.java
@@ -32,8 +32,6 @@ package com.google.protobuf;
import static com.google.protobuf.Internal.UTF_8;
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
@@ -41,12 +39,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
+import junit.framework.TestCase;
/**
* Tests for {@link NioByteString}.
@@ -56,11 +56,12 @@ public class NioByteStringTest extends TestCase {
private static final String CLASSNAME = NioByteString.class.getSimpleName();
private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L);
private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode();
- private static final ByteBuffer BUFFER = ByteBuffer.wrap(BYTES.clone());
- private static final ByteString TEST_STRING = new NioByteString(BUFFER);
+
+ private final ByteBuffer backingBuffer = ByteBuffer.wrap(BYTES.clone());
+ private final ByteString testString = new NioByteString(backingBuffer);
public void testExpectedType() {
- String actualClassName = getActualClassName(TEST_STRING);
+ String actualClassName = getActualClassName(testString);
assertEquals(CLASSNAME + " should match type exactly", CLASSNAME, actualClassName);
}
@@ -73,14 +74,14 @@ public class NioByteStringTest extends TestCase {
public void testByteAt() {
boolean stillEqual = true;
for (int i = 0; stillEqual && i < BYTES.length; ++i) {
- stillEqual = (BYTES[i] == TEST_STRING.byteAt(i));
+ stillEqual = (BYTES[i] == testString.byteAt(i));
}
assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
}
public void testByteIterator() {
boolean stillEqual = true;
- ByteString.ByteIterator iter = TEST_STRING.iterator();
+ ByteString.ByteIterator iter = testString.iterator();
for (int i = 0; stillEqual && i < BYTES.length; ++i) {
stillEqual = (iter.hasNext() && BYTES[i] == iter.nextByte());
}
@@ -98,7 +99,7 @@ public class NioByteStringTest extends TestCase {
public void testByteIterable() {
boolean stillEqual = true;
int j = 0;
- for (byte quantum : TEST_STRING) {
+ for (byte quantum : testString) {
stillEqual = (BYTES[j] == quantum);
++j;
}
@@ -108,15 +109,15 @@ public class NioByteStringTest extends TestCase {
public void testSize() {
assertEquals(CLASSNAME + " must have the expected size", BYTES.length,
- TEST_STRING.size());
+ testString.size());
}
public void testGetTreeDepth() {
- assertEquals(CLASSNAME + " must have depth 0", 0, TEST_STRING.getTreeDepth());
+ assertEquals(CLASSNAME + " must have depth 0", 0, testString.getTreeDepth());
}
public void testIsBalanced() {
- assertTrue(CLASSNAME + " is technically balanced", TEST_STRING.isBalanced());
+ assertTrue(CLASSNAME + " is technically balanced", testString.isBalanced());
}
public void testCopyTo_ByteArrayOffsetLength() {
@@ -124,7 +125,7 @@ public class NioByteStringTest extends TestCase {
int length = 100;
byte[] destination = new byte[destinationOffset + length];
int sourceOffset = 213;
- TEST_STRING.copyTo(destination, sourceOffset, destinationOffset, length);
+ testString.copyTo(destination, sourceOffset, destinationOffset, length);
boolean stillEqual = true;
for (int i = 0; stillEqual && i < length; ++i) {
stillEqual = BYTES[i + sourceOffset] == destination[i + destinationOffset];
@@ -139,7 +140,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy one too many bytes
- TEST_STRING.copyTo(destination, TEST_STRING.size() + 1 - length,
+ testString.copyTo(destination, testString.size() + 1 - length,
destinationOffset, length);
fail("Should have thrown an exception when copying too many bytes of a "
+ CLASSNAME);
@@ -149,7 +150,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy with illegal negative sourceOffset
- TEST_STRING.copyTo(destination, -1, destinationOffset, length);
+ testString.copyTo(destination, -1, destinationOffset, length);
fail("Should have thrown an exception when given a negative sourceOffset in "
+ CLASSNAME);
} catch (IndexOutOfBoundsException expected) {
@@ -158,7 +159,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy with illegal negative destinationOffset
- TEST_STRING.copyTo(destination, 0, -1, length);
+ testString.copyTo(destination, 0, -1, length);
fail("Should have thrown an exception when given a negative destinationOffset in "
+ CLASSNAME);
} catch (IndexOutOfBoundsException expected) {
@@ -167,7 +168,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy with illegal negative size
- TEST_STRING.copyTo(destination, 0, 0, -1);
+ testString.copyTo(destination, 0, 0, -1);
fail("Should have thrown an exception when given a negative size in "
+ CLASSNAME);
} catch (IndexOutOfBoundsException expected) {
@@ -176,7 +177,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy with illegal too-large sourceOffset
- TEST_STRING.copyTo(destination, 2 * TEST_STRING.size(), 0, length);
+ testString.copyTo(destination, 2 * testString.size(), 0, length);
fail("Should have thrown an exception when the destinationOffset is too large in "
+ CLASSNAME);
} catch (IndexOutOfBoundsException expected) {
@@ -185,7 +186,7 @@ public class NioByteStringTest extends TestCase {
try {
// Copy with illegal too-large destinationOffset
- TEST_STRING.copyTo(destination, 0, 2 * destination.length, length);
+ testString.copyTo(destination, 0, 2 * destination.length, length);
fail("Should have thrown an exception when the destinationOffset is too large in "
+ CLASSNAME);
} catch (IndexOutOfBoundsException expected) {
@@ -196,21 +197,21 @@ public class NioByteStringTest extends TestCase {
public void testCopyTo_ByteBuffer() {
// Same length.
ByteBuffer myBuffer = ByteBuffer.allocate(BYTES.length);
- TEST_STRING.copyTo(myBuffer);
+ testString.copyTo(myBuffer);
myBuffer.flip();
assertEquals(CLASSNAME + ".copyTo(ByteBuffer) must give back the same bytes",
- BUFFER, myBuffer);
+ backingBuffer, myBuffer);
// Target buffer bigger than required.
- myBuffer = ByteBuffer.allocate(TEST_STRING.size() + 1);
- TEST_STRING.copyTo(myBuffer);
+ myBuffer = ByteBuffer.allocate(testString.size() + 1);
+ testString.copyTo(myBuffer);
myBuffer.flip();
- assertEquals(BUFFER, myBuffer);
+ assertEquals(backingBuffer, myBuffer);
// Target buffer has no space.
myBuffer = ByteBuffer.allocate(0);
try {
- TEST_STRING.copyTo(myBuffer);
+ testString.copyTo(myBuffer);
fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
} catch (BufferOverflowException e) {
// Expected.
@@ -219,7 +220,7 @@ public class NioByteStringTest extends TestCase {
// Target buffer too small.
myBuffer = ByteBuffer.allocate(1);
try {
- TEST_STRING.copyTo(myBuffer);
+ testString.copyTo(myBuffer);
fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
} catch (BufferOverflowException e) {
// Expected.
@@ -227,26 +228,26 @@ public class NioByteStringTest extends TestCase {
}
public void testMarkSupported() {
- InputStream stream = TEST_STRING.newInput();
+ InputStream stream = testString.newInput();
assertTrue(CLASSNAME + ".newInput() must support marking", stream.markSupported());
}
public void testMarkAndReset() throws IOException {
- int fraction = TEST_STRING.size() / 3;
+ int fraction = testString.size() / 3;
- InputStream stream = TEST_STRING.newInput();
- stream.mark(TEST_STRING.size()); // First, mark() the end.
+ InputStream stream = testString.newInput();
+ stream.mark(testString.size()); // First, mark() the end.
skipFully(stream, fraction); // Skip a large fraction, but not all.
assertEquals(
CLASSNAME + ": after skipping to the 'middle', half the bytes are available",
- (TEST_STRING.size() - fraction), stream.available());
+ (testString.size() - fraction), stream.available());
stream.reset();
assertEquals(
CLASSNAME + ": after resetting, all bytes are available",
- TEST_STRING.size(), stream.available());
+ testString.size(), stream.available());
- skipFully(stream, TEST_STRING.size()); // Skip to the end.
+ skipFully(stream, testString.size()); // Skip to the end.
assertEquals(
CLASSNAME + ": after skipping to the end, no more bytes are available",
0, stream.available());
@@ -284,7 +285,7 @@ public class NioByteStringTest extends TestCase {
}
public void testAsReadOnlyByteBuffer() {
- ByteBuffer byteBuffer = TEST_STRING.asReadOnlyByteBuffer();
+ ByteBuffer byteBuffer = testString.asReadOnlyByteBuffer();
byte[] roundTripBytes = new byte[BYTES.length];
assertTrue(byteBuffer.remaining() == BYTES.length);
assertTrue(byteBuffer.isReadOnly());
@@ -294,7 +295,7 @@ public class NioByteStringTest extends TestCase {
}
public void testAsReadOnlyByteBufferList() {
- List<ByteBuffer> byteBuffers = TEST_STRING.asReadOnlyByteBufferList();
+ List<ByteBuffer> byteBuffers = testString.asReadOnlyByteBufferList();
int bytesSeen = 0;
byte[] roundTripBytes = new byte[BYTES.length];
for (ByteBuffer byteBuffer : byteBuffers) {
@@ -310,25 +311,98 @@ public class NioByteStringTest extends TestCase {
}
public void testToByteArray() {
- byte[] roundTripBytes = TEST_STRING.toByteArray();
+ byte[] roundTripBytes = testString.toByteArray();
assertTrue(CLASSNAME + ".toByteArray() must give back the same bytes",
Arrays.equals(BYTES, roundTripBytes));
}
public void testWriteTo() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
- TEST_STRING.writeTo(bos);
+ testString.writeTo(bos);
byte[] roundTripBytes = bos.toByteArray();
assertTrue(CLASSNAME + ".writeTo() must give back the same bytes",
Arrays.equals(BYTES, roundTripBytes));
}
+ public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException {
+ OutputStream os = new OutputStream() {
+ @Override
+ public void write(byte[] b, int off, int len) {
+ Arrays.fill(b, off, off + len, (byte) 0);
+ }
+
+ @Override
+ public void write(int b) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ byte[] original = Arrays.copyOf(BYTES, BYTES.length);
+ testString.writeTo(os);
+ assertTrue(CLASSNAME + ".writeTo() must NOT grant access to underlying buffer",
+ Arrays.equals(original, BYTES));
+ }
+
+ public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException {
+ OutputStream os = new OutputStream() {
+ @Override
+ public void write(byte[] b, int off, int len) {
+ Arrays.fill(b, off, off + len, (byte) 0);
+ }
+
+ @Override
+ public void write(int b) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ testString.writeToInternal(os, 0, testString.size());
+ byte[] allZeros = new byte[testString.size()];
+ assertTrue(CLASSNAME + ".writeToInternal() must grant access to underlying buffer",
+ Arrays.equals(allZeros, backingBuffer.array()));
+ }
+
+ public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException {
+ ByteOutput out = new ByteOutput() {
+ @Override
+ public void write(byte value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void write(byte[] value, int offset, int length) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void writeLazy(byte[] value, int offset, int length) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void write(ByteBuffer value) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void writeLazy(ByteBuffer value) throws IOException {
+ Arrays.fill(value.array(), value.arrayOffset(), value.arrayOffset() + value.limit(),
+ (byte) 0);
+ }
+ };
+
+ testString.writeTo(out);
+ byte[] allZeros = new byte[testString.size()];
+ assertTrue(CLASSNAME + ".writeTo() must grant access to underlying buffer",
+ Arrays.equals(allZeros, backingBuffer.array()));
+ }
+
public void testNewOutput() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ByteString.Output output = ByteString.newOutput();
- TEST_STRING.writeTo(output);
+ testString.writeTo(output);
assertEquals("Output Size returns correct result",
- output.size(), TEST_STRING.size());
+ output.size(), testString.size());
output.writeTo(bos);
assertTrue("Output.writeTo() must give back the same bytes",
Arrays.equals(BYTES, bos.toByteArray()));
@@ -336,7 +410,7 @@ public class NioByteStringTest extends TestCase {
// write the output stream to itself! This should cause it to double
output.writeTo(output);
assertEquals("Writing an output stream to itself is successful",
- TEST_STRING.concat(TEST_STRING), output.toByteString());
+ testString.concat(testString), output.toByteString());
output.reset();
assertEquals("Output.reset() resets the output", 0, output.size());
@@ -373,7 +447,7 @@ public class NioByteStringTest extends TestCase {
}
try {
- TEST_STRING.toString("invalid");
+ testString.toString("invalid");
fail("Should have thrown an exception.");
} catch (UnsupportedEncodingException expected) {
// This is success
@@ -381,36 +455,36 @@ public class NioByteStringTest extends TestCase {
}
public void testEquals() {
- assertEquals(CLASSNAME + " must not equal null", false, TEST_STRING.equals(null));
- assertEquals(CLASSNAME + " must equal self", TEST_STRING, TEST_STRING);
+ assertEquals(CLASSNAME + " must not equal null", false, testString.equals(null));
+ assertEquals(CLASSNAME + " must equal self", testString, testString);
assertFalse(CLASSNAME + " must not equal the empty string",
- TEST_STRING.equals(EMPTY));
+ testString.equals(EMPTY));
assertEquals(CLASSNAME + " empty strings must be equal",
- EMPTY, TEST_STRING.substring(55, 55));
+ EMPTY, testString.substring(55, 55));
assertEquals(CLASSNAME + " must equal another string with the same value",
- TEST_STRING, new NioByteString(BUFFER));
+ testString, new NioByteString(backingBuffer));
byte[] mungedBytes = mungedBytes();
assertFalse(CLASSNAME + " must not equal every string with the same length",
- TEST_STRING.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
+ testString.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
}
public void testEqualsLiteralByteString() {
ByteString literal = ByteString.copyFrom(BYTES);
assertEquals(CLASSNAME + " must equal LiteralByteString with same value", literal,
- TEST_STRING);
- assertEquals(CLASSNAME + " must equal LiteralByteString with same value", TEST_STRING,
+ testString);
+ assertEquals(CLASSNAME + " must equal LiteralByteString with same value", testString,
literal);
assertFalse(CLASSNAME + " must not equal the empty string",
- TEST_STRING.equals(ByteString.EMPTY));
+ testString.equals(ByteString.EMPTY));
assertEquals(CLASSNAME + " empty strings must be equal",
- ByteString.EMPTY, TEST_STRING.substring(55, 55));
+ ByteString.EMPTY, testString.substring(55, 55));
literal = ByteString.copyFrom(mungedBytes());
assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
- TEST_STRING.equals(literal));
+ testString.equals(literal));
assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
- literal.equals(TEST_STRING));
+ literal.equals(testString));
}
public void testEqualsRopeByteString() {
@@ -419,22 +493,22 @@ public class NioByteStringTest extends TestCase {
ByteString rope = p1.concat(p2);
assertEquals(CLASSNAME + " must equal RopeByteString with same value", rope,
- TEST_STRING);
- assertEquals(CLASSNAME + " must equal RopeByteString with same value", TEST_STRING,
+ testString);
+ assertEquals(CLASSNAME + " must equal RopeByteString with same value", testString,
rope);
assertFalse(CLASSNAME + " must not equal the empty string",
- TEST_STRING.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
+ testString.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
assertEquals(CLASSNAME + " empty strings must be equal",
- ByteString.EMPTY.concat(ByteString.EMPTY), TEST_STRING.substring(55, 55));
+ ByteString.EMPTY.concat(ByteString.EMPTY), testString.substring(55, 55));
byte[] mungedBytes = mungedBytes();
p1 = ByteString.copyFrom(mungedBytes, 0, 5);
p2 = ByteString.copyFrom(mungedBytes, 5, mungedBytes.length - 5);
rope = p1.concat(p2);
assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
- TEST_STRING.equals(rope));
+ testString.equals(rope));
assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
- rope.equals(TEST_STRING));
+ rope.equals(testString));
}
private byte[] mungedBytes() {
@@ -445,12 +519,12 @@ public class NioByteStringTest extends TestCase {
}
public void testHashCode() {
- int hash = TEST_STRING.hashCode();
+ int hash = testString.hashCode();
assertEquals(CLASSNAME + " must have expected hashCode", EXPECTED_HASH, hash);
}
public void testPeekCachedHashCode() {
- ByteString newString = new NioByteString(BUFFER);
+ ByteString newString = new NioByteString(backingBuffer);
assertEquals(CLASSNAME + ".peekCachedHashCode() should return zero at first", 0,
newString.peekCachedHashCode());
newString.hashCode();
@@ -461,15 +535,15 @@ public class NioByteStringTest extends TestCase {
public void testPartialHash() {
// partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
// This test would fail if the expected hash were 1. It's not.
- int hash = TEST_STRING.partialHash(TEST_STRING.size(), 0, TEST_STRING.size());
+ int hash = testString.partialHash(testString.size(), 0, testString.size());
assertEquals(CLASSNAME + ".partialHash() must yield expected hashCode",
EXPECTED_HASH, hash);
}
public void testNewInput() throws IOException {
- InputStream input = TEST_STRING.newInput();
+ InputStream input = testString.newInput();
assertEquals("InputStream.available() returns correct value",
- TEST_STRING.size(), input.available());
+ testString.size(), input.available());
boolean stillEqual = true;
for (byte referenceByte : BYTES) {
int expectedInt = (referenceByte & 0xFF);
@@ -482,8 +556,8 @@ public class NioByteStringTest extends TestCase {
}
public void testNewInput_skip() throws IOException {
- InputStream input = TEST_STRING.newInput();
- int stringSize = TEST_STRING.size();
+ InputStream input = testString.newInput();
+ int stringSize = testString.size();
int nearEndIndex = stringSize * 2 / 3;
long skipped1 = input.skip(nearEndIndex);
assertEquals("InputStream.skip()", skipped1, nearEndIndex);
@@ -492,7 +566,7 @@ public class NioByteStringTest extends TestCase {
assertTrue("InputStream.mark() is available", input.markSupported());
input.mark(0);
assertEquals("InputStream.skip(), read()",
- TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
+ testString.byteAt(nearEndIndex) & 0xFF, input.read());
assertEquals("InputStream.available()",
stringSize - skipped1 - 1, input.available());
long skipped2 = input.skip(stringSize);
@@ -504,11 +578,11 @@ public class NioByteStringTest extends TestCase {
assertEquals("InputStream.reset() succeded",
stringSize - skipped1, input.available());
assertEquals("InputStream.reset(), read()",
- TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
+ testString.byteAt(nearEndIndex) & 0xFF, input.read());
}
public void testNewCodedInput() throws IOException {
- CodedInputStream cis = TEST_STRING.newCodedInput();
+ CodedInputStream cis = testString.newCodedInput();
byte[] roundTripBytes = cis.readRawBytes(BYTES.length);
assertTrue(CLASSNAME + " must give the same bytes back from the CodedInputStream",
Arrays.equals(BYTES, roundTripBytes));
@@ -521,22 +595,22 @@ public class NioByteStringTest extends TestCase {
*/
public void testConcat_empty() {
assertSame(CLASSNAME + " concatenated with empty must give " + CLASSNAME,
- TEST_STRING.concat(EMPTY), TEST_STRING);
+ testString.concat(EMPTY), testString);
assertSame("empty concatenated with " + CLASSNAME + " must give " + CLASSNAME,
- EMPTY.concat(TEST_STRING), TEST_STRING);
+ EMPTY.concat(testString), testString);
}
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
- oos.writeObject(TEST_STRING);
+ oos.writeObject(testString);
oos.close();
byte[] pickled = out.toByteArray();
InputStream in = new ByteArrayInputStream(pickled);
ObjectInputStream ois = new ObjectInputStream(in);
Object o = ois.readObject();
assertTrue("Didn't get a ByteString back", o instanceof ByteString);
- assertEquals("Should get an equal ByteString back", TEST_STRING, o);
+ assertEquals("Should get an equal ByteString back", testString, o);
}
private static ByteString forString(String str) {
diff --git a/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java
index 37fa242d..e376b1cd 100644
--- a/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ParseExceptionsTest.java
@@ -1,5 +1,40 @@
+// 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import com.google.protobuf.DescriptorProtos.DescriptorProto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -7,11 +42,8 @@ import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/**
* Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser}
@@ -22,6 +54,7 @@ import static org.junit.Assert.fail;
*
* @author jh@squareup.com (Joshua Humphries)
*/
+@RunWith(JUnit4.class)
public class ParseExceptionsTest {
private interface ParseTester {
@@ -46,116 +79,143 @@ public class ParseExceptionsTest {
@Test public void message_parseFrom_InputStream() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseFrom(in);
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(in);
+ }
+ });
}
@Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
+ }
+ });
}
@Test public void message_parseFrom_CodedInputStream() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
+ }
+ });
}
@Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseFrom(CodedInputStream.newInstance(in),
- ExtensionRegistry.newInstance());
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseFrom(
+ CodedInputStream.newInstance(in), ExtensionRegistry.newInstance());
+ }
+ });
}
@Test public void message_parseDelimitedFrom_InputStream() {
setupDelimited();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseDelimitedFrom(in);
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseDelimitedFrom(in);
+ }
+ });
}
@Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
setupDelimited();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
+ }
+ });
}
@Test public void messageBuilder_mergeFrom_InputStream() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.newBuilder().mergeFrom(in).build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder().mergeFrom(in).build();
+ }
+ });
}
@Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder()
+ .mergeFrom(in, ExtensionRegistry.newInstance())
+ .build();
+ }
+ });
}
@Test public void messageBuilder_mergeFrom_CodedInputStream() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
+ }
+ });
}
@Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
setup();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- return DescriptorProto.newBuilder()
- .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ return DescriptorProto.newBuilder()
+ .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance())
+ .build();
+ }
+ });
}
@Test public void messageBuilder_mergeDelimitedFrom_InputStream() {
setupDelimited();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- DescriptorProto.Builder builder = DescriptorProto.newBuilder();
- builder.mergeDelimitedFrom(in);
- return builder.build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
+ builder.mergeDelimitedFrom(in);
+ return builder.build();
+ }
+ });
}
@Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
setupDelimited();
- verifyExceptions(new ParseTester() {
- public DescriptorProto parse(InputStream in) throws IOException {
- DescriptorProto.Builder builder = DescriptorProto.newBuilder();
- builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
- return builder.build();
- }
- });
+ verifyExceptions(
+ new ParseTester() {
+ @Override
+ public DescriptorProto parse(InputStream in) throws IOException {
+ DescriptorProto.Builder builder = DescriptorProto.newBuilder();
+ builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
+ return builder.build();
+ }
+ });
}
private void verifyExceptions(ParseTester parseTester) {
diff --git a/java/core/src/test/java/com/google/protobuf/ParserTest.java b/java/core/src/test/java/com/google/protobuf/ParserTest.java
index 5a92bacf..1e891112 100644
--- a/java/core/src/test/java/com/google/protobuf/ParserTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ParserTest.java
@@ -30,25 +30,21 @@
package com.google.protobuf;
-import com.google.protobuf.UnittestLite.TestAllTypesLite;
-import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
-import com.google.protobuf.UnittestLite.TestParsingMergeLite;
+import protobuf_unittest.UnittestOptimizeFor;
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
-import protobuf_unittest.UnittestOptimizeFor;
+import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestParsingMerge;
import protobuf_unittest.UnittestProto.TestRequired;
-import protobuf_unittest.UnittestProto;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InterruptedIOException;
+import junit.framework.TestCase;
/**
* Unit test for {@link Parser}.
@@ -80,6 +76,8 @@ public class ParserTest extends TestCase {
new ByteArrayInputStream(data), registry));
assertMessageEquals(message, parser.parseFrom(
CodedInputStream.newInstance(data), registry));
+ assertMessageEquals(
+ message, parser.parseFrom(message.toByteString().asReadOnlyByteBuffer(), registry));
}
@SuppressWarnings("unchecked")
@@ -100,6 +98,7 @@ public class ParserTest extends TestCase {
new ByteArrayInputStream(data)));
assertMessageEquals(message, parser.parseFrom(
CodedInputStream.newInstance(data)));
+ assertMessageEquals(message, parser.parseFrom(message.toByteString().asReadOnlyByteBuffer()));
}
private void assertMessageEquals(
@@ -179,16 +178,12 @@ public class ParserTest extends TestCase {
public void testParseExtensions() throws Exception {
assertRoundTripEquals(TestUtil.getAllExtensionsSet(),
TestUtil.getExtensionRegistry());
- assertRoundTripEquals(TestUtil.getAllLiteExtensionsSet(),
- TestUtil.getExtensionRegistryLite());
}
public void testParsePacked() throws Exception {
assertRoundTripEquals(TestUtil.getPackedSet());
assertRoundTripEquals(TestUtil.getPackedExtensionsSet(),
TestUtil.getExtensionRegistry());
- assertRoundTripEquals(TestUtil.getLitePackedExtensionsSet(),
- TestUtil.getExtensionRegistryLite());
}
public void testParseDelimitedTo() throws Exception {
@@ -196,20 +191,11 @@ public class ParserTest extends TestCase {
TestAllTypes normalMessage = TestUtil.getAllSet();
ByteArrayOutputStream output = new ByteArrayOutputStream();
normalMessage.writeDelimitedTo(output);
-
- // Write MessageLite with packed extension fields.
- TestPackedExtensionsLite packedMessage =
- TestUtil.getLitePackedExtensionsSet();
- packedMessage.writeDelimitedTo(output);
+ normalMessage.writeDelimitedTo(output);
InputStream input = new ByteArrayInputStream(output.toByteArray());
- assertMessageEquals(
- normalMessage,
- normalMessage.getParserForType().parseDelimitedFrom(input));
- assertMessageEquals(
- packedMessage,
- packedMessage.getParserForType().parseDelimitedFrom(
- input, TestUtil.getExtensionRegistryLite()));
+ assertMessageEquals(normalMessage, normalMessage.getParserForType().parseDelimitedFrom(input));
+ assertMessageEquals(normalMessage, normalMessage.getParserForType().parseDelimitedFrom(input));
}
public void testParseUnknownFields() throws Exception {
@@ -244,14 +230,6 @@ public class ParserTest extends TestCase {
assertEquals("hello", allTypes.getOptionalString());
}
- /** Helper method for {@link #testParsingMergeLite()}.*/
- private void assertMessageMerged(TestAllTypesLite allTypes)
- throws Exception {
- assertEquals(3, allTypes.getOptionalInt32());
- assertEquals(2, allTypes.getOptionalInt64());
- assertEquals("hello", allTypes.getOptionalString());
- }
-
public void testParsingMerge() throws Exception {
// Build messages.
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
@@ -313,65 +291,42 @@ public class ParserTest extends TestCase {
TestParsingMerge.repeatedExt));
}
- public void testParsingMergeLite() throws Exception {
- // Build messages.
- TestAllTypesLite.Builder builder =
- TestAllTypesLite.newBuilder();
- TestAllTypesLite msg1 = builder.setOptionalInt32(1).build();
- builder.clear();
- TestAllTypesLite msg2 = builder.setOptionalInt64(2).build();
- builder.clear();
- TestAllTypesLite msg3 = builder.setOptionalInt32(3)
- .setOptionalString("hello").build();
-
- // Build groups.
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG1 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
- .setField1(msg1).build();
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG2 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
- .setField1(msg2).build();
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1 optionalG3 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group1.newBuilder()
- .setField1(msg3).build();
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG1 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
- .setField1(msg1).build();
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG2 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
- .setField1(msg2).build();
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2 repeatedG3 =
- TestParsingMergeLite.RepeatedFieldsGenerator.Group2.newBuilder()
- .setField1(msg3).build();
-
- // Assign and serialize RepeatedFieldsGenerator.
- ByteString data = TestParsingMergeLite.RepeatedFieldsGenerator.newBuilder()
- .addField1(msg1).addField1(msg2).addField1(msg3)
- .addField2(msg1).addField2(msg2).addField2(msg3)
- .addField3(msg1).addField3(msg2).addField3(msg3)
- .addGroup1(optionalG1).addGroup1(optionalG2).addGroup1(optionalG3)
- .addGroup2(repeatedG1).addGroup2(repeatedG2).addGroup2(repeatedG3)
- .addExt1(msg1).addExt1(msg2).addExt1(msg3)
- .addExt2(msg1).addExt2(msg2).addExt2(msg3)
- .build().toByteString();
-
- // Parse TestParsingMergeLite.
- ExtensionRegistry registry = ExtensionRegistry.newInstance();
- UnittestLite.registerAllExtensions(registry);
- TestParsingMergeLite parsingMerge = TestParsingMergeLite.parser().parseFrom(data, registry);
-
- // Required and optional fields should be merged.
- assertMessageMerged(parsingMerge.getRequiredAllTypes());
- assertMessageMerged(parsingMerge.getOptionalAllTypes());
- assertMessageMerged(
- parsingMerge.getOptionalGroup().getOptionalGroupAllTypes());
- assertMessageMerged(parsingMerge.getExtension(
- TestParsingMergeLite.optionalExt));
+ public void testParseDelimitedFrom_firstByteInterrupted_preservesCause() {
+ try {
+ TestUtil.getAllSet().parseDelimitedFrom(
+ new InputStream() {
+ @Override
+ public int read() throws IOException {
+ throw new InterruptedIOException();
+ }
+ });
+ fail("Expected InterruptedIOException");
+ } catch (Exception e) {
+ assertEquals(InterruptedIOException.class, e.getClass());
+ }
+ }
- // Repeated fields should not be merged.
- assertEquals(3, parsingMerge.getRepeatedAllTypesCount());
- assertEquals(3, parsingMerge.getRepeatedGroupCount());
- assertEquals(3, parsingMerge.getExtensionCount(
- TestParsingMergeLite.repeatedExt));
+ public void testParseDelimitedFrom_secondByteInterrupted_preservesCause() {
+ try {
+ TestUtil.getAllSet().parseDelimitedFrom(
+ new InputStream() {
+ private int i;
+
+ @Override
+ public int read() throws IOException {
+ switch (i++) {
+ case 0:
+ return 1;
+ case 1:
+ throw new InterruptedIOException();
+ default:
+ throw new AssertionError();
+ }
+ }
+ });
+ fail("Expected InterruptedIOException");
+ } catch (Exception e) {
+ assertEquals(InterruptedIOException.class, e.getClass());
+ }
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
index 245c3dee..af717bfd 100644
--- a/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ProtobufArrayListTest.java
@@ -32,12 +32,11 @@ package com.google.protobuf;
import static java.util.Arrays.asList;
-import junit.framework.TestCase;
-
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
+import junit.framework.TestCase;
/**
* Tests for {@link ProtobufArrayList}.
@@ -63,20 +62,6 @@ public class ProtobufArrayListTest extends TestCase {
assertImmutable(ProtobufArrayList.<Integer>emptyList());
}
- public void testCopyConstructor() {
- ProtobufArrayList<Integer> copy = new ProtobufArrayList<Integer>(TERTIARY_LIST);
- assertEquals(TERTIARY_LIST, copy);
-
- copy = new ProtobufArrayList<Integer>(IntArrayList.emptyList());
- assertEquals(ProtobufArrayList.emptyList(), copy);
-
- copy = new ProtobufArrayList<Integer>(asList(1, 2, 3));
- assertEquals(asList(1, 2, 3), copy);
-
- copy = new ProtobufArrayList<Integer>(Collections.<Integer>emptyList());
- assertEquals(ProtobufArrayList.emptyList(), copy);
- }
-
public void testModificationWithIteration() {
list.addAll(asList(1, 2, 3, 4));
Iterator<Integer> iterator = list.iterator();
diff --git a/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java
index 49d52321..edbd0afd 100644
--- a/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderTest.java
+++ b/java/core/src/test/java/com/google/protobuf/RepeatedFieldBuilderV3Test.java
@@ -32,25 +32,23 @@ package com.google.protobuf;
import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
-
-import junit.framework.TestCase;
-
import java.util.Collections;
import java.util.List;
+import junit.framework.TestCase;
/**
- * Tests for {@link RepeatedFieldBuilder}. This tests basic functionality.
+ * Tests for {@link RepeatedFieldBuilderV3}. This tests basic functionality.
* More extensive testing is provided via other tests that exercise the
* builder.
*
* @author jonp@google.com (Jon Perlow)
*/
-public class RepeatedFieldBuilderTest extends TestCase {
+public class RepeatedFieldBuilderV3Test extends TestCase {
public void testBasicUse() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
- TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+ RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
+ TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
@@ -70,8 +68,8 @@ public class RepeatedFieldBuilderTest extends TestCase {
public void testGoingBackAndForth() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
- TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+ RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
+ TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(0).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
assertEquals(0, builder.getMessage(0).getOptionalInt32());
@@ -99,8 +97,8 @@ public class RepeatedFieldBuilderTest extends TestCase {
public void testVariousMethods() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
- TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+ RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
+ TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(2).build());
builder.addBuilder(0, TestAllTypes.getDefaultInstance())
@@ -141,8 +139,8 @@ public class RepeatedFieldBuilderTest extends TestCase {
public void testLists() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
- TestAllTypesOrBuilder> builder = newRepeatedFieldBuilder(mockParent);
+ RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
+ TestAllTypesOrBuilder> builder = newRepeatedFieldBuilderV3(mockParent);
builder.addMessage(TestAllTypes.newBuilder().setOptionalInt32(1).build());
builder.addMessage(0,
TestAllTypes.newBuilder().setOptionalInt32(0).build());
@@ -180,10 +178,10 @@ public class RepeatedFieldBuilderTest extends TestCase {
}
}
- private RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ private RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>
- newRepeatedFieldBuilder(GeneratedMessage.BuilderParent parent) {
- return new RepeatedFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ newRepeatedFieldBuilderV3(GeneratedMessage.BuilderParent parent) {
+ return new RepeatedFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(Collections.<TestAllTypes>emptyList(), false,
parent, false);
}
diff --git a/java/core/src/test/java/com/google/protobuf/ServiceTest.java b/java/core/src/test/java/com/google/protobuf/ServiceTest.java
index ff980d66..b895ad8d 100644
--- a/java/core/src/test/java/com/google/protobuf/ServiceTest.java
+++ b/java/core/src/test/java/com/google/protobuf/ServiceTest.java
@@ -35,21 +35,19 @@ import com.google.protobuf.Descriptors.MethodDescriptor;
import google.protobuf.no_generic_services_test.UnittestNoGenericServices;
import protobuf_unittest.MessageWithNoOuter;
import protobuf_unittest.ServiceWithNoOuter;
-import protobuf_unittest.UnittestProto.TestAllTypes;
-import protobuf_unittest.UnittestProto.TestService;
-import protobuf_unittest.UnittestProto.FooRequest;
-import protobuf_unittest.UnittestProto.FooResponse;
import protobuf_unittest.UnittestProto.BarRequest;
import protobuf_unittest.UnittestProto.BarResponse;
-
-import org.easymock.classextension.EasyMock;
-import org.easymock.classextension.IMocksControl;
-import org.easymock.IArgumentMatcher;
+import protobuf_unittest.UnittestProto.FooRequest;
+import protobuf_unittest.UnittestProto.FooResponse;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestService;
import java.util.HashSet;
import java.util.Set;
-
import junit.framework.TestCase;
+import org.easymock.classextension.EasyMock;
+import org.easymock.IArgumentMatcher;
+import org.easymock.classextension.IMocksControl;
/**
* Tests services and stubs.
@@ -175,12 +173,14 @@ public class ServiceTest extends TestCase {
MethodDescriptor fooMethod =
ServiceWithNoOuter.getDescriptor().findMethodByName("Foo");
MessageWithNoOuter request = MessageWithNoOuter.getDefaultInstance();
- RpcCallback<Message> callback = new RpcCallback<Message>() {
- public void run(Message parameter) {
- // No reason this should be run.
- fail();
- }
- };
+ RpcCallback<Message> callback =
+ new RpcCallback<Message>() {
+ @Override
+ public void run(Message parameter) {
+ // No reason this should be run.
+ fail();
+ }
+ };
RpcCallback<TestAllTypes> specializedCallback =
RpcUtil.specializeCallback(callback);
@@ -269,6 +269,8 @@ public class ServiceTest extends TestCase {
file.getServices().get(0).getMethods().get(0).getName());
}
+
+
// =================================================================
/**
@@ -290,7 +292,9 @@ public class ServiceTest extends TestCase {
public boolean isCalled() { return called; }
public void reset() { called = false; }
- public void run(Type message) { called = true; }
+ @Override
+ public void run(Type message) {
+ called = true; }
}
/** Implementation of the wrapsCallback() argument matcher. */
@@ -301,6 +305,7 @@ public class ServiceTest extends TestCase {
this.callback = callback;
}
+ @Override
@SuppressWarnings("unchecked")
public boolean matches(Object actual) {
if (!(actual instanceof RpcCallback)) {
@@ -313,6 +318,7 @@ public class ServiceTest extends TestCase {
return callback.isCalled();
}
+ @Override
public void appendTo(StringBuffer buffer) {
buffer.append("wrapsCallback(mockCallback)");
}
diff --git a/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java
index 58b80007..e3a8d4f4 100644
--- a/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderTest.java
+++ b/java/core/src/test/java/com/google/protobuf/SingleFieldBuilderV3Test.java
@@ -36,19 +36,19 @@ import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import junit.framework.TestCase;
/**
- * Tests for {@link SingleFieldBuilder}. This tests basic functionality.
+ * Tests for {@link SingleFieldBuilderV3}. This tests basic functionality.
* More extensive testing is provided via other tests that exercise the
* builder.
*
* @author jonp@google.com (Jon Perlow)
*/
-public class SingleFieldBuilderTest extends TestCase {
+public class SingleFieldBuilderV3Test extends TestCase {
public void testBasicUseAndInvalidations() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
- new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
@@ -76,9 +76,9 @@ public class SingleFieldBuilderTest extends TestCase {
public void testSetMessage() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
- new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
@@ -102,9 +102,9 @@ public class SingleFieldBuilderTest extends TestCase {
public void testClear() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
- new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
@@ -122,9 +122,9 @@ public class SingleFieldBuilderTest extends TestCase {
public void testMerge() {
TestUtil.MockBuilderParent mockParent = new TestUtil.MockBuilderParent();
- SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder> builder =
- new SingleFieldBuilder<TestAllTypes, TestAllTypes.Builder,
+ new SingleFieldBuilderV3<TestAllTypes, TestAllTypes.Builder,
TestAllTypesOrBuilder>(
TestAllTypes.getDefaultInstance(),
mockParent,
diff --git a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java
index 366086d3..a7f8342d 100644
--- a/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java
+++ b/java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java
@@ -30,8 +30,6 @@
package com.google.protobuf;
-import junit.framework.TestCase;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -40,6 +38,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
+import junit.framework.TestCase;
/**
* @author darick@google.com Darick Tong
@@ -56,14 +55,17 @@ public class SmallSortedMapTest extends TestCase {
this.value = value;
}
+ @Override
public K getKey() {
return key;
}
+ @Override
public V getValue() {
return value;
}
+ @Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
diff --git a/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
index 2c60fe0e..4af55429 100644
--- a/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
+++ b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java
@@ -92,5 +92,31 @@ public class TestBadIdentifiers extends TestCase {
assertEquals(0L, message.getExtension(
TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
+ assertEquals("", message.getFieldName32());
+ assertEquals("", message.getFieldName33());
+ assertEquals(0, message.get2Conflict34());
+ assertEquals(0, message.get2Conflict35());
+
+ }
+
+ public void testNumberFields() throws Exception {
+ TestBadIdentifiersProto.TestLeadingNumberFields message =
+ TestBadIdentifiersProto.TestLeadingNumberFields.getDefaultInstance();
+ // Make sure generated accessors are properly named.
+ assertFalse(message.has30DayImpressions());
+ assertEquals(0, message.get30DayImpressions());
+ assertEquals(0, message.get60DayImpressionsCount());
+ assertEquals(0, message.get60DayImpressionsList().size());
+
+ assertFalse(message.has2Underscores());
+ assertEquals("", message.get2Underscores());
+ assertEquals(0, message.get2RepeatedUnderscoresCount());
+ assertEquals(0, message.get2RepeatedUnderscoresList().size());
+
+ assertFalse(message.has32());
+ assertEquals(0, message.get32());
+ assertEquals(0, message.get64Count());
+ assertEquals(0, message.get64List().size());
+
}
}
diff --git a/java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java
new file mode 100644
index 00000000..37f94c03
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TestBadIdentifiersLite.java
@@ -0,0 +1,83 @@
+// 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 junit.framework.TestCase;
+
+/**
+ * Tests that proto2 api generation doesn't cause compile errors when compiling protocol buffers
+ * that have names that would otherwise conflict if not fully qualified (like @Deprecated
+ * and @Override).
+ *
+ * <p>Forked from {@link TestBadIdentifiers}.
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+public final class TestBadIdentifiersLite extends TestCase {
+
+ public void testCompilation() {
+ // If this compiles, it means the generation was correct.
+ TestBadIdentifiersProto.Deprecated.newBuilder();
+ TestBadIdentifiersProto.Override.newBuilder();
+ }
+
+ public void testConflictingFieldNames() throws Exception {
+ TestBadIdentifiersProto.TestConflictingFieldNames message =
+ TestBadIdentifiersProto.TestConflictingFieldNames.getDefaultInstance();
+ // Make sure generated accessors are properly named.
+ assertEquals(0, message.getInt32Field1Count());
+ assertEquals(0, message.getEnumField2Count());
+ assertEquals(0, message.getStringField3Count());
+ assertEquals(0, message.getBytesField4Count());
+ assertEquals(0, message.getMessageField5Count());
+
+ assertEquals(0, message.getInt32FieldCount11());
+ assertEquals(0, message.getEnumFieldCount12().getNumber());
+ assertEquals("", message.getStringFieldCount13());
+ assertEquals(ByteString.EMPTY, message.getBytesFieldCount14());
+ assertEquals(0, message.getMessageFieldCount15().getSerializedSize());
+
+ assertEquals(0, message.getInt32Field21Count());
+ assertEquals(0, message.getEnumField22Count());
+ assertEquals(0, message.getStringField23Count());
+ assertEquals(0, message.getBytesField24Count());
+ assertEquals(0, message.getMessageField25Count());
+
+ assertEquals(0, message.getInt32Field1List().size());
+ assertEquals(0, message.getInt32FieldList31());
+
+ assertEquals(0, message.getInt64FieldCount());
+ assertEquals(0L, message.getExtension(
+ TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldCount).longValue());
+ assertEquals(0L, message.getExtension(
+ TestBadIdentifiersProto.TestConflictingFieldNames.int64FieldList).longValue());
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/TestUtil.java b/java/core/src/test/java/com/google/protobuf/TestUtil.java
index 01acb884..b4bc3a3d 100644
--- a/java/core/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/core/src/test/java/com/google/protobuf/TestUtil.java
@@ -30,205 +30,200 @@
package com.google.protobuf;
-import protobuf_unittest.UnittestProto;
-import com.google.protobuf.UnittestLite;
-
+import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultStringExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalCordExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalStringExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite;
+import static protobuf_unittest.UnittestProto.OptionalGroup_extension;
+import static protobuf_unittest.UnittestProto.RepeatedGroup_extension;
+import static protobuf_unittest.UnittestProto.defaultBoolExtension;
+import static protobuf_unittest.UnittestProto.defaultBytesExtension;
+import static protobuf_unittest.UnittestProto.defaultCordExtension;
+import static protobuf_unittest.UnittestProto.defaultDoubleExtension;
+import static protobuf_unittest.UnittestProto.defaultFixed32Extension;
+import static protobuf_unittest.UnittestProto.defaultFixed64Extension;
+import static protobuf_unittest.UnittestProto.defaultFloatExtension;
+import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension;
+import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
// The static imports are to avoid 100+ char lines. The following is roughly equivalent to
// import static protobuf_unittest.UnittestProto.*;
import static protobuf_unittest.UnittestProto.defaultInt32Extension;
import static protobuf_unittest.UnittestProto.defaultInt64Extension;
-import static protobuf_unittest.UnittestProto.defaultUint32Extension;
-import static protobuf_unittest.UnittestProto.defaultUint64Extension;
-import static protobuf_unittest.UnittestProto.defaultSint32Extension;
-import static protobuf_unittest.UnittestProto.defaultSint64Extension;
-import static protobuf_unittest.UnittestProto.defaultFixed32Extension;
-import static protobuf_unittest.UnittestProto.defaultFixed64Extension;
+import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension;
import static protobuf_unittest.UnittestProto.defaultSfixed32Extension;
import static protobuf_unittest.UnittestProto.defaultSfixed64Extension;
-import static protobuf_unittest.UnittestProto.defaultFloatExtension;
-import static protobuf_unittest.UnittestProto.defaultDoubleExtension;
-import static protobuf_unittest.UnittestProto.defaultBoolExtension;
+import static protobuf_unittest.UnittestProto.defaultSint32Extension;
+import static protobuf_unittest.UnittestProto.defaultSint64Extension;
import static protobuf_unittest.UnittestProto.defaultStringExtension;
-import static protobuf_unittest.UnittestProto.defaultBytesExtension;
-import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension;
-import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension;
-import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
import static protobuf_unittest.UnittestProto.defaultStringPieceExtension;
-import static protobuf_unittest.UnittestProto.defaultCordExtension;
-
-import static protobuf_unittest.UnittestProto.oneofUint32Extension;
+import static protobuf_unittest.UnittestProto.defaultUint32Extension;
+import static protobuf_unittest.UnittestProto.defaultUint64Extension;
+import static protobuf_unittest.UnittestProto.oneofBytesExtension;
import static protobuf_unittest.UnittestProto.oneofNestedMessageExtension;
import static protobuf_unittest.UnittestProto.oneofStringExtension;
-import static protobuf_unittest.UnittestProto.oneofBytesExtension;
-
-import static protobuf_unittest.UnittestProto.optionalInt32Extension;
-import static protobuf_unittest.UnittestProto.optionalInt64Extension;
-import static protobuf_unittest.UnittestProto.optionalUint32Extension;
-import static protobuf_unittest.UnittestProto.optionalUint64Extension;
-import static protobuf_unittest.UnittestProto.optionalSint32Extension;
-import static protobuf_unittest.UnittestProto.optionalSint64Extension;
-import static protobuf_unittest.UnittestProto.optionalFixed32Extension;
-import static protobuf_unittest.UnittestProto.optionalFixed64Extension;
-import static protobuf_unittest.UnittestProto.optionalSfixed32Extension;
-import static protobuf_unittest.UnittestProto.optionalSfixed64Extension;
-import static protobuf_unittest.UnittestProto.optionalFloatExtension;
-import static protobuf_unittest.UnittestProto.optionalDoubleExtension;
+import static protobuf_unittest.UnittestProto.oneofUint32Extension;
import static protobuf_unittest.UnittestProto.optionalBoolExtension;
-import static protobuf_unittest.UnittestProto.optionalStringExtension;
import static protobuf_unittest.UnittestProto.optionalBytesExtension;
-import static protobuf_unittest.UnittestProto.optionalGroupExtension;
import static protobuf_unittest.UnittestProto.optionalCordExtension;
+import static protobuf_unittest.UnittestProto.optionalDoubleExtension;
+import static protobuf_unittest.UnittestProto.optionalFixed32Extension;
+import static protobuf_unittest.UnittestProto.optionalFixed64Extension;
+import static protobuf_unittest.UnittestProto.optionalFloatExtension;
import static protobuf_unittest.UnittestProto.optionalForeignEnumExtension;
import static protobuf_unittest.UnittestProto.optionalForeignMessageExtension;
+import static protobuf_unittest.UnittestProto.optionalGroupExtension;
import static protobuf_unittest.UnittestProto.optionalImportEnumExtension;
import static protobuf_unittest.UnittestProto.optionalImportMessageExtension;
+import static protobuf_unittest.UnittestProto.optionalInt32Extension;
+import static protobuf_unittest.UnittestProto.optionalInt64Extension;
+import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension;
import static protobuf_unittest.UnittestProto.optionalNestedEnumExtension;
import static protobuf_unittest.UnittestProto.optionalNestedMessageExtension;
import static protobuf_unittest.UnittestProto.optionalPublicImportMessageExtension;
-import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension;
+import static protobuf_unittest.UnittestProto.optionalSfixed32Extension;
+import static protobuf_unittest.UnittestProto.optionalSfixed64Extension;
+import static protobuf_unittest.UnittestProto.optionalSint32Extension;
+import static protobuf_unittest.UnittestProto.optionalSint64Extension;
+import static protobuf_unittest.UnittestProto.optionalStringExtension;
import static protobuf_unittest.UnittestProto.optionalStringPieceExtension;
-
-import static protobuf_unittest.UnittestProto.repeatedInt32Extension;
-import static protobuf_unittest.UnittestProto.repeatedInt64Extension;
-import static protobuf_unittest.UnittestProto.repeatedUint32Extension;
-import static protobuf_unittest.UnittestProto.repeatedUint64Extension;
-import static protobuf_unittest.UnittestProto.repeatedSint32Extension;
-import static protobuf_unittest.UnittestProto.repeatedSint64Extension;
+import static protobuf_unittest.UnittestProto.optionalUint32Extension;
+import static protobuf_unittest.UnittestProto.optionalUint64Extension;
+import static protobuf_unittest.UnittestProto.packedBoolExtension;
+import static protobuf_unittest.UnittestProto.packedDoubleExtension;
+import static protobuf_unittest.UnittestProto.packedEnumExtension;
+import static protobuf_unittest.UnittestProto.packedFixed32Extension;
+import static protobuf_unittest.UnittestProto.packedFixed64Extension;
+import static protobuf_unittest.UnittestProto.packedFloatExtension;
+import static protobuf_unittest.UnittestProto.packedInt32Extension;
+import static protobuf_unittest.UnittestProto.packedInt64Extension;
+import static protobuf_unittest.UnittestProto.packedSfixed32Extension;
+import static protobuf_unittest.UnittestProto.packedSfixed64Extension;
+import static protobuf_unittest.UnittestProto.packedSint32Extension;
+import static protobuf_unittest.UnittestProto.packedSint64Extension;
+import static protobuf_unittest.UnittestProto.packedUint32Extension;
+import static protobuf_unittest.UnittestProto.packedUint64Extension;
+import static protobuf_unittest.UnittestProto.repeatedBoolExtension;
+import static protobuf_unittest.UnittestProto.repeatedBytesExtension;
+import static protobuf_unittest.UnittestProto.repeatedCordExtension;
+import static protobuf_unittest.UnittestProto.repeatedDoubleExtension;
import static protobuf_unittest.UnittestProto.repeatedFixed32Extension;
import static protobuf_unittest.UnittestProto.repeatedFixed64Extension;
-import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension;
-import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension;
import static protobuf_unittest.UnittestProto.repeatedFloatExtension;
-import static protobuf_unittest.UnittestProto.repeatedDoubleExtension;
-import static protobuf_unittest.UnittestProto.repeatedBoolExtension;
-import static protobuf_unittest.UnittestProto.repeatedStringExtension;
-import static protobuf_unittest.UnittestProto.repeatedBytesExtension;
-import static protobuf_unittest.UnittestProto.repeatedGroupExtension;
-import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension;
+import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension;
import static protobuf_unittest.UnittestProto.repeatedForeignMessageExtension;
+import static protobuf_unittest.UnittestProto.repeatedGroupExtension;
+import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension;
import static protobuf_unittest.UnittestProto.repeatedImportMessageExtension;
+import static protobuf_unittest.UnittestProto.repeatedInt32Extension;
+import static protobuf_unittest.UnittestProto.repeatedInt64Extension;
import static protobuf_unittest.UnittestProto.repeatedLazyMessageExtension;
import static protobuf_unittest.UnittestProto.repeatedNestedEnumExtension;
-import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension;
-import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension;
+import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension;
+import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension;
+import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension;
+import static protobuf_unittest.UnittestProto.repeatedSint32Extension;
+import static protobuf_unittest.UnittestProto.repeatedSint64Extension;
+import static protobuf_unittest.UnittestProto.repeatedStringExtension;
import static protobuf_unittest.UnittestProto.repeatedStringPieceExtension;
-import static protobuf_unittest.UnittestProto.repeatedCordExtension;
-
-import static protobuf_unittest.UnittestProto.OptionalGroup_extension;
-import static protobuf_unittest.UnittestProto.RepeatedGroup_extension;
-
-import static protobuf_unittest.UnittestProto.packedInt32Extension;
-import static protobuf_unittest.UnittestProto.packedInt64Extension;
-import static protobuf_unittest.UnittestProto.packedUint32Extension;
-import static protobuf_unittest.UnittestProto.packedUint64Extension;
-import static protobuf_unittest.UnittestProto.packedSint32Extension;
-import static protobuf_unittest.UnittestProto.packedSint64Extension;
-import static protobuf_unittest.UnittestProto.packedFixed32Extension;
-import static protobuf_unittest.UnittestProto.packedFixed64Extension;
-import static protobuf_unittest.UnittestProto.packedSfixed32Extension;
-import static protobuf_unittest.UnittestProto.packedSfixed64Extension;
-import static protobuf_unittest.UnittestProto.packedFloatExtension;
-import static protobuf_unittest.UnittestProto.packedDoubleExtension;
-import static protobuf_unittest.UnittestProto.packedBoolExtension;
-import static protobuf_unittest.UnittestProto.packedEnumExtension;
-
-import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultStringExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
-import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
-
-import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
-import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
-
-import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalStringExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite;
-import static com.google.protobuf.UnittestLite.optionalCordExtensionLite;
-
-import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite;
-import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite;
-
-import static com.google.protobuf.UnittestLite.OptionalGroup_extension_lite;
-import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite;
-
-import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite;
-import static com.google.protobuf.UnittestLite.packedFloatExtensionLite;
-import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite;
-import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
-import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
+import static protobuf_unittest.UnittestProto.repeatedUint32Extension;
+import static protobuf_unittest.UnittestProto.repeatedUint64Extension;
+import com.google.protobuf.UnittestImportLite.ImportEnumLite;
+import com.google.protobuf.UnittestLite.ForeignEnumLite;
+import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
+import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
+import com.google.protobuf.test.UnittestImport.ImportEnum;
+import com.google.protobuf.test.UnittestImport.ImportMessage;
+import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage;
+import protobuf_unittest.UnittestProto;
+import protobuf_unittest.UnittestProto.ForeignEnum;
+import protobuf_unittest.UnittestProto.ForeignMessage;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder;
import protobuf_unittest.UnittestProto.TestAllTypes;
@@ -236,28 +231,12 @@ import protobuf_unittest.UnittestProto.TestAllTypesOrBuilder;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
+import protobuf_unittest.UnittestProto.TestRequired;
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
-import protobuf_unittest.UnittestProto.ForeignMessage;
-import protobuf_unittest.UnittestProto.ForeignEnum;
-import com.google.protobuf.test.UnittestImport.ImportEnum;
-import com.google.protobuf.test.UnittestImport.ImportMessage;
-import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage;
-
-import com.google.protobuf.UnittestLite.TestAllTypesLite;
-import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
-import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
-import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
-import com.google.protobuf.UnittestLite.ForeignMessageLite;
-import com.google.protobuf.UnittestLite.ForeignEnumLite;
-import com.google.protobuf.UnittestImportLite.ImportEnumLite;
-import com.google.protobuf.UnittestImportLite.ImportMessageLite;
-import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
-
-import junit.framework.Assert;
-
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
+import junit.framework.Assert;
/**
* Contains methods for setting all fields of {@code TestAllTypes} to
@@ -274,12 +253,24 @@ import java.io.RandomAccessFile;
public final class TestUtil {
private TestUtil() {}
+ public static final TestRequired TEST_REQUIRED_UNINITIALIZED =
+ TestRequired.newBuilder().setA(1).buildPartial();
+ public static final TestRequired TEST_REQUIRED_INITIALIZED =
+ TestRequired.newBuilder().setA(1).setB(2).setC(3).build();
+
/** Helper to convert a String to ByteString. */
static ByteString toBytes(String str) {
return ByteString.copyFrom(str.getBytes(Internal.UTF_8));
}
/**
+ * Dirties the message by resetting the momoized serialized size.
+ */
+ public static void resetMemoizedSize(AbstractMessage message) {
+ message.memoizedSize = -1;
+ }
+
+ /**
* Get a {@code TestAllTypes} with all fields set as they would be by
* {@link #setAllFields(TestAllTypes.Builder)}.
*/
@@ -300,16 +291,6 @@ public final class TestUtil {
}
/**
- * Get a {@code TestAllTypesLite.Builder} with all fields set as they would be by
- * {@link #setAllFields(TestAllTypesLite.Builder)}.
- */
- public static TestAllTypesLite.Builder getAllLiteSetBuilder() {
- TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
- setAllFields(builder);
- return builder;
- }
-
- /**
* Get a {@code TestAllExtensions} with all fields set as they would be by
* {@link #setAllExtensions(TestAllExtensions.Builder)}.
*/
@@ -319,12 +300,6 @@ public final class TestUtil {
return builder.build();
}
- public static TestAllExtensionsLite getAllLiteExtensionsSet() {
- TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
- setAllExtensions(builder);
- return builder.build();
- }
-
public static TestPackedTypes getPackedSet() {
TestPackedTypes.Builder builder = TestPackedTypes.newBuilder();
setPackedFields(builder);
@@ -343,157 +318,6 @@ public final class TestUtil {
return builder.build();
}
- public static TestPackedExtensionsLite getLitePackedExtensionsSet() {
- TestPackedExtensionsLite.Builder builder =
- TestPackedExtensionsLite.newBuilder();
- setPackedExtensions(builder);
- return builder.build();
- }
-
- /**
- * Set every field of {@code builder} to the values expected by
- * {@code assertAllFieldsSet()}.
- */
- public static void setAllFields(TestAllTypesLite.Builder builder) {
- builder.setOptionalInt32 (101);
- builder.setOptionalInt64 (102);
- builder.setOptionalUint32 (103);
- builder.setOptionalUint64 (104);
- builder.setOptionalSint32 (105);
- builder.setOptionalSint64 (106);
- builder.setOptionalFixed32 (107);
- builder.setOptionalFixed64 (108);
- builder.setOptionalSfixed32(109);
- builder.setOptionalSfixed64(110);
- builder.setOptionalFloat (111);
- builder.setOptionalDouble (112);
- builder.setOptionalBool (true);
- builder.setOptionalString ("115");
- builder.setOptionalBytes (toBytes("116"));
-
- builder.setOptionalGroup(
- TestAllTypesLite.OptionalGroup.newBuilder().setA(117).build());
- builder.setOptionalNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build());
- builder.setOptionalForeignMessage(
- ForeignMessageLite.newBuilder().setC(119).build());
- builder.setOptionalImportMessage(
- ImportMessageLite.newBuilder().setD(120).build());
- builder.setOptionalPublicImportMessage(
- PublicImportMessageLite.newBuilder().setE(126).build());
- builder.setOptionalLazyMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build());
-
- builder.setOptionalNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
- builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
- builder.setOptionalImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
-
- builder.setOptionalStringPiece("124");
- builder.setOptionalCord("125");
-
- // -----------------------------------------------------------------
-
- builder.addRepeatedInt32 (201);
- builder.addRepeatedInt64 (202);
- builder.addRepeatedUint32 (203);
- builder.addRepeatedUint64 (204);
- builder.addRepeatedSint32 (205);
- builder.addRepeatedSint64 (206);
- builder.addRepeatedFixed32 (207);
- builder.addRepeatedFixed64 (208);
- builder.addRepeatedSfixed32(209);
- builder.addRepeatedSfixed64(210);
- builder.addRepeatedFloat (211);
- builder.addRepeatedDouble (212);
- builder.addRepeatedBool (true);
- builder.addRepeatedString ("215");
- builder.addRepeatedBytes (toBytes("216"));
-
- builder.addRepeatedGroup(
- TestAllTypesLite.RepeatedGroup.newBuilder().setA(217).build());
- builder.addRepeatedNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build());
- builder.addRepeatedForeignMessage(
- ForeignMessageLite.newBuilder().setC(219).build());
- builder.addRepeatedImportMessage(
- ImportMessageLite.newBuilder().setD(220).build());
- builder.addRepeatedLazyMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build());
-
- builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAR);
- builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
- builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAR);
-
- builder.addRepeatedStringPiece("224");
- builder.addRepeatedCord("225");
-
- // Add a second one of each field.
- builder.addRepeatedInt32 (301);
- builder.addRepeatedInt64 (302);
- builder.addRepeatedUint32 (303);
- builder.addRepeatedUint64 (304);
- builder.addRepeatedSint32 (305);
- builder.addRepeatedSint64 (306);
- builder.addRepeatedFixed32 (307);
- builder.addRepeatedFixed64 (308);
- builder.addRepeatedSfixed32(309);
- builder.addRepeatedSfixed64(310);
- builder.addRepeatedFloat (311);
- builder.addRepeatedDouble (312);
- builder.addRepeatedBool (false);
- builder.addRepeatedString ("315");
- builder.addRepeatedBytes (toBytes("316"));
-
- builder.addRepeatedGroup(
- TestAllTypesLite.RepeatedGroup.newBuilder().setA(317).build());
- builder.addRepeatedNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build());
- builder.addRepeatedForeignMessage(
- ForeignMessageLite.newBuilder().setC(319).build());
- builder.addRepeatedImportMessage(
- ImportMessageLite.newBuilder().setD(320).build());
- builder.addRepeatedLazyMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build());
-
- builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
- builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
- builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
-
- builder.addRepeatedStringPiece("324");
- builder.addRepeatedCord("325");
-
- // -----------------------------------------------------------------
-
- builder.setDefaultInt32 (401);
- builder.setDefaultInt64 (402);
- builder.setDefaultUint32 (403);
- builder.setDefaultUint64 (404);
- builder.setDefaultSint32 (405);
- builder.setDefaultSint64 (406);
- builder.setDefaultFixed32 (407);
- builder.setDefaultFixed64 (408);
- builder.setDefaultSfixed32(409);
- builder.setDefaultSfixed64(410);
- builder.setDefaultFloat (411);
- builder.setDefaultDouble (412);
- builder.setDefaultBool (false);
- builder.setDefaultString ("415");
- builder.setDefaultBytes (toBytes("416"));
-
- builder.setDefaultNestedEnum (TestAllTypesLite.NestedEnum.FOO);
- builder.setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_FOO);
- builder.setDefaultImportEnum (ImportEnumLite.IMPORT_LITE_FOO);
-
- builder.setDefaultStringPiece("424");
- builder.setDefaultCord("425");
-
- builder.setOneofUint32(601);
- builder.setOneofNestedMessage(
- TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
- builder.setOneofString("603");
- builder.setOneofBytes(toBytes("604"));
- }
-
/**
* Set every field of {@code message} to the values expected by
* {@code assertAllFieldsSet()}.
@@ -1383,23 +1207,13 @@ public final class TestUtil {
return registry.getUnmodifiable();
}
- public static ExtensionRegistryLite getExtensionRegistryLite() {
- ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
- registerAllExtensionsLite(registry);
- return registry.getUnmodifiable();
- }
-
/**
* Register all of {@code TestAllExtensions}'s extensions with the
* given {@link ExtensionRegistry}.
*/
public static void registerAllExtensions(ExtensionRegistry registry) {
UnittestProto.registerAllExtensions(registry);
- registerAllExtensionsLite(registry);
- }
-
- public static void registerAllExtensionsLite(ExtensionRegistryLite registry) {
- UnittestLite.registerAllExtensions(registry);
+ TestUtilLite.registerAllExtensionsLite(registry);
}
/**
@@ -2193,195 +2007,6 @@ public final class TestUtil {
// Lite extensions
/**
- * Set every field of {@code message} to the values expected by
- * {@code assertAllExtensionsSet()}.
- */
- public static void setAllExtensions(TestAllExtensionsLite.Builder message) {
- message.setExtension(optionalInt32ExtensionLite , 101);
- message.setExtension(optionalInt64ExtensionLite , 102L);
- message.setExtension(optionalUint32ExtensionLite , 103);
- message.setExtension(optionalUint64ExtensionLite , 104L);
- message.setExtension(optionalSint32ExtensionLite , 105);
- message.setExtension(optionalSint64ExtensionLite , 106L);
- message.setExtension(optionalFixed32ExtensionLite , 107);
- message.setExtension(optionalFixed64ExtensionLite , 108L);
- message.setExtension(optionalSfixed32ExtensionLite, 109);
- message.setExtension(optionalSfixed64ExtensionLite, 110L);
- message.setExtension(optionalFloatExtensionLite , 111F);
- message.setExtension(optionalDoubleExtensionLite , 112D);
- message.setExtension(optionalBoolExtensionLite , true);
- message.setExtension(optionalStringExtensionLite , "115");
- message.setExtension(optionalBytesExtensionLite , toBytes("116"));
-
- message.setExtension(optionalGroupExtensionLite,
- OptionalGroup_extension_lite.newBuilder().setA(117).build());
- message.setExtension(optionalNestedMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build());
- message.setExtension(optionalForeignMessageExtensionLite,
- ForeignMessageLite.newBuilder().setC(119).build());
- message.setExtension(optionalImportMessageExtensionLite,
- ImportMessageLite.newBuilder().setD(120).build());
- message.setExtension(optionalPublicImportMessageExtensionLite,
- PublicImportMessageLite.newBuilder().setE(126).build());
- message.setExtension(optionalLazyMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build());
-
- message.setExtension(optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ);
- message.setExtension(optionalForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
- message.setExtension(optionalImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ);
-
- message.setExtension(optionalStringPieceExtensionLite, "124");
- message.setExtension(optionalCordExtensionLite, "125");
-
- // -----------------------------------------------------------------
-
- message.addExtension(repeatedInt32ExtensionLite , 201);
- message.addExtension(repeatedInt64ExtensionLite , 202L);
- message.addExtension(repeatedUint32ExtensionLite , 203);
- message.addExtension(repeatedUint64ExtensionLite , 204L);
- message.addExtension(repeatedSint32ExtensionLite , 205);
- message.addExtension(repeatedSint64ExtensionLite , 206L);
- message.addExtension(repeatedFixed32ExtensionLite , 207);
- message.addExtension(repeatedFixed64ExtensionLite , 208L);
- message.addExtension(repeatedSfixed32ExtensionLite, 209);
- message.addExtension(repeatedSfixed64ExtensionLite, 210L);
- message.addExtension(repeatedFloatExtensionLite , 211F);
- message.addExtension(repeatedDoubleExtensionLite , 212D);
- message.addExtension(repeatedBoolExtensionLite , true);
- message.addExtension(repeatedStringExtensionLite , "215");
- message.addExtension(repeatedBytesExtensionLite , toBytes("216"));
-
- message.addExtension(repeatedGroupExtensionLite,
- RepeatedGroup_extension_lite.newBuilder().setA(217).build());
- message.addExtension(repeatedNestedMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build());
- message.addExtension(repeatedForeignMessageExtensionLite,
- ForeignMessageLite.newBuilder().setC(219).build());
- message.addExtension(repeatedImportMessageExtensionLite,
- ImportMessageLite.newBuilder().setD(220).build());
- message.addExtension(repeatedLazyMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build());
-
- message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAR);
- message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR);
- message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAR);
-
- message.addExtension(repeatedStringPieceExtensionLite, "224");
- message.addExtension(repeatedCordExtensionLite, "225");
-
- // Add a second one of each field.
- message.addExtension(repeatedInt32ExtensionLite , 301);
- message.addExtension(repeatedInt64ExtensionLite , 302L);
- message.addExtension(repeatedUint32ExtensionLite , 303);
- message.addExtension(repeatedUint64ExtensionLite , 304L);
- message.addExtension(repeatedSint32ExtensionLite , 305);
- message.addExtension(repeatedSint64ExtensionLite , 306L);
- message.addExtension(repeatedFixed32ExtensionLite , 307);
- message.addExtension(repeatedFixed64ExtensionLite , 308L);
- message.addExtension(repeatedSfixed32ExtensionLite, 309);
- message.addExtension(repeatedSfixed64ExtensionLite, 310L);
- message.addExtension(repeatedFloatExtensionLite , 311F);
- message.addExtension(repeatedDoubleExtensionLite , 312D);
- message.addExtension(repeatedBoolExtensionLite , false);
- message.addExtension(repeatedStringExtensionLite , "315");
- message.addExtension(repeatedBytesExtensionLite , toBytes("316"));
-
- message.addExtension(repeatedGroupExtensionLite,
- RepeatedGroup_extension_lite.newBuilder().setA(317).build());
- message.addExtension(repeatedNestedMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build());
- message.addExtension(repeatedForeignMessageExtensionLite,
- ForeignMessageLite.newBuilder().setC(319).build());
- message.addExtension(repeatedImportMessageExtensionLite,
- ImportMessageLite.newBuilder().setD(320).build());
- message.addExtension(repeatedLazyMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build());
-
- message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ);
- message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
- message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ);
-
- message.addExtension(repeatedStringPieceExtensionLite, "324");
- message.addExtension(repeatedCordExtensionLite, "325");
-
- // -----------------------------------------------------------------
-
- message.setExtension(defaultInt32ExtensionLite , 401);
- message.setExtension(defaultInt64ExtensionLite , 402L);
- message.setExtension(defaultUint32ExtensionLite , 403);
- message.setExtension(defaultUint64ExtensionLite , 404L);
- message.setExtension(defaultSint32ExtensionLite , 405);
- message.setExtension(defaultSint64ExtensionLite , 406L);
- message.setExtension(defaultFixed32ExtensionLite , 407);
- message.setExtension(defaultFixed64ExtensionLite , 408L);
- message.setExtension(defaultSfixed32ExtensionLite, 409);
- message.setExtension(defaultSfixed64ExtensionLite, 410L);
- message.setExtension(defaultFloatExtensionLite , 411F);
- message.setExtension(defaultDoubleExtensionLite , 412D);
- message.setExtension(defaultBoolExtensionLite , false);
- message.setExtension(defaultStringExtensionLite , "415");
- message.setExtension(defaultBytesExtensionLite , toBytes("416"));
-
- message.setExtension(defaultNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.FOO);
- message.setExtension(defaultForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_FOO);
- message.setExtension(defaultImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_FOO);
-
- message.setExtension(defaultStringPieceExtensionLite, "424");
- message.setExtension(defaultCordExtensionLite, "425");
-
- message.setExtension(oneofUint32ExtensionLite, 601);
- message.setExtension(oneofNestedMessageExtensionLite,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
- message.setExtension(oneofStringExtensionLite, "603");
- message.setExtension(oneofBytesExtensionLite, toBytes("604"));
- }
-
- // -------------------------------------------------------------------
-
- /**
- * Modify the repeated extensions of {@code message} to contain the values
- * expected by {@code assertRepeatedExtensionsModified()}.
- */
- public static void modifyRepeatedExtensions(
- TestAllExtensionsLite.Builder message) {
- message.setExtension(repeatedInt32ExtensionLite , 1, 501);
- message.setExtension(repeatedInt64ExtensionLite , 1, 502L);
- message.setExtension(repeatedUint32ExtensionLite , 1, 503);
- message.setExtension(repeatedUint64ExtensionLite , 1, 504L);
- message.setExtension(repeatedSint32ExtensionLite , 1, 505);
- message.setExtension(repeatedSint64ExtensionLite , 1, 506L);
- message.setExtension(repeatedFixed32ExtensionLite , 1, 507);
- message.setExtension(repeatedFixed64ExtensionLite , 1, 508L);
- message.setExtension(repeatedSfixed32ExtensionLite, 1, 509);
- message.setExtension(repeatedSfixed64ExtensionLite, 1, 510L);
- message.setExtension(repeatedFloatExtensionLite , 1, 511F);
- message.setExtension(repeatedDoubleExtensionLite , 1, 512D);
- message.setExtension(repeatedBoolExtensionLite , 1, true);
- message.setExtension(repeatedStringExtensionLite , 1, "515");
- message.setExtension(repeatedBytesExtensionLite , 1, toBytes("516"));
-
- message.setExtension(repeatedGroupExtensionLite, 1,
- RepeatedGroup_extension_lite.newBuilder().setA(517).build());
- message.setExtension(repeatedNestedMessageExtensionLite, 1,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(518).build());
- message.setExtension(repeatedForeignMessageExtensionLite, 1,
- ForeignMessageLite.newBuilder().setC(519).build());
- message.setExtension(repeatedImportMessageExtensionLite, 1,
- ImportMessageLite.newBuilder().setD(520).build());
- message.setExtension(repeatedLazyMessageExtensionLite, 1,
- TestAllTypesLite.NestedMessage.newBuilder().setBb(527).build());
-
- message.setExtension(repeatedNestedEnumExtensionLite , 1, TestAllTypesLite.NestedEnum.FOO);
- message.setExtension(repeatedForeignEnumExtensionLite, 1, ForeignEnumLite.FOREIGN_LITE_FOO);
- message.setExtension(repeatedImportEnumExtensionLite , 1, ImportEnumLite.IMPORT_LITE_FOO);
-
- message.setExtension(repeatedStringPieceExtensionLite, 1, "524");
- message.setExtension(repeatedCordExtensionLite, 1, "525");
- }
-
- // -------------------------------------------------------------------
-
- /**
* Assert (using {@code junit.framework.Assert}} that all extensions of
* {@code message} are set to the values assigned by {@code setAllExtensions}.
*/
@@ -2880,38 +2505,6 @@ public final class TestUtil {
assertEqualsExactType("525", message.getExtension(repeatedCordExtensionLite, 1));
}
- public static void setPackedExtensions(TestPackedExtensionsLite.Builder message) {
- message.addExtension(packedInt32ExtensionLite , 601);
- message.addExtension(packedInt64ExtensionLite , 602L);
- message.addExtension(packedUint32ExtensionLite , 603);
- message.addExtension(packedUint64ExtensionLite , 604L);
- message.addExtension(packedSint32ExtensionLite , 605);
- message.addExtension(packedSint64ExtensionLite , 606L);
- message.addExtension(packedFixed32ExtensionLite , 607);
- message.addExtension(packedFixed64ExtensionLite , 608L);
- message.addExtension(packedSfixed32ExtensionLite, 609);
- message.addExtension(packedSfixed64ExtensionLite, 610L);
- message.addExtension(packedFloatExtensionLite , 611F);
- message.addExtension(packedDoubleExtensionLite , 612D);
- message.addExtension(packedBoolExtensionLite , true);
- message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR);
- // Add a second one of each field.
- message.addExtension(packedInt32ExtensionLite , 701);
- message.addExtension(packedInt64ExtensionLite , 702L);
- message.addExtension(packedUint32ExtensionLite , 703);
- message.addExtension(packedUint64ExtensionLite , 704L);
- message.addExtension(packedSint32ExtensionLite , 705);
- message.addExtension(packedSint64ExtensionLite , 706L);
- message.addExtension(packedFixed32ExtensionLite , 707);
- message.addExtension(packedFixed64ExtensionLite , 708L);
- message.addExtension(packedSfixed32ExtensionLite, 709);
- message.addExtension(packedSfixed64ExtensionLite, 710L);
- message.addExtension(packedFloatExtensionLite , 711F);
- message.addExtension(packedDoubleExtensionLite , 712D);
- message.addExtension(packedBoolExtensionLite , false);
- message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
- }
-
public static void assertPackedExtensionsSet(TestPackedExtensionsLite message) {
Assert.assertEquals(2, message.getExtensionCount(packedInt32ExtensionLite ));
Assert.assertEquals(2, message.getExtensionCount(packedInt64ExtensionLite ));
@@ -3015,6 +2608,9 @@ public final class TestUtil {
case FOO_CORD:
Assert.assertTrue(message.hasFooCord());
break;
+ case FOO_STRING_PIECE:
+ Assert.assertTrue(message.hasFooStringPiece());
+ break;
case FOO_BYTES:
Assert.assertTrue(message.hasFooBytes());
break;
@@ -3032,6 +2628,8 @@ public final class TestUtil {
break;
case FOO_NOT_SET:
break;
+ default:
+ // TODO(b/18683919): go/enum-switch-lsc
}
}
@@ -4182,7 +3780,8 @@ public final class TestUtil {
private static File getTestDataDir() {
// Search each parent directory looking for "src/google/protobuf".
- File ancestor = new File(".");
+ File ancestor = new File(System.getProperty("protobuf.dir", "."));
+ String initialPath = ancestor.getAbsolutePath();
try {
ancestor = ancestor.getCanonicalFile();
} catch (IOException e) {
@@ -4199,7 +3798,7 @@ public final class TestUtil {
throw new RuntimeException(
"Could not find golden files. This test must be run from within the " +
"protobuf source package so that it can read test data files from the " +
- "C++ source tree.");
+ "C++ source tree: " + initialPath);
}
/**
@@ -4263,7 +3862,7 @@ public final class TestUtil {
private int invalidations;
- //@Override (Java 1.6 override semantics, but we must support 1.5)
+ @Override
public void markDirty() {
invalidations++;
}
diff --git a/java/core/src/test/java/com/google/protobuf/TestUtilLite.java b/java/core/src/test/java/com/google/protobuf/TestUtilLite.java
new file mode 100644
index 00000000..8f33fa14
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TestUtilLite.java
@@ -0,0 +1,559 @@
+// 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.protobuf.UnittestLite.OptionalGroup_extension_lite;
+import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite;
+import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultStringExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
+import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalCordExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalStringExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite;
+import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite;
+
+import com.google.protobuf.UnittestImportLite.ImportEnumLite;
+import com.google.protobuf.UnittestImportLite.ImportMessageLite;
+import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
+import com.google.protobuf.UnittestLite.ForeignEnumLite;
+import com.google.protobuf.UnittestLite.ForeignMessageLite;
+import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
+import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
+
+/**
+ * Contains methods for setting fields of {@code TestAllTypesLite}, {@code TestAllExtensionsLite},
+ * and {@code TestPackedExtensionsLite}. This is analogous to the functionality in TestUtil.java but
+ * does not depend on the presence of any non-lite protos.
+ *
+ * <p>This code is not to be used outside of {@code com.google.protobuf} and
+ * subpackages.
+ */
+public final class TestUtilLite {
+ private TestUtilLite() {}
+
+ /** Helper to convert a String to ByteString. */
+ static ByteString toBytes(String str) {
+ return ByteString.copyFrom(str.getBytes(Internal.UTF_8));
+ }
+
+ /**
+ * Get a {@code TestAllTypesLite.Builder} with all fields set as they would be by
+ * {@link #setAllFields(TestAllTypesLite.Builder)}.
+ */
+ public static TestAllTypesLite.Builder getAllLiteSetBuilder() {
+ TestAllTypesLite.Builder builder = TestAllTypesLite.newBuilder();
+ setAllFields(builder);
+ return builder;
+ }
+
+ /**
+ * Get a {@code TestAllExtensionsLite} with all fields set as they would be by
+ * {@link #setAllExtensions(TestAllExtensionsLite.Builder)}.
+ */
+ public static TestAllExtensionsLite getAllLiteExtensionsSet() {
+ TestAllExtensionsLite.Builder builder = TestAllExtensionsLite.newBuilder();
+ setAllExtensions(builder);
+ return builder.build();
+ }
+
+ public static TestPackedExtensionsLite getLitePackedExtensionsSet() {
+ TestPackedExtensionsLite.Builder builder = TestPackedExtensionsLite.newBuilder();
+ setPackedExtensions(builder);
+ return builder.build();
+ }
+
+ /**
+ * Set every field of {@code builder} to the values expected by
+ * {@code assertAllFieldsSet()}.
+ */
+ public static void setAllFields(TestAllTypesLite.Builder builder) {
+ builder.setOptionalInt32 (101);
+ builder.setOptionalInt64 (102);
+ builder.setOptionalUint32 (103);
+ builder.setOptionalUint64 (104);
+ builder.setOptionalSint32 (105);
+ builder.setOptionalSint64 (106);
+ builder.setOptionalFixed32 (107);
+ builder.setOptionalFixed64 (108);
+ builder.setOptionalSfixed32(109);
+ builder.setOptionalSfixed64(110);
+ builder.setOptionalFloat (111);
+ builder.setOptionalDouble (112);
+ builder.setOptionalBool (true);
+ builder.setOptionalString ("115");
+ builder.setOptionalBytes (toBytes("116"));
+
+ builder.setOptionalGroup(
+ TestAllTypesLite.OptionalGroup.newBuilder().setA(117).build());
+ builder.setOptionalNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build());
+ builder.setOptionalForeignMessage(
+ ForeignMessageLite.newBuilder().setC(119).build());
+ builder.setOptionalImportMessage(
+ ImportMessageLite.newBuilder().setD(120).build());
+ builder.setOptionalPublicImportMessage(
+ PublicImportMessageLite.newBuilder().setE(126).build());
+ builder.setOptionalLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build());
+
+ builder.setOptionalNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
+ builder.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
+ builder.setOptionalImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
+
+ builder.setOptionalStringPiece("124");
+ builder.setOptionalCord("125");
+
+ // -----------------------------------------------------------------
+
+ builder.addRepeatedInt32 (201);
+ builder.addRepeatedInt64 (202);
+ builder.addRepeatedUint32 (203);
+ builder.addRepeatedUint64 (204);
+ builder.addRepeatedSint32 (205);
+ builder.addRepeatedSint64 (206);
+ builder.addRepeatedFixed32 (207);
+ builder.addRepeatedFixed64 (208);
+ builder.addRepeatedSfixed32(209);
+ builder.addRepeatedSfixed64(210);
+ builder.addRepeatedFloat (211);
+ builder.addRepeatedDouble (212);
+ builder.addRepeatedBool (true);
+ builder.addRepeatedString ("215");
+ builder.addRepeatedBytes (toBytes("216"));
+
+ builder.addRepeatedGroup(
+ TestAllTypesLite.RepeatedGroup.newBuilder().setA(217).build());
+ builder.addRepeatedNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build());
+ builder.addRepeatedForeignMessage(
+ ForeignMessageLite.newBuilder().setC(219).build());
+ builder.addRepeatedImportMessage(
+ ImportMessageLite.newBuilder().setD(220).build());
+ builder.addRepeatedLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build());
+
+ builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAR);
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR);
+ builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAR);
+
+ builder.addRepeatedStringPiece("224");
+ builder.addRepeatedCord("225");
+
+ // Add a second one of each field.
+ builder.addRepeatedInt32 (301);
+ builder.addRepeatedInt64 (302);
+ builder.addRepeatedUint32 (303);
+ builder.addRepeatedUint64 (304);
+ builder.addRepeatedSint32 (305);
+ builder.addRepeatedSint64 (306);
+ builder.addRepeatedFixed32 (307);
+ builder.addRepeatedFixed64 (308);
+ builder.addRepeatedSfixed32(309);
+ builder.addRepeatedSfixed64(310);
+ builder.addRepeatedFloat (311);
+ builder.addRepeatedDouble (312);
+ builder.addRepeatedBool (false);
+ builder.addRepeatedString ("315");
+ builder.addRepeatedBytes (toBytes("316"));
+
+ builder.addRepeatedGroup(
+ TestAllTypesLite.RepeatedGroup.newBuilder().setA(317).build());
+ builder.addRepeatedNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build());
+ builder.addRepeatedForeignMessage(
+ ForeignMessageLite.newBuilder().setC(319).build());
+ builder.addRepeatedImportMessage(
+ ImportMessageLite.newBuilder().setD(320).build());
+ builder.addRepeatedLazyMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build());
+
+ builder.addRepeatedNestedEnum (TestAllTypesLite.NestedEnum.BAZ);
+ builder.addRepeatedForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAZ);
+ builder.addRepeatedImportEnum (ImportEnumLite.IMPORT_LITE_BAZ);
+
+ builder.addRepeatedStringPiece("324");
+ builder.addRepeatedCord("325");
+
+ // -----------------------------------------------------------------
+
+ builder.setDefaultInt32 (401);
+ builder.setDefaultInt64 (402);
+ builder.setDefaultUint32 (403);
+ builder.setDefaultUint64 (404);
+ builder.setDefaultSint32 (405);
+ builder.setDefaultSint64 (406);
+ builder.setDefaultFixed32 (407);
+ builder.setDefaultFixed64 (408);
+ builder.setDefaultSfixed32(409);
+ builder.setDefaultSfixed64(410);
+ builder.setDefaultFloat (411);
+ builder.setDefaultDouble (412);
+ builder.setDefaultBool (false);
+ builder.setDefaultString ("415");
+ builder.setDefaultBytes (toBytes("416"));
+
+ builder.setDefaultNestedEnum (TestAllTypesLite.NestedEnum.FOO);
+ builder.setDefaultForeignEnum(ForeignEnumLite.FOREIGN_LITE_FOO);
+ builder.setDefaultImportEnum (ImportEnumLite.IMPORT_LITE_FOO);
+
+ builder.setDefaultStringPiece("424");
+ builder.setDefaultCord("425");
+
+ builder.setOneofUint32(601);
+ builder.setOneofNestedMessage(
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
+ builder.setOneofString("603");
+ builder.setOneofBytes(toBytes("604"));
+ }
+
+ /**
+ * Get an unmodifiable {@link ExtensionRegistryLite} containing all the
+ * extensions of {@code TestAllExtensionsLite}.
+ */
+ public static ExtensionRegistryLite getExtensionRegistryLite() {
+ ExtensionRegistryLite registry = ExtensionRegistryLite.newInstance();
+ registerAllExtensionsLite(registry);
+ return registry.getUnmodifiable();
+ }
+
+ /**
+ * Register all of {@code TestAllExtensionsLite}'s extensions with the
+ * given {@link ExtensionRegistryLite}.
+ */
+ public static void registerAllExtensionsLite(ExtensionRegistryLite registry) {
+ UnittestLite.registerAllExtensions(registry);
+ }
+
+ // ===================================================================
+ // Lite extensions
+
+ /**
+ * Set every field of {@code message} to the values expected by
+ * {@code assertAllExtensionsSet()}.
+ */
+ public static void setAllExtensions(TestAllExtensionsLite.Builder message) {
+ message.setExtension(optionalInt32ExtensionLite , 101);
+ message.setExtension(optionalInt64ExtensionLite , 102L);
+ message.setExtension(optionalUint32ExtensionLite , 103);
+ message.setExtension(optionalUint64ExtensionLite , 104L);
+ message.setExtension(optionalSint32ExtensionLite , 105);
+ message.setExtension(optionalSint64ExtensionLite , 106L);
+ message.setExtension(optionalFixed32ExtensionLite , 107);
+ message.setExtension(optionalFixed64ExtensionLite , 108L);
+ message.setExtension(optionalSfixed32ExtensionLite, 109);
+ message.setExtension(optionalSfixed64ExtensionLite, 110L);
+ message.setExtension(optionalFloatExtensionLite , 111F);
+ message.setExtension(optionalDoubleExtensionLite , 112D);
+ message.setExtension(optionalBoolExtensionLite , true);
+ message.setExtension(optionalStringExtensionLite , "115");
+ message.setExtension(optionalBytesExtensionLite , toBytes("116"));
+
+ message.setExtension(optionalGroupExtensionLite,
+ OptionalGroup_extension_lite.newBuilder().setA(117).build());
+ message.setExtension(optionalNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(118).build());
+ message.setExtension(optionalForeignMessageExtensionLite,
+ ForeignMessageLite.newBuilder().setC(119).build());
+ message.setExtension(optionalImportMessageExtensionLite,
+ ImportMessageLite.newBuilder().setD(120).build());
+ message.setExtension(optionalPublicImportMessageExtensionLite,
+ PublicImportMessageLite.newBuilder().setE(126).build());
+ message.setExtension(optionalLazyMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(127).build());
+
+ message.setExtension(optionalNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ);
+ message.setExtension(optionalForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
+ message.setExtension(optionalImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ);
+
+ message.setExtension(optionalStringPieceExtensionLite, "124");
+ message.setExtension(optionalCordExtensionLite, "125");
+
+ // -----------------------------------------------------------------
+
+ message.addExtension(repeatedInt32ExtensionLite , 201);
+ message.addExtension(repeatedInt64ExtensionLite , 202L);
+ message.addExtension(repeatedUint32ExtensionLite , 203);
+ message.addExtension(repeatedUint64ExtensionLite , 204L);
+ message.addExtension(repeatedSint32ExtensionLite , 205);
+ message.addExtension(repeatedSint64ExtensionLite , 206L);
+ message.addExtension(repeatedFixed32ExtensionLite , 207);
+ message.addExtension(repeatedFixed64ExtensionLite , 208L);
+ message.addExtension(repeatedSfixed32ExtensionLite, 209);
+ message.addExtension(repeatedSfixed64ExtensionLite, 210L);
+ message.addExtension(repeatedFloatExtensionLite , 211F);
+ message.addExtension(repeatedDoubleExtensionLite , 212D);
+ message.addExtension(repeatedBoolExtensionLite , true);
+ message.addExtension(repeatedStringExtensionLite , "215");
+ message.addExtension(repeatedBytesExtensionLite , toBytes("216"));
+
+ message.addExtension(repeatedGroupExtensionLite,
+ RepeatedGroup_extension_lite.newBuilder().setA(217).build());
+ message.addExtension(repeatedNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(218).build());
+ message.addExtension(repeatedForeignMessageExtensionLite,
+ ForeignMessageLite.newBuilder().setC(219).build());
+ message.addExtension(repeatedImportMessageExtensionLite,
+ ImportMessageLite.newBuilder().setD(220).build());
+ message.addExtension(repeatedLazyMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(227).build());
+
+ message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAR);
+ message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR);
+ message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAR);
+
+ message.addExtension(repeatedStringPieceExtensionLite, "224");
+ message.addExtension(repeatedCordExtensionLite, "225");
+
+ // Add a second one of each field.
+ message.addExtension(repeatedInt32ExtensionLite , 301);
+ message.addExtension(repeatedInt64ExtensionLite , 302L);
+ message.addExtension(repeatedUint32ExtensionLite , 303);
+ message.addExtension(repeatedUint64ExtensionLite , 304L);
+ message.addExtension(repeatedSint32ExtensionLite , 305);
+ message.addExtension(repeatedSint64ExtensionLite , 306L);
+ message.addExtension(repeatedFixed32ExtensionLite , 307);
+ message.addExtension(repeatedFixed64ExtensionLite , 308L);
+ message.addExtension(repeatedSfixed32ExtensionLite, 309);
+ message.addExtension(repeatedSfixed64ExtensionLite, 310L);
+ message.addExtension(repeatedFloatExtensionLite , 311F);
+ message.addExtension(repeatedDoubleExtensionLite , 312D);
+ message.addExtension(repeatedBoolExtensionLite , false);
+ message.addExtension(repeatedStringExtensionLite , "315");
+ message.addExtension(repeatedBytesExtensionLite , toBytes("316"));
+
+ message.addExtension(repeatedGroupExtensionLite,
+ RepeatedGroup_extension_lite.newBuilder().setA(317).build());
+ message.addExtension(repeatedNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(318).build());
+ message.addExtension(repeatedForeignMessageExtensionLite,
+ ForeignMessageLite.newBuilder().setC(319).build());
+ message.addExtension(repeatedImportMessageExtensionLite,
+ ImportMessageLite.newBuilder().setD(320).build());
+ message.addExtension(repeatedLazyMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(327).build());
+
+ message.addExtension(repeatedNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.BAZ);
+ message.addExtension(repeatedForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
+ message.addExtension(repeatedImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_BAZ);
+
+ message.addExtension(repeatedStringPieceExtensionLite, "324");
+ message.addExtension(repeatedCordExtensionLite, "325");
+
+ // -----------------------------------------------------------------
+
+ message.setExtension(defaultInt32ExtensionLite , 401);
+ message.setExtension(defaultInt64ExtensionLite , 402L);
+ message.setExtension(defaultUint32ExtensionLite , 403);
+ message.setExtension(defaultUint64ExtensionLite , 404L);
+ message.setExtension(defaultSint32ExtensionLite , 405);
+ message.setExtension(defaultSint64ExtensionLite , 406L);
+ message.setExtension(defaultFixed32ExtensionLite , 407);
+ message.setExtension(defaultFixed64ExtensionLite , 408L);
+ message.setExtension(defaultSfixed32ExtensionLite, 409);
+ message.setExtension(defaultSfixed64ExtensionLite, 410L);
+ message.setExtension(defaultFloatExtensionLite , 411F);
+ message.setExtension(defaultDoubleExtensionLite , 412D);
+ message.setExtension(defaultBoolExtensionLite , false);
+ message.setExtension(defaultStringExtensionLite , "415");
+ message.setExtension(defaultBytesExtensionLite , toBytes("416"));
+
+ message.setExtension(defaultNestedEnumExtensionLite, TestAllTypesLite.NestedEnum.FOO);
+ message.setExtension(defaultForeignEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_FOO);
+ message.setExtension(defaultImportEnumExtensionLite, ImportEnumLite.IMPORT_LITE_FOO);
+
+ message.setExtension(defaultStringPieceExtensionLite, "424");
+ message.setExtension(defaultCordExtensionLite, "425");
+
+ message.setExtension(oneofUint32ExtensionLite, 601);
+ message.setExtension(oneofNestedMessageExtensionLite,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(602).build());
+ message.setExtension(oneofStringExtensionLite, "603");
+ message.setExtension(oneofBytesExtensionLite, toBytes("604"));
+ }
+
+ // -------------------------------------------------------------------
+
+ /**
+ * Modify the repeated extensions of {@code message} to contain the values
+ * expected by {@code assertRepeatedExtensionsModified()}.
+ */
+ public static void modifyRepeatedExtensions(
+ TestAllExtensionsLite.Builder message) {
+ message.setExtension(repeatedInt32ExtensionLite , 1, 501);
+ message.setExtension(repeatedInt64ExtensionLite , 1, 502L);
+ message.setExtension(repeatedUint32ExtensionLite , 1, 503);
+ message.setExtension(repeatedUint64ExtensionLite , 1, 504L);
+ message.setExtension(repeatedSint32ExtensionLite , 1, 505);
+ message.setExtension(repeatedSint64ExtensionLite , 1, 506L);
+ message.setExtension(repeatedFixed32ExtensionLite , 1, 507);
+ message.setExtension(repeatedFixed64ExtensionLite , 1, 508L);
+ message.setExtension(repeatedSfixed32ExtensionLite, 1, 509);
+ message.setExtension(repeatedSfixed64ExtensionLite, 1, 510L);
+ message.setExtension(repeatedFloatExtensionLite , 1, 511F);
+ message.setExtension(repeatedDoubleExtensionLite , 1, 512D);
+ message.setExtension(repeatedBoolExtensionLite , 1, true);
+ message.setExtension(repeatedStringExtensionLite , 1, "515");
+ message.setExtension(repeatedBytesExtensionLite , 1, toBytes("516"));
+
+ message.setExtension(repeatedGroupExtensionLite, 1,
+ RepeatedGroup_extension_lite.newBuilder().setA(517).build());
+ message.setExtension(repeatedNestedMessageExtensionLite, 1,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(518).build());
+ message.setExtension(repeatedForeignMessageExtensionLite, 1,
+ ForeignMessageLite.newBuilder().setC(519).build());
+ message.setExtension(repeatedImportMessageExtensionLite, 1,
+ ImportMessageLite.newBuilder().setD(520).build());
+ message.setExtension(repeatedLazyMessageExtensionLite, 1,
+ TestAllTypesLite.NestedMessage.newBuilder().setBb(527).build());
+
+ message.setExtension(repeatedNestedEnumExtensionLite , 1, TestAllTypesLite.NestedEnum.FOO);
+ message.setExtension(repeatedForeignEnumExtensionLite, 1, ForeignEnumLite.FOREIGN_LITE_FOO);
+ message.setExtension(repeatedImportEnumExtensionLite , 1, ImportEnumLite.IMPORT_LITE_FOO);
+
+ message.setExtension(repeatedStringPieceExtensionLite, 1, "524");
+ message.setExtension(repeatedCordExtensionLite, 1, "525");
+ }
+
+ public static void setPackedExtensions(TestPackedExtensionsLite.Builder message) {
+ message.addExtension(packedInt32ExtensionLite , 601);
+ message.addExtension(packedInt64ExtensionLite , 602L);
+ message.addExtension(packedUint32ExtensionLite , 603);
+ message.addExtension(packedUint64ExtensionLite , 604L);
+ message.addExtension(packedSint32ExtensionLite , 605);
+ message.addExtension(packedSint64ExtensionLite , 606L);
+ message.addExtension(packedFixed32ExtensionLite , 607);
+ message.addExtension(packedFixed64ExtensionLite , 608L);
+ message.addExtension(packedSfixed32ExtensionLite, 609);
+ message.addExtension(packedSfixed64ExtensionLite, 610L);
+ message.addExtension(packedFloatExtensionLite , 611F);
+ message.addExtension(packedDoubleExtensionLite , 612D);
+ message.addExtension(packedBoolExtensionLite , true);
+ message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAR);
+ // Add a second one of each field.
+ message.addExtension(packedInt32ExtensionLite , 701);
+ message.addExtension(packedInt64ExtensionLite , 702L);
+ message.addExtension(packedUint32ExtensionLite , 703);
+ message.addExtension(packedUint64ExtensionLite , 704L);
+ message.addExtension(packedSint32ExtensionLite , 705);
+ message.addExtension(packedSint64ExtensionLite , 706L);
+ message.addExtension(packedFixed32ExtensionLite , 707);
+ message.addExtension(packedFixed64ExtensionLite , 708L);
+ message.addExtension(packedSfixed32ExtensionLite, 709);
+ message.addExtension(packedSfixed64ExtensionLite, 710L);
+ message.addExtension(packedFloatExtensionLite , 711F);
+ message.addExtension(packedDoubleExtensionLite , 712D);
+ message.addExtension(packedBoolExtensionLite , false);
+ message.addExtension(packedEnumExtensionLite, ForeignEnumLite.FOREIGN_LITE_BAZ);
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java
new file mode 100644
index 00000000..e338af21
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java
@@ -0,0 +1,182 @@
+// 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 com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FieldDescriptor;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+
+import junit.framework.TestCase;
+
+/**
+ * Test @{link TextFormatParseInfoTree}.
+ */
+public class TextFormatParseInfoTreeTest extends TestCase {
+
+ private static final Descriptor DESCRIPTOR = TestAllTypes.getDescriptor();
+ private static final FieldDescriptor OPTIONAL_INT32 =
+ DESCRIPTOR.findFieldByName("optional_int32");
+ private static final FieldDescriptor OPTIONAL_BOOLEAN =
+ DESCRIPTOR.findFieldByName("optional_boolean");
+ private static final FieldDescriptor REPEATED_INT32 =
+ DESCRIPTOR.findFieldByName("repeated_int32");
+ private static final FieldDescriptor OPTIONAL_NESTED_MESSAGE =
+ DESCRIPTOR.findFieldByName("optional_nested_message");
+ private static final FieldDescriptor REPEATED_NESTED_MESSAGE =
+ DESCRIPTOR.findFieldByName("repeated_nested_message");
+ private static final FieldDescriptor FIELD_BB =
+ TestAllTypes.NestedMessage.getDescriptor().findFieldByName("bb");
+
+ private static final TextFormatParseLocation LOC0 = TextFormatParseLocation.create(1, 2);
+ private static final TextFormatParseLocation LOC1 = TextFormatParseLocation.create(2, 3);
+
+ private TextFormatParseInfoTree.Builder rootBuilder;
+
+ @Override
+ public void setUp() {
+ rootBuilder = TextFormatParseInfoTree.builder();
+ }
+
+ public void testBuildEmptyParseTree() {
+ TextFormatParseInfoTree tree = rootBuilder.build();
+ assertTrue(tree.getLocations(null).isEmpty());
+ }
+
+ public void testGetLocationReturnsSingleLocation() {
+ rootBuilder.setLocation(OPTIONAL_INT32, LOC0);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertEquals(LOC0, root.getLocation(OPTIONAL_INT32, 0));
+ assertEquals(1, root.getLocations(OPTIONAL_INT32).size());
+ }
+
+ public void testGetLocationsReturnsNoParseLocationsForUnknownField() {
+ assertTrue(rootBuilder.build().getLocations(OPTIONAL_INT32).isEmpty());
+ rootBuilder.setLocation(OPTIONAL_BOOLEAN, LOC0);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertTrue(root.getLocations(OPTIONAL_INT32).isEmpty());
+ assertEquals(LOC0, root.getLocations(OPTIONAL_BOOLEAN).get(0));
+ }
+
+ public void testGetLocationThrowsIllegalArgumentExceptionForUnknownField() {
+ rootBuilder.setLocation(REPEATED_INT32, LOC0);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ try {
+ root.getNestedTree(OPTIONAL_INT32, 0);
+ fail("Did not detect unknown field");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ }
+
+ public void testGetLocationThrowsIllegalArgumentExceptionForInvalidIndex() {
+ TextFormatParseInfoTree root = rootBuilder.setLocation(OPTIONAL_INT32, LOC0).build();
+ try {
+ root.getLocation(OPTIONAL_INT32, 1);
+ fail("Invalid index not detected");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ try {
+ root.getLocation(OPTIONAL_INT32, -1);
+ fail("Negative index not detected");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ }
+
+ public void testGetLocationsReturnsMultipleLocations() {
+ rootBuilder.setLocation(REPEATED_INT32, LOC0);
+ rootBuilder.setLocation(REPEATED_INT32, LOC1);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertEquals(LOC0, root.getLocation(REPEATED_INT32, 0));
+ assertEquals(LOC1, root.getLocation(REPEATED_INT32, 1));
+ assertEquals(2, root.getLocations(REPEATED_INT32).size());
+ }
+
+ public void testGetNestedTreeThrowsIllegalArgumentExceptionForUnknownField() {
+ rootBuilder.setLocation(REPEATED_INT32, LOC0);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ try {
+ root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 0);
+ fail("Did not detect unknown field");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ }
+
+ public void testGetNestedTreesReturnsNoParseInfoTreesForUnknownField() {
+ rootBuilder.setLocation(REPEATED_INT32, LOC0);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertTrue(root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).isEmpty());
+ }
+
+ public void testGetNestedTreeThrowsIllegalArgumentExceptionForInvalidIndex() {
+ rootBuilder.setLocation(REPEATED_INT32, LOC0);
+ rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ try {
+ root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 1);
+ fail("Submessage index that is too large not detected");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ try {
+ rootBuilder.build().getNestedTree(OPTIONAL_NESTED_MESSAGE, -1);
+ fail("Invalid submessage index (-1) not detected");
+ } catch (IllegalArgumentException expected) {
+ // pass
+ }
+ }
+
+ public void testGetNestedTreesReturnsSingleTree() {
+ rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertEquals(1, root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).size());
+ TextFormatParseInfoTree subtree = root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).get(0);
+ assertNotNull(subtree);
+ }
+
+ public void testGetNestedTreesReturnsMultipleTrees() {
+ TextFormatParseInfoTree.Builder subtree1Builder =
+ rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE);
+ subtree1Builder.getBuilderForSubMessageField(FIELD_BB);
+ subtree1Builder.getBuilderForSubMessageField(FIELD_BB);
+ TextFormatParseInfoTree.Builder subtree2Builder =
+ rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE);
+ subtree2Builder.getBuilderForSubMessageField(FIELD_BB);
+ TextFormatParseInfoTree root = rootBuilder.build();
+ assertEquals(2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).size());
+ assertEquals(
+ 2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(0).getNestedTrees(FIELD_BB).size());
+ assertEquals(
+ 1, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(1).getNestedTrees(FIELD_BB).size());
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java
new file mode 100644
index 00000000..c42bfa6e
--- /dev/null
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java
@@ -0,0 +1,86 @@
+// 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 junit.framework.TestCase;
+
+/**
+ * Test @{link TextFormatParseLocation}.
+ */
+public class TextFormatParseLocationTest extends TestCase {
+
+ public void testCreateEmpty() {
+ TextFormatParseLocation location = TextFormatParseLocation.create(-1, -1);
+ assertEquals(TextFormatParseLocation.EMPTY, location);
+ }
+
+ public void testCreate() {
+ TextFormatParseLocation location = TextFormatParseLocation.create(2, 1);
+ assertEquals(2, location.getLine());
+ assertEquals(1, location.getColumn());
+ }
+
+ public void testCreateThrowsIllegalArgumentExceptionForInvalidIndex() {
+ try {
+ TextFormatParseLocation.create(-1, 0);
+ fail("Should throw IllegalArgumentException if line is less than 0");
+ } catch (IllegalArgumentException unused) {
+ // pass
+ }
+ try {
+ TextFormatParseLocation.create(0, -1);
+ fail("Should throw, column < 0");
+ } catch (IllegalArgumentException unused) {
+ // pass
+ }
+ }
+
+ public void testHashCode() {
+ TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1);
+ TextFormatParseLocation loc1 = TextFormatParseLocation.create(2, 1);
+
+ assertEquals(loc0.hashCode(), loc1.hashCode());
+ assertEquals(
+ TextFormatParseLocation.EMPTY.hashCode(), TextFormatParseLocation.EMPTY.hashCode());
+ }
+
+ public void testEquals() {
+ TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1);
+ TextFormatParseLocation loc1 = TextFormatParseLocation.create(1, 2);
+ TextFormatParseLocation loc2 = TextFormatParseLocation.create(2, 2);
+ TextFormatParseLocation loc3 = TextFormatParseLocation.create(2, 1);
+
+ assertEquals(loc0, loc3);
+ assertNotSame(loc0, loc1);
+ assertNotSame(loc0, loc2);
+ assertNotSame(loc1, loc2);
+ }
+}
diff --git a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
index 1df4fad7..720061d2 100644
--- a/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -30,8 +30,13 @@
package com.google.protobuf;
+import static com.google.protobuf.TestUtil.TEST_REQUIRED_INITIALIZED;
+import static com.google.protobuf.TestUtil.TEST_REQUIRED_UNINITIALIZED;
+
+import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
+import map_test.MapTestProto.TestMap;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import protobuf_unittest.UnittestProto.OneString;
@@ -40,11 +45,11 @@ import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestOneof2;
+import protobuf_unittest.UnittestProto.TestRequired;
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
-
-import junit.framework.TestCase;
-
import java.io.StringReader;
+import java.util.List;
+import junit.framework.TestCase;
/**
* Test case for {@link TextFormat}.
@@ -56,12 +61,11 @@ import java.io.StringReader;
public class TextFormatTest extends TestCase {
// A basic string with different escapable characters for testing.
- private final static String kEscapeTestString =
- "\"A string with ' characters \n and \r newlines and \t tabs and \001 "
- + "slashes \\";
+ private static final String ESCAPE_TEST_STRING =
+ "\"A string with ' characters \n and \r newlines and \t tabs and \001 " + "slashes \\";
// A representation of the above string with all the characters escaped.
- private final static String kEscapeTestStringEscaped =
+ private static final String ESCAPE_TEST_STRING_ESCAPED =
"\\\"A string with \\' characters \\n and \\r newlines "
+ "and \\t tabs and \\001 slashes \\\\";
@@ -167,6 +171,7 @@ public class TextFormatTest extends TestCase {
// Creates an example unknown field set.
private UnknownFieldSet makeUnknownFieldSet() {
+
return UnknownFieldSet.newBuilder()
.addField(5,
UnknownFieldSet.Field.newBuilder()
@@ -174,6 +179,12 @@ public class TextFormatTest extends TestCase {
.addFixed32(2)
.addFixed64(3)
.addLengthDelimited(ByteString.copyFromUtf8("4"))
+ .addLengthDelimited(UnknownFieldSet.newBuilder()
+ .addField(12,
+ UnknownFieldSet.Field.newBuilder()
+ .addVarint(6)
+ .build())
+ .build().toByteString())
.addGroup(
UnknownFieldSet.newBuilder()
.addField(10,
@@ -206,20 +217,23 @@ public class TextFormatTest extends TestCase {
.build();
assertEquals(
- "5: 1\n" +
- "5: 0x00000002\n" +
- "5: 0x0000000000000003\n" +
- "5: \"4\"\n" +
- "5 {\n" +
- " 10: 5\n" +
- "}\n" +
- "8: 1\n" +
- "8: 2\n" +
- "8: 3\n" +
- "15: 12379813812177893520\n" +
- "15: 0xabcd1234\n" +
- "15: 0xabcdef1234567890\n",
- TextFormat.printToString(message));
+ "5: 1\n"
+ + "5: 0x00000002\n"
+ + "5: 0x0000000000000003\n"
+ + "5: \"4\"\n"
+ + "5: {\n"
+ + " 12: 6\n"
+ + "}\n"
+ + "5 {\n"
+ + " 10: 5\n"
+ + "}\n"
+ + "8: 1\n"
+ + "8: 2\n"
+ + "8: 3\n"
+ + "15: 12379813812177893520\n"
+ + "15: 0xabcd1234\n"
+ + "15: 0xabcdef1234567890\n",
+ TextFormat.printToString(message));
}
public void testPrintField() throws Exception {
@@ -315,19 +329,58 @@ public class TextFormatTest extends TestCase {
// =================================================================
- public void testParse() throws Exception {
+ public void testMerge() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(allFieldsSetText, builder);
TestUtil.assertAllFieldsSet(builder.build());
}
- public void testParseReader() throws Exception {
+ public void testParse() throws Exception {
+ TestUtil.assertAllFieldsSet(
+ TextFormat.parse(allFieldsSetText, TestAllTypes.class));
+ }
+
+ public void testMergeInitialized() throws Exception {
+ TestRequired.Builder builder = TestRequired.newBuilder();
+ TextFormat.merge(TEST_REQUIRED_INITIALIZED.toString(), builder);
+ assertEquals(TEST_REQUIRED_INITIALIZED.toString(),
+ builder.buildPartial().toString());
+ assertTrue(builder.isInitialized());
+ }
+
+ public void testParseInitialized() throws Exception {
+ TestRequired parsed =
+ TextFormat.parse(TEST_REQUIRED_INITIALIZED.toString(),
+ TestRequired.class);
+ assertEquals(TEST_REQUIRED_INITIALIZED.toString(), parsed.toString());
+ assertTrue(parsed.isInitialized());
+ }
+
+ public void testMergeUninitialized() throws Exception {
+ TestRequired.Builder builder = TestRequired.newBuilder();
+ TextFormat.merge(TEST_REQUIRED_UNINITIALIZED.toString(), builder);
+ assertEquals(TEST_REQUIRED_UNINITIALIZED.toString(),
+ builder.buildPartial().toString());
+ assertFalse(builder.isInitialized());
+ }
+
+ public void testParseUninitialized() throws Exception {
+ try {
+ TextFormat.parse(TEST_REQUIRED_UNINITIALIZED.toString(),
+ TestRequired.class);
+ fail("Expected UninitializedMessageException.");
+ } catch (UninitializedMessageException e) {
+ assertEquals("Message missing required fields: b, c", e.getMessage());
+ }
+ }
+
+ public void testMergeReader() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(new StringReader(allFieldsSetText), builder);
TestUtil.assertAllFieldsSet(builder.build());
}
- public void testParseExtensions() throws Exception {
+ public void testMergeExtensions() throws Exception {
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
TextFormat.merge(allExtensionsSetText,
TestUtil.getExtensionRegistry(),
@@ -335,7 +388,14 @@ public class TextFormatTest extends TestCase {
TestUtil.assertAllExtensionsSet(builder.build());
}
- public void testParseCompatibility() throws Exception {
+ public void testParseExtensions() throws Exception {
+ TestUtil.assertAllExtensionsSet(
+ TextFormat.parse(allExtensionsSetText,
+ TestUtil.getExtensionRegistry(),
+ TestAllExtensions.class));
+ }
+
+ public void testMergeAndParseCompatibility() throws Exception {
String original = "repeated_float: inf\n" +
"repeated_float: -inf\n" +
"repeated_float: nan\n" +
@@ -360,21 +420,29 @@ public class TextFormatTest extends TestCase {
"repeated_double: Infinity\n" +
"repeated_double: -Infinity\n" +
"repeated_double: NaN\n";
+
+ // Test merge().
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(original, builder);
assertEquals(canonical, builder.build().toString());
+
+ // Test parse().
+ assertEquals(canonical,
+ TextFormat.parse(original, TestAllTypes.class).toString());
}
- public void testParseExotic() throws Exception {
+ public void testMergeAndParseExotic() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(exoticText, builder);
// Too lazy to check things individually. Don't try to debug this
// if testPrintExotic() is failing.
assertEquals(canonicalExoticText, builder.build().toString());
+ assertEquals(canonicalExoticText,
+ TextFormat.parse(exoticText, TestAllTypes.class).toString());
}
- public void testParseMessageSet() throws Exception {
+ public void testMergeMessageSet() throws Exception {
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
@@ -400,7 +468,7 @@ public class TextFormatTest extends TestCase {
TestMessageSetExtension1.messageSetExtension).getI());
}
- public void testParseMessageSetWithOverwriteForbidden() throws Exception {
+ public void testMergeMessageSetWithOverwriteForbidden() throws Exception {
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
extensionRegistry.add(TestMessageSetExtension1.messageSetExtension);
extensionRegistry.add(TestMessageSetExtension2.messageSetExtension);
@@ -427,20 +495,20 @@ public class TextFormatTest extends TestCase {
}
}
- public void testParseNumericEnum() throws Exception {
+ public void testMergeNumericEnum() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge("optional_nested_enum: 2", builder);
assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum());
}
- public void testParseAngleBrackets() throws Exception {
+ public void testMergeAngleBrackets() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge("OptionalGroup: < a: 1 >", builder);
assertTrue(builder.hasOptionalGroup());
assertEquals(1, builder.getOptionalGroup().getA());
}
- public void testParseComment() throws Exception {
+ public void testMergeComment() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(
"# this is a comment\n" +
@@ -452,6 +520,7 @@ public class TextFormatTest extends TestCase {
}
private void assertParseError(String error, String text) {
+ // Test merge().
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
try {
TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder);
@@ -459,6 +528,15 @@ public class TextFormatTest extends TestCase {
} catch (TextFormat.ParseException e) {
assertEquals(error, e.getMessage());
}
+
+ // Test parse().
+ try {
+ TextFormat.parse(
+ text, TestUtil.getExtensionRegistry(), TestAllTypes.class);
+ fail("Expected parse exception.");
+ } catch (TextFormat.ParseException e) {
+ assertEquals(error, e.getMessage());
+ }
}
@@ -497,10 +575,10 @@ public class TextFormatTest extends TestCase {
"integer: 82301481290849012385230157",
"optional_int32: 82301481290849012385230157");
assertParseError(
- "1:16: Expected \"true\" or \"false\".",
+ "1:16: Expected \"true\" or \"false\". Found \"maybe\".",
"optional_bool: maybe");
assertParseError(
- "1:16: Expected \"true\" or \"false\".",
+ "1:16: Expected \"true\" or \"false\". Found \"2\".",
"optional_bool: 2");
assertParseError(
"1:18: Expected string.",
@@ -520,15 +598,16 @@ public class TextFormatTest extends TestCase {
"optional_string: \"ueoauaoe\n" +
"optional_int32: 123");
assertParseError(
- "1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.",
+ "1:2: Input contains unknown fields and/or extensions:\n" +
+ "1:2:\tprotobuf_unittest.TestAllTypes.[nosuchext]",
"[nosuchext]: 123");
assertParseError(
"1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " +
"not extend message type \"protobuf_unittest.TestAllTypes\".",
"[protobuf_unittest.optional_int32_extension]: 123");
assertParseError(
- "1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " +
- "named \"nosuchfield\".",
+ "1:1: Input contains unknown fields and/or extensions:\n" +
+ "1:1:\tprotobuf_unittest.TestAllTypes.nosuchfield",
"nosuchfield: 123");
assertParseError(
"1:21: Expected \">\".",
@@ -563,10 +642,18 @@ public class TextFormatTest extends TestCase {
TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"",
TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\""));
- assertEquals(kEscapeTestStringEscaped,
- TextFormat.escapeText(kEscapeTestString));
- assertEquals(kEscapeTestString,
- TextFormat.unescapeText(kEscapeTestStringEscaped));
+ assertEquals(ESCAPE_TEST_STRING_ESCAPED, TextFormat.escapeText(ESCAPE_TEST_STRING));
+ assertEquals(ESCAPE_TEST_STRING, TextFormat.unescapeText(ESCAPE_TEST_STRING_ESCAPED));
+
+ // Invariant
+ assertEquals("hello",
+ TextFormat.escapeBytes(bytes("hello")));
+ assertEquals("hello",
+ TextFormat.escapeText("hello"));
+ assertEquals(bytes("hello"),
+ TextFormat.unescapeBytes("hello"));
+ assertEquals("hello",
+ TextFormat.unescapeText("hello"));
// Unicode handling.
assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234"));
@@ -763,11 +850,14 @@ public class TextFormatTest extends TestCase {
public void testParseBoolean() throws Exception {
String goodText =
"repeated_bool: t repeated_bool : 0\n" +
- "repeated_bool :f repeated_bool:1";
+ "repeated_bool :f repeated_bool:1\n" +
+ "repeated_bool: False repeated_bool: True";
String goodTextCanonical =
"repeated_bool: true\n" +
"repeated_bool: false\n" +
"repeated_bool: false\n" +
+ "repeated_bool: true\n" +
+ "repeated_bool: false\n" +
"repeated_bool: true\n";
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TextFormat.merge(goodText, builder);
@@ -811,7 +901,6 @@ public class TextFormatTest extends TestCase {
private void assertPrintFieldValue(String expect, Object value,
String fieldName) throws Exception {
- TestAllTypes.Builder builder = TestAllTypes.newBuilder();
StringBuilder sb = new StringBuilder();
TextFormat.printFieldValue(
TestAllTypes.getDescriptor().findFieldByName(fieldName),
@@ -847,7 +936,7 @@ public class TextFormatTest extends TestCase {
}
public void testShortDebugString_unknown() {
- assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5 { 10: 5 }"
+ assertEquals("5: 1 5: 0x00000002 5: 0x0000000000000003 5: \"4\" 5: { 12: 6 } 5 { 10: 5 }"
+ " 8: 1 8: 2 8: 3 15: 12379813812177893520 15: 0xabcd1234 15:"
+ " 0xabcdef1234567890",
TextFormat.shortDebugString(makeUnknownFieldSet()));
@@ -927,6 +1016,7 @@ public class TextFormatTest extends TestCase {
}
+ // See additional coverage in testOneofOverwriteForbidden and testMapOverwriteForbidden.
public void testParseNonRepeatedFields() throws Exception {
assertParseSuccessWithOverwriteForbidden(
"repeated_int32: 1\n" +
@@ -937,6 +1027,7 @@ public class TextFormatTest extends TestCase {
assertParseSuccessWithOverwriteForbidden(
"repeated_nested_message { bb: 1 }\n" +
"repeated_nested_message { bb: 2 }\n");
+
assertParseErrorWithOverwriteForbidden(
"3:17: Non-repeated field " +
"\"protobuf_unittest.TestAllTypes.optional_int32\" " +
@@ -975,12 +1066,40 @@ public class TextFormatTest extends TestCase {
assertParseSuccessWithOverwriteForbidden("repeated_int32: [ 1, 2 ]\n");
assertParseSuccessWithOverwriteForbidden("RepeatedGroup [{ a: 1 },{ a: 2 }]\n");
assertParseSuccessWithOverwriteForbidden("repeated_nested_message [{ bb: 1 }, { bb: 2 }]\n");
+ // See also testMapShortForm.
+ }
+
+ public void testParseShortRepeatedFormOfEmptyRepeatedFields() throws Exception {
+ assertParseSuccessWithOverwriteForbidden("repeated_foreign_enum: []");
+ assertParseSuccessWithOverwriteForbidden("repeated_int32: []\n");
+ assertParseSuccessWithOverwriteForbidden("RepeatedGroup []\n");
+ assertParseSuccessWithOverwriteForbidden("repeated_nested_message []\n");
+ // See also testMapShortFormEmpty.
+ }
+
+ public void testParseShortRepeatedFormWithTrailingComma() throws Exception {
+ assertParseErrorWithOverwriteForbidden(
+ "1:38: Expected identifier. Found \']\'",
+ "repeated_foreign_enum: [FOREIGN_FOO, ]\n");
+ assertParseErrorWithOverwriteForbidden(
+ "1:22: Couldn't parse integer: For input string: \"]\"",
+ "repeated_int32: [ 1, ]\n");
+ assertParseErrorWithOverwriteForbidden(
+ "1:25: Expected \"{\".",
+ "RepeatedGroup [{ a: 1 },]\n");
+ assertParseErrorWithOverwriteForbidden(
+ "1:37: Expected \"{\".",
+ "repeated_nested_message [{ bb: 1 }, ]\n");
+ // See also testMapShortFormTrailingComma.
}
public void testParseShortRepeatedFormOfNonRepeatedFields() throws Exception {
assertParseErrorWithOverwriteForbidden(
"1:17: Couldn't parse integer: For input string: \"[\"",
"optional_int32: [1]\n");
+ assertParseErrorWithOverwriteForbidden(
+ "1:17: Couldn't parse integer: For input string: \"[\"",
+ "optional_int32: []\n");
}
// =======================================================================
@@ -1018,4 +1137,182 @@ public class TextFormatTest extends TestCase {
assertFalse(oneof.hasFooString());
assertTrue(oneof.hasFooInt());
}
+
+ // =======================================================================
+ // test map
+
+ public void testMapTextFormat() throws Exception {
+ TestMap message =
+ TestMap.newBuilder()
+ .putInt32ToStringField(10, "apple")
+ .putInt32ToStringField(20, "banana")
+ .putInt32ToStringField(30, "cherry")
+ .build();
+ String text = TextFormat.printToUnicodeString(message);
+ {
+ TestMap.Builder dest = TestMap.newBuilder();
+ TextFormat.merge(text, dest);
+ assertEquals(message, dest.build());
+ }
+ {
+ TestMap.Builder dest = TestMap.newBuilder();
+ parserWithOverwriteForbidden.merge(text, dest);
+ assertEquals(message, dest.build());
+ }
+ }
+
+ public void testMapShortForm() throws Exception {
+ String text =
+ "string_to_int32_field [{ key: 'x' value: 10 }, { key: 'y' value: 20 }]\n"
+ + "int32_to_message_field "
+ + "[{ key: 1 value { value: 100 } }, { key: 2 value: { value: 200 } }]\n";
+ TestMap.Builder dest = TestMap.newBuilder();
+ parserWithOverwriteForbidden.merge(text, dest);
+ TestMap message = dest.build();
+ assertEquals(2, message.getStringToInt32Field().size());
+ assertEquals(2, message.getInt32ToMessageField().size());
+ assertEquals(10, message.getStringToInt32Field().get("x").intValue());
+ assertEquals(200, message.getInt32ToMessageField().get(2).getValue());
+ }
+
+ public void testMapShortFormEmpty() throws Exception {
+ String text = "string_to_int32_field []\n"
+ + "int32_to_message_field: []\n";
+ TestMap.Builder dest = TestMap.newBuilder();
+ parserWithOverwriteForbidden.merge(text, dest);
+ TestMap message = dest.build();
+ assertEquals(0, message.getStringToInt32Field().size());
+ assertEquals(0, message.getInt32ToMessageField().size());
+ }
+
+ public void testMapShortFormTrailingComma() throws Exception {
+ String text = "string_to_int32_field [{ key: 'x' value: 10 }, ]\n";
+ TestMap.Builder dest = TestMap.newBuilder();
+ try {
+ parserWithOverwriteForbidden.merge(text, dest);
+ fail("Expected parse exception.");
+ } catch (TextFormat.ParseException e) {
+ assertEquals("1:48: Expected \"{\".", e.getMessage());
+ }
+ }
+
+ public void testMapOverwrite() throws Exception {
+ String text =
+ "int32_to_int32_field { key: 1 value: 10 }\n"
+ + "int32_to_int32_field { key: 2 value: 20 }\n"
+ + "int32_to_int32_field { key: 1 value: 30 }\n";
+
+ {
+ // With default parser, last value set for the key holds.
+ TestMap.Builder builder = TestMap.newBuilder();
+ defaultParser.merge(text, builder);
+ TestMap map = builder.build();
+ assertEquals(2, map.getInt32ToInt32Field().size());
+ assertEquals(30, map.getInt32ToInt32Field().get(1).intValue());
+ }
+
+ {
+ // With overwrite forbidden, same behavior.
+ // TODO(b/29122459): Expect parse exception here.
+ TestMap.Builder builder = TestMap.newBuilder();
+ defaultParser.merge(text, builder);
+ TestMap map = builder.build();
+ assertEquals(2, map.getInt32ToInt32Field().size());
+ assertEquals(30, map.getInt32ToInt32Field().get(1).intValue());
+ }
+ }
+
+ // =======================================================================
+ // test location information
+
+ public void testParseInfoTreeBuilding() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+
+ Descriptor descriptor = TestAllTypes.getDescriptor();
+ TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder();
+ // Set to allow unknown fields
+ TextFormat.Parser parser =
+ TextFormat.Parser.newBuilder()
+ .setParseInfoTreeBuilder(treeBuilder)
+ .build();
+
+ final String stringData =
+ "optional_int32: 1\n"
+ + "optional_int64: 2\n"
+ + " optional_double: 2.4\n"
+ + "repeated_int32: 5\n"
+ + "repeated_int32: 10\n"
+ + "optional_nested_message <\n"
+ + " bb: 78\n"
+ + ">\n"
+ + "repeated_nested_message <\n"
+ + " bb: 79\n"
+ + ">\n"
+ + "repeated_nested_message <\n"
+ + " bb: 80\n"
+ + ">";
+
+ parser.merge(stringData, builder);
+ TextFormatParseInfoTree tree = treeBuilder.build();
+
+ // Verify that the tree has the correct positions.
+ assertLocation(tree, descriptor, "optional_int32", 0, 0, 0);
+ assertLocation(tree, descriptor, "optional_int64", 0, 1, 0);
+ assertLocation(tree, descriptor, "optional_double", 0, 2, 2);
+
+ assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0);
+ assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0);
+
+ assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0);
+ assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0);
+ assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0);
+
+ // Check for fields not set. For an invalid field, the location returned should be -1, -1.
+ assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1);
+ assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1);
+
+ // Verify inside the nested message.
+ FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message");
+
+ TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0);
+ assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2);
+
+ // Verify inside another nested message.
+ nestedField = descriptor.findFieldByName("repeated_nested_message");
+ nestedTree = tree.getNestedTrees(nestedField).get(0);
+ assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2);
+
+ nestedTree = tree.getNestedTrees(nestedField).get(1);
+ assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2);
+
+ // Verify a NULL tree for an unknown nested field.
+ try {
+ tree.getNestedTree(nestedField, 2);
+ fail("unknown nested field should throw");
+ } catch (IllegalArgumentException unused) {
+ // pass
+ }
+ }
+
+ private void assertLocation(
+ TextFormatParseInfoTree tree,
+ final Descriptor descriptor,
+ final String fieldName,
+ int index,
+ int line,
+ int column) {
+ List<TextFormatParseLocation> locs = tree.getLocations(descriptor.findFieldByName(fieldName));
+ if (index < locs.size()) {
+ TextFormatParseLocation location = locs.get(index);
+ TextFormatParseLocation expected = TextFormatParseLocation.create(line, column);
+ assertEquals(expected, location);
+ } else if (line != -1 && column != -1) {
+ fail(
+ String.format(
+ "Tree/descriptor/fieldname did not contain index %d, line %d column %d expected",
+ index,
+ line,
+ column));
+ }
+ }
}
diff --git a/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java b/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
index 8f45976f..88cbbf86 100644
--- a/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java
@@ -36,7 +36,6 @@ import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.FieldPresenceTestProto.TestAllTypes;
import com.google.protobuf.TextFormat.ParseException;
-
import junit.framework.TestCase;
/**
@@ -151,18 +150,15 @@ public class UnknownEnumValueTest extends TestCase {
assertEquals(4321, unknown4321.getNumber());
assertEquals(5432, unknown5432.getNumber());
assertEquals(6543, unknown6543.getNumber());
-
+
// Unknown EnumValueDescriptor will map to UNRECOGNIZED.
assertEquals(
- TestAllTypes.NestedEnum.valueOf(unknown4321),
- TestAllTypes.NestedEnum.UNRECOGNIZED);
+ TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown4321));
assertEquals(
- TestAllTypes.NestedEnum.valueOf(unknown5432),
- TestAllTypes.NestedEnum.UNRECOGNIZED);
+ TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown5432));
assertEquals(
- TestAllTypes.NestedEnum.valueOf(unknown6543),
- TestAllTypes.NestedEnum.UNRECOGNIZED);
-
+ TestAllTypes.NestedEnum.UNRECOGNIZED, TestAllTypes.NestedEnum.valueOf(unknown6543));
+
// Setters also accept unknown EnumValueDescriptor.
builder.setField(optionalNestedEnumField, unknown6543);
builder.setRepeatedField(repeatedNestedEnumField, 0, unknown4321);
diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
index dc987379..8ce0ca73 100644
--- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java
@@ -30,16 +30,25 @@
package com.google.protobuf;
+import static junit.framework.TestCase.assertEquals;
+
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
import com.google.protobuf.UnittestLite.TestAllTypesLite;
+import protobuf_unittest.UnittestProto;
+import protobuf_unittest.UnittestProto.ForeignEnum;
+import protobuf_unittest.UnittestProto.TestAllExtensions;
+import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestEmptyMessage;
+import protobuf_unittest.UnittestProto.TestPackedExtensions;
+import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
-
-import junit.framework.TestCase;
-
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import junit.framework.TestCase;
/**
* Tests for {@link UnknownFieldSetLite}.
@@ -47,47 +56,44 @@ import java.io.IOException;
* @author dweis@google.com (Daniel Weis)
*/
public class UnknownFieldSetLiteTest extends TestCase {
-
- public void testNoDataIsDefaultInstance() {
- assertSame(
- UnknownFieldSetLite.getDefaultInstance(),
- UnknownFieldSetLite.newBuilder()
- .build());
+ @Override
+ public void setUp() throws Exception {
+ allFields = TestUtil.getAllSet();
+ allFieldsData = allFields.toByteString();
+ emptyMessage = TestEmptyMessage.parseFrom(allFieldsData);
+ unknownFields = emptyMessage.getUnknownFields();
}
-
- public void testBuilderReuse() throws IOException {
- UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
- builder.mergeVarintField(10, 2);
- builder.build();
- try {
- builder.build();
- fail();
- } catch (UnsupportedOperationException e) {
- // Expected.
- }
-
- try {
- builder.mergeFieldFrom(0, CodedInputStream.newInstance(new byte[0]));
- fail();
- } catch (UnsupportedOperationException e) {
- // Expected.
+ TestAllTypes allFields;
+ ByteString allFieldsData;
+
+ // Constructs a protocol buffer which contains fields with all the same
+ // numbers as allFieldsData except that each field is some other wire
+ // type.
+ private ByteString getBizarroData() throws Exception {
+ UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder();
+
+ UnknownFieldSet.Field varintField = UnknownFieldSet.Field.newBuilder().addVarint(1).build();
+ UnknownFieldSet.Field fixed32Field = UnknownFieldSet.Field.newBuilder().addFixed32(1).build();
+
+ for (Map.Entry<Integer, UnknownFieldSet.Field> entry : unknownFields.asMap().entrySet()) {
+ if (entry.getValue().getVarintList().isEmpty()) {
+ // Original field is not a varint, so use a varint.
+ bizarroFields.addField(entry.getKey(), varintField);
+ } else {
+ // Original field *is* a varint, so use something else.
+ bizarroFields.addField(entry.getKey(), fixed32Field);
+ }
}
- try {
- builder.mergeVarintField(5, 1);
- fail();
- } catch (UnsupportedOperationException e) {
- // Expected.
- }
+ return bizarroFields.build().toByteString();
}
- public void testBuilderReuse_empty() {
- UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
- builder.build();
- builder.build();
- }
-
+ // An empty message that has been parsed from allFieldsData. So, it has
+ // unknown fields of every type.
+ TestEmptyMessage emptyMessage;
+ UnknownFieldSet unknownFields;
+
public void testDefaultInstance() {
UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
@@ -95,6 +101,14 @@ public class UnknownFieldSetLiteTest extends TestCase {
assertEquals(ByteString.EMPTY, toByteString(unknownFields));
}
+ public void testEmptyInstance() {
+ UnknownFieldSetLite instance = UnknownFieldSetLite.newInstance();
+
+ assertEquals(0, instance.getSerializedSize());
+ assertEquals(ByteString.EMPTY, toByteString(instance));
+ assertEquals(UnknownFieldSetLite.getDefaultInstance(), instance);
+ }
+
public void testMergeFieldFrom() throws IOException {
Foo foo = Foo.newBuilder()
.setValue(2)
@@ -121,6 +135,25 @@ public class UnknownFieldSetLiteTest extends TestCase {
assertEquals(foo.toByteString().size(), instance.getSerializedSize());
}
+ public void testHashCodeAfterDeserialization() throws IOException {
+ Foo foo = Foo.newBuilder()
+ .setValue(2)
+ .build();
+
+ Foo fooDeserialized = Foo.parseFrom(foo.toByteArray());
+
+ assertEquals(fooDeserialized, foo);
+ assertEquals(foo.hashCode(), fooDeserialized.hashCode());
+ }
+
+ public void testNewInstanceHashCode() {
+ UnknownFieldSetLite emptyFieldSet = UnknownFieldSetLite.getDefaultInstance();
+ UnknownFieldSetLite paddedFieldSet = UnknownFieldSetLite.newInstance();
+
+ assertEquals(emptyFieldSet, paddedFieldSet);
+ assertEquals(emptyFieldSet.hashCode(), paddedFieldSet.hashCode());
+ }
+
public void testMergeVarintField() throws IOException {
UnknownFieldSetLite unknownFields = UnknownFieldSetLite.newInstance();
unknownFields.mergeVarintField(10, 2);
@@ -365,4 +398,203 @@ public class UnknownFieldSetLiteTest extends TestCase {
}
return ByteString.copyFrom(byteArrayOutputStream.toByteArray());
}
+
+ public void testSerializeLite() throws Exception {
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
+ assertEquals(allFieldsData.size(), emptyMessageLite.getSerializedSize());
+ ByteString data = emptyMessageLite.toByteString();
+ TestAllTypes message = TestAllTypes.parseFrom(data);
+ TestUtil.assertAllFieldsSet(message);
+ assertEquals(allFieldsData, data);
+ }
+
+ public void testAllExtensionsLite() throws Exception {
+ TestAllExtensions allExtensions = TestUtil.getAllExtensionsSet();
+ ByteString allExtensionsData = allExtensions.toByteString();
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parser().parseFrom(allExtensionsData);
+ ByteString data = emptyMessageLite.toByteString();
+ TestAllExtensions message = TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
+ TestUtil.assertAllExtensionsSet(message);
+ assertEquals(allExtensionsData, data);
+ }
+
+ public void testAllPackedFieldsLite() throws Exception {
+ TestPackedTypes allPackedFields = TestUtil.getPackedSet();
+ ByteString allPackedData = allPackedFields.toByteString();
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(allPackedData);
+ ByteString data = emptyMessageLite.toByteString();
+ TestPackedTypes message = TestPackedTypes.parseFrom(data, TestUtil.getExtensionRegistry());
+ TestUtil.assertPackedFieldsSet(message);
+ assertEquals(allPackedData, data);
+ }
+
+ public void testAllPackedExtensionsLite() throws Exception {
+ TestPackedExtensions allPackedExtensions = TestUtil.getPackedExtensionsSet();
+ ByteString allPackedExtensionsData = allPackedExtensions.toByteString();
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(allPackedExtensionsData);
+ ByteString data = emptyMessageLite.toByteString();
+ TestPackedExtensions message =
+ TestPackedExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
+ TestUtil.assertPackedExtensionsSet(message);
+ assertEquals(allPackedExtensionsData, data);
+ }
+
+ public void testCopyFromLite() throws Exception {
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
+ UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
+ UnittestLite.TestEmptyMessageLite.newBuilder().mergeFrom(emptyMessageLite).build();
+ assertEquals(emptyMessageLite.toByteString(), emptyMessageLite2.toByteString());
+ }
+
+ public void testMergeFromLite() throws Exception {
+ TestAllTypes message1 =
+ TestAllTypes.newBuilder()
+ .setOptionalInt32(1)
+ .setOptionalString("foo")
+ .addRepeatedString("bar")
+ .setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ)
+ .build();
+
+ TestAllTypes message2 =
+ TestAllTypes.newBuilder()
+ .setOptionalInt64(2)
+ .setOptionalString("baz")
+ .addRepeatedString("qux")
+ .setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ)
+ .build();
+
+ ByteString data1 = message1.toByteString();
+ UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
+ UnittestLite.TestEmptyMessageLite.parseFrom(data1);
+ ByteString data2 = message2.toByteString();
+ UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
+ UnittestLite.TestEmptyMessageLite.parseFrom(data2);
+
+ message1 = TestAllTypes.newBuilder(message1).mergeFrom(message2).build();
+ emptyMessageLite1 =
+ UnittestLite.TestEmptyMessageLite.newBuilder(emptyMessageLite1)
+ .mergeFrom(emptyMessageLite2)
+ .build();
+
+ data1 = emptyMessageLite1.toByteString();
+ message2 = TestAllTypes.parseFrom(data1);
+
+ assertEquals(message1, message2);
+ }
+
+ public void testWrongTypeTreatedAsUnknownLite() throws Exception {
+ // Test that fields of the wrong wire type are treated like unknown fields
+ // when parsing.
+
+ ByteString bizarroData = getBizarroData();
+ TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData);
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
+ ByteString data = emptyMessageLite.toByteString();
+ TestAllTypes allTypesMessage2 = TestAllTypes.parseFrom(data);
+
+ assertEquals(allTypesMessage.toString(), allTypesMessage2.toString());
+ }
+
+ public void testUnknownExtensionsLite() throws Exception {
+ // Make sure fields are properly parsed to the UnknownFieldSet even when
+ // they are declared as extension numbers.
+
+ UnittestLite.TestEmptyMessageWithExtensionsLite message =
+ UnittestLite.TestEmptyMessageWithExtensionsLite.parseFrom(allFieldsData);
+
+ assertEquals(allFieldsData, message.toByteString());
+ }
+
+ public void testWrongExtensionTypeTreatedAsUnknownLite() throws Exception {
+ // Test that fields of the wrong wire type are treated like unknown fields
+ // when parsing extensions.
+
+ ByteString bizarroData = getBizarroData();
+ TestAllExtensions allExtensionsMessage = TestAllExtensions.parseFrom(bizarroData);
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
+
+ // All fields should have been interpreted as unknown, so the byte strings
+ // should be the same.
+ assertEquals(emptyMessageLite.toByteString(), allExtensionsMessage.toByteString());
+ }
+
+ public void testParseUnknownEnumValueLite() throws Exception {
+ Descriptors.FieldDescriptor singularField =
+ TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum");
+ Descriptors.FieldDescriptor repeatedField =
+ TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum");
+ assertNotNull(singularField);
+ assertNotNull(repeatedField);
+
+ ByteString data =
+ UnknownFieldSet.newBuilder()
+ .addField(
+ singularField.getNumber(),
+ UnknownFieldSet.Field.newBuilder()
+ .addVarint(TestAllTypes.NestedEnum.BAR.getNumber())
+ .addVarint(5) // not valid
+ .build())
+ .addField(
+ repeatedField.getNumber(),
+ UnknownFieldSet.Field.newBuilder()
+ .addVarint(TestAllTypes.NestedEnum.FOO.getNumber())
+ .addVarint(4) // not valid
+ .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber())
+ .addVarint(6) // not valid
+ .build())
+ .build()
+ .toByteString();
+
+ UnittestLite.TestEmptyMessageLite emptyMessageLite =
+ UnittestLite.TestEmptyMessageLite.parseFrom(data);
+ data = emptyMessageLite.toByteString();
+
+ {
+ TestAllTypes message = TestAllTypes.parseFrom(data);
+ assertEquals(TestAllTypes.NestedEnum.BAR, message.getOptionalNestedEnum());
+ assertEquals(
+ Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
+ message.getRepeatedNestedEnumList());
+ assertEquals(
+ Arrays.asList(5L),
+ message.getUnknownFields().getField(singularField.getNumber()).getVarintList());
+ assertEquals(
+ Arrays.asList(4L, 6L),
+ message.getUnknownFields().getField(repeatedField.getNumber()).getVarintList());
+ }
+
+ {
+ TestAllExtensions message =
+ TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
+ assertEquals(
+ TestAllTypes.NestedEnum.BAR,
+ message.getExtension(UnittestProto.optionalNestedEnumExtension));
+ assertEquals(
+ Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
+ message.getExtension(UnittestProto.repeatedNestedEnumExtension));
+ assertEquals(
+ Arrays.asList(5L),
+ message.getUnknownFields().getField(singularField.getNumber()).getVarintList());
+ assertEquals(
+ Arrays.asList(4L, 6L),
+ message.getUnknownFields().getField(repeatedField.getNumber()).getVarintList());
+ }
+ }
+
+ public void testClearLite() throws Exception {
+ UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
+ UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
+ UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
+ UnittestLite.TestEmptyMessageLite.newBuilder().mergeFrom(emptyMessageLite1).clear().build();
+ assertEquals(0, emptyMessageLite2.getSerializedSize());
+ ByteString data = emptyMessageLite2.toByteString();
+ assertEquals(0, data.size());
+ }
}
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 8c9dcafe..1a84806a 100644
--- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
@@ -38,11 +38,9 @@ import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
-
-import junit.framework.TestCase;
-
import java.util.Arrays;
import java.util.Map;
+import junit.framework.TestCase;
/**
* Tests related to unknown field handling.
@@ -50,6 +48,7 @@ import java.util.Map;
* @author kenton@google.com (Kenton Varda)
*/
public class UnknownFieldSetTest extends TestCase {
+ @Override
public void setUp() throws Exception {
descriptor = TestAllTypes.getDescriptor();
allFields = TestUtil.getAllSet();
@@ -446,208 +445,4 @@ public class UnknownFieldSetTest extends TestCase {
}
// =================================================================
-
- public void testSerializeLite() throws Exception {
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
- assertEquals(allFieldsData.size(), emptyMessageLite.getSerializedSize());
- ByteString data = emptyMessageLite.toByteString();
- TestAllTypes message = TestAllTypes.parseFrom(data);
- TestUtil.assertAllFieldsSet(message);
- assertEquals(allFieldsData, data);
- }
-
- public void testAllExtensionsLite() throws Exception {
- TestAllExtensions allExtensions = TestUtil.getAllExtensionsSet();
- ByteString allExtensionsData = allExtensions.toByteString();
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parser().parseFrom(allExtensionsData);
- ByteString data = emptyMessageLite.toByteString();
- TestAllExtensions message =
- TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
- TestUtil.assertAllExtensionsSet(message);
- assertEquals(allExtensionsData, data);
- }
-
- public void testAllPackedFieldsLite() throws Exception {
- TestPackedTypes allPackedFields = TestUtil.getPackedSet();
- ByteString allPackedData = allPackedFields.toByteString();
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(allPackedData);
- ByteString data = emptyMessageLite.toByteString();
- TestPackedTypes message =
- TestPackedTypes.parseFrom(data, TestUtil.getExtensionRegistry());
- TestUtil.assertPackedFieldsSet(message);
- assertEquals(allPackedData, data);
- }
-
- public void testAllPackedExtensionsLite() throws Exception {
- TestPackedExtensions allPackedExtensions = TestUtil.getPackedExtensionsSet();
- ByteString allPackedExtensionsData = allPackedExtensions.toByteString();
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(allPackedExtensionsData);
- ByteString data = emptyMessageLite.toByteString();
- TestPackedExtensions message =
- TestPackedExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
- TestUtil.assertPackedExtensionsSet(message);
- assertEquals(allPackedExtensionsData, data);
- }
-
- public void testCopyFromLite() throws Exception {
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
- UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
- UnittestLite.TestEmptyMessageLite.newBuilder()
- .mergeFrom(emptyMessageLite).build();
- assertEquals(emptyMessageLite.toByteString(), emptyMessageLite2.toByteString());
- }
-
- public void testMergeFromLite() throws Exception {
- TestAllTypes message1 = TestAllTypes.newBuilder()
- .setOptionalInt32(1)
- .setOptionalString("foo")
- .addRepeatedString("bar")
- .setOptionalNestedEnum(TestAllTypes.NestedEnum.BAZ)
- .build();
-
- TestAllTypes message2 = TestAllTypes.newBuilder()
- .setOptionalInt64(2)
- .setOptionalString("baz")
- .addRepeatedString("qux")
- .setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ)
- .build();
-
- ByteString data1 = message1.toByteString();
- UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
- UnittestLite.TestEmptyMessageLite.parseFrom(data1);
- ByteString data2 = message2.toByteString();
- UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
- UnittestLite.TestEmptyMessageLite.parseFrom(data2);
-
- message1 = TestAllTypes.newBuilder(message1).mergeFrom(message2).build();
- emptyMessageLite1 = UnittestLite.TestEmptyMessageLite.newBuilder(emptyMessageLite1)
- .mergeFrom(emptyMessageLite2).build();
-
- data1 = emptyMessageLite1.toByteString();
- message2 = TestAllTypes.parseFrom(data1);
-
- assertEquals(message1, message2);
- }
-
- public void testWrongTypeTreatedAsUnknownLite() throws Exception {
- // Test that fields of the wrong wire type are treated like unknown fields
- // when parsing.
-
- ByteString bizarroData = getBizarroData();
- TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData);
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
- ByteString data = emptyMessageLite.toByteString();
- TestAllTypes allTypesMessage2 = TestAllTypes.parseFrom(data);
-
- assertEquals(allTypesMessage.toString(), allTypesMessage2.toString());
- }
-
- public void testUnknownExtensionsLite() throws Exception {
- // Make sure fields are properly parsed to the UnknownFieldSet even when
- // they are declared as extension numbers.
-
- UnittestLite.TestEmptyMessageWithExtensionsLite message =
- UnittestLite.TestEmptyMessageWithExtensionsLite.parseFrom(allFieldsData);
-
- assertEquals(allFieldsData, message.toByteString());
- }
-
- public void testWrongExtensionTypeTreatedAsUnknownLite() throws Exception {
- // Test that fields of the wrong wire type are treated like unknown fields
- // when parsing extensions.
-
- ByteString bizarroData = getBizarroData();
- TestAllExtensions allExtensionsMessage =
- TestAllExtensions.parseFrom(bizarroData);
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(bizarroData);
-
- // All fields should have been interpreted as unknown, so the byte strings
- // should be the same.
- assertEquals(emptyMessageLite.toByteString(),
- allExtensionsMessage.toByteString());
- }
-
- public void testParseUnknownEnumValueLite() throws Exception {
- Descriptors.FieldDescriptor singularField =
- TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum");
- Descriptors.FieldDescriptor repeatedField =
- TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum");
- assertNotNull(singularField);
- assertNotNull(repeatedField);
-
- ByteString data =
- UnknownFieldSet.newBuilder()
- .addField(singularField.getNumber(),
- UnknownFieldSet.Field.newBuilder()
- .addVarint(TestAllTypes.NestedEnum.BAR.getNumber())
- .addVarint(5) // not valid
- .build())
- .addField(repeatedField.getNumber(),
- UnknownFieldSet.Field.newBuilder()
- .addVarint(TestAllTypes.NestedEnum.FOO.getNumber())
- .addVarint(4) // not valid
- .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber())
- .addVarint(6) // not valid
- .build())
- .build()
- .toByteString();
-
- UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.parseFrom(data);
- data = emptyMessageLite.toByteString();
-
- {
- TestAllTypes message = TestAllTypes.parseFrom(data);
- assertEquals(TestAllTypes.NestedEnum.BAR,
- message.getOptionalNestedEnum());
- assertEquals(
- Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
- message.getRepeatedNestedEnumList());
- assertEquals(Arrays.asList(5L),
- message.getUnknownFields()
- .getField(singularField.getNumber())
- .getVarintList());
- assertEquals(Arrays.asList(4L, 6L),
- message.getUnknownFields()
- .getField(repeatedField.getNumber())
- .getVarintList());
- }
-
- {
- TestAllExtensions message =
- TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
- assertEquals(TestAllTypes.NestedEnum.BAR,
- message.getExtension(UnittestProto.optionalNestedEnumExtension));
- assertEquals(
- Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ),
- message.getExtension(UnittestProto.repeatedNestedEnumExtension));
- assertEquals(Arrays.asList(5L),
- message.getUnknownFields()
- .getField(singularField.getNumber())
- .getVarintList());
- assertEquals(Arrays.asList(4L, 6L),
- message.getUnknownFields()
- .getField(repeatedField.getNumber())
- .getVarintList());
- }
- }
-
- public void testClearLite() throws Exception {
- UnittestLite.TestEmptyMessageLite emptyMessageLite1 =
- UnittestLite.TestEmptyMessageLite.parseFrom(allFieldsData);
- UnittestLite.TestEmptyMessageLite emptyMessageLite2 =
- UnittestLite.TestEmptyMessageLite.newBuilder()
- .mergeFrom(emptyMessageLite1).clear().build();
- assertEquals(0, emptyMessageLite2.getSerializedSize());
- ByteString data = emptyMessageLite2.toByteString();
- assertEquals(0, data.size());
- }
-
}
diff --git a/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java b/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
index b1c75fc3..00f201ca 100644
--- a/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
+++ b/java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java
@@ -30,11 +30,10 @@
package com.google.protobuf;
-import junit.framework.TestCase;
-
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
+import junit.framework.TestCase;
/**
* Tests for {@link UnmodifiableLazyStringList}.
diff --git a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java
index 0175005d..03c33ecf 100644
--- a/java/core/src/test/java/com/google/protobuf/WireFormatTest.java
+++ b/java/core/src/test/java/com/google/protobuf/WireFormatTest.java
@@ -30,26 +30,23 @@
package com.google.protobuf;
-import junit.framework.TestCase;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.List;
-
+import protobuf_unittest.UnittestMset.RawMessageSet;
+import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
+import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import protobuf_unittest.UnittestProto;
import protobuf_unittest.UnittestProto.TestAllExtensions;
import protobuf_unittest.UnittestProto.TestAllTypes;
+import protobuf_unittest.UnittestProto.TestExtensionInsideTable;
import protobuf_unittest.UnittestProto.TestFieldOrderings;
import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
-import protobuf_unittest.UnittestMset.RawMessageSet;
-import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
-import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
-import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
-import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import junit.framework.TestCase;
/**
* Tests related to parsing and serialization.
@@ -127,32 +124,6 @@ public class WireFormatTest extends TestCase {
TestUtil.assertPackedFieldsSet(message2);
}
- public void testSerializeExtensionsLite() throws Exception {
- // TestAllTypes and TestAllExtensions should have compatible wire formats,
- // so if we serialize a TestAllExtensions then parse it as TestAllTypes
- // it should work.
-
- TestAllExtensionsLite message = TestUtil.getAllLiteExtensionsSet();
- ByteString rawBytes = message.toByteString();
- assertEquals(rawBytes.size(), message.getSerializedSize());
-
- TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes);
-
- TestUtil.assertAllFieldsSet(message2);
- }
-
- public void testSerializePackedExtensionsLite() throws Exception {
- // TestPackedTypes and TestPackedExtensions should have compatible wire
- // formats; check that they serialize to the same string.
- TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet();
- ByteString rawBytes = message.toByteString();
-
- TestPackedTypes message2 = TestUtil.getPackedSet();
- ByteString rawBytes2 = message2.toByteString();
-
- assertEquals(rawBytes, rawBytes2);
- }
-
public void testParseExtensions() throws Exception {
// TestAllTypes and TestAllExtensions should have compatible wire formats,
// so if we serialize a TestAllTypes then parse it as TestAllExtensions
@@ -182,48 +153,6 @@ public class WireFormatTest extends TestCase {
TestUtil.assertPackedExtensionsSet(message2);
}
- public void testParseExtensionsLite() throws Exception {
- // TestAllTypes and TestAllExtensions should have compatible wire formats,
- // so if we serialize a TestAllTypes then parse it as TestAllExtensions
- // it should work.
-
- TestAllTypes message = TestUtil.getAllSet();
- ByteString rawBytes = message.toByteString();
-
- ExtensionRegistryLite registry_lite = TestUtil.getExtensionRegistryLite();
-
- TestAllExtensionsLite message2 =
- TestAllExtensionsLite.parseFrom(rawBytes, registry_lite);
-
- TestUtil.assertAllExtensionsSet(message2);
-
- // Try again using a full extension registry.
- ExtensionRegistry registry = TestUtil.getExtensionRegistry();
-
- TestAllExtensionsLite message3 =
- TestAllExtensionsLite.parseFrom(rawBytes, registry);
-
- TestUtil.assertAllExtensionsSet(message3);
- }
-
- public void testParsePackedExtensionsLite() throws Exception {
- // Ensure that packed extensions can be properly parsed.
- TestPackedExtensionsLite message = TestUtil.getLitePackedExtensionsSet();
- ByteString rawBytes = message.toByteString();
-
- ExtensionRegistryLite registry = TestUtil.getExtensionRegistryLite();
-
- TestPackedExtensionsLite message2 =
- TestPackedExtensionsLite.parseFrom(rawBytes, registry);
-
- TestUtil.assertPackedExtensionsSet(message2);
- }
-
- public void testExtensionsSerializedSize() throws Exception {
- assertNotSame(TestUtil.getAllSet().getSerializedSize(),
- TestUtil.getAllExtensionsSet().getSerializedSize());
- }
-
public void testSerializeDelimited() throws Exception {
ByteArrayOutputStream output = new ByteArrayOutputStream();
TestUtil.getAllSet().writeDelimitedTo(output);
@@ -307,6 +236,26 @@ public class WireFormatTest extends TestCase {
getTestFieldOrderingsRegistry());
assertEquals(source, dest);
}
+
+ private static ExtensionRegistry getTestExtensionInsideTableRegistry() {
+ ExtensionRegistry result = ExtensionRegistry.newInstance();
+ result.add(UnittestProto.testExtensionInsideTableExtension);
+ return result;
+ }
+
+ public void testExtensionInsideTable() throws Exception {
+ // Make sure the extension within the range of table is parsed correctly in experimental
+ // runtime.
+ TestExtensionInsideTable source =
+ TestExtensionInsideTable.newBuilder()
+ .setField1(1)
+ .setExtension(UnittestProto.testExtensionInsideTableExtension, 23)
+ .build();
+ TestExtensionInsideTable dest =
+ TestExtensionInsideTable.parseFrom(source.toByteString(),
+ getTestExtensionInsideTableRegistry());
+ assertEquals(source, dest);
+ }
public void testParseMultipleExtensionRangesDynamic() throws Exception {
// Same as above except with DynamicMessage.
diff --git a/java/core/src/test/proto/com/google/protobuf/deprecated_file.proto b/java/core/src/test/proto/com/google/protobuf/deprecated_file.proto
new file mode 100644
index 00000000..ca90e927
--- /dev/null
+++ b/java/core/src/test/proto/com/google/protobuf/deprecated_file.proto
@@ -0,0 +1,38 @@
+// 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.
+
+syntax = "proto3";
+
+package deprecated_file;
+
+option deprecated = true;
+
+// TODO (liujisi): Add deprecation options on messages, enums fields as well and
+// add tests to verify those annotations are actually generated.
diff --git a/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
index 8f3ca8c6..2367bd8b 100644
--- a/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/field_presence_test.proto
@@ -36,7 +36,6 @@ import "google/protobuf/unittest.proto";
option java_package = "com.google.protobuf";
option java_outer_classname = "FieldPresenceTestProto";
-option java_generate_equals_and_hash = true;
message TestAllTypes {
enum NestedEnum {
@@ -54,6 +53,7 @@ message TestAllTypes {
NestedEnum optional_nested_enum = 4;
NestedMessage optional_nested_message = 5;
protobuf_unittest.TestRequired optional_proto2_message = 6;
+ NestedMessage optional_lazy_message = 7 [lazy=true];
oneof oneof_field {
int32 oneof_int32 = 11;
@@ -81,6 +81,7 @@ message TestOptionalFieldsOnly {
TestAllTypes.NestedEnum optional_nested_enum = 4;
TestAllTypes.NestedMessage optional_nested_message = 5;
protobuf_unittest.TestRequired optional_proto2_message = 6;
+ TestAllTypes.NestedMessage optional_lazy_message = 7 [lazy=true];
}
message TestRepeatedFieldsOnly {
diff --git a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto
index 86837250..b18b0d79 100644
--- a/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto
+++ b/java/core/src/test/proto/com/google/protobuf/lite_equals_and_hash.proto
@@ -34,14 +34,26 @@ syntax = "proto2";
package protobuf_unittest.lite_equals_and_hash;
-// This proto definition is used to test that java_generate_equals_and_hash
-// works correctly with the LITE_RUNTIME.
-option java_generate_equals_and_hash = true;
option optimize_for = LITE_RUNTIME;
+message TestOneofEquals {
+ oneof oneof_field {
+ string name = 1;
+ int32 value = 2;
+ }
+}
+
message Foo {
optional int32 value = 1;
repeated Bar bar = 2;
+ map<string, string> my_map = 3;
+ oneof Single {
+ sint64 sint64 = 4;
+ // LINT: ALLOW_GROUPS
+ group MyGroup = 5 {
+ optional int32 value = 1;
+ }
+ }
extensions 100 to max;
}
@@ -70,3 +82,8 @@ extend Foo {
}
}
+message TestRecursiveOneof {
+ oneof Foo {
+ TestRecursiveOneof r = 1;
+ }
+}
diff --git a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
index d5418f28..2ca0251c 100644
--- a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_lite_test.proto
@@ -32,7 +32,6 @@ syntax = "proto2";
option java_outer_classname = "MapForProto2TestProto";
-option java_generate_equals_and_hash = true;
message TestMap {
message MessageValue {
@@ -70,6 +69,53 @@ message TestRecursiveMap {
optional int32 value = 1;
map<int32, TestRecursiveMap> recursive_map_field = 2;
}
+
+
+// a decoy of TestMap for testing parsing errors
+message BizarroTestMap {
+ map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
+ map<string, int32> int32_to_string_field = 2; // different key and value types
+ map<string, int32> int32_to_bytes_field = 3; // different key types, same value
+ map<string, bytes> int32_to_enum_field = 4; // different key and value types
+ map<string, bytes> int32_to_message_field = 5; // different key and value types
+ map<string, bytes> string_to_int32_field = 6; // same key type, different value
+}
+
+// Used to test that java reserved words can be used as protobuf field names
+// Not all reserved words are tested (to avoid bloat) but instead an arbitrary
+// subset of them chosen to cover various keyword categories like
+// type, modifier, declaration, etc.
+message ReservedAsMapField {
+ map<string, uint32> if = 1;
+ map<string, uint32> const = 2;
+ map<string, uint32> private = 3;
+ map<string, uint32> class = 4;
+ map<string, uint32> int = 5;
+ map<string, uint32> void = 6;
+ map<string, uint32> string = 7; // These are also proto keywords
+ map<string, uint32> package = 8;
+ map<string, uint32> enum = 9; // Most recent Java reserved word
+ map<string, uint32> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
+
+message ReservedAsMapFieldWithEnumValue {
+ enum SampleEnum {
+ A = 0;
+ B = 1;
+ }
+ map<string, SampleEnum> if = 1;
+ map<string, SampleEnum> const = 2;
+ map<string, SampleEnum> private = 3;
+ map<string, SampleEnum> class = 4;
+ map<string, SampleEnum> int = 5;
+ map<string, SampleEnum> void = 6;
+ map<string, SampleEnum> string = 7; // These are also proto keywords
+ map<string, SampleEnum> package = 8;
+ map<string, SampleEnum> enum = 9; // Most recent Java reserved word
+ map<string, SampleEnum> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
package map_for_proto2_lite_test;
option java_package = "map_lite_test";
option optimize_for = LITE_RUNTIME;
diff --git a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
index a9be5166..974f8a2c 100644
--- a/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_for_proto2_test.proto
@@ -34,7 +34,6 @@ package map_for_proto2_test;
option java_package = "map_test";
option java_outer_classname = "MapForProto2TestProto";
-option java_generate_equals_and_hash = true;
message TestMap {
message MessageValue {
@@ -72,3 +71,50 @@ message TestRecursiveMap {
optional int32 value = 1;
map<int32, TestRecursiveMap> recursive_map_field = 2;
}
+
+
+// a decoy of TestMap for testing parsing errors
+message BizarroTestMap {
+ map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
+ map<string, int32> int32_to_string_field = 2; // different key and value types
+ map<string, int32> int32_to_bytes_field = 3; // different key types, same value
+ map<string, bytes> int32_to_enum_field = 4; // different key and value types
+ map<string, bytes> int32_to_message_field = 5; // different key and value types
+ map<string, bytes> string_to_int32_field = 6; // same key type, different value
+}
+
+// Used to test that java reserved words can be used as protobuf field names
+// Not all reserved words are tested (to avoid bloat) but instead an arbitrary
+// subset of them chosen to cover various keyword categories like
+// type, modifier, declaration, etc.
+message ReservedAsMapField {
+ map<string, uint32> if = 1;
+ map<string, uint32> const = 2;
+ map<string, uint32> private = 3;
+ map<string, uint32> class = 4;
+ map<string, uint32> int = 5;
+ map<string, uint32> void = 6;
+ map<string, uint32> string = 7; // These are also proto keywords
+ map<string, uint32> package = 8;
+ map<string, uint32> enum = 9; // Most recent Java reserved word
+ map<string, uint32> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
+
+message ReservedAsMapFieldWithEnumValue {
+ enum SampleEnum {
+ A = 0;
+ B = 1;
+ }
+ map<string, SampleEnum> if = 1;
+ map<string, SampleEnum> const = 2;
+ map<string, SampleEnum> private = 3;
+ map<string, SampleEnum> class = 4;
+ map<string, SampleEnum> int = 5;
+ map<string, SampleEnum> void = 6;
+ map<string, SampleEnum> string = 7; // These are also proto keywords
+ map<string, SampleEnum> package = 8;
+ map<string, SampleEnum> enum = 9; // Most recent Java reserved word
+ map<string, SampleEnum> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
diff --git a/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto b/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto
new file mode 100644
index 00000000..c04f5d57
--- /dev/null
+++ b/java/core/src/test/proto/com/google/protobuf/map_lite_test.proto
@@ -0,0 +1,111 @@
+// 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.
+
+syntax = "proto3";
+
+package map_lite_test;
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "map_lite_test";
+option java_outer_classname = "MapTestProto";
+
+message TestMap {
+ message MessageValue {
+ int32 value = 1;
+ }
+ enum EnumValue {
+ FOO = 0;
+ BAR = 1;
+ BAZ = 2;
+ QUX = 3;
+ }
+
+ map<int32, int32> int32_to_int32_field = 1;
+ map<int32, string> int32_to_string_field = 2;
+ map<int32, bytes> int32_to_bytes_field = 3;
+ map<int32, EnumValue> int32_to_enum_field = 4;
+ map<int32, MessageValue> int32_to_message_field = 5;
+ map<string, int32> string_to_int32_field = 6;
+ map<uint32, int32> uint32_to_int32_field = 7;
+ map<int64, int32> int64_to_int32_field = 8;
+}
+
+// Used to test that a nested builder containing map fields will properly
+// propagate the onChange event and mark its parent dirty when a change
+// is made to a map field.
+message TestOnChangeEventPropagation {
+ TestMap optional_message = 1;
+}
+
+// a decoy of TestMap for testing parsing errors
+message BizarroTestMap {
+ map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
+ map<string, int32> int32_to_string_field = 2; // different key and value types
+ map<string, int32> int32_to_bytes_field = 3; // different key types, same value
+ map<string, bytes> int32_to_enum_field = 4; // different key and value types
+ map<string, bytes> int32_to_message_field = 5; // different key and value types
+ map<string, bytes> string_to_int32_field = 6; // same key type, different value
+}
+
+// Used to test that java reserved words can be used as protobuf field names
+// Not all reserved words are tested (to avoid bloat) but instead an arbitrary
+// subset of them chosen to cover various keyword categories like
+// type, modifier, declaration, etc.
+message ReservedAsMapField {
+ map<string, uint32> if = 1;
+ map<string, uint32> const = 2;
+ map<string, uint32> private = 3;
+ map<string, uint32> class = 4;
+ map<string, uint32> int = 5;
+ map<string, uint32> void = 6;
+ map<string, uint32> string = 7; // These are also proto keywords
+ map<string, uint32> package = 8;
+ map<string, uint32> enum = 9; // Most recent Java reserved word
+ map<string, uint32> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
+
+message ReservedAsMapFieldWithEnumValue {
+ enum SampleEnum {
+ A = 0;
+ B = 1;
+ }
+ map<string, SampleEnum> if = 1;
+ map<string, SampleEnum> const = 2;
+ map<string, SampleEnum> private = 3;
+ map<string, SampleEnum> class = 4;
+ map<string, SampleEnum> int = 5;
+ map<string, SampleEnum> void = 6;
+ map<string, SampleEnum> string = 7; // These are also proto keywords
+ map<string, SampleEnum> package = 8;
+ map<string, SampleEnum> enum = 9; // Most recent Java reserved word
+ map<string, SampleEnum> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
diff --git a/java/core/src/test/proto/com/google/protobuf/map_test.proto b/java/core/src/test/proto/com/google/protobuf/map_test.proto
index 2280ac03..bc2105e5 100644
--- a/java/core/src/test/proto/com/google/protobuf/map_test.proto
+++ b/java/core/src/test/proto/com/google/protobuf/map_test.proto
@@ -34,7 +34,6 @@ package map_test;
option java_package = "map_test";
option java_outer_classname = "MapTestProto";
-option java_generate_equals_and_hash = true;
message TestMap {
message MessageValue {
@@ -53,11 +52,59 @@ message TestMap {
map<int32, EnumValue> int32_to_enum_field = 4;
map<int32, MessageValue> int32_to_message_field = 5;
map<string, int32> string_to_int32_field = 6;
+ map<uint32, int32> uint32_to_int32_field = 7;
+ map<int64, int32> int64_to_int32_field = 8;
}
-// Used to test that a nested bulider containing map fields will properly
+// Used to test that a nested builder containing map fields will properly
// propagate the onChange event and mark its parent dirty when a change
// is made to a map field.
message TestOnChangeEventPropagation {
TestMap optional_message = 1;
}
+
+// a decoy of TestMap for testing parsing errors
+message BizarroTestMap {
+ map<int32, bytes> int32_to_int32_field = 1; // same key type, different value
+ map<string, int32> int32_to_string_field = 2; // different key and value types
+ map<string, int32> int32_to_bytes_field = 3; // different key types, same value
+ map<string, bytes> int32_to_enum_field = 4; // different key and value types
+ map<string, bytes> int32_to_message_field = 5; // different key and value types
+ map<string, bytes> string_to_int32_field = 6; // same key type, different value
+}
+
+// Used to test that java reserved words can be used as protobuf field names
+// Not all reserved words are tested (to avoid bloat) but instead an arbitrary
+// subset of them chosen to cover various keyword categories like
+// type, modifier, declaration, etc.
+message ReservedAsMapField {
+ map<string, uint32> if = 1;
+ map<string, uint32> const = 2;
+ map<string, uint32> private = 3;
+ map<string, uint32> class = 4;
+ map<string, uint32> int = 5;
+ map<string, uint32> void = 6;
+ map<string, uint32> string = 7; // These are also proto keywords
+ map<string, uint32> package = 8;
+ map<string, uint32> enum = 9; // Most recent Java reserved word
+ map<string, uint32> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
+
+message ReservedAsMapFieldWithEnumValue {
+ enum SampleEnum {
+ A = 0;
+ B = 1;
+ }
+ map<string, SampleEnum> if = 1;
+ map<string, SampleEnum> const = 2;
+ map<string, SampleEnum> private = 3;
+ map<string, SampleEnum> class = 4;
+ map<string, SampleEnum> int = 5;
+ map<string, SampleEnum> void = 6;
+ map<string, SampleEnum> string = 7; // These are also proto keywords
+ map<string, SampleEnum> package = 8;
+ map<string, SampleEnum> enum = 9; // Most recent Java reserved word
+ map<string, SampleEnum> null = 10;
+ // null is not a 'reserved word' per se but as a literal needs similar care
+}
diff --git a/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
index 2b1f65e4..ff5bf3ae 100644
--- a/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
+++ b/java/core/src/test/proto/com/google/protobuf/test_bad_identifiers.proto
@@ -43,7 +43,6 @@ package io_protocol_tests;
option java_package = "com.google.protobuf";
option java_outer_classname = "TestBadIdentifiersProto";
-option java_generate_equals_and_hash = true;
message TestMessage {
optional string cached_size = 1;
@@ -149,6 +148,12 @@ message TestConflictingFieldNames {
// the method getInt32FieldList().
required int32 int32_field_list = 31; // NO_PROTO3
+ // These field pairs have the same Java converted name
+ optional string field_name = 32; // NO_PROTO3
+ optional string field__name = 33; // NO_PROTO3
+ optional int32 _2conflict = 34; // NO_PROTO3
+ optional int32 __2conflict = 35;
+
extensions 1000 to max; // NO_PROTO3
repeated int64 int64_field = 41;
@@ -167,3 +172,14 @@ message TestMapField {
map<int32, int32> map_field = 1;
}
+
+message TestLeadingNumberFields {
+ optional int32 _30day_impressions = 1;
+ repeated string _60day_impressions = 2;
+
+ optional string __2_underscores = 3;
+ repeated string __2repeated_underscores = 4;
+
+ optional int32 _32 = 32;
+ repeated int64 _64 = 64;
+}