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