diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2015-08-22 18:25:48 -0700 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2015-08-22 18:25:48 -0700 |
commit | eee38b0c018b3279f77d03dff796f440f40d3516 (patch) | |
tree | 7ff0978e30238d493fc7899b75abeb6d66939f07 /java | |
parent | c3bc155aceda36ecb01cde2367a3b427f2d7ce40 (diff) | |
download | protobuf-eee38b0c018b3279f77d03dff796f440f40d3516.tar.gz protobuf-eee38b0c018b3279f77d03dff796f440f40d3516.tar.bz2 protobuf-eee38b0c018b3279f77d03dff796f440f40d3516.zip |
Down-integrate from google3.
Diffstat (limited to 'java')
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; |