| // 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 com.google.protobuf; |
| |
| import static com.google.protobuf.WireFormat.FIXED32_SIZE; |
| import static com.google.protobuf.WireFormat.FIXED64_SIZE; |
| import static com.google.protobuf.WireFormat.MAX_VARINT32_SIZE; |
| import static com.google.protobuf.WireFormat.MAX_VARINT_SIZE; |
| import static java.lang.Math.max; |
| |
| import 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(); |
| |
| /** @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. */ |
| @Deprecated public static final int LITTLE_ENDIAN_32_SIZE = FIXED32_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> |
| */ |
| public 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 FIXED32_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 FIXED32_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 FIXED64_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 FIXED64_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 FIXED32_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 FIXED64_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 |
| && !Android.isOnAndroidDevice() |
| && spaceLeft() >= MAX_VARINT32_SIZE) { |
| if ((value & ~0x7F) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| return; |
| } |
| UnsafeUtil.putByte(buffer, position++, (byte) (value | 0x80)); |
| value >>>= 7; |
| if ((value & ~0x7F) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| return; |
| } |
| UnsafeUtil.putByte(buffer, position++, (byte) (value | 0x80)); |
| value >>>= 7; |
| if ((value & ~0x7F) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| return; |
| } |
| UnsafeUtil.putByte(buffer, position++, (byte) (value | 0x80)); |
| value >>>= 7; |
| if ((value & ~0x7F) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| return; |
| } |
| UnsafeUtil.putByte(buffer, position++, (byte) (value | 0x80)); |
| value >>>= 7; |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| } 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) { |
| while (true) { |
| if ((value & ~0x7FL) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| return; |
| } else { |
| UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80)); |
| 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 += FIXED32_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 += FIXED64_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, offset, 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 = position; |
| while (true) { |
| if ((value & ~0x7F) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| break; |
| } else { |
| UnsafeUtil.putByte(buffer, position++, (byte) ((value & 0x7F) | 0x80)); |
| value >>>= 7; |
| } |
| } |
| int delta = (int) (position - originalPos); |
| 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 = position; |
| while (true) { |
| if ((value & ~0x7FL) == 0) { |
| UnsafeUtil.putByte(buffer, position++, (byte) value); |
| break; |
| } else { |
| UnsafeUtil.putByte(buffer, position++, (byte) (((int) value & 0x7F) | 0x80)); |
| value >>>= 7; |
| } |
| } |
| int delta = (int) (position - originalPos); |
| 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 += FIXED32_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 += FIXED64_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 + FIXED32_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 + FIXED64_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); |
| } |
| } |
| |
| @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 { |
| if (position == limit) { |
| doFlush(); |
| } |
| |
| buffer(value); |
| } |
| |
| @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 { |
| flushIfNotAvailable(MAX_VARINT32_SIZE); |
| bufferUInt32NoTag(value); |
| } |
| |
| @Override |
| public void writeFixed32NoTag(final int value) throws IOException { |
| flushIfNotAvailable(FIXED32_SIZE); |
| bufferFixed32NoTag(value); |
| } |
| |
| @Override |
| public void writeUInt64NoTag(long value) throws IOException { |
| flushIfNotAvailable(MAX_VARINT_SIZE); |
| bufferUInt64NoTag(value); |
| } |
| |
| @Override |
| public void writeFixed64NoTag(final long value) throws IOException { |
| flushIfNotAvailable(FIXED64_SIZE); |
| bufferFixed64NoTag(value); |
| } |
| |
| @Override |
| public void writeStringNoTag(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 = computeUInt32SizeNoTag(maxLength); |
| |
| // If we are streaming and the potential length is too big to fit in our buffer, we take the |
| // slower path. |
| if (maxLengthVarIntSize + maxLength > limit) { |
| // 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); |
| writeUInt32NoTag(actualLength); |
| writeLazy(encodedBytes, 0, actualLength); |
| return; |
| } |
| |
| // Fast path: we have enough space available in our buffer for the string... |
| if (maxLengthVarIntSize + maxLength > limit - position) { |
| // Flush to free up space. |
| doFlush(); |
| } |
| |
| final int oldPosition = position; |
| try { |
| // 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 = computeUInt32SizeNoTag(value.length()); |
| |
| 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; |
| int length = newPosition - oldPosition - minLengthVarIntSize; |
| bufferUInt32NoTag(length); |
| position = newPosition; |
| totalBytesWritten += length; |
| } else { |
| int length = Utf8.encodedLength(value); |
| bufferUInt32NoTag(length); |
| position = Utf8.encode(value, buffer, position, length); |
| totalBytesWritten += length; |
| } |
| } catch (UnpairedSurrogateException e) { |
| // Roll back the change and convert to an IOException. |
| totalBytesWritten -= position - oldPosition; |
| 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() throws IOException { |
| if (position > 0) { |
| // Flush the buffer. |
| doFlush(); |
| } |
| } |
| |
| @Override |
| public void write(byte[] value, int offset, int length) throws IOException { |
| flush(); |
| out.write(value, offset, length); |
| totalBytesWritten += length; |
| } |
| |
| @Override |
| public void writeLazy(byte[] value, int offset, int length) throws IOException { |
| flush(); |
| out.writeLazy(value, offset, length); |
| totalBytesWritten += length; |
| } |
| |
| @Override |
| public void write(ByteBuffer value) throws IOException { |
| flush(); |
| int length = value.remaining(); |
| out.write(value); |
| totalBytesWritten += length; |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) throws IOException { |
| flush(); |
| int length = value.remaining(); |
| out.writeLazy(value); |
| totalBytesWritten += length; |
| } |
| |
| private void flushIfNotAvailable(int requiredSize) throws IOException { |
| if (limit - position < requiredSize) { |
| doFlush(); |
| } |
| } |
| |
| private void doFlush() throws IOException { |
| out.write(buffer, 0, position); |
| position = 0; |
| } |
| } |
| |
| /** |
| * An {@link CodedOutputStream} that decorates an {@link OutputStream}. It performs internal |
| * buffering to optimize writes to the {@link OutputStream}. |
| */ |
| private static final class OutputStreamEncoder extends AbstractBufferedEncoder { |
| private final OutputStream out; |
| |
| OutputStreamEncoder(OutputStream 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 + FIXED32_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 + FIXED64_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); |
| } |
| } |
| |
| @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 { |
| if (position == limit) { |
| doFlush(); |
| } |
| |
| buffer(value); |
| } |
| |
| @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 { |
| flushIfNotAvailable(MAX_VARINT32_SIZE); |
| bufferUInt32NoTag(value); |
| } |
| |
| @Override |
| public void writeFixed32NoTag(final int value) throws IOException { |
| flushIfNotAvailable(FIXED32_SIZE); |
| bufferFixed32NoTag(value); |
| } |
| |
| @Override |
| public void writeUInt64NoTag(long value) throws IOException { |
| flushIfNotAvailable(MAX_VARINT_SIZE); |
| bufferUInt64NoTag(value); |
| } |
| |
| @Override |
| public void writeFixed64NoTag(final long value) throws IOException { |
| flushIfNotAvailable(FIXED64_SIZE); |
| bufferFixed64NoTag(value); |
| } |
| |
| @Override |
| public void writeStringNoTag(String value) throws IOException { |
| 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); |
| |
| // If we are streaming and the potential length is too big to fit in our buffer, we take the |
| // slower path. |
| if (maxLengthVarIntSize + maxLength > limit) { |
| // 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); |
| writeUInt32NoTag(actualLength); |
| writeLazy(encodedBytes, 0, actualLength); |
| return; |
| } |
| |
| // Fast path: we have enough space available in our buffer for the string... |
| if (maxLengthVarIntSize + maxLength > limit - position) { |
| // Flush to free up space. |
| doFlush(); |
| } |
| |
| // 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 = computeUInt32SizeNoTag(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; |
| bufferUInt32NoTag(length); |
| position = newPosition; |
| } else { |
| length = Utf8.encodedLength(value); |
| bufferUInt32NoTag(length); |
| position = Utf8.encode(value, buffer, position, length); |
| } |
| totalBytesWritten += length; |
| } catch (UnpairedSurrogateException e) { |
| // Be extra careful and restore the original position for retrying the write with the |
| // less efficient path. |
| totalBytesWritten -= position - oldPosition; |
| position = oldPosition; |
| throw e; |
| } catch (ArrayIndexOutOfBoundsException e) { |
| throw new OutOfSpaceException(e); |
| } |
| } catch (UnpairedSurrogateException e) { |
| inefficientWriteStringNoTag(value, e); |
| } |
| } |
| |
| @Override |
| public void flush() throws IOException { |
| if (position > 0) { |
| // Flush the buffer. |
| doFlush(); |
| } |
| } |
| |
| @Override |
| public void write(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; |
| doFlush(); |
| |
| // 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. |
| out.write(value, offset, length); |
| } |
| totalBytesWritten += 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 { |
| int length = value.remaining(); |
| if (limit - position >= length) { |
| // We have room in the current buffer. |
| value.get(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; |
| value.get(buffer, position, bytesWritten); |
| length -= bytesWritten; |
| position = limit; |
| totalBytesWritten += bytesWritten; |
| doFlush(); |
| |
| // Now deal with the rest. |
| // Since we have an output stream, this is our buffer |
| // and buffer offset == 0 |
| while (length > limit) { |
| // Copy data into the buffer before writing it to OutputStream. |
| value.get(buffer, 0, limit); |
| out.write(buffer, 0, limit); |
| length -= limit; |
| totalBytesWritten += limit; |
| } |
| value.get(buffer, 0, length); |
| position = length; |
| totalBytesWritten += length; |
| } |
| } |
| |
| @Override |
| public void writeLazy(ByteBuffer value) throws IOException { |
| write(value); |
| } |
| |
| private void flushIfNotAvailable(int requiredSize) throws IOException { |
| if (limit - position < requiredSize) { |
| doFlush(); |
| } |
| } |
| |
| private void doFlush() throws IOException { |
| out.write(buffer, 0, position); |
| position = 0; |
| } |
| } |
| } |