diff options
Diffstat (limited to 'java/core/src/main/java/com/google/protobuf/CodedOutputStream.java')
-rw-r--r-- | java/core/src/main/java/com/google/protobuf/CodedOutputStream.java | 1206 |
1 files changed, 611 insertions, 595 deletions
diff --git a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java index d8ebad21..b92394b8 100644 --- a/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -49,13 +49,17 @@ import java.util.logging.Logger; * you are writing some other format of your own design, use the latter. * * <p>This class is totally unsynchronized. - * - * @author kneton@google.com Kenton Varda */ public final class CodedOutputStream { - private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName()); + private static final int LITTLE_ENDIAN_64_SIZE = 8; + + /** + * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. + */ + @Deprecated public static final int LITTLE_ENDIAN_32_SIZE = 4; + // TODO(dweis): Consider migrating to a ByteBuffer. private final byte[] buffer; private final int limit; @@ -77,12 +81,13 @@ public final class CodedOutputStream { * CodedOutputStream. */ static int computePreferredBufferSize(int dataLength) { - if (dataLength > DEFAULT_BUFFER_SIZE) return DEFAULT_BUFFER_SIZE; + if (dataLength > DEFAULT_BUFFER_SIZE) { + return DEFAULT_BUFFER_SIZE; + } return dataLength; } - private CodedOutputStream(final byte[] buffer, final int offset, - final int length) { + private CodedOutputStream(final byte[] buffer, final int offset, final int length) { output = null; this.buffer = buffer; position = offset; @@ -108,8 +113,7 @@ public final class CodedOutputStream { * Create a new {@code CodedOutputStream} wrapping the given * {@code OutputStream} with a given buffer size. */ - public static CodedOutputStream newInstance(final OutputStream output, - final int bufferSize) { + public static CodedOutputStream newInstance(final OutputStream output, final int bufferSize) { return new CodedOutputStream(output, new byte[bufferSize]); } @@ -131,9 +135,8 @@ public final class CodedOutputStream { * array is faster than writing to an {@code OutputStream}. See also * {@link ByteString#newCodedBuilder}. */ - public static CodedOutputStream newInstance(final byte[] flatArray, - final int offset, - final int length) { + public static CodedOutputStream newInstance( + final byte[] flatArray, final int offset, final int length) { return new CodedOutputStream(flatArray, offset, length); } @@ -147,13 +150,13 @@ public final class CodedOutputStream { /** * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer. */ - public static CodedOutputStream newInstance(ByteBuffer byteBuffer, - int bufferSize) { + public static CodedOutputStream newInstance(ByteBuffer byteBuffer, int bufferSize) { return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize); } private static class ByteBufferOutputStream extends OutputStream { private final ByteBuffer byteBuffer; + public ByteBufferOutputStream(ByteBuffer byteBuffer) { this.byteBuffer = byteBuffer; } @@ -171,106 +174,120 @@ public final class CodedOutputStream { // ----------------------------------------------------------------- - /** Write a {@code double} field, including tag, to the stream. */ - public void writeDouble(final int fieldNumber, final double value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeDoubleNoTag(value); + /** Encode and write a tag. */ + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); } - /** Write a {@code float} field, including tag, to the stream. */ - public void writeFloat(final int fieldNumber, final float value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeFloatNoTag(value); + /** Write an {@code int32} field, including tag, to the stream. */ + public void writeInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); } - /** Write a {@code uint64} field, including tag, to the stream. */ - public void writeUInt64(final int fieldNumber, final long value) - throws IOException { + /** Write a {@code uint32} field, including tag, to the stream. */ + public void writeUInt32(final int fieldNumber, final int value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeUInt64NoTag(value); + writeUInt32NoTag(value); + } + + /** Write a {@code sint32} field, including tag, to the stream. */ + public void writeSInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeSInt32NoTag(value); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public void writeSFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeSFixed32NoTag(value); } /** Write an {@code int64} field, including tag, to the stream. */ - public void writeInt64(final int fieldNumber, final long value) - throws IOException { + public void writeInt64(final int fieldNumber, final long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeInt64NoTag(value); } - /** Write an {@code int32} field, including tag, to the stream. */ - public void writeInt32(final int fieldNumber, final int value) - throws IOException { + /** Write a {@code uint64} field, including tag, to the stream. */ + public void writeUInt64(final int fieldNumber, final long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeInt32NoTag(value); + writeUInt64NoTag(value); + } + + /** Write an {@code sint64} field, including tag, to the stream. */ + public void writeSInt64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeSInt64NoTag(value); } /** Write a {@code fixed64} field, including tag, to the stream. */ - public void writeFixed64(final int fieldNumber, final long value) - throws IOException { + public void writeFixed64(final int fieldNumber, final long value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); writeFixed64NoTag(value); } - /** Write a {@code fixed32} field, including tag, to the stream. */ - public void writeFixed32(final int fieldNumber, final int value) - throws IOException { + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public void writeSFixed64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeSFixed64NoTag(value); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public void writeFloat(final int fieldNumber, final float value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeFixed32NoTag(value); + writeFloatNoTag(value); + } + + /** Write a {@code double} field, including tag, to the stream. */ + public void writeDouble(final int fieldNumber, final double value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeDoubleNoTag(value); } /** Write a {@code bool} field, including tag, to the stream. */ - public void writeBool(final int fieldNumber, final boolean value) - throws IOException { + public void writeBool(final int fieldNumber, final boolean value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); writeBoolNoTag(value); } - /** Write a {@code string} field, including tag, to the stream. */ - public void writeString(final int fieldNumber, final String value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeStringNoTag(value); - } - - /** Write a {@code group} field, including tag, to the stream. */ - public void writeGroup(final int fieldNumber, final MessageLite value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); - writeGroupNoTag(value); - writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + /** + * Write an enum field, including tag, to the stream. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public void writeEnum(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeEnumNoTag(value); } - - /** Write an embedded message field, including tag, to the stream. */ - public void writeMessage(final int fieldNumber, final MessageLite value) - throws IOException { + /** Write a {@code string} field, including tag, to the stream. */ + public void writeString(final int fieldNumber, final String value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); - writeMessageNoTag(value); + writeStringNoTag(value); } - /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeBytes(final int fieldNumber, final ByteString value) - throws IOException { + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeBytesNoTag(value); } /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, final byte[] value) - throws IOException { + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeByteArrayNoTag(value); } /** Write a {@code bytes} field, including tag, to the stream. */ - public void writeByteArray(final int fieldNumber, - final byte[] value, - final int offset, - final int length) - throws IOException { + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeByteArrayNoTag(value, offset, length); } @@ -285,64 +302,100 @@ public final class CodedOutputStream { * of a ByteBuffer, you can call * {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}. */ - public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) - throws IOException { + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) throws IOException { writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); writeByteBufferNoTag(value); } - /** Write a {@code uint32} field, including tag, to the stream. */ - public void writeUInt32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeUInt32NoTag(value); + /** Write a single byte. */ + public void writeRawByte(final byte value) throws IOException { + if (position == limit) { + refreshBuffer(); + } + + buffer[position++] = value; + ++totalBytesWritten; } - /** - * Write an enum field, including tag, to the stream. Caller is responsible - * for converting the enum value to its numeric value. - */ - public void writeEnum(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeEnumNoTag(value); + /** Write a single byte, represented by an integer value. */ + public void writeRawByte(final int value) throws IOException { + writeRawByte((byte) value); } - /** Write an {@code sfixed32} field, including tag, to the stream. */ - public void writeSFixed32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); - writeSFixed32NoTag(value); + /** Write an array of bytes. */ + public void writeRawBytes(final byte[] value) throws IOException { + writeRawBytes(value, 0, value.length); } - /** Write an {@code sfixed64} field, including tag, to the stream. */ - public void writeSFixed64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); - writeSFixed64NoTag(value); + /** Write part of an array of bytes. */ + public void writeRawBytes(final byte[] value, int offset, int length) throws IOException { + if (limit - position >= length) { + // We have room in the current buffer. + System.arraycopy(value, offset, buffer, position, length); + position += length; + totalBytesWritten += length; + } else { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + final int bytesWritten = limit - position; + System.arraycopy(value, offset, buffer, position, bytesWritten); + offset += bytesWritten; + length -= bytesWritten; + position = limit; + totalBytesWritten += bytesWritten; + refreshBuffer(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + if (length <= limit) { + // Fits in new buffer. + System.arraycopy(value, offset, buffer, 0, length); + position = length; + } else { + // Write is very big. Let's do it all at once. + output.write(value, offset, length); + } + totalBytesWritten += length; + } } - /** Write an {@code sint32} field, including tag, to the stream. */ - public void writeSInt32(final int fieldNumber, final int value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeSInt32NoTag(value); + /** Write a byte string. */ + public void writeRawBytes(final ByteString value) throws IOException { + writeRawBytes(value, 0, value.size()); } - /** Write an {@code sint64} field, including tag, to the stream. */ - public void writeSInt64(final int fieldNumber, final long value) - throws IOException { - writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); - writeSInt64NoTag(value); + /** + * Write a ByteBuffer. This method will write all content of the ByteBuffer + * regardless of the current position and limit (i.e., the number of bytes + * to be written is value.capacity(), not value.remaining()). Furthermore, + * this method doesn't alter the state of the passed-in ByteBuffer. Its + * position, limit, mark, etc. will remain unchanged. If you only want to + * write the remaining bytes of a ByteBuffer, you can call + * {@code writeRawBytes(byteBuffer.slice())}. + */ + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + writeRawBytes(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + writeRawBytesInternal(duplicated); + } + } + + /** Write an embedded message field, including tag, to the stream. */ + public void writeMessage(final int fieldNumber, final MessageLite value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); } /** * Write a MessageSet extension field to the stream. For historical reasons, * the wire format differs from normal fields. */ - public void writeMessageSetExtension(final int fieldNumber, - final MessageLite value) - throws IOException { + public void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); @@ -353,9 +406,8 @@ public final class CodedOutputStream { * Write an unparsed MessageSet extension field to the stream. For * historical reasons, the wire format differs from normal fields. */ - public void writeRawMessageSetExtension(final int fieldNumber, - final ByteString value) - throws IOException { + public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); @@ -364,19 +416,34 @@ public final class CodedOutputStream { // ----------------------------------------------------------------- - /** Write a {@code double} field to the stream. */ - public void writeDoubleNoTag(final double value) throws IOException { - writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + /** Write an {@code int32} field to the stream. */ + public void writeInt32NoTag(final int value) throws IOException { + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } } - /** Write a {@code float} field to the stream. */ - public void writeFloatNoTag(final float value) throws IOException { - writeRawLittleEndian32(Float.floatToRawIntBits(value)); + /** Write a {@code uint32} field to the stream. */ + public void writeUInt32NoTag(final int value) throws IOException { + writeRawVarint32(value); } - /** Write a {@code uint64} field to the stream. */ - public void writeUInt64NoTag(final long value) throws IOException { - writeRawVarint64(value); + /** Write a {@code sint32} field to the stream. */ + public void writeSInt32NoTag(final int value) throws IOException { + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write a {@code fixed32} field to the stream. */ + public void writeFixed32NoTag(final int value) throws IOException { + writeRawLittleEndian32(value); + } + + /** Write a {@code sfixed32} field to the stream. */ + public void writeSFixed32NoTag(final int value) throws IOException { + writeRawLittleEndian32(value); } /** Write an {@code int64} field to the stream. */ @@ -384,14 +451,14 @@ public final class CodedOutputStream { writeRawVarint64(value); } - /** Write an {@code int32} field to the stream. */ - public void writeInt32NoTag(final int value) throws IOException { - if (value >= 0) { - writeRawVarint32(value); - } else { - // Must sign-extend. - writeRawVarint64(value); - } + /** Write a {@code uint64} field to the stream. */ + public void writeUInt64NoTag(final long value) throws IOException { + writeRawVarint64(value); + } + + /** Write a {@code sint64} field to the stream. */ + public void writeSInt64NoTag(final long value) throws IOException { + writeRawVarint64(encodeZigZag64(value)); } /** Write a {@code fixed64} field to the stream. */ @@ -399,9 +466,19 @@ public final class CodedOutputStream { writeRawLittleEndian64(value); } - /** Write a {@code fixed32} field to the stream. */ - public void writeFixed32NoTag(final int value) throws IOException { - writeRawLittleEndian32(value); + /** Write a {@code sfixed64} field to the stream. */ + public void writeSFixed64NoTag(final long value) throws IOException { + writeRawLittleEndian64(value); + } + + /** Write a {@code float} field to the stream. */ + public void writeFloatNoTag(final float value) throws IOException { + writeRawLittleEndian32(Float.floatToRawIntBits(value)); + } + + /** Write a {@code double} field to the stream. */ + public void writeDoubleNoTag(final double value) throws IOException { + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); } /** Write a {@code bool} field to the stream. */ @@ -409,6 +486,14 @@ public final class CodedOutputStream { writeRawByte(value ? 1 : 0); } + /** + * Write an enum field to the stream. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public void writeEnumNoTag(final int value) throws IOException { + writeInt32NoTag(value); + } + /** Write a {@code string} field to the stream. */ // TODO(dweis): Document behavior on ill-formed UTF-16 input. public void writeStringNoTag(final String value) throws IOException { @@ -421,89 +506,6 @@ public final class CodedOutputStream { } } - /** 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); - } - - - /** Write an embedded message field to the stream. */ - public void writeMessageNoTag(final MessageLite value) throws IOException { - writeRawVarint32(value.getSerializedSize()); - value.writeTo(this); - } - - /** Write a {@code bytes} field to the stream. */ public void writeBytesNoTag(final ByteString value) throws IOException { writeRawVarint32(value.size()); @@ -516,86 +518,53 @@ public final class CodedOutputStream { writeRawBytes(value); } - /** Write a {@code bytes} field to the stream. */ - public void writeByteArrayNoTag(final byte[] value, - final int offset, - final int length) throws IOException { - writeRawVarint32(length); - writeRawBytes(value, offset, length); + /** Write an embedded message field to the stream. */ + public void writeMessageNoTag(final MessageLite value) throws IOException { + writeRawVarint32(value.getSerializedSize()); + value.writeTo(this); } + // ================================================================= + // ================================================================= + /** - * Write a {@code bytes} field to the stream. This method will write all - * content of the ByteBuffer regardless of the current position and limit - * (i.e., the number of bytes to be written is value.capacity(), not - * value.remaining()). Furthermore, this method doesn't alter the state of - * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain - * unchanged. If you only want to write the remaining bytes of a ByteBuffer, - * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}. + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. */ - public void writeByteBufferNoTag(final ByteBuffer value) throws IOException { - writeRawVarint32(value.capacity()); - writeRawBytes(value); - } - - /** Write a {@code uint32} field to the stream. */ - public void writeUInt32NoTag(final int value) throws IOException { - writeRawVarint32(value); + public static int computeInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); } /** - * Write an enum field to the stream. Caller is responsible - * for converting the enum value to its numeric value. + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field, including tag. */ - public void writeEnumNoTag(final int value) throws IOException { - writeInt32NoTag(value); - } - - /** Write an {@code sfixed32} field to the stream. */ - public void writeSFixed32NoTag(final int value) throws IOException { - writeRawLittleEndian32(value); - } - - /** Write an {@code sfixed64} field to the stream. */ - public void writeSFixed64NoTag(final long value) throws IOException { - writeRawLittleEndian64(value); - } - - /** Write an {@code sint32} field to the stream. */ - public void writeSInt32NoTag(final int value) throws IOException { - writeRawVarint32(encodeZigZag32(value)); - } - - /** Write an {@code sint64} field to the stream. */ - public void writeSInt64NoTag(final long value) throws IOException { - writeRawVarint64(encodeZigZag64(value)); + public static int computeUInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); } - // ================================================================= - /** - * Compute the number of bytes that would be needed to encode a - * {@code double} field, including tag. + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field, including tag. */ - public static int computeDoubleSize(final int fieldNumber, - final double value) { - return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); + public static int computeSInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a - * {@code float} field, including tag. + * {@code fixed32} field, including tag. */ - public static int computeFloatSize(final int fieldNumber, final float value) { - return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); + public static int computeFixed32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); } /** - * Compute the number of bytes that would be needed to encode a - * {@code uint64} field, including tag. + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field, including tag. */ - public static int computeUInt64Size(final int fieldNumber, final long value) { - return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); + public static int computeSFixed32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); } /** @@ -607,73 +576,83 @@ public final class CodedOutputStream { } /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); + } + + /** * Compute the number of bytes that would be needed to encode an - * {@code int32} field, including tag. + * {@code sint64} field, including tag. */ - public static int computeInt32Size(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); + public static int computeSInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a * {@code fixed64} field, including tag. */ - public static int computeFixed64Size(final int fieldNumber, - final long value) { + public static int computeFixed64Size(final int fieldNumber, final long value) { return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); } /** - * Compute the number of bytes that would be needed to encode a - * {@code fixed32} field, including tag. + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. */ - public static int computeFixed32Size(final int fieldNumber, - final int value) { - return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); + public static int computeSFixed64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a - * {@code bool} field, including tag. + * {@code float} field, including tag. */ - public static int computeBoolSize(final int fieldNumber, - final boolean value) { - return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); + public static int computeFloatSize(final int fieldNumber, final float value) { + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a - * {@code string} field, including tag. + * {@code double} field, including tag. */ - public static int computeStringSize(final int fieldNumber, - final String value) { - return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); + public static int computeDoubleSize(final int fieldNumber, final double value) { + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a - * {@code group} field, including tag. + * {@code bool} field, including tag. */ - public static int computeGroupSize(final int fieldNumber, - final MessageLite value) { - return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); + public static int computeBoolSize(final int fieldNumber, final boolean value) { + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode an - * embedded message field, including tag. + * enum field, including tag. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). */ - public static int computeMessageSize(final int fieldNumber, - final MessageLite value) { - return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); + public static int computeEnumSize(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field, including tag. + */ + public static int computeStringSize(final int fieldNumber, final String value) { + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); } /** * Compute the number of bytes that would be needed to encode a * {@code bytes} field, including tag. */ - public static int computeBytesSize(final int fieldNumber, - final ByteString value) { + public static int computeBytesSize(final int fieldNumber, final ByteString value) { return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); } @@ -681,8 +660,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bytes} field, including tag. */ - public static int computeByteArraySize(final int fieldNumber, - final byte[] value) { + public static int computeByteArraySize(final int fieldNumber, final byte[] value) { return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); } @@ -690,8 +668,7 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode a * {@code bytes} field, including tag. */ - public static int computeByteBufferSize(final int fieldNumber, - final ByteBuffer value) { + public static int computeByteBufferSize(final int fieldNumber, final ByteBuffer value) { return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value); } @@ -699,114 +676,111 @@ public final class CodedOutputStream { * Compute the number of bytes that would be needed to encode an * embedded message in lazy field, including tag. */ - public static int computeLazyFieldSize(final int fieldNumber, - final LazyFieldLite value) { + public static int computeLazyFieldSize(final int fieldNumber, final LazyFieldLite value) { return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value); } /** - * Compute the number of bytes that would be needed to encode a - * {@code uint32} field, including tag. + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. */ - public static int computeUInt32Size(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); + public static int computeMessageSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); } /** - * Compute the number of bytes that would be needed to encode an - * enum field, including tag. Caller is responsible for converting the - * enum value to its numeric value. + * Compute the number of bytes that would be needed to encode a + * MessageSet extension to the stream. For historical reasons, + * the wire format differs from normal fields. */ - public static int computeEnumSize(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); + public static int computeMessageSetExtensionSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); } /** * Compute the number of bytes that would be needed to encode an - * {@code sfixed32} field, including tag. + * unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. */ - public static int computeSFixed32Size(final int fieldNumber, - final int value) { - return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); + public static int computeRawMessageSetExtensionSize( + final int fieldNumber, final ByteString value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); } /** * Compute the number of bytes that would be needed to encode an - * {@code sfixed64} field, including tag. + * lazily parsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. */ - public static int computeSFixed64Size(final int fieldNumber, - final long value) { - return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); + public static int computeLazyFieldMessageSetExtensionSize( + final int fieldNumber, final LazyFieldLite value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); } - /** - * Compute the number of bytes that would be needed to encode an - * {@code sint32} field, including tag. - */ - public static int computeSInt32Size(final int fieldNumber, final int value) { - return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); + // ----------------------------------------------------------------- + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(final int fieldNumber) { + return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); } /** * Compute the number of bytes that would be needed to encode an - * {@code sint64} field, including tag. + * {@code int32} field, including tag. */ - public static int computeSInt64Size(final int fieldNumber, final long value) { - return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); + public static int computeInt32SizeNoTag(final 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 - * MessageSet extension to the stream. For historical reasons, - * the wire format differs from normal fields. + * {@code uint32} field. */ - public static int computeMessageSetExtensionSize( - final int fieldNumber, final MessageLite value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value); + public static int computeUInt32SizeNoTag(final int value) { + return computeRawVarint32Size(value); } /** * Compute the number of bytes that would be needed to encode an - * unparsed MessageSet extension field to the stream. For - * historical reasons, the wire format differs from normal fields. + * {@code sint32} field. */ - public static int computeRawMessageSetExtensionSize( - final int fieldNumber, final ByteString value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); + public static int computeSInt32SizeNoTag(final int value) { + return computeRawVarint32Size(encodeZigZag32(value)); } /** - * Compute the number of bytes that would be needed to encode an - * lazily parsed MessageSet extension field to the stream. For - * historical reasons, the wire format differs from normal fields. + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field. */ - public static int computeLazyFieldMessageSetExtensionSize( - final int fieldNumber, final LazyFieldLite value) { - return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + - computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + - computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); + public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return LITTLE_ENDIAN_32_SIZE; } - // ----------------------------------------------------------------- - /** - * Compute the number of bytes that would be needed to encode a - * {@code double} field, including tag. + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field. */ - public static int computeDoubleSizeNoTag(final double value) { - return LITTLE_ENDIAN_64_SIZE; + public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return LITTLE_ENDIAN_32_SIZE; } /** - * Compute the number of bytes that would be needed to encode a - * {@code float} field, including tag. + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. */ - public static int computeFloatSizeNoTag(final float value) { - return LITTLE_ENDIAN_32_SIZE; + public static int computeInt64SizeNoTag(final long value) { + return computeRawVarint64Size(value); } /** @@ -819,50 +793,62 @@ public final class CodedOutputStream { /** * Compute the number of bytes that would be needed to encode an - * {@code int64} field, including tag. + * {@code sint64} field. */ - public static int computeInt64SizeNoTag(final long value) { - return computeRawVarint64Size(value); + public static int computeSInt64SizeNoTag(final long value) { + return computeRawVarint64Size(encodeZigZag64(value)); } /** - * Compute the number of bytes that would be needed to encode an - * {@code int32} field, including tag. + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field. */ - public static int computeInt32SizeNoTag(final int value) { - if (value >= 0) { - return computeRawVarint32Size(value); - } else { - // Must sign-extend. - return 10; - } + public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { + return LITTLE_ENDIAN_64_SIZE; } /** - * Compute the number of bytes that would be needed to encode a - * {@code fixed64} field. + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field. */ - public static int computeFixed64SizeNoTag(final long value) { + public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { return LITTLE_ENDIAN_64_SIZE; } /** * Compute the number of bytes that would be needed to encode a - * {@code fixed32} field. + * {@code float} field, including tag. */ - public static int computeFixed32SizeNoTag(final int value) { + public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) { return LITTLE_ENDIAN_32_SIZE; } /** * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a * {@code bool} field. */ - public static int computeBoolSizeNoTag(final boolean value) { + public static int computeBoolSizeNoTag(@SuppressWarnings("unused") final boolean unused) { return 1; } /** + * Compute the number of bytes that would be needed to encode an enum field. + * The provided value is the numeric value used to represent the enum value on the wire + * (not the enum ordinal value). + */ + public static int computeEnumSizeNoTag(final int value) { + return computeInt32SizeNoTag(value); + } + + /** * Compute the number of bytes that would be needed to encode a * {@code string} field. */ @@ -880,23 +866,6 @@ public final class CodedOutputStream { } /** - * Compute the number of bytes that would be needed to encode a - * {@code group} field. - */ - public static int computeGroupSizeNoTag(final MessageLite value) { - return value.getSerializedSize(); - } - - /** - * Compute the number of bytes that would be needed to encode an embedded - * message field. - */ - public static int computeMessageSizeNoTag(final MessageLite value) { - final int size = value.getSerializedSize(); - return computeRawVarint32Size(size) + size; - } - - /** * Compute the number of bytes that would be needed to encode an embedded * message stored in lazy field. */ @@ -910,8 +879,7 @@ public final class CodedOutputStream { * {@code bytes} field. */ public static int computeBytesSizeNoTag(final ByteString value) { - return computeRawVarint32Size(value.size()) + - value.size(); + return computeRawVarint32Size(value.size()) + value.size(); } /** @@ -931,72 +899,47 @@ public final class CodedOutputStream { } /** - * Compute the number of bytes that would be needed to encode a - * {@code uint32} field. - */ - public static int computeUInt32SizeNoTag(final 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(final int value) { - return computeInt32SizeNoTag(value); - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code sfixed32} field. - */ - public static int computeSFixed32SizeNoTag(final int value) { - return LITTLE_ENDIAN_32_SIZE; - } - - /** - * Compute the number of bytes that would be needed to encode an - * {@code sfixed64} field. + * Compute the number of bytes that would be needed to encode an embedded + * message field. */ - public static int computeSFixed64SizeNoTag(final long value) { - return LITTLE_ENDIAN_64_SIZE; + public static int computeMessageSizeNoTag(final MessageLite value) { + final int size = value.getSerializedSize(); + return computeRawVarint32Size(size) + size; } /** - * Compute the number of bytes that would be needed to encode an - * {@code sint32} field. + * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 32-bit integer. + * @return An unsigned 32-bit integer, stored in a signed int because + * Java has no explicit unsigned support. */ - public static int computeSInt32SizeNoTag(final int value) { - return computeRawVarint32Size(encodeZigZag32(value)); + public static int encodeZigZag32(final int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); } /** - * Compute the number of bytes that would be needed to encode an - * {@code sint64} field. + * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 64-bit integer. + * @return An unsigned 64-bit integer, stored in a signed int because + * Java has no explicit unsigned support. */ - public static int computeSInt64SizeNoTag(final long value) { - return computeRawVarint64Size(encodeZigZag64(value)); + public static long encodeZigZag64(final long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); } // ================================================================= /** - * Internal helper that writes the current buffer to the output. The - * buffer position is reset to its initial value when this returns. - */ - private void refreshBuffer() throws IOException { - if (output == null) { - // We're writing to a single buffer. - throw new OutOfSpaceException(); - } - - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - output.write(buffer, 0, position); - position = 0; - } - - /** * Flushes the stream and forces any buffered bytes to be written. This * does not flush the underlying OutputStream. */ @@ -1015,8 +958,8 @@ public final class CodedOutputStream { return limit - position; } else { throw new UnsupportedOperationException( - "spaceLeft() can only be called on CodedOutputStreams that are " + - "writing to a flat array."); + "spaceLeft() can only be called on CodedOutputStreams that are " + + "writing to a flat array."); } } @@ -1029,8 +972,7 @@ public final class CodedOutputStream { */ public void checkNoSpaceLeft() { if (spaceLeft() != 0) { - throw new IllegalStateException( - "Did not write as much data as expected."); + throw new IllegalStateException("Did not write as much data as expected."); } } @@ -1063,53 +1005,96 @@ public final class CodedOutputStream { return totalBytesWritten; } - /** Write a single byte. */ - public void writeRawByte(final byte value) throws IOException { - if (position == limit) { - refreshBuffer(); - } - - buffer[position++] = value; - ++totalBytesWritten; - } + // ================================================================= - /** Write a single byte, represented by an integer value. */ - public void writeRawByte(final int value) throws IOException { - writeRawByte((byte) value); - } + /** + * Internal helper that writes the current buffer to the output. The + * buffer position is reset to its initial value when this returns. + */ + private void refreshBuffer() throws IOException { + if (output == null) { + // We're writing to a single buffer. + throw new OutOfSpaceException(); + } - /** Write a byte string. */ - public void writeRawBytes(final ByteString value) throws IOException { - writeRawBytes(value, 0, value.size()); + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + output.write(buffer, 0, position); + position = 0; } - /** Write an array of bytes. */ - public void writeRawBytes(final byte[] value) throws IOException { - writeRawBytes(value, 0, value.length); + /** 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 ByteBuffer. This method will write all content of the ByteBuffer - * regardless of the current position and limit (i.e., the number of bytes - * to be written is value.capacity(), not value.remaining()). Furthermore, - * this method doesn't alter the state of the passed-in ByteBuffer. Its - * position, limit, mark, etc. will remain unchanged. If you only want to - * write the remaining bytes of a ByteBuffer, you can call - * {@code writeRawBytes(byteBuffer.slice())}. + * 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. */ - public void writeRawBytes(final ByteBuffer value) throws IOException { - if (value.hasArray()) { - writeRawBytes(value.array(), value.arrayOffset(), value.capacity()); + 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 { - ByteBuffer duplicated = value.duplicate(); - duplicated.clear(); - writeRawBytesInternal(duplicated); + // 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 ByteBuffer that isn't backed by an array. */ - private void writeRawBytesInternal(final ByteBuffer value) - throws IOException { + private void writeRawBytesInternal(final ByteBuffer value) throws IOException { int length = value.remaining(); if (limit - position >= length) { // We have room in the current buffer. @@ -1143,43 +1128,29 @@ public final class CodedOutputStream { } } - /** Write part of an array of bytes. */ - public void writeRawBytes(final byte[] value, int offset, int length) - throws IOException { - if (limit - position >= length) { - // We have room in the current buffer. - System.arraycopy(value, offset, buffer, position, length); - position += length; - totalBytesWritten += length; - } else { - // Write extends past current buffer. Fill the rest of this buffer and - // flush. - final int bytesWritten = limit - position; - System.arraycopy(value, offset, buffer, position, bytesWritten); - offset += bytesWritten; - length -= bytesWritten; - position = limit; - totalBytesWritten += bytesWritten; - refreshBuffer(); + /** Write a {@code bytes} field to the stream. Visible for testing. */ + void writeByteArrayNoTag(final byte[] value, final int offset, final int length) + throws IOException { + writeRawVarint32(length); + writeRawBytes(value, offset, length); + } - // Now deal with the rest. - // Since we have an output stream, this is our buffer - // and buffer offset == 0 - if (length <= limit) { - // Fits in new buffer. - System.arraycopy(value, offset, buffer, 0, length); - position = length; - } else { - // Write is very big. Let's do it all at once. - output.write(value, offset, length); - } - totalBytesWritten += length; - } + /** + * Write a {@code bytes} field to the stream. This method will write all + * content of the ByteBuffer regardless of the current position and limit + * (i.e., the number of bytes to be written is value.capacity(), not + * value.remaining()). Furthermore, this method doesn't alter the state of + * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain + * unchanged. If you only want to write the remaining bytes of a ByteBuffer, + * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}. + */ + private void writeByteBufferNoTag(final ByteBuffer value) throws IOException { + writeRawVarint32(value.capacity()); + writeRawBytes(value); } /** Write part of a byte string. */ - public void writeRawBytes(final ByteString value, int offset, int length) - throws IOException { + private void writeRawBytes(final ByteString value, int offset, int length) throws IOException { if (limit - position >= length) { // We have room in the current buffer. value.copyTo(buffer, offset, position, length); @@ -1210,21 +1181,57 @@ public final class CodedOutputStream { } } - /** Encode and write a tag. */ - public void writeTag(final int fieldNumber, final int wireType) - throws IOException { - writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); + // ================================================================= + + /** + * Write a {@code group} field, including tag, to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public void writeGroup(final int fieldNumber, final MessageLite value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + writeGroupNoTag(value); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); } - /** Compute the number of bytes that would be needed to encode a tag. */ - public static int computeTagSize(final int fieldNumber) { - return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); + /** + * Write a {@code group} field to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public void writeGroupNoTag(final MessageLite value) throws IOException { + value.writeTo(this); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field, including tag. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public static int computeGroupSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field. + */ + @Deprecated + public static int computeGroupSizeNoTag(final MessageLite value) { + return value.getSerializedSize(); } /** * Encode and write a varint. {@code value} is treated as * unsigned, so it won't be sign-extended if negative. + * + * @deprecated use {@link #writeUInt32NoTag} instead. */ + @Deprecated public void writeRawVarint32(int value) throws IOException { while (true) { if ((value & ~0x7F) == 0) { @@ -1238,95 +1245,104 @@ public final class CodedOutputStream { } /** - * Compute the number of bytes that would be needed to encode a varint. - * {@code value} is treated as unsigned, so it won't be sign-extended if - * negative. + * Encode and write a varint. + * + * @deprecated use {@link #writeUInt64NoTag} instead. */ - public static int computeRawVarint32Size(final int value) { - if ((value & (~0 << 7)) == 0) return 1; - if ((value & (~0 << 14)) == 0) return 2; - if ((value & (~0 << 21)) == 0) return 3; - if ((value & (~0 << 28)) == 0) return 4; - return 5; - } - - /** Encode and write a varint. */ + @Deprecated public void writeRawVarint64(long value) throws IOException { while (true) { if ((value & ~0x7FL) == 0) { - writeRawByte((int)value); + writeRawByte((int) value); return; } else { - writeRawByte(((int)value & 0x7F) | 0x80); + writeRawByte(((int) value & 0x7F) | 0x80); value >>>= 7; } } } - /** Compute the number of bytes that would be needed to encode a varint. */ + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + * + * @deprecated use {@link #computeUInt32SizeNoTag(int)} instead. + */ + @Deprecated + public static int computeRawVarint32Size(final int value) { + if ((value & (~0 << 7)) == 0) { + return 1; + } + if ((value & (~0 << 14)) == 0) { + return 2; + } + if ((value & (~0 << 21)) == 0) { + return 3; + } + if ((value & (~0 << 28)) == 0) { + return 4; + } + return 5; + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * + * @deprecated use {@link #computeUInt64SizeNoTag(long)} instead. + */ + @Deprecated public static int computeRawVarint64Size(long value) { // handle two popular special cases up front ... - if ((value & (~0L << 7)) == 0L) return 1; - if (value < 0L) return 10; + if ((value & (~0L << 7)) == 0L) { + return 1; + } + if (value < 0L) { + return 10; + } // ... leaving us with 8 remaining, which we can divide and conquer int n = 2; - if ((value & (~0L << 35)) != 0L) { n += 4; value >>>= 28; } - if ((value & (~0L << 21)) != 0L) { n += 2; value >>>= 14; } - if ((value & (~0L << 14)) != 0L) { n += 1; } + if ((value & (~0L << 35)) != 0L) { + n += 4; + value >>>= 28; + } + if ((value & (~0L << 21)) != 0L) { + n += 2; + value >>>= 14; + } + if ((value & (~0L << 14)) != 0L) { + n += 1; + } return n; } - /** Write a little-endian 32-bit integer. */ - public void writeRawLittleEndian32(final int value) throws IOException { - writeRawByte((value ) & 0xFF); - writeRawByte((value >> 8) & 0xFF); - writeRawByte((value >> 16) & 0xFF); - writeRawByte((value >> 24) & 0xFF); - } - - public static final int LITTLE_ENDIAN_32_SIZE = 4; - - /** Write a little-endian 64-bit integer. */ - public void writeRawLittleEndian64(final long value) throws IOException { - writeRawByte((int)(value ) & 0xFF); - writeRawByte((int)(value >> 8) & 0xFF); - writeRawByte((int)(value >> 16) & 0xFF); - writeRawByte((int)(value >> 24) & 0xFF); - writeRawByte((int)(value >> 32) & 0xFF); - writeRawByte((int)(value >> 40) & 0xFF); - writeRawByte((int)(value >> 48) & 0xFF); - writeRawByte((int)(value >> 56) & 0xFF); - } - - public static final int LITTLE_ENDIAN_64_SIZE = 8; - /** - * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers - * into values that can be efficiently encoded with varint. (Otherwise, - * negative values must be sign-extended to 64 bits to be varint encoded, - * thus always taking 10 bytes on the wire.) + * Write a little-endian 32-bit integer. * - * @param n A signed 32-bit integer. - * @return An unsigned 32-bit integer, stored in a signed int because - * Java has no explicit unsigned support. + * @deprecated Use {@link #writeFixed32NoTag} instead. */ - public static int encodeZigZag32(final int n) { - // Note: the right-shift must be arithmetic - return (n << 1) ^ (n >> 31); + @Deprecated + public void writeRawLittleEndian32(final int value) throws IOException { + writeRawByte((value) & 0xFF); + writeRawByte((value >> 8) & 0xFF); + writeRawByte((value >> 16) & 0xFF); + writeRawByte((value >> 24) & 0xFF); } /** - * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers - * into values that can be efficiently encoded with varint. (Otherwise, - * negative values must be sign-extended to 64 bits to be varint encoded, - * thus always taking 10 bytes on the wire.) + * Write a little-endian 64-bit integer. * - * @param n A signed 64-bit integer. - * @return An unsigned 64-bit integer, stored in a signed int because - * Java has no explicit unsigned support. + * @deprecated Use {@link #writeFixed64NoTag} instead. */ - public static long encodeZigZag64(final long n) { - // Note: the right-shift must be arithmetic - return (n << 1) ^ (n >> 63); + @Deprecated + public void writeRawLittleEndian64(final long value) throws IOException { + writeRawByte((int) (value) & 0xFF); + writeRawByte((int) (value >> 8) & 0xFF); + writeRawByte((int) (value >> 16) & 0xFF); + writeRawByte((int) (value >> 24) & 0xFF); + writeRawByte((int) (value >> 32) & 0xFF); + writeRawByte((int) (value >> 40) & 0xFF); + writeRawByte((int) (value >> 48) & 0xFF); + writeRawByte((int) (value >> 56) & 0xFF); } } |