http://git-wip-us.apache.org/repos/asf/hbase/blob/6786b2b6/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedOutputStream.java ---------------------------------------------------------------------- diff --git a/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedOutputStream.java b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedOutputStream.java new file mode 100644 index 0000000..03871c9 --- /dev/null +++ b/hbase-protocol-shaded/src/main/java/org/apache/hadoop/hbase/shaded/com/google/protobuf/CodedOutputStream.java @@ -0,0 +1,3001 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package org.apache.hadoop.hbase.shaded.com.google.protobuf; + +import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.FIXED_32_SIZE; +import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.FIXED_64_SIZE; +import static org.apache.hadoop.hbase.shaded.com.google.protobuf.WireFormat.MAX_VARINT_SIZE; +import static java.lang.Math.max; + +import org.apache.hadoop.hbase.shaded.com.google.protobuf.Utf8.UnpairedSurrogateException; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Encodes and writes protocol message fields. + * + * <p>This class contains two kinds of methods: methods that write specific + * protocol message constructs and field types (e.g. {@link #writeTag} and + * {@link #writeInt32}) and methods that write low-level values (e.g. + * {@link #writeRawVarint32} and {@link #writeRawBytes}). If you are + * writing encoded protocol messages, you should use the former methods, but if + * you are writing some other format of your own design, use the latter. + * + * <p>This class is totally unsynchronized. + */ +public abstract class CodedOutputStream extends ByteOutput { + private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName()); + private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations(); + private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset(); + + /** + * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. + */ + @Deprecated + public static final int LITTLE_ENDIAN_32_SIZE = FIXED_32_SIZE; + + /** + * The buffer size used in {@link #newInstance(OutputStream)}. + */ + public static final int DEFAULT_BUFFER_SIZE = 4096; + + /** + * Returns the buffer size to efficiently write dataLength bytes to this + * CodedOutputStream. Used by AbstractMessageLite. + * + * @return the buffer size to efficiently write dataLength bytes to this + * CodedOutputStream. + */ + static int computePreferredBufferSize(int dataLength) { + if (dataLength > DEFAULT_BUFFER_SIZE) { + return DEFAULT_BUFFER_SIZE; + } + return dataLength; + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream}. + * + * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or + * modify the provided byte arrays. Doing so may result in corrupted data, which would be + * difficult to debug. + */ + public static CodedOutputStream newInstance(final OutputStream output) { + return newInstance(output, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream} with a given + * buffer size. + * + * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or + * modify the provided byte arrays. Doing so may result in corrupted data, which would be + * difficult to debug. + */ + public static CodedOutputStream newInstance(final OutputStream output, final int bufferSize) { + return new OutputStreamEncoder(output, bufferSize); + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array. If more bytes are written than fit in the array, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance(final byte[] flatArray) { + return newInstance(flatArray, 0, flatArray.length); + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array slice. If more bytes are written than fit in the slice, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance( + final byte[] flatArray, final int offset, final int length) { + return new ArrayEncoder(flatArray, offset, length); + } + + /** Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. */ + public static CodedOutputStream newInstance(ByteBuffer buffer) { + if (buffer.hasArray()) { + return new HeapNioEncoder(buffer); + } + if (buffer.isDirect() && !buffer.isReadOnly()) { + return UnsafeDirectNioEncoder.isSupported() + ? newUnsafeInstance(buffer) + : newSafeInstance(buffer); + } + throw new IllegalArgumentException("ByteBuffer is read-only"); + } + + /** For testing purposes only. */ + static CodedOutputStream newUnsafeInstance(ByteBuffer buffer) { + return new UnsafeDirectNioEncoder(buffer); + } + + /** For testing purposes only. */ + static CodedOutputStream newSafeInstance(ByteBuffer buffer) { + return new SafeDirectNioEncoder(buffer); + } + + /** + * Configures serialization to be deterministic. + * + * <p>The deterministic serialization guarantees that for a given binary, equal (defined by the + * {@code equals()} methods in protos) messages will always be serialized to the same bytes. This + * implies: + * + * <ul> + * <li>repeated serialization of a message will return the same bytes + * <li>different processes of the same binary (which may be executing on different machines) will + * serialize equal messages to the same bytes. + * </ul> + * + * <p>Note the deterministic serialization is NOT canonical across languages; it is also unstable + * across different builds with schema changes due to unknown fields. Users who need canonical + * serialization, e.g. persistent storage in a canonical form, fingerprinting, etc, should define + * their own canonicalization specification and implement the serializer using reflection APIs + * rather than relying on this API. + * + * <p> Once set, the serializer will: (Note this is an implementation detail and may subject to + * change in the future) + * + * <ul> + * <li> sort map entries by keys in lexicographical order or numerical order. Note: For string + * keys, the order is based on comparing the Unicode value of each character in the strings. + * The order may be different from the deterministic serialization in other languages where + * maps are sorted on the lexicographical order of the UTF8 encoded keys. + * </ul> + */ + void useDeterministicSerialization() { + serializationDeterministic = true; + } + + boolean isSerializationDeterministic() { + return serializationDeterministic; + } + private boolean serializationDeterministic; + + /** + * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. + * + * @deprecated the size parameter is no longer used since use of an internal buffer is useless + * (and wasteful) when writing to a {@link ByteBuffer}. Use {@link #newInstance(ByteBuffer)} + * instead. + */ + @Deprecated + public static CodedOutputStream newInstance(ByteBuffer byteBuffer, + @SuppressWarnings("unused") int unused) { + return newInstance(byteBuffer); + } + + /** + * Create a new {@code CodedOutputStream} that writes to the provided {@link ByteOutput}. + * + * <p> NOTE: The {@link ByteOutput} <strong>MUST NOT</strong> modify the provided buffers. Doing + * so may result in corrupted data, which would be difficult to debug. + * + * @param byteOutput the output target for encoded bytes. + * @param bufferSize the size of the internal scratch buffer to be used for string encoding. + * Setting this to {@code 0} will disable buffering, requiring an allocation for each encoded + * string. + */ + static CodedOutputStream newInstance(ByteOutput byteOutput, int bufferSize) { + if (bufferSize < 0) { + throw new IllegalArgumentException("bufferSize must be positive"); + } + + return new ByteOutputEncoder(byteOutput, bufferSize); + } + + // Disallow construction outside of this class. + private CodedOutputStream() { + } + + // ----------------------------------------------------------------- + + /** Encode and write a tag. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeTag(int fieldNumber, int wireType) throws IOException; + + /** Write an {@code int32} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeInt32(int fieldNumber, int value) throws IOException; + + /** Write a {@code uint32} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt32(int fieldNumber, int value) throws IOException; + + /** Write a {@code sint32} field, including tag, to the stream. */ + public final void writeSInt32(final int fieldNumber, final int value) throws IOException { + writeUInt32(fieldNumber, encodeZigZag32(value)); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed32(int fieldNumber, int value) throws IOException; + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public final void writeSFixed32(final int fieldNumber, final int value) throws IOException { + writeFixed32(fieldNumber, value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public final void writeInt64(final int fieldNumber, final long value) throws IOException { + writeUInt64(fieldNumber, value); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt64(int fieldNumber, long value) throws IOException; + + /** Write an {@code sint64} field, including tag, to the stream. */ + public final void writeSInt64(final int fieldNumber, final long value) throws IOException { + writeUInt64(fieldNumber, encodeZigZag64(value)); + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed64(int fieldNumber, long value) throws IOException; + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public final void writeSFixed64(final int fieldNumber, final long value) throws IOException { + writeFixed64(fieldNumber, value); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public final void writeFloat(final int fieldNumber, final float value) throws IOException { + writeFixed32(fieldNumber, Float.floatToRawIntBits(value)); + } + + /** Write a {@code double} field, including tag, to the stream. */ + public final void writeDouble(final int fieldNumber, final double value) throws IOException { + writeFixed64(fieldNumber, Double.doubleToRawLongBits(value)); + } + + /** Write a {@code bool} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBool(int fieldNumber, boolean value) throws IOException; + + /** + * Write an enum field, including tag, to the stream. The provided value is the numeric + * value used to represent the enum value on the wire (not the enum ordinal value). + */ + public final void writeEnum(final int fieldNumber, final int value) throws IOException { + writeInt32(fieldNumber, value); + } + + /** Write a {@code string} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeString(int fieldNumber, String value) throws IOException; + + /** Write a {@code bytes} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBytes(int fieldNumber, ByteString value) throws IOException; + + /** Write a {@code bytes} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteArray(int fieldNumber, byte[] value) throws IOException; + + /** Write a {@code bytes} field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteArray(int fieldNumber, byte[] value, int offset, int length) + throws IOException; + + /** + * Write a {@code bytes} field, including tag, to the stream. + * 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 writeByteBuffer(fieldNumber, byteBuffer.slice())}. + */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException; + + /** + * Write a single byte. + */ + public final void writeRawByte(final byte value) throws IOException { + write(value); + } + + /** Write a single byte, represented by an integer value. */ + public final void writeRawByte(final int value) throws IOException { + write((byte) value); + } + + /** Write an array of bytes. */ + public final void writeRawBytes(final byte[] value) throws IOException { + write(value, 0, value.length); + } + + /** + * Write part of an array of bytes. + */ + public final void writeRawBytes(final byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + /** Write a byte string. */ + public final void writeRawBytes(final ByteString value) throws IOException { + value.writeTo(this); + } + + /** + * Write a ByteBuffer. This method will write all content of the ByteBuffer + * regardless of the current position and limit (i.e., the number of bytes + * to be written is value.capacity(), not value.remaining()). Furthermore, + * this method doesn't alter the state of the passed-in ByteBuffer. Its + * position, limit, mark, etc. will remain unchanged. If you only want to + * write the remaining bytes of a ByteBuffer, you can call + * {@code writeRawBytes(byteBuffer.slice())}. + */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeRawBytes(final ByteBuffer value) throws IOException; + + /** Write an embedded message field, including tag, to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException; + + /** + * Write a MessageSet extension field to the stream. For historical reasons, + * the wire format differs from normal fields. + */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException; + + /** + * Write an unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException; + + // ----------------------------------------------------------------- + + /** Write an {@code int32} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeInt32NoTag(final int value) throws IOException; + + /** Write a {@code uint32} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt32NoTag(int value) throws IOException; + + /** Write a {@code sint32} field to the stream. */ + public final void writeSInt32NoTag(final int value) throws IOException { + writeUInt32NoTag(encodeZigZag32(value)); + } + + /** Write a {@code fixed32} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed32NoTag(int value) throws IOException; + + /** Write a {@code sfixed32} field to the stream. */ + public final void writeSFixed32NoTag(final int value) throws IOException { + writeFixed32NoTag(value); + } + + /** Write an {@code int64} field to the stream. */ + public final void writeInt64NoTag(final long value) throws IOException { + writeUInt64NoTag(value); + } + + /** Write a {@code uint64} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeUInt64NoTag(long value) throws IOException; + + /** Write a {@code sint64} field to the stream. */ + public final void writeSInt64NoTag(final long value) throws IOException { + writeUInt64NoTag(encodeZigZag64(value)); + } + + /** Write a {@code fixed64} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeFixed64NoTag(long value) throws IOException; + + /** Write a {@code sfixed64} field to the stream. */ + public final void writeSFixed64NoTag(final long value) throws IOException { + writeFixed64NoTag(value); + } + + /** Write a {@code float} field to the stream. */ + public final void writeFloatNoTag(final float value) throws IOException { + writeFixed32NoTag(Float.floatToRawIntBits(value)); + } + + /** Write a {@code double} field to the stream. */ + public final void writeDoubleNoTag(final double value) throws IOException { + writeFixed64NoTag(Double.doubleToRawLongBits(value)); + } + + /** Write a {@code bool} field to the stream. */ + public final void writeBoolNoTag(final boolean value) throws IOException { + write((byte) (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 final void writeEnumNoTag(final int value) throws IOException { + writeInt32NoTag(value); + } + + /** Write a {@code string} field to the stream. */ + // TODO(dweis): Document behavior on ill-formed UTF-16 input. + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeStringNoTag(String value) throws IOException; + + /** Write a {@code bytes} field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeBytesNoTag(final ByteString value) throws IOException; + + /** Write a {@code bytes} field to the stream. */ + public final void writeByteArrayNoTag(final byte[] value) throws IOException { + writeByteArrayNoTag(value, 0, value.length); + } + + /** Write an embedded message field to the stream. */ + // Abstract to avoid overhead of additional virtual method calls. + public abstract void writeMessageNoTag(final MessageLite value) throws IOException; + + //================================================================= + + @ExperimentalApi + @Override + public abstract void write(byte value) throws IOException; + + @ExperimentalApi + @Override + public abstract void write(byte[] value, int offset, int length) throws IOException; + + @ExperimentalApi + @Override + public abstract void writeLazy(byte[] value, int offset, int length) throws IOException; + + @Override + public abstract void write(ByteBuffer value) throws IOException; + + @ExperimentalApi + @Override + public abstract void writeLazy(ByteBuffer value) throws IOException; + + // ================================================================= + // ================================================================= + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field, including tag. + */ + public static int computeUInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field, including tag. + */ + public static int computeSInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field, including tag. + */ + public static int computeFixed32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field, including tag. + */ + public static int computeSFixed32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); + } + + /** + * 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 sint64} field, including tag. + */ + 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) { + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. + */ + 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 float} field, including tag. + */ + 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 double} field, including tag. + */ + 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 bool} field, including tag. + */ + 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 + * 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 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) { + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); + } + + /** + * 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) { + return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value); + } + + /** + * 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) { + return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value); + } + + /** + * 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) { + return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. + */ + public static int computeMessageSize(final int fieldNumber, final MessageLite value) { + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * MessageSet extension to the stream. For historical reasons, + * the wire format differs from normal fields. + */ + 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 + * unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ + 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 + * lazily parsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ + public static int computeLazyFieldMessageSetExtensionSize( + final int fieldNumber, final LazyFieldLite value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value); + } + + // ----------------------------------------------------------------- + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(final int fieldNumber) { + return computeUInt32SizeNoTag(WireFormat.makeTag(fieldNumber, 0)); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32SizeNoTag(final int value) { + if (value >= 0) { + return computeUInt32SizeNoTag(value); + } else { + // Must sign-extend. + return MAX_VARINT_SIZE; + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field. + */ + public static int computeUInt32SizeNoTag(final int value) { + if ((value & (~0 << 7)) == 0) { + return 1; + } + if ((value & (~0 << 14)) == 0) { + return 2; + } + if ((value & (~0 << 21)) == 0) { + return 3; + } + if ((value & (~0 << 28)) == 0) { + return 4; + } + return 5; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field. + */ + public static int computeSInt32SizeNoTag(final int value) { + return computeUInt32SizeNoTag(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field. + */ + public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return FIXED_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field. + */ + public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) { + return FIXED_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64SizeNoTag(final long value) { + return computeUInt64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64SizeNoTag(long value) { + // handle two popular special cases up front ... + if ((value & (~0L << 7)) == 0L) { + return 1; + } + if (value < 0L) { + return 10; + } + // ... leaving us with 8 remaining, which we can divide and conquer + int n = 2; + if ((value & (~0L << 35)) != 0L) { + n += 4; value >>>= 28; + } + if ((value & (~0L << 21)) != 0L) { + n += 2; value >>>= 14; + } + if ((value & (~0L << 14)) != 0L) { + n += 1; + } + return n; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field. + */ + public static int computeSInt64SizeNoTag(final long value) { + return computeUInt64SizeNoTag(encodeZigZag64(value)); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field. + */ + public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { + return FIXED_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field. + */ + public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) { + return FIXED_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) { + return FIXED_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 FIXED_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field. + */ + 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. + */ + public static int computeStringSizeNoTag(final String value) { + int length; + try { + length = Utf8.encodedLength(value); + } catch (UnpairedSurrogateException e) { + // TODO(dweis): Consider using nio Charset methods instead. + final byte[] bytes = value.getBytes(Internal.UTF_8); + length = bytes.length; + } + + return computeLengthDelimitedFieldSize(length); + } + + /** + * Compute the number of bytes that would be needed to encode an embedded + * message stored in lazy field. + */ + public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) { + return computeLengthDelimitedFieldSize(value.getSerializedSize()); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field. + */ + public static int computeBytesSizeNoTag(final ByteString value) { + return computeLengthDelimitedFieldSize(value.size()); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field. + */ + public static int computeByteArraySizeNoTag(final byte[] value) { + return computeLengthDelimitedFieldSize(value.length); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field. + */ + public static int computeByteBufferSizeNoTag(final ByteBuffer value) { + return computeLengthDelimitedFieldSize(value.capacity()); + } + + /** + * Compute the number of bytes that would be needed to encode an embedded + * message field. + */ + public static int computeMessageSizeNoTag(final MessageLite value) { + return computeLengthDelimitedFieldSize(value.getSerializedSize()); + } + + static int computeLengthDelimitedFieldSize(int fieldLength) { + return computeUInt32SizeNoTag(fieldLength) + fieldLength; + } + + /** + * 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 encodeZigZag32(final int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); + } + + /** + * 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 long encodeZigZag64(final long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } + + // ================================================================= + + /** + * Flushes the stream and forces any buffered bytes to be written. This + * does not flush the underlying OutputStream. + */ + public abstract void flush() throws IOException; + + /** + * If writing to a flat array, return the space left in the array. + * Otherwise, throws {@code UnsupportedOperationException}. + */ + public abstract int spaceLeft(); + + /** + * Verifies that {@link #spaceLeft()} returns zero. It's common to create + * a byte array that is exactly big enough to hold a message, then write to + * it with a {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} + * after writing verifies that the message was actually as big as expected, + * which can help catch bugs. + */ + public final void checkNoSpaceLeft() { + if (spaceLeft() != 0) { + throw new IllegalStateException("Did not write as much data as expected."); + } + } + + /** + * If you create a CodedOutputStream around a simple flat array, you must + * not attempt to write more bytes than the array has space. Otherwise, + * this exception will be thrown. + */ + public static class OutOfSpaceException extends IOException { + private static final long serialVersionUID = -6947486886997889499L; + + private static final String MESSAGE = + "CodedOutputStream was writing to a flat byte array and ran out of space."; + + OutOfSpaceException() { + super(MESSAGE); + } + + OutOfSpaceException(String explanationMessage) { + super(MESSAGE + ": " + explanationMessage); + } + + OutOfSpaceException(Throwable cause) { + super(MESSAGE, cause); + } + + OutOfSpaceException(String explanationMessage, Throwable cause) { + super(MESSAGE + ": " + explanationMessage, cause); + } + } + + /** + * Get the total number of bytes successfully written to this stream. The + * returned value is not guaranteed to be accurate if exceptions have been + * found in the middle of writing. + */ + public abstract int getTotalBytesWritten(); + + // ================================================================= + + /** Write a {@code bytes} field to the stream. Visible for testing. */ + abstract void writeByteArrayNoTag(final byte[] value, final int offset, final int length) + throws IOException; + + final void inefficientWriteStringNoTag(String value, UnpairedSurrogateException cause) + throws IOException { + logger.log(Level.WARNING, + "Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", cause); + + // Unfortunately there does not appear to be any way to tell Java to encode + // UTF-8 directly into our buffer, so we have to let it create its own byte + // array and then copy. + // TODO(dweis): Consider using nio Charset methods instead. + final byte[] bytes = value.getBytes(Internal.UTF_8); + try { + writeUInt32NoTag(bytes.length); + writeLazy(bytes, 0, bytes.length); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } catch (OutOfSpaceException e) { + throw e; + } + } + + // ================================================================= + + /** + * Write a {@code group} field, including tag, to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public final void writeGroup(final int fieldNumber, final MessageLite value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + writeGroupNoTag(value); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** + * Write a {@code group} field to the stream. + * + * @deprecated groups are deprecated. + */ + @Deprecated + public final 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 final void writeRawVarint32(int value) throws IOException { + writeUInt32NoTag(value); + } + + /** + * Encode and write a varint. + * + * @deprecated use {@link #writeUInt64NoTag} instead. + */ + @Deprecated + public final void writeRawVarint64(long value) throws IOException { + writeUInt64NoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + * + * @deprecated use {@link #computeUInt32SizeNoTag(int)} instead. + */ + @Deprecated + public static int computeRawVarint32Size(final int value) { + return computeUInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * + * @deprecated use {@link #computeUInt64SizeNoTag(long)} instead. + */ + @Deprecated + public static int computeRawVarint64Size(long value) { + return computeUInt64SizeNoTag(value); + } + + /** + * Write a little-endian 32-bit integer. + * + * @deprecated Use {@link #writeFixed32NoTag} instead. + */ + @Deprecated + public final void writeRawLittleEndian32(final int value) throws IOException { + writeFixed32NoTag(value); + } + + /** + * Write a little-endian 64-bit integer. + * + * @deprecated Use {@link #writeFixed64NoTag} instead. + */ + @Deprecated + public final void writeRawLittleEndian64(final long value) throws IOException { + writeFixed64NoTag(value); + } + + // ================================================================= + + /** + * A {@link CodedOutputStream} that writes directly to a byte array. + */ + private static class ArrayEncoder extends CodedOutputStream { + private final byte[] buffer; + private final int offset; + private final int limit; + private int position; + + ArrayEncoder(byte[] buffer, int offset, int length) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + if ((offset | length | (buffer.length - (offset + length))) < 0) { + throw new IllegalArgumentException(String.format( + "Array range is invalid. Buffer.length=%d, offset=%d, length=%d", + buffer.length, offset, length)); + } + this.buffer = buffer; + this.offset = offset; + position = offset; + limit = offset + length; + } + + @Override + public final void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public final void writeInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + @Override + public final void writeUInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + @Override + public final void writeFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + @Override + public final void writeUInt64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + @Override + public final void writeFixed64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + @Override + public final void writeBool(final int fieldNumber, final boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + write((byte) (value ? 1 : 0)); + } + + @Override + public final void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public final void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public final void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public final void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public final void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public final void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public final void writeByteArrayNoTag(final byte[] value, int offset, int length) + throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public final void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public final void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public final void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public final void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public final void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public final void write(byte value) throws IOException { + try { + buffer[position++] = value; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); + } + } + + @Override + public final void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public final void writeUInt32NoTag(int value) throws IOException { + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) { + long pos = ARRAY_BASE_OFFSET + position; + while (true) { + if ((value & ~0x7F) == 0) { + UnsafeUtil.putByte(buffer, pos++, (byte) value); + position++; + return; + } else { + UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); + position++; + value >>>= 7; + } + } + } else { + try { + while (true) { + if ((value & ~0x7F) == 0) { + buffer[position++] = (byte) value; + return; + } else { + buffer[position++] = (byte) ((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); + } + } + } + + @Override + public final void writeFixed32NoTag(int value) throws IOException { + try { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); + } + } + + @Override + public final void writeUInt64NoTag(long value) throws IOException { + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) { + long pos = ARRAY_BASE_OFFSET + position; + while (true) { + if ((value & ~0x7FL) == 0) { + UnsafeUtil.putByte(buffer, pos++, (byte) value); + position++; + return; + } else { + UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); + position++; + value >>>= 7; + } + } + } else { + try { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer[position++] = (byte) value; + return; + } else { + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + value >>>= 7; + } + } + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); + } + } + } + + @Override + public final void writeFixed64NoTag(long value) throws IOException { + try { + buffer[position++] = (byte) ((int) (value) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 8) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 16) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 24) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 32) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 40) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 48) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); + } + } + + @Override + public final void write(byte[] value, int offset, int length) throws IOException { + try { + System.arraycopy(value, offset, buffer, position, length); + position += length; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e); + } + } + + @Override + public final void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public final void write(ByteBuffer value) throws IOException { + final int length = value.remaining(); + try { + value.get(buffer, position, length); + position += length; + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e); + } + } + + @Override + public final void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + @Override + public final void writeStringNoTag(String value) throws IOException { + final int oldPosition = position; + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxLength); + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + if (minLengthVarIntSize == maxLengthVarIntSize) { + position = oldPosition + minLengthVarIntSize; + int newPosition = Utf8.encode(value, buffer, position, spaceLeft()); + // Since this class is stateful and tracks the position, we rewind and store the state, + // prepend the length, then reset it back to the end of the string. + position = oldPosition; + int length = newPosition - oldPosition - minLengthVarIntSize; + writeUInt32NoTag(length); + position = newPosition; + } else { + int length = Utf8.encodedLength(value); + writeUInt32NoTag(length); + position = Utf8.encode(value, buffer, position, spaceLeft()); + } + } catch (UnpairedSurrogateException e) { + // Roll back the change - we fall back to inefficient path. + position = oldPosition; + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() { + // Do nothing. + } + + @Override + public final int spaceLeft() { + return limit - position; + } + + @Override + public final int getTotalBytesWritten() { + return position - offset; + } + } + + /** + * A {@link CodedOutputStream} that writes directly to a heap {@link ByteBuffer}. Writes are + * done directly to the underlying array. The buffer position is only updated after a flush. + */ + private static final class HeapNioEncoder extends ArrayEncoder { + private final ByteBuffer byteBuffer; + private int initialPosition; + + HeapNioEncoder(ByteBuffer byteBuffer) { + super(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), + byteBuffer.remaining()); + this.byteBuffer = byteBuffer; + this.initialPosition = byteBuffer.position(); + } + + @Override + public void flush() { + // Update the position on the buffer. + byteBuffer.position(initialPosition + getTotalBytesWritten()); + } + } + + /** + * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer}, using only + * safe operations.. + */ + private static final class SafeDirectNioEncoder extends CodedOutputStream { + private final ByteBuffer originalBuffer; + private final ByteBuffer buffer; + private final int initialPosition; + + SafeDirectNioEncoder(ByteBuffer buffer) { + this.originalBuffer = buffer; + this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); + initialPosition = buffer.position(); + } + + @Override + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + @Override + public void writeUInt32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + @Override + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + @Override + public void writeUInt64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + @Override + public void writeFixed64(final int fieldNumber, final long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + @Override + public void writeBool(final int fieldNumber, final boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + write((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeMessage(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public void writeMessageSetExtension(final int fieldNumber, final MessageLite value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeRawMessageSetExtension(final int fieldNumber, final ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeMessageNoTag(final MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public void write(byte value) throws IOException { + try { + buffer.put(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public void writeUInt32NoTag(int value) throws IOException { + try { + while (true) { + if ((value & ~0x7F) == 0) { + buffer.put((byte) value); + return; + } else { + buffer.put((byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeFixed32NoTag(int value) throws IOException { + try { + buffer.putInt(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeUInt64NoTag(long value) throws IOException { + try { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer.put((byte) value); + return; + } else { + buffer.put((byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeFixed64NoTag(long value) throws IOException { + try { + buffer.putLong(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + try { + buffer.put(value, offset, length); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public void write(ByteBuffer value) throws IOException { + try { + buffer.put(value); + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + @Override + public void writeStringNoTag(String value) throws IOException { + final int startPos = buffer.position(); + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + final int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR; + final int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize); + final int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + if (minLengthVarIntSize == maxLengthVarIntSize) { + // Save the current position and increment past the length field. We'll come back + // and write the length field after the encoding is complete. + final int startOfBytes = buffer.position() + minLengthVarIntSize; + buffer.position(startOfBytes); + + // Encode the string. + encode(value); + + // Now go back to the beginning and write the length. + int endOfBytes = buffer.position(); + buffer.position(startPos); + writeUInt32NoTag(endOfBytes - startOfBytes); + + // Reposition the buffer past the written data. + buffer.position(endOfBytes); + } else { + final int length = Utf8.encodedLength(value); + writeUInt32NoTag(length); + encode(value); + } + } catch (UnpairedSurrogateException e) { + // Roll back the change and convert to an IOException. + buffer.position(startPos); + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IllegalArgumentException e) { + // Thrown by buffer.position() if out of range. + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() { + // Update the position of the original buffer. + originalBuffer.position(buffer.position()); + } + + @Override + public int spaceLeft() { + return buffer.remaining(); + } + + @Override + public int getTotalBytesWritten() { + return buffer.position() - initialPosition; + } + + private void encode(String value) throws IOException { + try { + Utf8.encodeUtf8(value, buffer); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + } + + /** + * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer} using {@code + * sun.misc.Unsafe}. + */ + private static final class UnsafeDirectNioEncoder extends CodedOutputStream { + private final ByteBuffer originalBuffer; + private final ByteBuffer buffer; + private final long address; + private final long initialPosition; + private final long limit; + private final long oneVarintLimit; + private long position; + + UnsafeDirectNioEncoder(ByteBuffer buffer) { + this.originalBuffer = buffer; + this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); + address = UnsafeUtil.addressOffset(buffer); + initialPosition = address + buffer.position(); + limit = address + buffer.limit(); + oneVarintLimit = limit - MAX_VARINT_SIZE; + position = initialPosition; + } + + static boolean isSupported() { + return UnsafeUtil.hasUnsafeByteBufferOperations(); + } + + @Override + public void writeTag(int fieldNumber, int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + @Override + public void writeUInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + @Override + public void writeFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + @Override + public void writeUInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + @Override + public void writeFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + @Override + public void writeBool(int fieldNumber, boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + write((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(int fieldNumber, String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(int fieldNumber, ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(int fieldNumber, byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray(int fieldNumber, byte[] value, int offset, int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeMessage(int fieldNumber, MessageLite value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + @Override + public void writeMessageSetExtension(int fieldNumber, MessageLite value) throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeRawMessageSetExtension(int fieldNumber, ByteString value) throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + @Override + public void writeMessageNoTag(MessageLite value) throws IOException { + writeUInt32NoTag(value.getSerializedSize()); + value.writeTo(this); + } + + @Override + public void write(byte value) throws IOException { + if (position >= limit) { + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); + } + UnsafeUtil.putByte(position++, value); + } + + @Override + public void writeBytesNoTag(ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); + } + } + + @Override + public void writeInt32NoTag(int value) throws IOException { + if (value >= 0) { + writeUInt32NoTag(value); + } else { + // Must sign-extend. + writeUInt64NoTag(value); + } + } + + @Override + public void writeUInt32NoTag(int value) throws IOException { + if (position <= oneVarintLimit) { + // Optimization to avoid bounds checks on each iteration. + while (true) { + if ((value & ~0x7F) == 0) { + UnsafeUtil.putByte(position++, (byte) value); + return; + } else { + UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } else { + while (position < limit) { + if ((value & ~0x7F) == 0) { + UnsafeUtil.putByte(position++, (byte) value); + return; + } else { + UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); + } + } + + @Override + public void writeFixed32NoTag(int value) throws IOException { + buffer.putInt(bufferPos(position), value); + position += FIXED_32_SIZE; + } + + @Override + public void writeUInt64NoTag(long value) throws IOException { + if (position <= oneVarintLimit) { + // Optimization to avoid bounds checks on each iteration. + while (true) { + if ((value & ~0x7FL) == 0) { + UnsafeUtil.putByte(position++, (byte) value); + return; + } else { + UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + } else { + while (position < limit) { + if ((value & ~0x7FL) == 0) { + UnsafeUtil.putByte(position++, (byte) value); + return; + } else { + UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); + } + } + + @Override + public void writeFixed64NoTag(long value) throws IOException { + buffer.putLong(bufferPos(position), value); + position += FIXED_64_SIZE; + } + + @Override + public void write(byte[] value, int offset, int length) throws IOException { + if (value == null + || offset < 0 + || length < 0 + || (value.length - length) < offset + || (limit - length) < position) { + if (value == null) { + throw new NullPointerException("value"); + } + throw new OutOfSpaceException( + String.format("Pos: %d, limit: %d, len: %d", position, limit, length)); + } + + UnsafeUtil.copyMemory( + value, UnsafeUtil.getArrayBaseOffset() + offset, null, position, length); + position += length; + } + + @Override + public void writeLazy(byte[] value, int offset, int length) throws IOException { + write(value, offset, length); + } + + @Override + public void write(ByteBuffer value) throws IOException { + try { + int length = value.remaining(); + repositionBuffer(position); + buffer.put(value); + position += length; + } catch (BufferOverflowException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void writeLazy(ByteBuffer value) throws IOException { + write(value); + } + + @Override + public void writeStringNoTag(String value) throws IOException { + long prevPos = position; + try { + // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), + // and at most 3 times of it. We take advantage of this in both branches below. + int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR; + int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize); + int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); + if (minLengthVarIntSize == maxLengthVarIntSize) { + // Save the current position and increment past the length field. We'll come back + // and write the length field after the encoding is complete. + int stringStart = bufferPos(position) + minLengthVarIntSize; + buffer.position(stringStart); + + // Encode the string. + Utf8.encodeUtf8(value, buffer); + + // Write the length and advance the position. + int length = buffer.position() - stringStart; + writeUInt32NoTag(length); + position += length; + } else { + // Calculate and write the encoded length. + int length = Utf8.encodedLength(value); + writeUInt32NoTag(length); + + // Write the string and advance the position. + repositionBuffer(position); + Utf8.encodeUtf8(value, buffer); + position += length; + } + } catch (UnpairedSurrogateException e) { + // Roll back the change and convert to an IOException. + position = prevPos; + repositionBuffer(position); + + // TODO(nathanmittler): We should throw an IOException here instead. + inefficientWriteStringNoTag(value, e); + } catch (IllegalArgumentException e) { + // Thrown by buffer.position() if out of range. + throw new OutOfSpaceException(e); + } catch (IndexOutOfBoundsException e) { + throw new OutOfSpaceException(e); + } + } + + @Override + public void flush() { + // Update the position of the original buffer. + originalBuffer.position(bufferPos(position)); + } + + @Override + public int spaceLeft() { + return (int) (limit - position); + } + + @Override + public int getTotalBytesWritten() { + return (int) (position - initialPosition); + } + + private void repositionBuffer(long pos) { + buffer.position(bufferPos(pos)); + } + + private int bufferPos(long pos) { + return (int) (pos - address); + } + } + + /** + * Abstract base class for buffered encoders. + */ + private abstract static class AbstractBufferedEncoder extends CodedOutputStream { + final byte[] buffer; + final int limit; + int position; + int totalBytesWritten; + + AbstractBufferedEncoder(int bufferSize) { + if (bufferSize < 0) { + throw new IllegalArgumentException("bufferSize must be >= 0"); + } + // As an optimization, we require that the buffer be able to store at least 2 + // varints so that we can buffer any integer write (tag + value). This reduces the + // number of range checks for a single write to 1 (i.e. if there is not enough space + // to buffer the tag+value, flush and then buffer it). + this.buffer = new byte[max(bufferSize, MAX_VARINT_SIZE * 2)]; + this.limit = buffer.length; + } + + @Override + public final int spaceLeft() { + throw new UnsupportedOperationException( + "spaceLeft() can only be called on CodedOutputStreams that are " + + "writing to a flat array or ByteBuffer."); + } + + @Override + public final int getTotalBytesWritten() { + return totalBytesWritten; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void buffer(byte value) { + buffer[position++] = value; + totalBytesWritten++; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferTag(final int fieldNumber, final int wireType) { + bufferUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferInt32NoTag(final int value) { + if (value >= 0) { + bufferUInt32NoTag(value); + } else { + // Must sign-extend. + bufferUInt64NoTag(value); + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferUInt32NoTag(int value) { + if (HAS_UNSAFE_ARRAY_OPERATIONS) { + final long originalPos = ARRAY_BASE_OFFSET + position; + long pos = originalPos; + while (true) { + if ((value & ~0x7F) == 0) { + UnsafeUtil.putByte(buffer, pos++, (byte) value); + break; + } else { + UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + } + int delta = (int) (pos - originalPos); + position += delta; + totalBytesWritten += delta; + } else { + while (true) { + if ((value & ~0x7F) == 0) { + buffer[position++] = (byte) value; + totalBytesWritten++; + return; + } else { + buffer[position++] = (byte) ((value & 0x7F) | 0x80); + totalBytesWritten++; + value >>>= 7; + } + } + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferUInt64NoTag(long value) { + if (HAS_UNSAFE_ARRAY_OPERATIONS) { + final long originalPos = ARRAY_BASE_OFFSET + position; + long pos = originalPos; + while (true) { + if ((value & ~0x7FL) == 0) { + UnsafeUtil.putByte(buffer, pos++, (byte) value); + break; + } else { + UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); + value >>>= 7; + } + } + int delta = (int) (pos - originalPos); + position += delta; + totalBytesWritten += delta; + } else { + while (true) { + if ((value & ~0x7FL) == 0) { + buffer[position++] = (byte) value; + totalBytesWritten++; + return; + } else { + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80); + totalBytesWritten++; + value >>>= 7; + } + } + } + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferFixed32NoTag(int value) { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + totalBytesWritten += FIXED_32_SIZE; + } + + /** + * This method does not perform bounds checking on the array. Checking array bounds is the + * responsibility of the caller. + */ + final void bufferFixed64NoTag(long value) { + buffer[position++] = (byte) (value & 0xFF); + buffer[position++] = (byte) ((value >> 8) & 0xFF); + buffer[position++] = (byte) ((value >> 16) & 0xFF); + buffer[position++] = (byte) ((value >> 24) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 32) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 40) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 48) & 0xFF); + buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); + totalBytesWritten += FIXED_64_SIZE; + } + } + + /** + * A {@link CodedOutputStream} that decorates a {@link ByteOutput}. It internal buffer only to + * support string encoding operations. All other writes are just passed through to the + * {@link ByteOutput}. + */ + private static final class ByteOutputEncoder extends AbstractBufferedEncoder { + private final ByteOutput out; + + ByteOutputEncoder(ByteOutput out, int bufferSize) { + super(bufferSize); + if (out == null) { + throw new NullPointerException("out"); + } + this.out = out; + } + + @Override + public void writeTag(final int fieldNumber, final int wireType) throws IOException { + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); + } + + @Override + public void writeInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferInt32NoTag(value); + } + + @Override + public void writeUInt32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt32NoTag(value); + } + + @Override + public void writeFixed32(final int fieldNumber, final int value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + bufferFixed32NoTag(value); + } + + @Override + public void writeUInt64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE * 2); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + bufferUInt64NoTag(value); + } + + @Override + public void writeFixed64(final int fieldNumber, final long value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE); + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + bufferFixed64NoTag(value); + } + + @Override + public void writeBool(final int fieldNumber, final boolean value) throws IOException { + flushIfNotAvailable(MAX_VARINT_SIZE + 1); + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + buffer((byte) (value ? 1 : 0)); + } + + @Override + public void writeString(final int fieldNumber, final String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + @Override + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + @Override + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException { + writeByteArray(fieldNumber, value, 0, value.length); + } + + @Override + public void writeByteArray( + final int fieldNumber, final byte[] value, final int offset, final int length) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeByteArrayNoTag(value, offset, length); + } + + @Override + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeUInt32NoTag(value.capacity()); + writeRawBytes(value); + } + + @Override + public void writeBytesNoTag(final ByteString value) throws IOException { + writeUInt32NoTag(value.size()); + value.writeTo(this); + } + + @Override + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException { + writeUInt32NoTag(length); + write(value, offset, length); + } + + @Override + public void writeRawBytes(final ByteBuffer value) throws IOException { + if (value.hasArray()) { + write(value.array(), value.arrayOffset(), value.capacity()); + } else { + ByteBuffer duplicated = value.duplicate(); + duplicated.clear(); + write(duplicated); +
<TRUNCATED>