| Index: third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
|
| diff --git a/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ad174d0ff6049c73dfad44cc55ed02e29284fb06
|
| --- /dev/null
|
| +++ b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java
|
| @@ -0,0 +1,2664 @@
|
| +// 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 java.lang.Math.max;
|
| +
|
| +import com.google.protobuf.Utf8.UnpairedSurrogateException;
|
| +
|
| +import java.io.IOException;
|
| +import java.io.OutputStream;
|
| +import java.lang.reflect.Field;
|
| +import java.nio.BufferOverflowException;
|
| +import java.nio.ByteBuffer;
|
| +import java.nio.ByteOrder;
|
| +import java.security.AccessController;
|
| +import java.security.PrivilegedExceptionAction;
|
| +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 sun.misc.Unsafe UNSAFE = getUnsafe();
|
| + private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations();
|
| + private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset();
|
| +
|
| + private static final int FIXED_32_SIZE = 4;
|
| + private static final int FIXED_64_SIZE = 8;
|
| + private static final int MAX_VARINT_SIZE = 10;
|
| +
|
| + /**
|
| + * @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead.
|
| + */
|
| + @Deprecated
|
| + public static final int LITTLE_ENDIAN_32_SIZE = FIXED_32_SIZE;
|
| +
|
| + /**
|
| + * The buffer size used in {@link #newInstance(OutputStream)}.
|
| + */
|
| + public static final int DEFAULT_BUFFER_SIZE = 4096;
|
| +
|
| + /**
|
| + * Returns the buffer size to efficiently write dataLength bytes to this
|
| + * CodedOutputStream. Used by AbstractMessageLite.
|
| + *
|
| + * @return the buffer size to efficiently write dataLength bytes to this
|
| + * CodedOutputStream.
|
| + */
|
| + static int computePreferredBufferSize(int dataLength) {
|
| + if (dataLength > DEFAULT_BUFFER_SIZE) {
|
| + return DEFAULT_BUFFER_SIZE;
|
| + }
|
| + return dataLength;
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream}.
|
| + *
|
| + * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or
|
| + * modify the provided byte arrays. Doing so may result in corrupted data, which would be
|
| + * difficult to debug.
|
| + */
|
| + public static CodedOutputStream newInstance(final OutputStream output) {
|
| + return newInstance(output, DEFAULT_BUFFER_SIZE);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} wrapping the given {@code OutputStream} with a given
|
| + * buffer size.
|
| + *
|
| + * <p> NOTE: The provided {@link OutputStream} <strong>MUST NOT</strong> retain access or
|
| + * modify the provided byte arrays. Doing so may result in corrupted data, which would be
|
| + * difficult to debug.
|
| + */
|
| + public static CodedOutputStream newInstance(final OutputStream output, final int bufferSize) {
|
| + return new OutputStreamEncoder(output, bufferSize);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} that writes directly to the given
|
| + * byte array. If more bytes are written than fit in the array,
|
| + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat
|
| + * array is faster than writing to an {@code OutputStream}. See also
|
| + * {@link ByteString#newCodedBuilder}.
|
| + */
|
| + public static CodedOutputStream newInstance(final byte[] flatArray) {
|
| + return newInstance(flatArray, 0, flatArray.length);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} that writes directly to the given
|
| + * byte array slice. If more bytes are written than fit in the slice,
|
| + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat
|
| + * array is faster than writing to an {@code OutputStream}. See also
|
| + * {@link ByteString#newCodedBuilder}.
|
| + */
|
| + public static CodedOutputStream newInstance(
|
| + final byte[] flatArray, final int offset, final int length) {
|
| + return new ArrayEncoder(flatArray, offset, length);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}.
|
| + */
|
| + public static CodedOutputStream newInstance(ByteBuffer byteBuffer) {
|
| + if (byteBuffer.hasArray()) {
|
| + return new NioHeapEncoder(byteBuffer);
|
| + }
|
| + return new NioEncoder(byteBuffer);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}.
|
| + *
|
| + * @deprecated the size parameter is no longer used since use of an internal buffer is useless
|
| + * (and wasteful) when writing to a {@link ByteBuffer}. Use {@link #newInstance(ByteBuffer)}
|
| + * instead.
|
| + */
|
| + @Deprecated
|
| + public static CodedOutputStream newInstance(ByteBuffer byteBuffer,
|
| + @SuppressWarnings("unused") int unused) {
|
| + return newInstance(byteBuffer);
|
| + }
|
| +
|
| + /**
|
| + * Create a new {@code CodedOutputStream} that writes to the provided {@link ByteOutput}.
|
| + *
|
| + * <p> NOTE: The {@link ByteOutput} <strong>MUST NOT</strong> modify the provided buffers. Doing
|
| + * so may result in corrupted data, which would be difficult to debug.
|
| + *
|
| + * @param byteOutput the output target for encoded bytes.
|
| + * @param bufferSize the size of the internal scratch buffer to be used for string encoding.
|
| + * Setting this to {@code 0} will disable buffering, requiring an allocation for each encoded
|
| + * string.
|
| + */
|
| + static CodedOutputStream newInstance(ByteOutput byteOutput, int bufferSize) {
|
| + if (bufferSize < 0) {
|
| + throw new IllegalArgumentException("bufferSize must be positive");
|
| + }
|
| +
|
| + return new ByteOutputEncoder(byteOutput, bufferSize);
|
| + }
|
| +
|
| + // Disallow construction outside of this class.
|
| + private CodedOutputStream() {
|
| + }
|
| +
|
| + // -----------------------------------------------------------------
|
| +
|
| + /** Encode and write a tag. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeTag(int fieldNumber, int wireType) throws IOException;
|
| +
|
| + /** Write an {@code int32} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeInt32(int fieldNumber, int value) throws IOException;
|
| +
|
| + /** Write a {@code uint32} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeUInt32(int fieldNumber, int value) throws IOException;
|
| +
|
| + /** Write a {@code sint32} field, including tag, to the stream. */
|
| + public final void writeSInt32(final int fieldNumber, final int value) throws IOException {
|
| + writeUInt32(fieldNumber, encodeZigZag32(value));
|
| + }
|
| +
|
| + /** Write a {@code fixed32} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeFixed32(int fieldNumber, int value) throws IOException;
|
| +
|
| + /** Write an {@code sfixed32} field, including tag, to the stream. */
|
| + public final void writeSFixed32(final int fieldNumber, final int value) throws IOException {
|
| + writeFixed32(fieldNumber, value);
|
| + }
|
| +
|
| + /** Write an {@code int64} field, including tag, to the stream. */
|
| + public final void writeInt64(final int fieldNumber, final long value) throws IOException {
|
| + writeUInt64(fieldNumber, value);
|
| + }
|
| +
|
| + /** Write a {@code uint64} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeUInt64(int fieldNumber, long value) throws IOException;
|
| +
|
| + /** Write an {@code sint64} field, including tag, to the stream. */
|
| + public final void writeSInt64(final int fieldNumber, final long value) throws IOException {
|
| + writeUInt64(fieldNumber, encodeZigZag64(value));
|
| + }
|
| +
|
| + /** Write a {@code fixed64} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeFixed64(int fieldNumber, long value) throws IOException;
|
| +
|
| + /** Write an {@code sfixed64} field, including tag, to the stream. */
|
| + public final void writeSFixed64(final int fieldNumber, final long value) throws IOException {
|
| + writeFixed64(fieldNumber, value);
|
| + }
|
| +
|
| + /** Write a {@code float} field, including tag, to the stream. */
|
| + public final void writeFloat(final int fieldNumber, final float value) throws IOException {
|
| + writeFixed32(fieldNumber, Float.floatToRawIntBits(value));
|
| + }
|
| +
|
| + /** Write a {@code double} field, including tag, to the stream. */
|
| + public final void writeDouble(final int fieldNumber, final double value) throws IOException {
|
| + writeFixed64(fieldNumber, Double.doubleToRawLongBits(value));
|
| + }
|
| +
|
| + /** Write a {@code bool} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeBool(int fieldNumber, boolean value) throws IOException;
|
| +
|
| + /**
|
| + * Write an enum field, including tag, to the stream. The provided value is the numeric
|
| + * value used to represent the enum value on the wire (not the enum ordinal value).
|
| + */
|
| + public final void writeEnum(final int fieldNumber, final int value) throws IOException {
|
| + writeInt32(fieldNumber, value);
|
| + }
|
| +
|
| + /** Write a {@code string} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeString(int fieldNumber, String value) throws IOException;
|
| +
|
| + /** Write a {@code bytes} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeBytes(int fieldNumber, ByteString value) throws IOException;
|
| +
|
| + /** Write a {@code bytes} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeByteArray(int fieldNumber, byte[] value) throws IOException;
|
| +
|
| + /** Write a {@code bytes} field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeByteArray(int fieldNumber, byte[] value, int offset, int length)
|
| + throws IOException;
|
| +
|
| + /**
|
| + * Write a {@code bytes} field, including tag, to the stream.
|
| + * This method will write all content of the ByteBuffer regardless of the
|
| + * current position and limit (i.e., the number of bytes to be written is
|
| + * value.capacity(), not value.remaining()). Furthermore, this method doesn't
|
| + * alter the state of the passed-in ByteBuffer. Its position, limit, mark,
|
| + * etc. will remain unchanged. If you only want to write the remaining bytes
|
| + * of a ByteBuffer, you can call
|
| + * {@code writeByteBuffer(fieldNumber, byteBuffer.slice())}.
|
| + */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException;
|
| +
|
| + /**
|
| + * Write a single byte.
|
| + */
|
| + public final void writeRawByte(final byte value) throws IOException {
|
| + write(value);
|
| + }
|
| +
|
| + /** Write a single byte, represented by an integer value. */
|
| + public final void writeRawByte(final int value) throws IOException {
|
| + write((byte) value);
|
| + }
|
| +
|
| + /** Write an array of bytes. */
|
| + public final void writeRawBytes(final byte[] value) throws IOException {
|
| + write(value, 0, value.length);
|
| + }
|
| +
|
| + /**
|
| + * Write part of an array of bytes.
|
| + */
|
| + public final void writeRawBytes(final byte[] value, int offset, int length) throws IOException {
|
| + write(value, offset, length);
|
| + }
|
| +
|
| + /** Write a byte string. */
|
| + public final void writeRawBytes(final ByteString value) throws IOException {
|
| + value.writeTo(this);
|
| + }
|
| +
|
| + /**
|
| + * Write a ByteBuffer. This method will write all content of the ByteBuffer
|
| + * regardless of the current position and limit (i.e., the number of bytes
|
| + * to be written is value.capacity(), not value.remaining()). Furthermore,
|
| + * this method doesn't alter the state of the passed-in ByteBuffer. Its
|
| + * position, limit, mark, etc. will remain unchanged. If you only want to
|
| + * write the remaining bytes of a ByteBuffer, you can call
|
| + * {@code writeRawBytes(byteBuffer.slice())}.
|
| + */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeRawBytes(final ByteBuffer value) throws IOException;
|
| +
|
| + /** Write an embedded message field, including tag, to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeMessage(final int fieldNumber, final MessageLite value)
|
| + throws IOException;
|
| +
|
| + /**
|
| + * Write a MessageSet extension field to the stream. For historical reasons,
|
| + * the wire format differs from normal fields.
|
| + */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeMessageSetExtension(final int fieldNumber, final MessageLite value)
|
| + throws IOException;
|
| +
|
| + /**
|
| + * Write an unparsed MessageSet extension field to the stream. For
|
| + * historical reasons, the wire format differs from normal fields.
|
| + */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeRawMessageSetExtension(final int fieldNumber, final ByteString value)
|
| + throws IOException;
|
| +
|
| + // -----------------------------------------------------------------
|
| +
|
| + /** Write an {@code int32} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeInt32NoTag(final int value) throws IOException;
|
| +
|
| + /** Write a {@code uint32} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeUInt32NoTag(int value) throws IOException;
|
| +
|
| + /** Write a {@code sint32} field to the stream. */
|
| + public final void writeSInt32NoTag(final int value) throws IOException {
|
| + writeUInt32NoTag(encodeZigZag32(value));
|
| + }
|
| +
|
| + /** Write a {@code fixed32} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeFixed32NoTag(int value) throws IOException;
|
| +
|
| + /** Write a {@code sfixed32} field to the stream. */
|
| + public final void writeSFixed32NoTag(final int value) throws IOException {
|
| + writeFixed32NoTag(value);
|
| + }
|
| +
|
| + /** Write an {@code int64} field to the stream. */
|
| + public final void writeInt64NoTag(final long value) throws IOException {
|
| + writeUInt64NoTag(value);
|
| + }
|
| +
|
| + /** Write a {@code uint64} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeUInt64NoTag(long value) throws IOException;
|
| +
|
| + /** Write a {@code sint64} field to the stream. */
|
| + public final void writeSInt64NoTag(final long value) throws IOException {
|
| + writeUInt64NoTag(encodeZigZag64(value));
|
| + }
|
| +
|
| + /** Write a {@code fixed64} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeFixed64NoTag(long value) throws IOException;
|
| +
|
| + /** Write a {@code sfixed64} field to the stream. */
|
| + public final void writeSFixed64NoTag(final long value) throws IOException {
|
| + writeFixed64NoTag(value);
|
| + }
|
| +
|
| + /** Write a {@code float} field to the stream. */
|
| + public final void writeFloatNoTag(final float value) throws IOException {
|
| + writeFixed32NoTag(Float.floatToRawIntBits(value));
|
| + }
|
| +
|
| + /** Write a {@code double} field to the stream. */
|
| + public final void writeDoubleNoTag(final double value) throws IOException {
|
| + writeFixed64NoTag(Double.doubleToRawLongBits(value));
|
| + }
|
| +
|
| + /** Write a {@code bool} field to the stream. */
|
| + public final void writeBoolNoTag(final boolean value) throws IOException {
|
| + write((byte) (value ? 1 : 0));
|
| + }
|
| +
|
| + /**
|
| + * Write an enum field to the stream. The provided value is the numeric
|
| + * value used to represent the enum value on the wire (not the enum ordinal value).
|
| + */
|
| + public final void writeEnumNoTag(final int value) throws IOException {
|
| + writeInt32NoTag(value);
|
| + }
|
| +
|
| + /** Write a {@code string} field to the stream. */
|
| + // TODO(dweis): Document behavior on ill-formed UTF-16 input.
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeStringNoTag(String value) throws IOException;
|
| +
|
| + /** Write a {@code bytes} field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeBytesNoTag(final ByteString value) throws IOException;
|
| +
|
| + /** Write a {@code bytes} field to the stream. */
|
| + public final void writeByteArrayNoTag(final byte[] value) throws IOException {
|
| + writeByteArrayNoTag(value, 0, value.length);
|
| + }
|
| +
|
| + /** Write an embedded message field to the stream. */
|
| + // Abstract to avoid overhead of additional virtual method calls.
|
| + public abstract void writeMessageNoTag(final MessageLite value) throws IOException;
|
| +
|
| + //=================================================================
|
| +
|
| + @ExperimentalApi
|
| + @Override
|
| + public abstract void write(byte value) throws IOException;
|
| +
|
| + @ExperimentalApi
|
| + @Override
|
| + public abstract void write(byte[] value, int offset, int length) throws IOException;
|
| +
|
| + @ExperimentalApi
|
| + @Override
|
| + public abstract void writeLazy(byte[] value, int offset, int length) throws IOException;
|
| +
|
| + @Override
|
| + public abstract void write(ByteBuffer value) throws IOException;
|
| +
|
| + @ExperimentalApi
|
| + @Override
|
| + public abstract void writeLazy(ByteBuffer value) throws IOException;
|
| +
|
| + // =================================================================
|
| + // =================================================================
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code int32} field, including tag.
|
| + */
|
| + public static int computeInt32Size(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code uint32} field, including tag.
|
| + */
|
| + public static int computeUInt32Size(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sint32} field, including tag.
|
| + */
|
| + public static int computeSInt32Size(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code fixed32} field, including tag.
|
| + */
|
| + public static int computeFixed32Size(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sfixed32} field, including tag.
|
| + */
|
| + public static int computeSFixed32Size(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code int64} field, including tag.
|
| + */
|
| + public static int computeInt64Size(final int fieldNumber, final long value) {
|
| + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code uint64} field, including tag.
|
| + */
|
| + public static int computeUInt64Size(final int fieldNumber, final long value) {
|
| + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sint64} field, including tag.
|
| + */
|
| + public static int computeSInt64Size(final int fieldNumber, final long value) {
|
| + return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code fixed64} field, including tag.
|
| + */
|
| + public static int computeFixed64Size(final int fieldNumber, final long value) {
|
| + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sfixed64} field, including tag.
|
| + */
|
| + public static int computeSFixed64Size(final int fieldNumber, final long value) {
|
| + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code float} field, including tag.
|
| + */
|
| + public static int computeFloatSize(final int fieldNumber, final float value) {
|
| + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code double} field, including tag.
|
| + */
|
| + public static int computeDoubleSize(final int fieldNumber, final double value) {
|
| + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bool} field, including tag.
|
| + */
|
| + public static int computeBoolSize(final int fieldNumber, final boolean value) {
|
| + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * enum field, including tag. The provided value is the numeric
|
| + * value used to represent the enum value on the wire (not the enum ordinal value).
|
| + */
|
| + public static int computeEnumSize(final int fieldNumber, final int value) {
|
| + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code string} field, including tag.
|
| + */
|
| + public static int computeStringSize(final int fieldNumber, final String value) {
|
| + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field, including tag.
|
| + */
|
| + public static int computeBytesSize(final int fieldNumber, final ByteString value) {
|
| + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field, including tag.
|
| + */
|
| + public static int computeByteArraySize(final int fieldNumber, final byte[] value) {
|
| + return computeTagSize(fieldNumber) + computeByteArraySizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field, including tag.
|
| + */
|
| + public static int computeByteBufferSize(final int fieldNumber, final ByteBuffer value) {
|
| + return computeTagSize(fieldNumber) + computeByteBufferSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * embedded message in lazy field, including tag.
|
| + */
|
| + public static int computeLazyFieldSize(final int fieldNumber, final LazyFieldLite value) {
|
| + return computeTagSize(fieldNumber) + computeLazyFieldSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * embedded message field, including tag.
|
| + */
|
| + public static int computeMessageSize(final int fieldNumber, final MessageLite value) {
|
| + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * MessageSet extension to the stream. For historical reasons,
|
| + * the wire format differs from normal fields.
|
| + */
|
| + public static int computeMessageSetExtensionSize(final int fieldNumber, final MessageLite value) {
|
| + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
|
| + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
|
| + + computeMessageSize(WireFormat.MESSAGE_SET_MESSAGE, value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * unparsed MessageSet extension field to the stream. For
|
| + * historical reasons, the wire format differs from normal fields.
|
| + */
|
| + public static int computeRawMessageSetExtensionSize(
|
| + final int fieldNumber, final ByteString value) {
|
| + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
|
| + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
|
| + + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * lazily parsed MessageSet extension field to the stream. For
|
| + * historical reasons, the wire format differs from normal fields.
|
| + */
|
| + public static int computeLazyFieldMessageSetExtensionSize(
|
| + final int fieldNumber, final LazyFieldLite value) {
|
| + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2
|
| + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber)
|
| + + computeLazyFieldSize(WireFormat.MESSAGE_SET_MESSAGE, value);
|
| + }
|
| +
|
| + // -----------------------------------------------------------------
|
| +
|
| + /** Compute the number of bytes that would be needed to encode a tag. */
|
| + public static int computeTagSize(final int fieldNumber) {
|
| + return computeUInt32SizeNoTag(WireFormat.makeTag(fieldNumber, 0));
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code int32} field, including tag.
|
| + */
|
| + public static int computeInt32SizeNoTag(final int value) {
|
| + if (value >= 0) {
|
| + return computeUInt32SizeNoTag(value);
|
| + } else {
|
| + // Must sign-extend.
|
| + return MAX_VARINT_SIZE;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code uint32} field.
|
| + */
|
| + public static int computeUInt32SizeNoTag(final int value) {
|
| + if ((value & (~0 << 7)) == 0) {
|
| + return 1;
|
| + }
|
| + if ((value & (~0 << 14)) == 0) {
|
| + return 2;
|
| + }
|
| + if ((value & (~0 << 21)) == 0) {
|
| + return 3;
|
| + }
|
| + if ((value & (~0 << 28)) == 0) {
|
| + return 4;
|
| + }
|
| + return 5;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sint32} field.
|
| + */
|
| + public static int computeSInt32SizeNoTag(final int value) {
|
| + return computeUInt32SizeNoTag(encodeZigZag32(value));
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code fixed32} field.
|
| + */
|
| + public static int computeFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
|
| + return FIXED_32_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sfixed32} field.
|
| + */
|
| + public static int computeSFixed32SizeNoTag(@SuppressWarnings("unused") final int unused) {
|
| + return FIXED_32_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code int64} field, including tag.
|
| + */
|
| + public static int computeInt64SizeNoTag(final long value) {
|
| + return computeUInt64SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code uint64} field, including tag.
|
| + */
|
| + public static int computeUInt64SizeNoTag(long value) {
|
| + // handle two popular special cases up front ...
|
| + if ((value & (~0L << 7)) == 0L) {
|
| + return 1;
|
| + }
|
| + if (value < 0L) {
|
| + return 10;
|
| + }
|
| + // ... leaving us with 8 remaining, which we can divide and conquer
|
| + int n = 2;
|
| + if ((value & (~0L << 35)) != 0L) {
|
| + n += 4; value >>>= 28;
|
| + }
|
| + if ((value & (~0L << 21)) != 0L) {
|
| + n += 2; value >>>= 14;
|
| + }
|
| + if ((value & (~0L << 14)) != 0L) {
|
| + n += 1;
|
| + }
|
| + return n;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sint64} field.
|
| + */
|
| + public static int computeSInt64SizeNoTag(final long value) {
|
| + return computeUInt64SizeNoTag(encodeZigZag64(value));
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code fixed64} field.
|
| + */
|
| + public static int computeFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
|
| + return FIXED_64_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an
|
| + * {@code sfixed64} field.
|
| + */
|
| + public static int computeSFixed64SizeNoTag(@SuppressWarnings("unused") final long unused) {
|
| + return FIXED_64_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code float} field, including tag.
|
| + */
|
| + public static int computeFloatSizeNoTag(@SuppressWarnings("unused") final float unused) {
|
| + return FIXED_32_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code double} field, including tag.
|
| + */
|
| + public static int computeDoubleSizeNoTag(@SuppressWarnings("unused") final double unused) {
|
| + return FIXED_64_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bool} field.
|
| + */
|
| + public static int computeBoolSizeNoTag(@SuppressWarnings("unused") final boolean unused) {
|
| + return 1;
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an enum field.
|
| + * The provided value is the numeric value used to represent the enum value on the wire
|
| + * (not the enum ordinal value).
|
| + */
|
| + public static int computeEnumSizeNoTag(final int value) {
|
| + return computeInt32SizeNoTag(value);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code string} field.
|
| + */
|
| + public static int computeStringSizeNoTag(final String value) {
|
| + int length;
|
| + try {
|
| + length = Utf8.encodedLength(value);
|
| + } catch (UnpairedSurrogateException e) {
|
| + // TODO(dweis): Consider using nio Charset methods instead.
|
| + final byte[] bytes = value.getBytes(Internal.UTF_8);
|
| + length = bytes.length;
|
| + }
|
| +
|
| + return computeLengthDelimitedFieldSize(length);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an embedded
|
| + * message stored in lazy field.
|
| + */
|
| + public static int computeLazyFieldSizeNoTag(final LazyFieldLite value) {
|
| + return computeLengthDelimitedFieldSize(value.getSerializedSize());
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field.
|
| + */
|
| + public static int computeBytesSizeNoTag(final ByteString value) {
|
| + return computeLengthDelimitedFieldSize(value.size());
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field.
|
| + */
|
| + public static int computeByteArraySizeNoTag(final byte[] value) {
|
| + return computeLengthDelimitedFieldSize(value.length);
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode a
|
| + * {@code bytes} field.
|
| + */
|
| + public static int computeByteBufferSizeNoTag(final ByteBuffer value) {
|
| + return computeLengthDelimitedFieldSize(value.capacity());
|
| + }
|
| +
|
| + /**
|
| + * Compute the number of bytes that would be needed to encode an embedded
|
| + * message field.
|
| + */
|
| + public static int computeMessageSizeNoTag(final MessageLite value) {
|
| + return computeLengthDelimitedFieldSize(value.getSerializedSize());
|
| + }
|
| +
|
| + private 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(Throwable cause) {
|
| + super(MESSAGE, 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(new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public final void writeInt32NoTag(int value) throws IOException {
|
| + if (value >= 0) {
|
| + writeUInt32NoTag(value);
|
| + } else {
|
| + // Must sign-extend.
|
| + writeUInt64NoTag(value);
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public final void writeUInt32NoTag(int value) throws IOException {
|
| + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
|
| + long pos = ARRAY_BASE_OFFSET + position;
|
| + while (true) {
|
| + if ((value & ~0x7F) == 0) {
|
| + UNSAFE.putByte(buffer, pos++, (byte) value);
|
| + position++;
|
| + return;
|
| + } else {
|
| + UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
|
| + position++;
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + } else {
|
| + try {
|
| + while (true) {
|
| + if ((value & ~0x7F) == 0) {
|
| + buffer[position++] = (byte) value;
|
| + return;
|
| + } else {
|
| + buffer[position++] = (byte) ((value & 0x7F) | 0x80);
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + } catch (IndexOutOfBoundsException e) {
|
| + throw new OutOfSpaceException(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
|
| + }
|
| + }
|
| + }
|
| +
|
| + @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(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public final void writeUInt64NoTag(long value) throws IOException {
|
| + if (HAS_UNSAFE_ARRAY_OPERATIONS && spaceLeft() >= MAX_VARINT_SIZE) {
|
| + long pos = ARRAY_BASE_OFFSET + position;
|
| + while (true) {
|
| + if ((value & ~0x7FL) == 0) {
|
| + UNSAFE.putByte(buffer, pos++, (byte) value);
|
| + position++;
|
| + return;
|
| + } else {
|
| + UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
|
| + position++;
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + } else {
|
| + try {
|
| + while (true) {
|
| + if ((value & ~0x7FL) == 0) {
|
| + buffer[position++] = (byte) value;
|
| + return;
|
| + } else {
|
| + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80);
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + } catch (IndexOutOfBoundsException e) {
|
| + throw new OutOfSpaceException(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
|
| + }
|
| + }
|
| + }
|
| +
|
| + @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(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)));
|
| + }
|
| + }
|
| +
|
| + @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(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
|
| + }
|
| + }
|
| +
|
| + @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(
|
| + new IndexOutOfBoundsException(
|
| + String.format("Pos: %d, limit: %d, len: %d", position, limit, length)));
|
| + }
|
| + }
|
| +
|
| + @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 NioHeapEncoder extends ArrayEncoder {
|
| + private final ByteBuffer byteBuffer;
|
| + private int initialPosition;
|
| +
|
| + NioHeapEncoder(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 {@link ByteBuffer}.
|
| + */
|
| + private static final class NioEncoder extends CodedOutputStream {
|
| + private final ByteBuffer originalBuffer;
|
| + private final ByteBuffer buffer;
|
| + private final int initialPosition;
|
| +
|
| + NioEncoder(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);
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Abstract base class for buffered encoders.
|
| + */
|
| + private abstract static class AbstractBufferedEncoder extends CodedOutputStream {
|
| + final byte[] buffer;
|
| + final int limit;
|
| + int position;
|
| + int totalBytesWritten;
|
| +
|
| + AbstractBufferedEncoder(int bufferSize) {
|
| + if (bufferSize < 0) {
|
| + throw new IllegalArgumentException("bufferSize must be >= 0");
|
| + }
|
| + // As an optimization, we require that the buffer be able to store at least 2
|
| + // varints so that we can buffer any integer write (tag + value). This reduces the
|
| + // number of range checks for a single write to 1 (i.e. if there is not enough space
|
| + // to buffer the tag+value, flush and then buffer it).
|
| + this.buffer = new byte[max(bufferSize, MAX_VARINT_SIZE * 2)];
|
| + this.limit = buffer.length;
|
| + }
|
| +
|
| + @Override
|
| + public final int spaceLeft() {
|
| + throw new UnsupportedOperationException(
|
| + "spaceLeft() can only be called on CodedOutputStreams that are "
|
| + + "writing to a flat array or ByteBuffer.");
|
| + }
|
| +
|
| + @Override
|
| + public final int getTotalBytesWritten() {
|
| + return totalBytesWritten;
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void buffer(byte value) {
|
| + buffer[position++] = value;
|
| + totalBytesWritten++;
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferTag(final int fieldNumber, final int wireType) {
|
| + bufferUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferInt32NoTag(final int value) {
|
| + if (value >= 0) {
|
| + bufferUInt32NoTag(value);
|
| + } else {
|
| + // Must sign-extend.
|
| + bufferUInt64NoTag(value);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferUInt32NoTag(int value) {
|
| + if (HAS_UNSAFE_ARRAY_OPERATIONS) {
|
| + final long originalPos = ARRAY_BASE_OFFSET + position;
|
| + long pos = originalPos;
|
| + while (true) {
|
| + if ((value & ~0x7F) == 0) {
|
| + UNSAFE.putByte(buffer, pos++, (byte) value);
|
| + break;
|
| + } else {
|
| + UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80));
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + int delta = (int) (pos - originalPos);
|
| + position += delta;
|
| + totalBytesWritten += delta;
|
| + } else {
|
| + while (true) {
|
| + if ((value & ~0x7F) == 0) {
|
| + buffer[position++] = (byte) value;
|
| + totalBytesWritten++;
|
| + return;
|
| + } else {
|
| + buffer[position++] = (byte) ((value & 0x7F) | 0x80);
|
| + totalBytesWritten++;
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferUInt64NoTag(long value) {
|
| + if (HAS_UNSAFE_ARRAY_OPERATIONS) {
|
| + final long originalPos = ARRAY_BASE_OFFSET + position;
|
| + long pos = originalPos;
|
| + while (true) {
|
| + if ((value & ~0x7FL) == 0) {
|
| + UNSAFE.putByte(buffer, pos++, (byte) value);
|
| + break;
|
| + } else {
|
| + UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80));
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + int delta = (int) (pos - originalPos);
|
| + position += delta;
|
| + totalBytesWritten += delta;
|
| + } else {
|
| + while (true) {
|
| + if ((value & ~0x7FL) == 0) {
|
| + buffer[position++] = (byte) value;
|
| + totalBytesWritten++;
|
| + return;
|
| + } else {
|
| + buffer[position++] = (byte) (((int) value & 0x7F) | 0x80);
|
| + totalBytesWritten++;
|
| + value >>>= 7;
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferFixed32NoTag(int value) {
|
| + buffer[position++] = (byte) (value & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 8) & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 16) & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 24) & 0xFF);
|
| + totalBytesWritten += FIXED_32_SIZE;
|
| + }
|
| +
|
| + /**
|
| + * This method does not perform bounds checking on the array. Checking array bounds is the
|
| + * responsibility of the caller.
|
| + */
|
| + final void bufferFixed64NoTag(long value) {
|
| + buffer[position++] = (byte) (value & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 8) & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 16) & 0xFF);
|
| + buffer[position++] = (byte) ((value >> 24) & 0xFF);
|
| + buffer[position++] = (byte) ((int) (value >> 32) & 0xFF);
|
| + buffer[position++] = (byte) ((int) (value >> 40) & 0xFF);
|
| + buffer[position++] = (byte) ((int) (value >> 48) & 0xFF);
|
| + buffer[position++] = (byte) ((int) (value >> 56) & 0xFF);
|
| + totalBytesWritten += FIXED_64_SIZE;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * A {@link CodedOutputStream} that decorates a {@link ByteOutput}. It internal buffer only to
|
| + * support string encoding operations. All other writes are just passed through to the
|
| + * {@link ByteOutput}.
|
| + */
|
| + private static final class ByteOutputEncoder extends AbstractBufferedEncoder {
|
| + private final ByteOutput out;
|
| +
|
| + ByteOutputEncoder(ByteOutput out, int bufferSize) {
|
| + super(bufferSize);
|
| + if (out == null) {
|
| + throw new NullPointerException("out");
|
| + }
|
| + this.out = out;
|
| + }
|
| +
|
| + @Override
|
| + public void writeTag(final int fieldNumber, final int wireType) throws IOException {
|
| + writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType));
|
| + }
|
| +
|
| + @Override
|
| + public void writeInt32(final int fieldNumber, final int value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE * 2);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + bufferInt32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeUInt32(final int fieldNumber, final int value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE * 2);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + bufferUInt32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeFixed32(final int fieldNumber, final int value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_32_SIZE);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
|
| + bufferFixed32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeUInt64(final int fieldNumber, final long value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE * 2);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + bufferUInt64NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeFixed64(final int fieldNumber, final long value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
|
| + bufferFixed64NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBool(final int fieldNumber, final boolean value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE + 1);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + buffer((byte) (value ? 1 : 0));
|
| + }
|
| +
|
| + @Override
|
| + public void writeString(final int fieldNumber, final String value) throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeStringNoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeBytesNoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
|
| + writeByteArray(fieldNumber, value, 0, value.length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArray(
|
| + final int fieldNumber, final byte[] value, final int offset, final int length)
|
| + throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeByteArrayNoTag(value, offset, length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
|
| + throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeUInt32NoTag(value.capacity());
|
| + writeRawBytes(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBytesNoTag(final ByteString value) throws IOException {
|
| + writeUInt32NoTag(value.size());
|
| + value.writeTo(this);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException {
|
| + writeUInt32NoTag(length);
|
| + write(value, offset, length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeRawBytes(final ByteBuffer value) throws IOException {
|
| + if (value.hasArray()) {
|
| + write(value.array(), value.arrayOffset(), value.capacity());
|
| + } else {
|
| + ByteBuffer duplicated = value.duplicate();
|
| + duplicated.clear();
|
| + write(duplicated);
|
| + }
|
| + }
|
| +
|
| + @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_VARINT_SIZE);
|
| + bufferUInt32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeFixed32NoTag(final int value) throws IOException {
|
| + flushIfNotAvailable(FIXED_32_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(FIXED_64_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 + FIXED_32_SIZE);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED32);
|
| + bufferFixed32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeUInt64(final int fieldNumber, final long value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE * 2);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + bufferUInt64NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeFixed64(final int fieldNumber, final long value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE + FIXED_64_SIZE);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_FIXED64);
|
| + bufferFixed64NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBool(final int fieldNumber, final boolean value) throws IOException {
|
| + flushIfNotAvailable(MAX_VARINT_SIZE + 1);
|
| + bufferTag(fieldNumber, WireFormat.WIRETYPE_VARINT);
|
| + buffer((byte) (value ? 1 : 0));
|
| + }
|
| +
|
| + @Override
|
| + public void writeString(final int fieldNumber, final String value) throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeStringNoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBytes(final int fieldNumber, final ByteString value) throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeBytesNoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArray(final int fieldNumber, final byte[] value) throws IOException {
|
| + writeByteArray(fieldNumber, value, 0, value.length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArray(
|
| + final int fieldNumber, final byte[] value, final int offset, final int length)
|
| + throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeByteArrayNoTag(value, offset, length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
|
| + throws IOException {
|
| + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
| + writeUInt32NoTag(value.capacity());
|
| + writeRawBytes(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeBytesNoTag(final ByteString value) throws IOException {
|
| + writeUInt32NoTag(value.size());
|
| + value.writeTo(this);
|
| + }
|
| +
|
| + @Override
|
| + public void writeByteArrayNoTag(final byte[] value, int offset, int length) throws IOException {
|
| + writeUInt32NoTag(length);
|
| + write(value, offset, length);
|
| + }
|
| +
|
| + @Override
|
| + public void writeRawBytes(final ByteBuffer value) throws IOException {
|
| + if (value.hasArray()) {
|
| + write(value.array(), value.arrayOffset(), value.capacity());
|
| + } else {
|
| + ByteBuffer duplicated = value.duplicate();
|
| + duplicated.clear();
|
| + write(duplicated);
|
| + }
|
| + }
|
| +
|
| + @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_VARINT_SIZE);
|
| + bufferUInt32NoTag(value);
|
| + }
|
| +
|
| + @Override
|
| + public void writeFixed32NoTag(final int value) throws IOException {
|
| + flushIfNotAvailable(FIXED_32_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(FIXED_64_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;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this
|
| + * platform.
|
| + */
|
| + private static sun.misc.Unsafe getUnsafe() {
|
| + sun.misc.Unsafe unsafe = null;
|
| + try {
|
| + unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() {
|
| + @Override
|
| + public sun.misc.Unsafe run() throws Exception {
|
| + Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
|
| +
|
| + for (Field f : k.getDeclaredFields()) {
|
| + f.setAccessible(true);
|
| + Object x = f.get(null);
|
| + if (k.isInstance(x)) {
|
| + return k.cast(x);
|
| + }
|
| + }
|
| + // The sun.misc.Unsafe field does not exist.
|
| + return null;
|
| + }
|
| + });
|
| + } catch (Throwable e) {
|
| + // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError
|
| + // for Unsafe.
|
| + }
|
| +
|
| + logger.log(Level.FINEST, "sun.misc.Unsafe: {}",
|
| + unsafe != null ? "available" : "unavailable");
|
| + return unsafe;
|
| + }
|
| +
|
| + /**
|
| + * Indicates whether or not unsafe array operations are supported on this platform.
|
| + */
|
| + // TODO(nathanmittler): Add support for Android's MemoryBlock.
|
| + private static boolean supportsUnsafeArrayOperations() {
|
| + boolean supported = false;
|
| + if (UNSAFE != null) {
|
| + try {
|
| + UNSAFE.getClass().getMethod("arrayBaseOffset", Class.class);
|
| + UNSAFE.getClass().getMethod("putByte", Object.class, long.class, byte.class);
|
| + supported = true;
|
| + } catch (Throwable e) {
|
| + // Do nothing.
|
| + }
|
| + }
|
| + logger.log(Level.FINEST, "Unsafe array operations: {}",
|
| + supported ? "available" : "unavailable");
|
| + return supported;
|
| + }
|
| +
|
| + /**
|
| + * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not
|
| + * available.
|
| + */
|
| + private static <T> int byteArrayBaseOffset() {
|
| + return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1;
|
| + }
|
| +}
|
|
|