diff options
Diffstat (limited to 'java/src/main')
10 files changed, 653 insertions, 214 deletions
diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java index 2f61859d..24c32a47 100644 --- a/java/src/main/java/com/google/protobuf/AbstractMessage.java +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -86,8 +86,25 @@ public abstract class AbstractMessage implements Message { for (Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { FieldDescriptor field = entry.getKey(); if (field.isRepeated()) { - for (Object element : (List) entry.getValue()) { - output.writeField(field.getType(), field.getNumber(), element); + List valueList = (List) entry.getValue(); + if (field.getOptions().getPacked()) { + + output.writeTag(field.getNumber(), + WireFormat.WIRETYPE_LENGTH_DELIMITED); + int dataSize = 0; + for (Object element : valueList) { + dataSize += CodedOutputStream.computeFieldSizeNoTag( + field.getType(), element); + } + output.writeRawVarint32(dataSize); + + for (Object element : valueList) { + output.writeFieldNoTag(field.getType(), element); + } + } else { + for (Object element : valueList) { + output.writeField(field.getType(), field.getNumber(), element); + } } } else { output.writeField(field.getType(), field.getNumber(), entry.getValue()); @@ -145,9 +162,21 @@ public abstract class AbstractMessage implements Message { for (Map.Entry<FieldDescriptor, Object> entry : getAllFields().entrySet()) { FieldDescriptor field = entry.getKey(); if (field.isRepeated()) { - for (Object element : (List) entry.getValue()) { - size += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), element); + List valueList = (List) entry.getValue(); + if (field.getOptions().getPacked()) { + int dataSize = 0; + for (Object element : valueList) { + dataSize += CodedOutputStream.computeFieldSizeNoTag( + field.getType(), element); + } + size += dataSize; + size += CodedOutputStream.computeTagSize(field.getNumber()); + size += CodedOutputStream.computeRawVarint32Size(dataSize); + } else { + for (Object element : valueList) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } } } else { size += CodedOutputStream.computeFieldSize( @@ -165,7 +194,7 @@ public abstract class AbstractMessage implements Message { memoizedSize = size; return size; } - + @Override public boolean equals(Object other) { if (other == this) { @@ -180,7 +209,7 @@ public abstract class AbstractMessage implements Message { } return getAllFields().equals(otherMessage.getAllFields()); } - + @Override public int hashCode() { int hash = 41; diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java index caef068b..8f277a9f 100644 --- a/java/src/main/java/com/google/protobuf/CodedInputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -193,7 +193,7 @@ public final class CodedInputStream { /** Read a {@code string} field value from the stream. */ public String readString() throws IOException { int size = readRawVarint32(); - if (size < bufferSize - bufferPos && size > 0) { + if (size <= (bufferSize - bufferPos) && size > 0) { // Fast path: We already have the bytes in a contiguous buffer, so // just copy directly from it. String result = new String(buffer, bufferPos, size, "UTF-8"); @@ -584,6 +584,19 @@ public final class CodedInputStream { } /** + * Returns the number of bytes to be read before the current limit. + * If no limit is set, returns -1. + */ + public int getBytesUntilLimit() { + if (currentLimit == Integer.MAX_VALUE) { + return -1; + } + + int currentAbsolutePosition = totalBytesRetired + bufferPos; + return currentLimit - currentAbsolutePosition; + } + + /** * Called with {@code this.buffer} is empty to read more bytes from the * input. If {@code mustSucceed} is true, refillBuffer() gurantees that * either there will be at least one byte in the buffer when it returns diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java index d232841a..e8be8611 100644 --- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -118,71 +118,61 @@ public final class CodedOutputStream { /** Write a {@code double} field, including tag, to the stream. */ public void writeDouble(int fieldNumber, double value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + writeDoubleNoTag(value); } /** Write a {@code float} field, including tag, to the stream. */ public void writeFloat(int fieldNumber, float value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeRawLittleEndian32(Float.floatToRawIntBits(value)); + writeFloatNoTag(value); } /** Write a {@code uint64} field, including tag, to the stream. */ public void writeUInt64(int fieldNumber, long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint64(value); + writeUInt64NoTag(value); } /** Write an {@code int64} field, including tag, to the stream. */ public void writeInt64(int fieldNumber, long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint64(value); + writeInt64NoTag(value); } /** Write an {@code int32} field, including tag, to the stream. */ public void writeInt32(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - if (value >= 0) { - writeRawVarint32(value); - } else { - // Must sign-extend. - writeRawVarint64(value); - } + writeInt32NoTag(value); } /** Write a {@code fixed64} field, including tag, to the stream. */ public void writeFixed64(int fieldNumber, long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeRawLittleEndian64(value); + writeFixed64NoTag(value); } /** Write a {@code fixed32} field, including tag, to the stream. */ public void writeFixed32(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeRawLittleEndian32(value); + writeFixed32NoTag(value); } /** Write a {@code bool} field, including tag, to the stream. */ public void writeBool(int fieldNumber, boolean value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawByte(value ? 1 : 0); + writeBoolNoTag(value); } /** Write a {@code string} field, including tag, to the stream. */ public void writeString(int fieldNumber, String value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - // 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. - byte[] bytes = value.getBytes("UTF-8"); - writeRawVarint32(bytes.length); - writeRawBytes(bytes); + writeStringNoTag(value); } /** Write a {@code group} field, including tag, to the stream. */ public void writeGroup(int fieldNumber, Message value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); - value.writeTo(this); + writeGroupNoTag(value); writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); } @@ -190,29 +180,26 @@ public final class CodedOutputStream { public void writeUnknownGroup(int fieldNumber, UnknownFieldSet value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); - value.writeTo(this); + writeUnknownGroupNoTag(value); writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); } /** Write an embedded message field, including tag, to the stream. */ public void writeMessage(int fieldNumber, Message value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeRawVarint32(value.getSerializedSize()); - value.writeTo(this); + writeMessageNoTag(value); } /** Write a {@code bytes} field, including tag, to the stream. */ public void writeBytes(int fieldNumber, ByteString value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - byte[] bytes = value.toByteArray(); - writeRawVarint32(bytes.length); - writeRawBytes(bytes); + writeBytesNoTag(value); } /** Write a {@code uint32} field, including tag, to the stream. */ public void writeUInt32(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint32(value); + writeUInt32NoTag(value); } /** @@ -221,31 +208,31 @@ public final class CodedOutputStream { */ public void writeEnum(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint32(value); + writeEnumNoTag(value); } /** Write an {@code sfixed32} field, including tag, to the stream. */ public void writeSFixed32(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeRawLittleEndian32(value); + writeSFixed32NoTag(value); } /** Write an {@code sfixed64} field, including tag, to the stream. */ public void writeSFixed64(int fieldNumber, long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeRawLittleEndian64(value); + writeSFixed64NoTag(value); } /** Write an {@code sint32} field, including tag, to the stream. */ public void writeSInt32(int fieldNumber, int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint32(encodeZigZag32(value)); + writeSInt32NoTag(value); } /** Write an {@code sint64} field, including tag, to the stream. */ public void writeSInt64(int fieldNumber, long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeRawVarint64(encodeZigZag64(value)); + writeSInt64NoTag(value); } /** @@ -283,32 +270,168 @@ public final class CodedOutputStream { * this field. */ public void writeField(Descriptors.FieldDescriptor.Type type, - int number, Object value) throws IOException { + int number, + Object value) throws IOException { + // Special case for groups, which need a start and end tag; other fields + // can just use writeTag() and writeFieldNoTag(). + if (type == Descriptors.FieldDescriptor.Type.GROUP) { + writeGroup(number, (Message) value); + } else { + writeTag(number, WireFormat.getWireFormatForFieldType(type)); + writeFieldNoTag(type, value); + } + } + + /** + * Write a field of arbitrary type, without its tag, to the stream. + * + * @param type The field's type. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public void writeFieldNoTag(Descriptors.FieldDescriptor.Type type, + Object value) throws IOException { switch (type) { - case DOUBLE : writeDouble (number, (Double )value); break; - case FLOAT : writeFloat (number, (Float )value); break; - case INT64 : writeInt64 (number, (Long )value); break; - case UINT64 : writeUInt64 (number, (Long )value); break; - case INT32 : writeInt32 (number, (Integer )value); break; - case FIXED64 : writeFixed64 (number, (Long )value); break; - case FIXED32 : writeFixed32 (number, (Integer )value); break; - case BOOL : writeBool (number, (Boolean )value); break; - case STRING : writeString (number, (String )value); break; - case GROUP : writeGroup (number, (Message )value); break; - case MESSAGE : writeMessage (number, (Message )value); break; - case BYTES : writeBytes (number, (ByteString)value); break; - case UINT32 : writeUInt32 (number, (Integer )value); break; - case SFIXED32: writeSFixed32(number, (Integer )value); break; - case SFIXED64: writeSFixed64(number, (Long )value); break; - case SINT32 : writeSInt32 (number, (Integer )value); break; - case SINT64 : writeSInt64 (number, (Long )value); break; + case DOUBLE : writeDoubleNoTag ((Double ) value); break; + case FLOAT : writeFloatNoTag ((Float ) value); break; + case INT64 : writeInt64NoTag ((Long ) value); break; + case UINT64 : writeUInt64NoTag ((Long ) value); break; + case INT32 : writeInt32NoTag ((Integer ) value); break; + case FIXED64 : writeFixed64NoTag ((Long ) value); break; + case FIXED32 : writeFixed32NoTag ((Integer ) value); break; + case BOOL : writeBoolNoTag ((Boolean ) value); break; + case STRING : writeStringNoTag ((String ) value); break; + case GROUP : writeGroupNoTag ((Message ) value); break; + case MESSAGE : writeMessageNoTag ((Message ) value); break; + case BYTES : writeBytesNoTag ((ByteString) value); break; + case UINT32 : writeUInt32NoTag ((Integer ) value); break; + case SFIXED32: writeSFixed32NoTag((Integer ) value); break; + case SFIXED64: writeSFixed64NoTag((Long ) value); break; + case SINT32 : writeSInt32NoTag ((Integer ) value); break; + case SINT64 : writeSInt64NoTag ((Long ) value); break; case ENUM: - writeEnum(number, ((Descriptors.EnumValueDescriptor)value).getNumber()); + writeEnumNoTag(((Descriptors.EnumValueDescriptor) value).getNumber()); break; } } + // ----------------------------------------------------------------- + + /** Write a {@code double} field to the stream. */ + public void writeDoubleNoTag(double value) throws IOException { + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + } + + /** Write a {@code float} field to the stream. */ + public void writeFloatNoTag(float value) throws IOException { + writeRawLittleEndian32(Float.floatToRawIntBits(value)); + } + + /** Write a {@code uint64} field to the stream. */ + public void writeUInt64NoTag(long value) throws IOException { + writeRawVarint64(value); + } + + /** Write an {@code int64} field to the stream. */ + public void writeInt64NoTag(long value) throws IOException { + writeRawVarint64(value); + } + + /** Write an {@code int32} field to the stream. */ + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } + } + + /** Write a {@code fixed64} field to the stream. */ + public void writeFixed64NoTag(long value) throws IOException { + writeRawLittleEndian64(value); + } + + /** Write a {@code fixed32} field to the stream. */ + public void writeFixed32NoTag(int value) throws IOException { + writeRawLittleEndian32(value); + } + + /** Write a {@code bool} field to the stream. */ + public void writeBoolNoTag(boolean value) throws IOException { + writeRawByte(value ? 1 : 0); + } + + /** Write a {@code string} field to the stream. */ + public void writeStringNoTag(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. + byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code group} field to the stream. */ + public void writeGroupNoTag(Message value) throws IOException { + value.writeTo(this); + } + + /** Write a group represented by an {@link UnknownFieldSet}. */ + public void writeUnknownGroupNoTag(UnknownFieldSet value) + throws IOException { + value.writeTo(this); + } + + /** Write an embedded message field to the stream. */ + public void writeMessageNoTag(Message value) throws IOException { + writeRawVarint32(value.getSerializedSize()); + value.writeTo(this); + } + + /** Write a {@code bytes} field to the stream. */ + public void writeBytesNoTag(ByteString value) throws IOException { + byte[] bytes = value.toByteArray(); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code uint32} field to the stream. */ + public void writeUInt32NoTag(int value) throws IOException { + writeRawVarint32(value); + } + + /** + * Write an enum field to the stream. Caller is responsible + * for converting the enum value to its numeric value. + */ + public void writeEnumNoTag(int value) throws IOException { + writeRawVarint32(value); + } + + /** Write an {@code sfixed32} field to the stream. */ + public void writeSFixed32NoTag(int value) throws IOException { + writeRawLittleEndian32(value); + } + + /** Write an {@code sfixed64} field to the stream. */ + public void writeSFixed64NoTag(long value) throws IOException { + writeRawLittleEndian64(value); + } + + /** Write an {@code sint32} field to the stream. */ + public void writeSInt32NoTag(int value) throws IOException { + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write an {@code sint64} field to the stream. */ + public void writeSInt64NoTag(long value) throws IOException { + writeRawVarint64(encodeZigZag64(value)); + } + // ================================================================= /** @@ -316,7 +439,7 @@ public final class CodedOutputStream { * {@code double} field, including tag. */ public static int computeDoubleSize(int fieldNumber, double value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); } /** @@ -324,7 +447,7 @@ public final class CodedOutputStream { * {@code float} field, including tag. */ public static int computeFloatSize(int fieldNumber, float value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); } /** @@ -332,7 +455,7 @@ public final class CodedOutputStream { * {@code uint64} field, including tag. */ public static int computeUInt64Size(int fieldNumber, long value) { - return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); } /** @@ -340,7 +463,7 @@ public final class CodedOutputStream { * {@code int64} field, including tag. */ public static int computeInt64Size(int fieldNumber, long value) { - return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); } /** @@ -348,12 +471,7 @@ public final class CodedOutputStream { * {@code int32} field, including tag. */ public static int computeInt32Size(int fieldNumber, int value) { - if (value >= 0) { - return computeTagSize(fieldNumber) + computeRawVarint32Size(value); - } else { - // Must sign-extend. - return computeTagSize(fieldNumber) + 10; - } + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); } /** @@ -361,7 +479,7 @@ public final class CodedOutputStream { * {@code fixed64} field, including tag. */ public static int computeFixed64Size(int fieldNumber, long value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); } /** @@ -369,7 +487,7 @@ public final class CodedOutputStream { * {@code fixed32} field, including tag. */ public static int computeFixed32Size(int fieldNumber, int value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); } /** @@ -377,7 +495,7 @@ public final class CodedOutputStream { * {@code bool} field, including tag. */ public static int computeBoolSize(int fieldNumber, boolean value) { - return computeTagSize(fieldNumber) + 1; + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); } /** @@ -385,14 +503,7 @@ public final class CodedOutputStream { * {@code string} field, including tag. */ public static int computeStringSize(int fieldNumber, String value) { - try { - byte[] bytes = value.getBytes("UTF-8"); - return computeTagSize(fieldNumber) + - computeRawVarint32Size(bytes.length) + - bytes.length; - } catch (java.io.UnsupportedEncodingException e) { - throw new RuntimeException("UTF-8 not supported.", e); - } + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); } /** @@ -400,7 +511,7 @@ public final class CodedOutputStream { * {@code group} field, including tag. */ public static int computeGroupSize(int fieldNumber, Message value) { - return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); } /** @@ -410,7 +521,8 @@ public final class CodedOutputStream { */ public static int computeUnknownGroupSize(int fieldNumber, UnknownFieldSet value) { - return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + return computeTagSize(fieldNumber) * 2 + + computeUnknownGroupSizeNoTag(value); } /** @@ -418,8 +530,7 @@ public final class CodedOutputStream { * embedded message field, including tag. */ public static int computeMessageSize(int fieldNumber, Message value) { - int size = value.getSerializedSize(); - return computeTagSize(fieldNumber) + computeRawVarint32Size(size) + size; + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); } /** @@ -427,9 +538,7 @@ public final class CodedOutputStream { * {@code bytes} field, including tag. */ public static int computeBytesSize(int fieldNumber, ByteString value) { - return computeTagSize(fieldNumber) + - computeRawVarint32Size(value.size()) + - value.size(); + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); } /** @@ -437,7 +546,7 @@ public final class CodedOutputStream { * {@code uint32} field, including tag. */ public static int computeUInt32Size(int fieldNumber, int value) { - return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); } /** @@ -446,7 +555,7 @@ public final class CodedOutputStream { * enum value to its numeric value. */ public static int computeEnumSize(int fieldNumber, int value) { - return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); } /** @@ -454,7 +563,7 @@ public final class CodedOutputStream { * {@code sfixed32} field, including tag. */ public static int computeSFixed32Size(int fieldNumber, int value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); } /** @@ -462,7 +571,7 @@ public final class CodedOutputStream { * {@code sfixed64} field, including tag. */ public static int computeSFixed64Size(int fieldNumber, long value) { - return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); } /** @@ -470,8 +579,7 @@ public final class CodedOutputStream { * {@code sint32} field, including tag. */ public static int computeSInt32Size(int fieldNumber, int value) { - return computeTagSize(fieldNumber) + - computeRawVarint32Size(encodeZigZag32(value)); + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); } /** @@ -479,8 +587,7 @@ public final class CodedOutputStream { * {@code sint64} field, including tag. */ public static int computeSInt64Size(int fieldNumber, long value) { - return computeTagSize(fieldNumber) + - computeRawVarint64Size(encodeZigZag64(value)); + return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); } /** @@ -507,6 +614,174 @@ public final class CodedOutputStream { computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); } + // ----------------------------------------------------------------- + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSizeNoTag(double value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSizeNoTag(float value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64SizeNoTag(long value) { + return computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64SizeNoTag(long value) { + return computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32SizeNoTag(int value) { + if (value >= 0) { + return computeRawVarint32Size(value); + } else { + // Must sign-extend. + return 10; + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field. + */ + public static int computeFixed64SizeNoTag(long value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field. + */ + public static int computeFixed32SizeNoTag(int value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field. + */ + public static int computeBoolSizeNoTag(boolean value) { + return 1; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field. + */ + public static int computeStringSizeNoTag(String value) { + try { + byte[] bytes = value.getBytes("UTF-8"); + return computeRawVarint32Size(bytes.length) + + bytes.length; + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field. + */ + public static int computeGroupSizeNoTag(Message value) { + return value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field represented by an {@code UnknownFieldSet}, including + * tag. + */ + public static int computeUnknownGroupSizeNoTag(UnknownFieldSet value) { + return value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode an embedded + * message field. + */ + public static int computeMessageSizeNoTag(Message value) { + int size = value.getSerializedSize(); + return computeRawVarint32Size(size) + size; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field. + */ + public static int computeBytesSizeNoTag(ByteString value) { + return computeRawVarint32Size(value.size()) + + value.size(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field. + */ + public static int computeUInt32SizeNoTag(int value) { + return computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an enum field. + * Caller is responsible for converting the enum value to its numeric value. + */ + public static int computeEnumSizeNoTag(int value) { + return computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field. + */ + public static int computeSFixed32SizeNoTag(int value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field. + */ + public static int computeSFixed64SizeNoTag(long value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field. + */ + public static int computeSInt32SizeNoTag(int value) { + return computeRawVarint32Size(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field. + */ + public static int computeSInt64SizeNoTag(long value) { + return computeRawVarint64Size(encodeZigZag64(value)); + } + /** * Compute the number of bytes that would be needed to encode a * field of arbitrary type, including tag, to the stream. @@ -521,28 +796,48 @@ public final class CodedOutputStream { public static int computeFieldSize( Descriptors.FieldDescriptor.Type type, int number, Object value) { + int tagSize = computeTagSize(number); + if (type == Descriptors.FieldDescriptor.Type.GROUP) { + tagSize *= 2; + } + return tagSize + computeFieldSizeNoTag(type, value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * field of arbitrary type, excluding tag, to the stream. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public static int computeFieldSizeNoTag( + Descriptors.FieldDescriptor.Type type, Object value) { switch (type) { - case DOUBLE : return computeDoubleSize (number, (Double )value); - case FLOAT : return computeFloatSize (number, (Float )value); - case INT64 : return computeInt64Size (number, (Long )value); - case UINT64 : return computeUInt64Size (number, (Long )value); - case INT32 : return computeInt32Size (number, (Integer )value); - case FIXED64 : return computeFixed64Size (number, (Long )value); - case FIXED32 : return computeFixed32Size (number, (Integer )value); - case BOOL : return computeBoolSize (number, (Boolean )value); - case STRING : return computeStringSize (number, (String )value); - case GROUP : return computeGroupSize (number, (Message )value); - case MESSAGE : return computeMessageSize (number, (Message )value); - case BYTES : return computeBytesSize (number, (ByteString)value); - case UINT32 : return computeUInt32Size (number, (Integer )value); - case SFIXED32: return computeSFixed32Size(number, (Integer )value); - case SFIXED64: return computeSFixed64Size(number, (Long )value); - case SINT32 : return computeSInt32Size (number, (Integer )value); - case SINT64 : return computeSInt64Size (number, (Long )value); + case DOUBLE : return computeDoubleSizeNoTag ((Double )value); + case FLOAT : return computeFloatSizeNoTag ((Float )value); + case INT64 : return computeInt64SizeNoTag ((Long )value); + case UINT64 : return computeUInt64SizeNoTag ((Long )value); + case INT32 : return computeInt32SizeNoTag ((Integer )value); + case FIXED64 : return computeFixed64SizeNoTag ((Long )value); + case FIXED32 : return computeFixed32SizeNoTag ((Integer )value); + case BOOL : return computeBoolSizeNoTag ((Boolean )value); + case STRING : return computeStringSizeNoTag ((String )value); + case GROUP : return computeGroupSizeNoTag ((Message )value); + case MESSAGE : return computeMessageSizeNoTag ((Message )value); + case BYTES : return computeBytesSizeNoTag ((ByteString)value); + case UINT32 : return computeUInt32SizeNoTag ((Integer )value); + case SFIXED32: return computeSFixed32SizeNoTag((Integer )value); + case SFIXED64: return computeSFixed64SizeNoTag((Long )value); + case SINT32 : return computeSInt32SizeNoTag ((Integer )value); + case SINT64 : return computeSInt64SizeNoTag ((Long )value); case ENUM: - return computeEnumSize(number, - ((Descriptors.EnumValueDescriptor)value).getNumber()); + return computeEnumSizeNoTag( + ((Descriptors.EnumValueDescriptor)value).getNumber()); } throw new RuntimeException( diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index ee4d4e3a..80266a59 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -857,6 +857,19 @@ public final class Descriptors { "Field numbers must be positive integers."); } + // Only repeated primitive fields may be packed. + if (proto.getOptions().getPacked()) { + if (proto.getLabel() != FieldDescriptorProto.Label.LABEL_REPEATED || + proto.getType() == FieldDescriptorProto.Type.TYPE_STRING || + proto.getType() == FieldDescriptorProto.Type.TYPE_GROUP || + proto.getType() == FieldDescriptorProto.Type.TYPE_MESSAGE || + proto.getType() == FieldDescriptorProto.Type.TYPE_BYTES) { + throw new DescriptorValidationException(this, + "[packed = true] can only be specified for repeated primitive " + + "fields."); + } + } + if (isExtension) { if (!proto.hasExtendee()) { throw new DescriptorValidationException(this, diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java index fefa23ec..99ae253c 100644 --- a/java/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -211,6 +211,10 @@ public final class DynamicMessage extends AbstractMessage { return new Builder(type); } + public Builder toBuilder() { + return newBuilderForType().mergeFrom(this); + } + /** Verifies that the field is a field of this message. */ private void verifyContainingType(FieldDescriptor field) { if (field.getContainingType() != type) { @@ -251,6 +255,7 @@ public final class DynamicMessage extends AbstractMessage { } fields.mergeFrom(other); + mergeUnknownFields(other.getUnknownFields()); return this; } diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java index 98a1c328..01da3faf 100644 --- a/java/src/main/java/com/google/protobuf/FieldSet.java +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -351,8 +351,7 @@ final class FieldSet { setField(field, entry.getValue()); } else { setField(field, - existingValue.newBuilderForType() - .mergeFrom(existingValue) + existingValue.toBuilder() .mergeFrom((Message)entry.getValue()) .build()); } @@ -384,8 +383,7 @@ final class FieldSet { setField(field, value); } else { setField(field, - existingValue.newBuilderForType() - .mergeFrom(existingValue) + existingValue.toBuilder() .mergeFrom((Message)value) .build()); } @@ -463,60 +461,83 @@ final class FieldSet { } if (field == null || - wireType != WireFormat.getWireFormatForFieldType(field.getType())) { + wireType != WireFormat.getWireFormatForField(field)) { // Unknown field or wrong wire type. Skip. return unknownFields.mergeFieldFrom(tag, input); } else { - Object value; - switch (field.getType()) { - case GROUP: { - Message.Builder subBuilder; - if (defaultInstance != null) { - subBuilder = defaultInstance.newBuilderForType(); - } else { - subBuilder = builder.newBuilderForField(field); + if (field.getOptions().getPacked()) { + int length = input.readRawVarint32(); + int limit = input.pushLimit(length); + if (field.getType() == FieldDescriptor.Type.ENUM) { + while (input.getBytesUntilLimit() > 0) { + int rawValue = input.readEnum(); + Object value = field.getEnumType().findValueByNumber(rawValue); + if (value == null) { + // If the number isn't recognized as a valid value for this + // enum, drop it (don't even add it to unknownFields). + return true; + } + builder.addRepeatedField(field, value); } - if (!field.isRepeated()) { - subBuilder.mergeFrom((Message) builder.getField(field)); + } else { + while (input.getBytesUntilLimit() > 0) { + Object value = input.readPrimitiveField(field.getType()); + builder.addRepeatedField(field, value); } - input.readGroup(field.getNumber(), subBuilder, extensionRegistry); - value = subBuilder.build(); - break; } - case MESSAGE: { - Message.Builder subBuilder; - if (defaultInstance != null) { - subBuilder = defaultInstance.newBuilderForType(); - } else { - subBuilder = builder.newBuilderForField(field); + input.popLimit(limit); + } else { + Object value; + switch (field.getType()) { + case GROUP: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readGroup(field.getNumber(), subBuilder, extensionRegistry); + value = subBuilder.build(); + break; } - if (!field.isRepeated()) { - subBuilder.mergeFrom((Message) builder.getField(field)); + case MESSAGE: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readMessage(subBuilder, extensionRegistry); + value = subBuilder.build(); + break; } - input.readMessage(subBuilder, extensionRegistry); - value = subBuilder.build(); - break; - } - case ENUM: { - int rawValue = input.readEnum(); - value = field.getEnumType().findValueByNumber(rawValue); - // If the number isn't recognized as a valid value for this enum, - // drop it. - if (value == null) { - unknownFields.mergeVarintField(fieldNumber, rawValue); - return true; + case ENUM: { + int rawValue = input.readEnum(); + value = field.getEnumType().findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + unknownFields.mergeVarintField(fieldNumber, rawValue); + return true; + } + break; } - break; + default: + value = input.readPrimitiveField(field.getType()); + break; } - default: - value = input.readPrimitiveField(field.getType()); - break; - } - if (field.isRepeated()) { - builder.addRepeatedField(field, value); - } else { - builder.setField(field, value); + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } } } @@ -636,8 +657,24 @@ final class FieldSet { output.writeMessageSetExtension(field.getNumber(), (Message)value); } else { if (field.isRepeated()) { - for (Object element : (List)value) { - output.writeField(field.getType(), field.getNumber(), element); + List valueList = (List)value; + if (field.getOptions().getPacked()) { + output.writeTag(field.getNumber(), + WireFormat.WIRETYPE_LENGTH_DELIMITED); + // Compute the total data size so the length can be written. + int dataSize = 0; + for (Object element : valueList) { + dataSize += output.computeFieldSizeNoTag(field.getType(), element); + } + output.writeRawVarint32(dataSize); + // Write the data itself, without any tags. + for (Object element : valueList) { + output.writeFieldNoTag(field.getType(), element); + } + } else { + for (Object element : valueList) { + output.writeField(field.getType(), field.getNumber(), element); + } } } else { output.writeField(field.getType(), field.getNumber(), value); @@ -658,12 +695,23 @@ final class FieldSet { if (field.isExtension() && field.getContainingType().getOptions().getMessageSetWireFormat()) { size += CodedOutputStream.computeMessageSetExtensionSize( - field.getNumber(), (Message)value); + field.getNumber(), (Message) value); } else { if (field.isRepeated()) { - for (Object element : (List)value) { - size += CodedOutputStream.computeFieldSize( - field.getType(), field.getNumber(), element); + if (field.getOptions().getPacked()) { + int dataSize = 0; + for (Object element : (List)value) { + dataSize += CodedOutputStream.computeFieldSizeNoTag( + field.getType(), element); + } + size += dataSize + + CodedOutputStream.computeTagSize(field.getNumber()) + + CodedOutputStream.computeRawVarint32Size(dataSize); + } else { + for (Object element : (List)value) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } } } else { size += CodedOutputStream.computeFieldSize( diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index b1be8b14..77c88c30 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -31,8 +31,8 @@ package com.google.protobuf; import com.google.protobuf.Descriptors.Descriptor; -import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; import java.io.IOException; import java.lang.reflect.Method; @@ -75,7 +75,7 @@ public abstract class GeneratedMessage extends AbstractMessage { Descriptor descriptor = internalGetFieldAccessorTable().descriptor; for (FieldDescriptor field : descriptor.getFields()) { if (field.isRepeated()) { - List value = (List)getField(field); + List value = (List) getField(field); if (!value.isEmpty()) { result.put(field, value); } @@ -87,7 +87,7 @@ public abstract class GeneratedMessage extends AbstractMessage { } return result; } - + public boolean isInitialized() { for (FieldDescriptor field : getDescriptorForType().getFields()) { // Check that all required fields are present. @@ -99,7 +99,9 @@ public abstract class GeneratedMessage extends AbstractMessage { // Check that embedded messages are initialized. if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { if (field.isRepeated()) { - for (Message element : (List<Message>) getField(field)) { + @SuppressWarnings("unchecked") + List<Message> messageList = (List<Message>) getField(field); + for (Message element : messageList) { if (!element.isInitialized()) { return false; } @@ -189,7 +191,7 @@ public abstract class GeneratedMessage extends AbstractMessage { setField(field, entry.getValue()); } } - return (BuilderType)this; + return (BuilderType) this; } public Descriptor getDescriptorForType() { @@ -214,7 +216,7 @@ public abstract class GeneratedMessage extends AbstractMessage { // The underlying list object is still modifiable at this point. // Make sure not to expose the modifiable list to the caller. return Collections.unmodifiableList( - (List)internalGetResult().getField(field)); + (List) internalGetResult().getField(field)); } else { return internalGetResult().getField(field); } @@ -223,12 +225,12 @@ public abstract class GeneratedMessage extends AbstractMessage { public BuilderType setField(Descriptors.FieldDescriptor field, Object value) { internalGetFieldAccessorTable().getField(field).set(this, value); - return (BuilderType)this; + return (BuilderType) this; } public BuilderType clearField(Descriptors.FieldDescriptor field) { internalGetFieldAccessorTable().getField(field).clear(this); - return (BuilderType)this; + return (BuilderType) this; } public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { @@ -244,13 +246,13 @@ public abstract class GeneratedMessage extends AbstractMessage { int index, Object value) { internalGetFieldAccessorTable().getField(field) .setRepeated(this, index, value); - return (BuilderType)this; + return (BuilderType) this; } public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, Object value) { internalGetFieldAccessorTable().getField(field).addRepeated(this, value); - return (BuilderType)this; + return (BuilderType) this; } public final UnknownFieldSet getUnknownFields() { @@ -259,7 +261,7 @@ public abstract class GeneratedMessage extends AbstractMessage { public final BuilderType setUnknownFields(UnknownFieldSet unknownFields) { internalGetResult().unknownFields = unknownFields; - return (BuilderType)this; + return (BuilderType) this; } public final BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { @@ -268,7 +270,7 @@ public abstract class GeneratedMessage extends AbstractMessage { UnknownFieldSet.newBuilder(result.unknownFields) .mergeFrom(unknownFields) .build(); - return (BuilderType)this; + return (BuilderType) this; } public boolean isInitialized() { @@ -287,7 +289,18 @@ public abstract class GeneratedMessage extends AbstractMessage { return unknownFields.mergeFieldFrom(tag, input); } + /** + * Adds the {@code values} to the {@code list}. + * + * @throws NullPointerException if any of the elements of {@code values} is + * null. + */ protected <T> void addAll(Iterable<T> values, Collection<? super T> list) { + for (T value : values) { + if (value == null) { + throw new NullPointerException(); + } + } if (values instanceof Collection) { @SuppressWarnings("unsafe") Collection<T> collection = (Collection<T>) values; @@ -378,9 +391,9 @@ public abstract class GeneratedMessage extends AbstractMessage { verifyExtensionContainingType(extension); Object value = extensions.getField(extension.getDescriptor()); if (value == null) { - return (Type)extension.getMessageDefaultInstance(); + return (Type) extension.getMessageDefaultInstance(); } else { - return (Type)extension.fromReflectionType(value); + return (Type) extension.fromReflectionType(value); } } @@ -389,7 +402,7 @@ public abstract class GeneratedMessage extends AbstractMessage { public final <Type> Type getExtension( GeneratedExtension<MessageType, List<Type>> extension, int index) { verifyExtensionContainingType(extension); - return (Type)extension.singularFromReflectionType( + return (Type) extension.singularFromReflectionType( extensions.getRepeatedField(extension.getDescriptor(), index)); } @@ -397,7 +410,7 @@ public abstract class GeneratedMessage extends AbstractMessage { protected boolean extensionsAreInitialized() { return extensions.isInitialized(); } - + public boolean isInitialized() { return super.isInitialized() && extensionsAreInitialized(); } @@ -580,7 +593,7 @@ public abstract class GeneratedMessage extends AbstractMessage { message.verifyExtensionContainingType(extension); message.extensions.setField(extension.getDescriptor(), extension.toReflectionType(value)); - return (BuilderType)this; + return (BuilderType) this; } /** Set the value of one element of a repeated extension. */ @@ -592,7 +605,7 @@ public abstract class GeneratedMessage extends AbstractMessage { message.extensions.setRepeatedField( extension.getDescriptor(), index, extension.singularToReflectionType(value)); - return (BuilderType)this; + return (BuilderType) this; } /** Append a value to a repeated extension. */ @@ -602,7 +615,7 @@ public abstract class GeneratedMessage extends AbstractMessage { message.verifyExtensionContainingType(extension); message.extensions.addRepeatedField( extension.getDescriptor(), extension.singularToReflectionType(value)); - return (BuilderType)this; + return (BuilderType) this; } /** Clear an extension. */ @@ -611,7 +624,7 @@ public abstract class GeneratedMessage extends AbstractMessage { ExtendableMessage<MessageType> message = internalGetResult(); message.verifyExtensionContainingType(extension); message.extensions.clearField(extension.getDescriptor()); - return (BuilderType)this; + return (BuilderType) this; } /** @@ -639,7 +652,7 @@ public abstract class GeneratedMessage extends AbstractMessage { ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.setField(field, value); - return (BuilderType)this; + return (BuilderType) this; } else { return super.setField(field, value); } @@ -650,7 +663,7 @@ public abstract class GeneratedMessage extends AbstractMessage { ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.clearField(field); - return (BuilderType)this; + return (BuilderType) this; } else { return super.clearField(field); } @@ -662,7 +675,7 @@ public abstract class GeneratedMessage extends AbstractMessage { ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.setRepeatedField(field, index, value); - return (BuilderType)this; + return (BuilderType) this; } else { return super.setRepeatedField(field, index, value); } @@ -674,11 +687,15 @@ public abstract class GeneratedMessage extends AbstractMessage { ExtendableMessage<MessageType> message = internalGetResult(); message.verifyContainingType(field); message.extensions.addRepeatedField(field, value); - return (BuilderType)this; + return (BuilderType) this; } else { return super.addRepeatedField(field, value); } } + + protected final void mergeExtensionFields(ExtendableMessage other) { + internalGetResult().extensions.mergeFrom(other.extensions); + } } // ----------------------------------------------------------------- @@ -750,8 +767,8 @@ public abstract class GeneratedMessage extends AbstractMessage { enumValueOf = null; enumGetValueDescriptor = null; messageDefaultInstance = - (Message)invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), - null); + (Message) invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), + null); break; case ENUM: enumValueOf = getMethodOrDie(type, "valueOf", @@ -797,7 +814,7 @@ public abstract class GeneratedMessage extends AbstractMessage { descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. List result = new ArrayList(); - for (Object element : (List)value) { + for (Object element : (List) value) { result.add(singularFromReflectionType(element)); } return result; @@ -826,10 +843,10 @@ public abstract class GeneratedMessage extends AbstractMessage { // This should not happen in normal use. But, to be nice, we'll // copy the message to whatever type the caller was expecting. return messageDefaultInstance.newBuilderForType() - .mergeFrom((Message)value).build(); + .mergeFrom((Message) value).build(); } case ENUM: - return invokeOrDie(enumValueOf, null, (EnumValueDescriptor)value); + return invokeOrDie(enumValueOf, null, (EnumValueDescriptor) value); default: return value; } @@ -847,7 +864,7 @@ public abstract class GeneratedMessage extends AbstractMessage { if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { // Must convert the whole list. List result = new ArrayList(); - for (Object element : (List)value) { + for (Object element : (List) value) { result.add(singularToReflectionType(element)); } return result; @@ -900,9 +917,9 @@ public abstract class GeneratedMessage extends AbstractMessage { } catch (java.lang.reflect.InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { - throw (RuntimeException)cause; + throw (RuntimeException) cause; } else if (cause instanceof Error) { - throw (Error)cause; + throw (Error) cause; } else { throw new RuntimeException( "Unexpected exception thrown by generated accessor method.", cause); @@ -915,6 +932,7 @@ public abstract class GeneratedMessage extends AbstractMessage { * with access to the fields of a message object using Java reflection. */ public static final class FieldAccessorTable { + /** * Construct a FieldAccessorTable for a particular message class. Only * one FieldAccessorTable should ever be constructed per class. @@ -1039,7 +1057,7 @@ public abstract class GeneratedMessage extends AbstractMessage { "addRepeatedField() called on a singular field."); } public boolean has(GeneratedMessage message) { - return (Boolean)invokeOrDie(hasMethod, message); + return (Boolean) invokeOrDie(hasMethod, message); } public int getRepeatedCount(GeneratedMessage message) { throw new UnsupportedOperationException( @@ -1092,7 +1110,7 @@ public abstract class GeneratedMessage extends AbstractMessage { // 2) Insures that the caller cannot modify the list later on and // have the modifications be reflected in the message. clear(builder); - for (Object element : (List)value) { + for (Object element : (List) value) { addRepeated(builder, element); } } @@ -1111,7 +1129,7 @@ public abstract class GeneratedMessage extends AbstractMessage { "hasField() called on a singular field."); } public int getRepeatedCount(GeneratedMessage message) { - return (Integer)invokeOrDie(getCountMethod, message); + return (Integer) invokeOrDie(getCountMethod, message); } public void clear(GeneratedMessage.Builder builder) { invokeOrDie(clearMethod, builder); @@ -1169,7 +1187,7 @@ public abstract class GeneratedMessage extends AbstractMessage { @SuppressWarnings("unchecked") public Object get(GeneratedMessage message) { List newList = new ArrayList(); - for (Object element : (List)super.get(message)) { + for (Object element : (List) super.get(message)) { newList.add(invokeOrDie(getValueDescriptorMethod, element)); } return Collections.unmodifiableList(newList); @@ -1210,8 +1228,8 @@ public abstract class GeneratedMessage extends AbstractMessage { // is an alternative implementation of the same type -- e.g. a // DynamicMessage -- we should accept it. In this case we can make // a copy of the message. - return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) - .mergeFrom((Message)value).build(); + return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message) value).build(); } } @@ -1219,7 +1237,7 @@ public abstract class GeneratedMessage extends AbstractMessage { super.set(builder, coerceType(value)); } public Message.Builder newBuilder() { - return (Message.Builder)invokeOrDie(newBuilderMethod, null); + return (Message.Builder) invokeOrDie(newBuilderMethod, null); } } @@ -1244,8 +1262,8 @@ public abstract class GeneratedMessage extends AbstractMessage { // is an alternative implementation of the same type -- e.g. a // DynamicMessage -- we should accept it. In this case we can make // a copy of the message. - return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) - .mergeFrom((Message)value).build(); + return ((Message.Builder) invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message) value).build(); } } @@ -1257,7 +1275,7 @@ public abstract class GeneratedMessage extends AbstractMessage { super.addRepeated(builder, coerceType(value)); } public Message.Builder newBuilder() { - return (Message.Builder)invokeOrDie(newBuilderMethod, null); + return (Message.Builder) invokeOrDie(newBuilderMethod, null); } } } diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index 9635387a..2f8700db 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -195,6 +195,12 @@ public interface Message { Builder newBuilderForType(); /** + * Constructs a builder initialized with the current message. Use this to + * derive a new message from the current one. + */ + Builder toBuilder(); + + /** * Abstract interface implemented by Protocol Message builders. */ public static interface Builder extends Cloneable { diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java index 3a1b1d4e..3dcf68c8 100644 --- a/java/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -397,13 +397,15 @@ public final class TextFormat { private int previousLine = 0; private int previousColumn = 0; + // We use possesive quantifiers (*+ and ++) because otherwise the Java + // regex matcher has stack overflows on large inputs. private static Pattern WHITESPACE = - Pattern.compile("(\\s|(#.*$))+", Pattern.MULTILINE); + Pattern.compile("(\\s|(#.*$))++", Pattern.MULTILINE); private static Pattern TOKEN = Pattern.compile( - "[a-zA-Z_][0-9a-zA-Z_+-]*|" + // an identifier - "[0-9+-][0-9a-zA-Z_.+-]*|" + // a number - "\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|" + // a double-quoted string - "\'([^\"\n\\\\]|\\\\.)*(\'|\\\\?$)", // a single-quoted string + "[a-zA-Z_][0-9a-zA-Z_+-]*+|" + // an identifier + "[0-9+-][0-9a-zA-Z_.+-]*+|" + // a number + "\"([^\"\n\\\\]|\\\\.)*+(\"|\\\\?$)|" + // a double-quoted string + "\'([^\"\n\\\\]|\\\\.)*+(\'|\\\\?$)", // a single-quoted string Pattern.MULTILINE); private static Pattern DOUBLE_INFINITY = Pattern.compile( diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index ff042c05..2faf2448 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -96,6 +96,16 @@ public final class WireFormat { "There is no way to get here, but the compiler thinks otherwise."); } + /** Given a field descriptor, returns the wire type. This differs from + * getWireFormatForFieldType for packed repeated fields. */ + static int getWireFormatForField(Descriptors.FieldDescriptor descriptor) { + if (descriptor.getOptions().getPacked()) { + return WIRETYPE_LENGTH_DELIMITED; + } else { + return getWireFormatForFieldType(descriptor.getType()); + } + } + // Field numbers for feilds in MessageSet wire format. static final int MESSAGE_SET_ITEM = 1; static final int MESSAGE_SET_TYPE_ID = 2; |