aboutsummaryrefslogtreecommitdiffhomepage
path: root/java
diff options
context:
space:
mode:
authorGravatar Feng Xiao <xfxyjwf@gmail.com>2015-08-22 18:25:48 -0700
committerGravatar Feng Xiao <xfxyjwf@gmail.com>2015-08-22 18:25:48 -0700
commiteee38b0c018b3279f77d03dff796f440f40d3516 (patch)
tree7ff0978e30238d493fc7899b75abeb6d66939f07 /java
parentc3bc155aceda36ecb01cde2367a3b427f2d7ce40 (diff)
Down-integrate from google3.
Diffstat (limited to 'java')
-rw-r--r--java/src/main/java/com/google/protobuf/ByteString.java22
-rw-r--r--java/src/main/java/com/google/protobuf/CodedOutputStream.java103
-rw-r--r--java/src/main/java/com/google/protobuf/Descriptors.java6
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessage.java115
-rw-r--r--java/src/main/java/com/google/protobuf/GeneratedMessageLite.java109
-rw-r--r--java/src/main/java/com/google/protobuf/Internal.java12
-rw-r--r--java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java2
-rw-r--r--java/src/main/java/com/google/protobuf/LazyStringArrayList.java5
-rw-r--r--java/src/main/java/com/google/protobuf/LazyStringList.java13
-rw-r--r--java/src/main/java/com/google/protobuf/Message.java3
-rw-r--r--java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java2
-rw-r--r--java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java257
-rw-r--r--java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java5
-rw-r--r--java/src/main/java/com/google/protobuf/Utf8.java132
-rw-r--r--java/src/main/java/com/google/protobuf/WireFormat.java2
-rw-r--r--java/src/test/java/com/google/protobuf/BoundedByteStringTest.java1
-rw-r--r--java/src/test/java/com/google/protobuf/CheckUtf8Test.java7
-rw-r--r--java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java110
-rw-r--r--java/src/test/java/com/google/protobuf/FieldPresenceTest.java10
-rw-r--r--java/src/test/java/com/google/protobuf/GeneratedMessageTest.java3
-rw-r--r--java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java21
-rw-r--r--java/src/test/java/com/google/protobuf/LiteTest.java3
-rw-r--r--java/src/test/java/com/google/protobuf/LiteralByteStringTest.java61
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java26
-rw-r--r--java/src/test/java/com/google/protobuf/MapForProto2Test.java26
-rw-r--r--java/src/test/java/com/google/protobuf/MapTest.java42
-rw-r--r--java/src/test/java/com/google/protobuf/ParserTest.java16
-rw-r--r--java/src/test/java/com/google/protobuf/RopeByteStringTest.java8
-rw-r--r--java/src/test/java/com/google/protobuf/TestUtil.java1
-rw-r--r--java/src/test/java/com/google/protobuf/TextFormatTest.java2
-rw-r--r--java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java2
-rw-r--r--java/src/test/java/com/google/protobuf/WireFormatTest.java2
32 files changed, 882 insertions, 247 deletions
diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java
index 1d5d4e8a..0bd1750d 100644
--- a/java/src/main/java/com/google/protobuf/ByteString.java
+++ b/java/src/main/java/com/google/protobuf/ByteString.java
@@ -294,10 +294,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* <b>Performance notes:</b> The returned {@code ByteString} is an
* 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. If the caller knows the precise length of
- * the stream and wishes to avoid all unnecessary copies and
- * allocations, consider using the two-argument version of this
- * method, below.
+ * 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.
*
* @param streamToDrain The source stream, which is read completely
* but not closed.
@@ -320,12 +320,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* <b>Performance notes:</b> The returned {@code ByteString} is an
* immutable tree of byte arrays ("chunks") of the stream data. The
- * chunkSize parameter sets the size of these byte arrays. In
- * particular, if the chunkSize is precisely the same as the length
- * of the stream, unnecessary allocations and copies will be
- * avoided. Otherwise, the chunks will be of the given size, except
- * for the last chunk, which will be resized (via a reallocation and
- * copy) to contain the remainder of the stream.
+ * chunkSize parameter sets the size of these byte arrays.
+ *
+ * <p>Each byte read from the input stream will be copied twice to ensure
+ * that the resulting ByteString is truly immutable.
*
* @param streamToDrain The source stream, which is read completely
* but not closed.
@@ -386,6 +384,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
if (bytesRead == 0) {
return null;
} else {
+ // Always make a copy since InputStream could steal a reference to buf.
return ByteString.copyFrom(buf, 0, bytesRead);
}
}
@@ -736,7 +735,8 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* returns the number of bytes remaining in the stream. The methods
* {@link InputStream#read(byte[])}, {@link InputStream#read(byte[],int,int)}
* and {@link InputStream#skip(long)} will read/skip as many bytes as are
- * available.
+ * available. The method {@link InputStream#markSupported()} returns
+ * {@code true}.
* <p>
* The methods in the returned {@link InputStream} might <b>not</b> be
* thread safe.
diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
index 954fde08..291bd20a 100644
--- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -30,9 +30,13 @@
package com.google.protobuf;
+import com.google.protobuf.Utf8.UnpairedSurrogateException;
+
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/**
* Encodes and writes protocol message fields.
@@ -49,6 +53,10 @@ import java.nio.ByteBuffer;
* @author kneton@google.com Kenton Varda
*/
public final class CodedOutputStream {
+
+ private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
+
+ // TODO(dweis): Consider migrating to a ByteBuffer.
private final byte[] buffer;
private final int limit;
private int position;
@@ -415,15 +423,87 @@ public final class CodedOutputStream {
}
/** 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 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 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);
@@ -826,9 +906,16 @@ public final class CodedOutputStream {
* {@code string} field.
*/
public static int computeStringSizeNoTag(final String value) {
- final byte[] bytes = value.getBytes(Internal.UTF_8);
- return computeRawVarint32Size(bytes.length) +
- bytes.length;
+ int length;
+ try {
+ length = Utf8.encodedLength(value);
+ } catch (UnpairedSurrogateException e) {
+ // TODO(dweis): Consider using nio Charset methods instead.
+ final byte[] bytes = value.getBytes(Internal.UTF_8);
+ length = bytes.length;
+ }
+
+ return computeRawVarint32Size(length) + length;
}
/**
@@ -1007,9 +1094,15 @@ public final class CodedOutputStream {
public static class OutOfSpaceException extends IOException {
private static final long serialVersionUID = -6947486886997889499L;
+ private static final String MESSAGE =
+ "CodedOutputStream was writing to a flat byte array and ran out of space.";
+
OutOfSpaceException() {
- super("CodedOutputStream was writing to a flat byte array and ran " +
- "out of space.");
+ super(MESSAGE);
+ }
+
+ OutOfSpaceException(Throwable cause) {
+ super(MESSAGE, cause);
}
}
diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java
index 3658410c..fb9005bd 100644
--- a/java/src/main/java/com/google/protobuf/Descriptors.java
+++ b/java/src/main/java/com/google/protobuf/Descriptors.java
@@ -1118,9 +1118,9 @@ public final class Descriptors {
static {
// Refuse to init if someone added a new declared type.
if (Type.values().length != FieldDescriptorProto.Type.values().length) {
- throw new RuntimeException(
- "descriptor.proto has a new declared type but Desrciptors.java " +
- "wasn't updated.");
+ throw new RuntimeException(""
+ + "descriptor.proto has a new declared type but Descriptors.java "
+ + "wasn't updated.");
}
}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
index 9457d999..d84fa75c 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java
@@ -121,22 +121,44 @@ public abstract class GeneratedMessage extends AbstractMessage
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
- for (final FieldDescriptor field : descriptor.getFields()) {
- if (field.isRepeated()) {
- final List<?> value = (List<?>) getField(field);
- if (!value.isEmpty()) {
- result.put(field, value);
+ 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 (hasField(field)) {
- if (getBytesForString
- && field.getJavaType() == FieldDescriptor.JavaType.STRING) {
- result.put(field, getFieldRaw(field));
- } else {
- result.put(field, getField(field));
+ // 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;
}
@@ -398,17 +420,40 @@ public abstract class GeneratedMessage extends AbstractMessage
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
- for (final FieldDescriptor field : descriptor.getFields()) {
- if (field.isRepeated()) {
- final List value = (List) getField(field);
- if (!value.isEmpty()) {
- result.put(field, value);
+ 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 (hasField(field)) {
- result.put(field, getField(field));
+ // 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;
}
@@ -2696,4 +2741,38 @@ public abstract class GeneratedMessage extends AbstractMessage
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);
+ }
+ }
}
diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
index bd6bc463..a535b718 100644
--- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
+++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java
@@ -48,7 +48,6 @@ 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}.
@@ -60,24 +59,6 @@ public abstract class GeneratedMessageLite<
BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
extends AbstractMessageLite
implements Serializable {
-
- /**
- * Holds all the {@link PrototypeHolder}s for loaded classes.
- */
- // TODO(dweis): Consider different concurrency values.
- // TODO(dweis): This will prevent garbage collection of the class loader.
- // Ideally we'd use something like ClassValue but that's Java 7 only.
- private static final Map<Class<?>, PrototypeHolder<?, ?>> PROTOTYPE_MAP =
- new ConcurrentHashMap<Class<?>, PrototypeHolder<?, ?>>();
-
- // For use by generated code only.
- protected static <
- MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
- BuilderType extends GeneratedMessageLite.Builder<
- MessageType, BuilderType>> void onLoad(Class<MessageType> clazz,
- PrototypeHolder<MessageType, BuilderType> protoTypeHolder) {
- PROTOTYPE_MAP.put(clazz, protoTypeHolder);
- }
private static final long serialVersionUID = 1L;
@@ -90,20 +71,17 @@ public abstract class GeneratedMessageLite<
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final Parser<MessageType> getParserForType() {
- return (Parser<MessageType>) PROTOTYPE_MAP
- .get(getClass()).getParserForType();
+ return (Parser<MessageType>) dynamicMethod(MethodToInvoke.GET_PARSER);
}
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final MessageType getDefaultInstanceForType() {
- return (MessageType) PROTOTYPE_MAP
- .get(getClass()).getDefaultInstanceForType();
+ return (MessageType) dynamicMethod(MethodToInvoke.GET_DEFAULT_INSTANCE);
}
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final BuilderType newBuilderForType() {
- return (BuilderType) PROTOTYPE_MAP
- .get(getClass()).newBuilderForType();
+ return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
}
/**
@@ -141,7 +119,9 @@ public abstract class GeneratedMessageLite<
MERGE_FROM,
MAKE_IMMUTABLE,
NEW_INSTANCE,
- NEW_BUILDER;
+ NEW_BUILDER,
+ GET_DEFAULT_INSTANCE,
+ GET_PARSER;
}
/**
@@ -168,9 +148,21 @@ public abstract class GeneratedMessageLite<
* <p>
* For use by generated code only.
*/
- protected abstract Object dynamicMethod(
- MethodToInvoke method,
- Object... args);
+ protected abstract Object dynamicMethod(MethodToInvoke method, Object arg0, Object arg1);
+
+ /**
+ * Same as {@link #dynamicMethod(MethodToInvoke, Object, Object)} with {@code null} padding.
+ */
+ protected Object dynamicMethod(MethodToInvoke method, Object arg0) {
+ return dynamicMethod(method, arg0, null);
+ }
+
+ /**
+ * Same as {@link #dynamicMethod(MethodToInvoke, Object, Object)} with {@code null} padding.
+ */
+ protected Object dynamicMethod(MethodToInvoke method) {
+ return dynamicMethod(method, null, null);
+ }
/**
* Merge some unknown fields into the {@link UnknownFieldSetLite} for this
@@ -1059,18 +1051,22 @@ public abstract class GeneratedMessageLite<
@SuppressWarnings("unchecked")
protected Object readResolve() throws ObjectStreamException {
try {
- Class messageClass = Class.forName(messageClassName);
- Parser<?> parser =
- (Parser<?>) messageClass.getField("PARSER").get(null);
- return parser.parsePartialFrom(asBytes);
+ Class<?> messageClass = Class.forName(messageClassName);
+ java.lang.reflect.Field defaultInstanceField =
+ messageClass.getDeclaredField("DEFAULT_INSTANCE");
+ 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", e);
+ throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
- throw new RuntimeException("Unable to find PARSER", e);
+ throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e);
} catch (SecurityException e) {
- throw new RuntimeException("Unable to call PARSER", e);
+ throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) {
- throw new RuntimeException("Unable to call parseFrom method", e);
+ throw new RuntimeException("Unable to call parsePartialFrom", e);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Unable to understand proto buffer", e);
}
@@ -1103,45 +1099,6 @@ public abstract class GeneratedMessageLite<
return (GeneratedExtension<MessageType, T>) extension;
}
-
- /**
- * Represents the state needed to implement *ForType methods. Generated code
- * must provide a static singleton instance by adding it with
- * {@link GeneratedMessageLite#onLoad(Class, PrototypeHolder)} on class load.
- * <ul>
- * <li>{@link #getDefaultInstanceForType()}
- * <li>{@link #getParserForType()}
- * <li>{@link #newBuilderForType()}
- * </ul>
- * This allows us to trade three generated methods for a static Map.
- */
- protected static class PrototypeHolder<
- MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
- BuilderType extends GeneratedMessageLite.Builder<
- MessageType, BuilderType>> {
-
- private final MessageType defaultInstance;
- private final Parser<MessageType> parser;
-
- public PrototypeHolder(
- MessageType defaultInstance, Parser<MessageType> parser) {
- this.defaultInstance = defaultInstance;
- this.parser = parser;
- }
-
- public MessageType getDefaultInstanceForType() {
- return defaultInstance;
- }
-
- public Parser<MessageType> getParserForType() {
- return parser;
- }
-
- @SuppressWarnings("unchecked") // Guaranteed by runtime.
- public BuilderType newBuilderForType() {
- return (BuilderType) defaultInstance.toBuilder();
- }
- }
/**
* A static helper method for checking if a message is initialized, optionally memoizing.
diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java
index 20054b79..fefda904 100644
--- a/java/src/main/java/com/google/protobuf/Internal.java
+++ b/java/src/main/java/com/google/protobuf/Internal.java
@@ -31,6 +31,7 @@
package com.google.protobuf;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.AbstractList;
@@ -358,6 +359,17 @@ public class Internal {
}
}
+ @SuppressWarnings("unchecked")
+ public static <T extends MessageLite> T getDefaultInstance(Class<T> clazz) {
+ try {
+ Method method = clazz.getMethod("getDefaultInstance");
+ return (T) method.invoke(method);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Failed to get default instance for " + clazz, e);
+ }
+ }
+
/**
* An empty byte array constant used in generated code.
*/
diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
index 367fa23b..0a761052 100644
--- a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
+++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java
@@ -69,7 +69,7 @@ public class InvalidProtocolBufferException extends IOException {
static InvalidProtocolBufferException truncatedMessage() {
return new InvalidProtocolBufferException(
"While parsing a protocol message, the input ended unexpectedly " +
- "in the middle of a field. This could mean either than the " +
+ "in the middle of a field. This could mean either that the " +
"input has been truncated or that an embedded message " +
"misreported its own length.");
}
diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
index a2997e1c..c3be3cca 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
+++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java
@@ -215,6 +215,11 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
modCount++;
}
+ @Override
+ public Object getRaw(int index) {
+ return list.get(index);
+ }
+
// @Override
public ByteString getByteString(int index) {
Object o = list.get(index);
diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/src/main/java/com/google/protobuf/LazyStringList.java
index 235126b6..3eeedca1 100644
--- a/java/src/main/java/com/google/protobuf/LazyStringList.java
+++ b/java/src/main/java/com/google/protobuf/LazyStringList.java
@@ -56,7 +56,18 @@ public interface LazyStringList extends ProtocolStringList {
* ({@code index < 0 || index >= size()})
*/
ByteString getByteString(int index);
-
+
+ /**
+ * Returns the element at the specified position in this list as an Object
+ * that will either be a String or a ByteString.
+ *
+ * @param index index of the element to return
+ * @return the element at the specified position in this list
+ * @throws IndexOutOfBoundsException if the index is out of range
+ * ({@code index < 0 || index >= size()})
+ */
+ Object getRaw(int index);
+
/**
* Returns the element at the specified position in this list as byte[].
*
diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java
index fa0265e2..9516d71f 100644
--- a/java/src/main/java/com/google/protobuf/Message.java
+++ b/java/src/main/java/com/google/protobuf/Message.java
@@ -121,6 +121,9 @@ public interface Message extends MessageLite, MessageOrBuilder {
* 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++.
*/
diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
index be737b1a..f91cdbce 100644
--- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
+++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java
@@ -73,7 +73,7 @@ public class RepeatedFieldBuilder
private GeneratedMessage.BuilderParent parent;
// List of messages. Never null. It may be immutable, in which case
- // isMessagesListImmutable will be true. See note below.
+ // isMessagesListMutable will be false. See note below.
private List<MType> messages;
// Whether messages is an mutable array that can be modified.
diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
index 7ea84022..45d5fc35 100644
--- a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
+++ b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java
@@ -31,6 +31,7 @@
package com.google.protobuf;
import java.io.IOException;
+import java.util.Arrays;
/**
* {@code UnknownFieldSetLite} is used to keep track of fields which were seen
@@ -45,8 +46,11 @@ import java.io.IOException;
*/
public final class UnknownFieldSetLite {
+ private static final int[] EMPTY_INT_ARRAY = new int[0];
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
private static final UnknownFieldSetLite DEFAULT_INSTANCE =
- new UnknownFieldSetLite(ByteString.EMPTY);
+ new UnknownFieldSetLite(0, EMPTY_INT_ARRAY, EMPTY_OBJECT_ARRAY);
/**
* Get an empty {@code UnknownFieldSetLite}.
@@ -71,19 +75,41 @@ public final class UnknownFieldSetLite {
* {@code second}.
*/
static UnknownFieldSetLite concat(UnknownFieldSetLite first, UnknownFieldSetLite second) {
- return new UnknownFieldSetLite(first.byteString.concat(second.byteString));
+ int count = first.count + second.count;
+ int[] tags = Arrays.copyOf(first.tags, count);
+ System.arraycopy(second.tags, 0, tags, first.count, second.count);
+ Object[] objects = Arrays.copyOf(first.objects, count);
+ System.arraycopy(second.objects, 0, objects, first.count, second.count);
+ return new UnknownFieldSetLite(count, tags, objects);
}
-
+
+ /**
+ * The number of elements in the set.
+ */
+ private int count;
+
+ /**
+ * The tag numbers for the elements in the set.
+ */
+ private int[] tags;
+
/**
- * The internal representation of the unknown fields.
+ * The boxed values of the elements in the set.
*/
- private final ByteString byteString;
+ private Object[] objects;
+
+ /**
+ * The lazily computed serialized size of the set.
+ */
+ private int memoizedSerializedSize = -1;
/**
- * Constructs the {@code UnknownFieldSetLite} as a thin wrapper around {@link ByteString}.
+ * Constructs the {@code UnknownFieldSetLite}.
*/
- private UnknownFieldSetLite(ByteString byteString) {
- this.byteString = byteString;
+ private UnknownFieldSetLite(int count, int[] tags, Object[] objects) {
+ this.count = count;
+ this.tags = tags;
+ this.objects = objects;
}
/**
@@ -92,17 +118,73 @@ public final class UnknownFieldSetLite {
* <p>For use by generated code only.
*/
public void writeTo(CodedOutputStream output) throws IOException {
- output.writeRawBytes(byteString);
+ for (int i = 0; i < count; i++) {
+ int tag = tags[i];
+ int fieldNumber = WireFormat.getTagFieldNumber(tag);
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ output.writeUInt64(fieldNumber, (Long) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_FIXED32:
+ output.writeFixed32(fieldNumber, (Integer) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_FIXED64:
+ output.writeFixed64(fieldNumber, (Long) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ output.writeBytes(fieldNumber, (ByteString) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_START_GROUP:
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
+ ((UnknownFieldSetLite) objects[i]).writeTo(output);
+ output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
+ break;
+ default:
+ throw InvalidProtocolBufferException.invalidWireType();
+ }
+ }
}
-
/**
* Get the number of bytes required to encode this set.
*
* <p>For use by generated code only.
*/
public int getSerializedSize() {
- return byteString.size();
+ 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);
+ switch (WireFormat.getTagWireType(tag)) {
+ case WireFormat.WIRETYPE_VARINT:
+ size += CodedOutputStream.computeUInt64Size(fieldNumber, (Long) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_FIXED32:
+ size += CodedOutputStream.computeFixed32Size(fieldNumber, (Integer) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_FIXED64:
+ size += CodedOutputStream.computeFixed64Size(fieldNumber, (Long) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_LENGTH_DELIMITED:
+ size += CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) objects[i]);
+ break;
+ case WireFormat.WIRETYPE_START_GROUP:
+ size += CodedOutputStream.computeTagSize(fieldNumber) * 2
+ + ((UnknownFieldSetLite) objects[i]).getSerializedSize();
+ break;
+ default:
+ throw new IllegalStateException(InvalidProtocolBufferException.invalidWireType());
+ }
+ }
+
+ memoizedSerializedSize = size;
+
+ return size;
}
@Override
@@ -111,16 +193,34 @@ public final class UnknownFieldSetLite {
return true;
}
- if (obj instanceof UnknownFieldSetLite) {
- return byteString.equals(((UnknownFieldSetLite) obj).byteString);
+ if (obj == null) {
+ return false;
+ }
+
+ if (!(obj instanceof UnknownFieldSetLite)) {
+ return false;
+ }
+
+ 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)) {
+ return false;
}
- return false;
+ return true;
}
@Override
public int hashCode() {
- return byteString.hashCode();
+ int hashCode = 17;
+
+ hashCode = 31 * hashCode + count;
+ hashCode = 31 * hashCode + Arrays.hashCode(tags);
+ hashCode = 31 * hashCode + Arrays.deepHashCode(objects);
+
+ return hashCode;
}
/**
@@ -131,28 +231,49 @@ public final class UnknownFieldSetLite {
* <p>For use by generated code only.
*/
public static final class Builder {
+
+ // Arbitrarily chosen.
+ // TODO(dweis): Tune this number?
+ private static final int MIN_CAPACITY = 8;
+
+ private int count = 0;
+ private int[] tags = EMPTY_INT_ARRAY;
+ private Object[] objects = EMPTY_OBJECT_ARRAY;
- private ByteString.Output byteStringOutput;
- private CodedOutputStream output;
private boolean built;
/**
- * Constructs a {@code Builder}. Lazily initialized by
- * {@link #ensureInitializedButNotBuilt()}.
+ * Constructs a {@code Builder}.
*/
private Builder() {}
/**
* Ensures internal state is initialized for use.
*/
- private void ensureInitializedButNotBuilt() {
+ private void ensureNotBuilt() {
if (built) {
throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders.");
}
-
- if (output == null && byteStringOutput == null) {
- byteStringOutput = ByteString.newOutput(100 /* initialCapacity */);
- output = CodedOutputStream.newInstance(byteStringOutput);
+ }
+
+ private void storeField(int tag, Object value) {
+ ensureCapacity();
+
+ tags[count] = tag;
+ objects[count] = value;
+ count++;
+ }
+
+ /**
+ * Ensures that our arrays are long enough to store more metadata.
+ */
+ private void ensureCapacity() {
+ if (count == tags.length) {
+ int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1;
+ int newLength = count + increment;
+
+ tags = Arrays.copyOf(tags, newLength);
+ objects = Arrays.copyOf(objects, newLength);
}
}
@@ -166,31 +287,28 @@ public final class UnknownFieldSetLite {
*/
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
throws IOException {
- ensureInitializedButNotBuilt();
+ ensureNotBuilt();
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
- output.writeUInt64(fieldNumber, input.readInt64());
+ storeField(tag, input.readInt64());
return true;
case WireFormat.WIRETYPE_FIXED32:
- output.writeFixed32(fieldNumber, input.readFixed32());
+ storeField(tag, input.readFixed32());
return true;
case WireFormat.WIRETYPE_FIXED64:
- output.writeFixed64(fieldNumber, input.readFixed64());
+ storeField(tag, input.readFixed64());
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
- output.writeBytes(fieldNumber, input.readBytes());
+ storeField(tag, input.readBytes());
return true;
case WireFormat.WIRETYPE_START_GROUP:
final Builder subBuilder = newBuilder();
subBuilder.mergeFrom(input);
input.checkLastTagWas(
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
-
- output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
- subBuilder.build().writeTo(output);
- output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
+ storeField(tag, subBuilder.build());
return true;
case WireFormat.WIRETYPE_END_GROUP:
return false;
@@ -210,12 +328,10 @@ public final class UnknownFieldSetLite {
if (fieldNumber == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
- ensureInitializedButNotBuilt();
- try {
- output.writeUInt64(fieldNumber, value);
- } catch (IOException e) {
- // Should never happen.
- }
+ ensureNotBuilt();
+
+ storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value);
+
return this;
}
@@ -229,11 +345,24 @@ public final class UnknownFieldSetLite {
if (fieldNumber == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
- ensureInitializedButNotBuilt();
- try {
- output.writeBytes(fieldNumber, value);
- } catch (IOException e) {
- // Should never happen.
+ ensureNotBuilt();
+
+ storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value);
+
+ return this;
+ }
+
+ /**
+ * Parse an entire message from {@code input} and merge its fields into
+ * this set.
+ */
+ private Builder mergeFrom(final CodedInputStream input) throws IOException {
+ // Ensures initialization in mergeFieldFrom.
+ while (true) {
+ final int tag = input.readTag();
+ if (tag == 0 || !mergeFieldFrom(tag, input)) {
+ break;
+ }
}
return this;
}
@@ -254,44 +383,12 @@ public final class UnknownFieldSetLite {
}
built = true;
-
- final UnknownFieldSetLite result;
- // If we were never initialized, no data was written.
- if (output == null) {
- result = getDefaultInstance();
- } else {
- try {
- output.flush();
- } catch (IOException e) {
- // Should never happen.
- }
- ByteString byteString = byteStringOutput.toByteString();
- if (byteString.isEmpty()) {
- result = getDefaultInstance();
- } else {
- result = new UnknownFieldSetLite(byteString);
- }
+
+ if (count == 0) {
+ return DEFAULT_INSTANCE;
}
- // Allow for garbage collection.
- output = null;
- byteStringOutput = null;
- return result;
- }
-
- /**
- * Parse an entire message from {@code input} and merge its fields into
- * this set.
- */
- private Builder mergeFrom(final CodedInputStream input) throws IOException {
- // Ensures initialization in mergeFieldFrom.
- while (true) {
- final int tag = input.readTag();
- if (tag == 0 || !mergeFieldFrom(tag, input)) {
- break;
- }
- }
- return this;
+ return new UnknownFieldSetLite(count, tags, objects);
}
}
}
diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
index 5cc005df..5257c5a2 100644
--- a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
+++ b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java
@@ -57,6 +57,11 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
public String get(int index) {
return list.get(index);
}
+
+ @Override
+ public Object getRaw(int index) {
+ return list.getRaw(index);
+ }
@Override
public int size() {
diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/src/main/java/com/google/protobuf/Utf8.java
index 4271b41b..0699778f 100644
--- a/java/src/main/java/com/google/protobuf/Utf8.java
+++ b/java/src/main/java/com/google/protobuf/Utf8.java
@@ -66,6 +66,12 @@ package com.google.protobuf;
*/
final class Utf8 {
private Utf8() {}
+
+ /**
+ * Maximum number of bytes per Java UTF-16 char in UTF-8.
+ * @see java.nio.charset.CharsetEncoder#maxBytesPerChar()
+ */
+ static final int MAX_BYTES_PER_CHAR = 3;
/**
* State value indicating that the byte sequence is well-formed and
@@ -346,4 +352,130 @@ final class Utf8 {
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) {
+ super("Unpaired surrogate at index " + index);
+ }
+ }
+
+ /**
+ * 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
+ * both time and space.
+ *
+ * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
+ * surrogates)
+ */
+ static int encodedLength(CharSequence sequence) {
+ // Warning to maintainers: this implementation is highly optimized.
+ int utf16Length = sequence.length();
+ int utf8Length = utf16Length;
+ int i = 0;
+
+ // This loop optimizes for pure ASCII.
+ while (i < utf16Length && sequence.charAt(i) < 0x80) {
+ i++;
+ }
+
+ // This loop optimizes for chars less than 0x800.
+ for (; i < utf16Length; i++) {
+ char c = sequence.charAt(i);
+ if (c < 0x800) {
+ utf8Length += ((0x7f - c) >>> 31); // branch free!
+ } else {
+ utf8Length += encodedLengthGeneral(sequence, i);
+ break;
+ }
+ }
+
+ if (utf8Length < utf16Length) {
+ // Necessary and sufficient condition for overflow because of maximum 3x expansion
+ throw new IllegalArgumentException("UTF-8 length does not fit in int: "
+ + (utf8Length + (1L << 32)));
+ }
+ return utf8Length;
+ }
+
+ private static int encodedLengthGeneral(CharSequence sequence, int start) {
+ int utf16Length = sequence.length();
+ int utf8Length = 0;
+ for (int i = start; i < utf16Length; i++) {
+ char c = sequence.charAt(i);
+ if (c < 0x800) {
+ utf8Length += (0x7f - c) >>> 31; // branch free!
+ } else {
+ utf8Length += 2;
+ // jdk7+: if (Character.isSurrogate(c)) {
+ 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) {
+ throw new UnpairedSurrogateException(i);
+ }
+ i++;
+ }
+ }
+ }
+ 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));
+ }
+ 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));
+ } 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);
+ }
+ throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
+ }
+ }
+ return j;
+ }
+ // End Guava UTF-8 methods.
}
diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java
index ba83b666..8dbe1ae3 100644
--- a/java/src/main/java/com/google/protobuf/WireFormat.java
+++ b/java/src/main/java/com/google/protobuf/WireFormat.java
@@ -58,7 +58,7 @@ public final class WireFormat {
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
/** Given a tag value, determines the wire type (the lower 3 bits). */
- static int getTagWireType(final int tag) {
+ public static int getTagWireType(final int tag) {
return tag & TAG_TYPE_MASK;
}
diff --git a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
index 1562a1a6..447e6ef3 100644
--- a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java
@@ -85,6 +85,7 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
testString.substring(2, testString.length() - 6), roundTripString);
}
+ @Override
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
diff --git a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java b/java/src/test/java/com/google/protobuf/CheckUtf8Test.java
index 6470e9ca..3d6381c9 100644
--- a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java
+++ b/java/src/test/java/com/google/protobuf/CheckUtf8Test.java
@@ -58,8 +58,7 @@ public class CheckUtf8Test extends TestCase {
public void testParseRequiredStringWithGoodUtf8() throws Exception {
ByteString serialized =
BytesWrapper.newBuilder().setReq(UTF8_BYTE_STRING).build().toByteString();
- assertEquals(UTF8_BYTE_STRING_TEXT,
- StringWrapper.PARSER.parseFrom(serialized).getReq());
+ assertEquals(UTF8_BYTE_STRING_TEXT, StringWrapper.parser().parseFrom(serialized).getReq());
}
public void testBuildRequiredStringWithBadUtf8() throws Exception {
@@ -93,7 +92,7 @@ public class CheckUtf8Test extends TestCase {
ByteString serialized =
BytesWrapper.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
- StringWrapper.PARSER.parseFrom(serialized);
+ 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());
@@ -131,7 +130,7 @@ public class CheckUtf8Test extends TestCase {
ByteString serialized =
BytesWrapperSize.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
- StringWrapperSize.PARSER.parseFrom(serialized);
+ StringWrapperSize.parser().parseFrom(serialized);
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/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
index 365789c0..360e759e 100644
--- a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
+++ b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java
@@ -40,6 +40,7 @@ import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -325,10 +326,41 @@ public class CodedOutputStreamTest extends TestCase {
for (int i = 0; i < 1024; ++i) {
codedStream.writeRawBytes(value, 0, value.length);
}
+ 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);
+ 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);
- assertEquals(value.length * 1024, codedStream.getTotalBytesWritten());
+
+ // Verify that the total bytes written is correct
+ assertEquals((value.length * 1024) + stringSize, codedStream.getTotalBytesWritten());
+ }
+
+ // TODO(dweis): Write a comprehensive test suite for CodedOutputStream that covers more than just
+ // this case.
+ public void testWriteStringNoTag_fastpath() throws Exception {
+ int bufferSize = 153;
+ String threeBytesPer = "\u0981";
+ String string = threeBytesPer;
+ for (int i = 0; i < 50; i++) {
+ string += threeBytesPer;
+ }
+ // These checks ensure we will tickle the slower fast path.
+ assertEquals(1, CodedOutputStream.computeRawVarint32Size(string.length()));
+ assertEquals(
+ 2, CodedOutputStream.computeRawVarint32Size(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);
}
public void testWriteToByteBuffer() throws Exception {
@@ -398,4 +430,80 @@ public class CodedOutputStreamTest extends TestCase {
assertEqualBytes(bytes(0x02, 0x33, 0x44, 0x00), destination);
assertEquals(3, codedStream.getTotalBytesWritten());
}
+
+ 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),
+ "foobar" + newString(Character.MIN_LOW_SURROGATE),
+ newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE)
+ };
+
+ CodedOutputStream outputWithStream = CodedOutputStream.newInstance(new ByteArrayOutputStream());
+ CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[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);
+ }
+ }
+
+ private static String newString(char... chars) {
+ return new String(chars);
+ }
+
+ /** 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(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) {}
+ }
+ }
+
+ 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
+ };
+ 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
+ }
+ }
+
+ private void testEncodingOfString(char c, int length) throws Exception {
+ String fullString = fullString(c, length);
+ TestAllTypes testAllTypes = TestAllTypes.newBuilder()
+ .setOptionalString(fullString)
+ .build();
+ assertEquals(
+ fullString, TestAllTypes.parseFrom(testAllTypes.toByteArray()).getOptionalString());
+ }
+
+ private String fullString(char c, int length) {
+ char[] result = new char[length];
+ Arrays.fill(result, c);
+ return new String(result);
+ }
}
diff --git a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
index acf2b023..eaeec0b8 100644
--- a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
+++ b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java
@@ -142,6 +142,16 @@ public class FieldPresenceTest extends TestCase {
"OneofNestedMessage"));
}
+ public void testOneofEquals() throws Exception {
+ TestAllTypes.Builder builder = TestAllTypes.newBuilder();
+ TestAllTypes message1 = builder.build();
+ // Set message2's oneof_uint32 field to defalut value. The two
+ // messages should be different when check with oneof case.
+ builder.setOneofUint32(0);
+ TestAllTypes message2 = builder.build();
+ assertFalse(message1.equals(message2));
+ }
+
public void testFieldPresence() {
// Optional non-message fields set to their default value are treated the
// same way as not set.
diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
index 2bd8d1a9..70812b95 100644
--- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
+++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java
@@ -187,8 +187,7 @@ public class GeneratedMessageTest extends TestCase {
}
public void testParsedMessagesAreImmutable() throws Exception {
- TestAllTypes value = TestAllTypes.PARSER.parseFrom(
- TestUtil.getAllSet().toByteString());
+ TestAllTypes value = TestAllTypes.parser().parseFrom(TestUtil.getAllSet().toByteString());
assertIsUnmodifiable(value.getRepeatedInt32List());
assertIsUnmodifiable(value.getRepeatedInt64List());
assertIsUnmodifiable(value.getRepeatedUint32List());
diff --git a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
index acd18003..0ef414aa 100644
--- a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
+++ b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java
@@ -89,7 +89,7 @@ public class LazyStringEndToEndTest extends TestCase {
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8,
ByteString.copyFrom(sink));
}
-
+
public void testCaching() {
String a = "a";
String b = "b";
@@ -106,24 +106,13 @@ public class LazyStringEndToEndTest extends TestCase {
assertSame(c, proto.getRepeatedString(1));
- // There's no way to directly observe that the ByteString is cached
- // correctly on serialization, but we can observe that it had to recompute
- // the string after serialization.
+ // Ensure serialization keeps strings cached.
proto.toByteString();
- String aPrime = proto.getOptionalString();
- assertNotSame(a, aPrime);
- assertEquals(a, aPrime);
- String bPrime = proto.getRepeatedString(0);
- assertNotSame(b, bPrime);
- assertEquals(b, bPrime);
- String cPrime = proto.getRepeatedString(1);
- assertNotSame(c, cPrime);
- assertEquals(c, cPrime);
// And now the string should stay cached.
- assertSame(aPrime, proto.getOptionalString());
- assertSame(bPrime, proto.getRepeatedString(0));
- assertSame(cPrime, proto.getRepeatedString(1));
+ assertSame(a, proto.getOptionalString());
+ assertSame(b, proto.getRepeatedString(0));
+ assertSame(c, proto.getRepeatedString(1));
}
public void testNoStringCachingIfOnlyBytesAccessed() throws Exception {
diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java
index 8c3b5e5c..b1f298ff 100644
--- a/java/src/test/java/com/google/protobuf/LiteTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteTest.java
@@ -42,6 +42,7 @@ 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.TestNestedExtensionLite;
import junit.framework.TestCase;
@@ -1400,6 +1401,8 @@ public class LiteTest extends TestCase {
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();
diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
index 958b6a7e..7dfda2ae 100644
--- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java
@@ -34,6 +34,7 @@ import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@@ -209,6 +210,62 @@ public class LiteralByteStringTest extends TestCase {
Arrays.equals(referenceBytes, myBuffer.array()));
}
+ public void testMarkSupported() {
+ InputStream stream = stringUnderTest.newInput();
+ assertTrue(classUnderTest + ".newInput() must support marking", stream.markSupported());
+ }
+
+ public void testMarkAndReset() throws IOException {
+ int fraction = stringUnderTest.size() / 3;
+
+ InputStream stream = stringUnderTest.newInput();
+ stream.mark(stringUnderTest.size()); // First, mark() the end.
+
+ skipFully(stream, fraction); // Skip a large fraction, but not all.
+ int available = stream.available();
+ assertTrue(
+ classUnderTest + ": after skipping to the 'middle', half the bytes are available",
+ (stringUnderTest.size() - fraction) == available);
+ stream.reset();
+
+ skipFully(stream, stringUnderTest.size()); // Skip to the end.
+ available = stream.available();
+ assertTrue(
+ classUnderTest + ": after skipping to the end, no more bytes are available",
+ 0 == available);
+ }
+
+ /**
+ * Discards {@code n} bytes of data from the input stream. This method
+ * will block until the full amount has been skipped. Does not close the
+ * stream.
+ * <p>Copied from com.google.common.io.ByteStreams to avoid adding dependency.
+ *
+ * @param in the input stream to read from
+ * @param n the number of bytes to skip
+ * @throws EOFException if this stream reaches the end before skipping all
+ * the bytes
+ * @throws IOException if an I/O error occurs, or the stream does not
+ * support skipping
+ */
+ static void skipFully(InputStream in, long n) throws IOException {
+ long toSkip = n;
+ while (n > 0) {
+ long amt = in.skip(n);
+ if (amt == 0) {
+ // Force a blocking read to avoid infinite loop
+ if (in.read() == -1) {
+ long skipped = toSkip - n;
+ throw new EOFException("reached end of stream after skipping "
+ + skipped + " bytes; " + toSkip + " bytes expected");
+ }
+ n--;
+ } else {
+ n -= amt;
+ }
+ }
+ }
+
public void testAsReadOnlyByteBuffer() {
ByteBuffer byteBuffer = stringUnderTest.asReadOnlyByteBuffer();
byte[] roundTripBytes = new byte[referenceBytes.length];
@@ -305,13 +362,13 @@ public class LiteralByteStringTest extends TestCase {
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
}
- public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException{
+ public void testToString_returnsCanonicalEmptyString() {
assertSame(classUnderTest + " must be the same string references",
ByteString.EMPTY.toString(Internal.UTF_8),
new LiteralByteString(new byte[]{}).toString(Internal.UTF_8));
}
- public void testToString_raisesException() throws UnsupportedEncodingException{
+ public void testToString_raisesException() {
try {
ByteString.EMPTY.toString("invalid");
fail("Should have thrown an exception.");
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
index 6cff689f..3d8c9bc4 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
+++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java
@@ -74,6 +74,16 @@ public class MapForProto2LiteTest extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
+ private void copyMapValues(TestMap source, TestMap.Builder destination) {
+ destination
+ .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
+ .putAllInt32ToStringField(source.getInt32ToStringField())
+ .putAllInt32ToBytesField(source.getInt32ToBytesField())
+ .putAllInt32ToEnumField(source.getInt32ToEnumField())
+ .putAllInt32ToMessageField(source.getInt32ToMessageField())
+ .putAllStringToInt32Field(source.getStringToInt32Field());
+ }
+
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@@ -330,26 +340,36 @@ public class MapForProto2LiteTest extends TestCase {
assertMapValuesCleared(message);
}
+ public void testPutAll() throws Exception {
+ TestMap.Builder sourceBuilder = TestMap.newBuilder();
+ setMapValues(sourceBuilder);
+ TestMap source = sourceBuilder.build();
+
+ TestMap.Builder destination = TestMap.newBuilder();
+ copyMapValues(source, destination);
+ assertMapValuesSet(destination.build());
+ }
+
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ 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());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
index 7e984040..1fa3cbdb 100644
--- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java
+++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java
@@ -78,6 +78,16 @@ public class MapForProto2Test extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
+ private void copyMapValues(TestMap source, TestMap.Builder destination) {
+ destination
+ .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
+ .putAllInt32ToStringField(source.getInt32ToStringField())
+ .putAllInt32ToBytesField(source.getInt32ToBytesField())
+ .putAllInt32ToEnumField(source.getInt32ToEnumField())
+ .putAllInt32ToMessageField(source.getInt32ToMessageField())
+ .putAllStringToInt32Field(source.getStringToInt32Field());
+ }
+
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@@ -310,26 +320,36 @@ public class MapForProto2Test extends TestCase {
assertMapValuesCleared(message);
}
+ public void testPutAll() throws Exception {
+ TestMap.Builder sourceBuilder = TestMap.newBuilder();
+ setMapValues(sourceBuilder);
+ TestMap source = sourceBuilder.build();
+
+ TestMap.Builder destination = TestMap.newBuilder();
+ copyMapValues(source, destination);
+ assertMapValuesSet(destination.build());
+ }
+
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ 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());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java
index 0509be15..0e5c1284 100644
--- a/java/src/test/java/com/google/protobuf/MapTest.java
+++ b/java/src/test/java/com/google/protobuf/MapTest.java
@@ -79,6 +79,16 @@ public class MapTest extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
+ private void copyMapValues(TestMap source, TestMap.Builder destination) {
+ destination
+ .putAllInt32ToInt32Field(source.getInt32ToInt32Field())
+ .putAllInt32ToStringField(source.getInt32ToStringField())
+ .putAllInt32ToBytesField(source.getInt32ToBytesField())
+ .putAllInt32ToEnumField(source.getInt32ToEnumField())
+ .putAllInt32ToMessageField(source.getInt32ToMessageField())
+ .putAllStringToInt32Field(source.getStringToInt32Field());
+ }
+
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@@ -311,26 +321,52 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message);
}
+ public void testPutAll() throws Exception {
+ TestMap.Builder sourceBuilder = TestMap.newBuilder();
+ setMapValues(sourceBuilder);
+ TestMap source = sourceBuilder.build();
+
+ TestMap.Builder destination = TestMap.newBuilder();
+ copyMapValues(source, destination);
+ assertMapValuesSet(destination.build());
+ }
+
+ 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.Builder destinationBuilder = TestMap.newBuilder();
+ destinationBuilder.putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue());
+ TestMap destination = destinationBuilder.build();
+
+ assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
+ assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue());
+ assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue());
+ }
+
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ 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());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
- message = TestMap.PARSER.parseFrom(message.toByteString());
+ message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}
diff --git a/java/src/test/java/com/google/protobuf/ParserTest.java b/java/src/test/java/com/google/protobuf/ParserTest.java
index b11d8cb9..5a92bacf 100644
--- a/java/src/test/java/com/google/protobuf/ParserTest.java
+++ b/java/src/test/java/com/google/protobuf/ParserTest.java
@@ -58,8 +58,7 @@ import java.io.InputStream;
public class ParserTest extends TestCase {
public void testGeneratedMessageParserSingleton() throws Exception {
for (int i = 0; i < 10; i++) {
- assertEquals(TestAllTypes.PARSER,
- TestUtil.getAllSet().getParserForType());
+ assertEquals(TestAllTypes.parser(), TestUtil.getAllSet().getParserForType());
}
}
@@ -125,8 +124,7 @@ public class ParserTest extends TestCase {
public void testParsePartial() throws Exception {
- assertParsePartial(TestRequired.PARSER,
- TestRequired.newBuilder().setA(1).buildPartial());
+ assertParsePartial(TestRequired.parser(), TestRequired.newBuilder().setA(1).buildPartial());
}
private <T extends MessageLite> void assertParsePartial(
@@ -216,8 +214,8 @@ public class ParserTest extends TestCase {
public void testParseUnknownFields() throws Exception {
// All fields will be treated as unknown fields in emptyMessage.
- TestEmptyMessage emptyMessage = TestEmptyMessage.PARSER.parseFrom(
- TestUtil.getAllSet().toByteString());
+ TestEmptyMessage emptyMessage =
+ TestEmptyMessage.parser().parseFrom(TestUtil.getAllSet().toByteString());
assertEquals(
TestUtil.getAllSet().toByteString(),
emptyMessage.toByteString());
@@ -298,8 +296,7 @@ public class ParserTest extends TestCase {
// Parse TestParsingMerge.
ExtensionRegistry registry = ExtensionRegistry.newInstance();
UnittestProto.registerAllExtensions(registry);
- TestParsingMerge parsingMerge =
- TestParsingMerge.PARSER.parseFrom(data, registry);
+ TestParsingMerge parsingMerge = TestParsingMerge.parser().parseFrom(data, registry);
// Required and optional fields should be merged.
assertMessageMerged(parsingMerge.getRequiredAllTypes());
@@ -361,8 +358,7 @@ public class ParserTest extends TestCase {
// Parse TestParsingMergeLite.
ExtensionRegistry registry = ExtensionRegistry.newInstance();
UnittestLite.registerAllExtensions(registry);
- TestParsingMergeLite parsingMerge =
- TestParsingMergeLite.PARSER.parseFrom(data, registry);
+ TestParsingMergeLite parsingMerge = TestParsingMergeLite.parser().parseFrom(data, registry);
// Required and optional fields should be merged.
assertMessageMerged(parsingMerge.getRequiredAllTypes());
diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
index bd0d15e3..4ec3a409 100644
--- a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
+++ b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java
@@ -119,7 +119,7 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
@Override
- public void testCharsetToString() throws UnsupportedEncodingException {
+ public void testCharsetToString() {
String sourceString = "I love unicode \u1234\u5678 characters";
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
int copies = 250;
@@ -145,14 +145,15 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
@Override
- public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException {
+ public void testToString_returnsCanonicalEmptyString() {
RopeByteString ropeByteString =
RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
assertSame(classUnderTest + " must be the same string references",
ByteString.EMPTY.toString(Internal.UTF_8), ropeByteString.toString(Internal.UTF_8));
}
- public void testToString_raisesException() throws UnsupportedEncodingException{
+ @Override
+ public void testToString_raisesException() {
try {
ByteString byteString =
RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
@@ -172,6 +173,7 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
}
+ @Override
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java
index 19a96d0e..792e8665 100644
--- a/java/src/test/java/com/google/protobuf/TestUtil.java
+++ b/java/src/test/java/com/google/protobuf/TestUtil.java
@@ -732,6 +732,7 @@ public final class TestUtil {
Assert.assertEquals("424", message.getDefaultStringPiece());
Assert.assertEquals("425", message.getDefaultCord());
+ Assert.assertEquals(TestAllTypes.OneofFieldCase.ONEOF_BYTES, message.getOneofFieldCase());
Assert.assertFalse(message.hasOneofUint32());
Assert.assertFalse(message.hasOneofNestedMessage());
Assert.assertFalse(message.hasOneofString());
diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java
index 5d846646..8294b865 100644
--- a/java/src/test/java/com/google/protobuf/TextFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java
@@ -32,7 +32,6 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
-import protobuf_unittest.UnittestMset.TestMessageSet;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import protobuf_unittest.UnittestProto.OneString;
@@ -41,6 +40,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestOneof2;
+import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
import junit.framework.TestCase;
diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
index 93a5ee22..8c9dcafe 100644
--- a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
+++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java
@@ -461,7 +461,7 @@ public class UnknownFieldSetTest extends TestCase {
TestAllExtensions allExtensions = TestUtil.getAllExtensionsSet();
ByteString allExtensionsData = allExtensions.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
- UnittestLite.TestEmptyMessageLite.PARSER.parseFrom(allExtensionsData);
+ UnittestLite.TestEmptyMessageLite.parser().parseFrom(allExtensionsData);
ByteString data = emptyMessageLite.toByteString();
TestAllExtensions message =
TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());
diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java
index 6858524e..0175005d 100644
--- a/java/src/test/java/com/google/protobuf/WireFormatTest.java
+++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java
@@ -44,10 +44,10 @@ import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
-import protobuf_unittest.UnittestMset.TestMessageSet;
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;