summaryrefslogtreecommitdiffstats
path: root/java/src/main/java/com/google/protobuf/CodedOutputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/main/java/com/google/protobuf/CodedOutputStream.java')
-rw-r--r--java/src/main/java/com/google/protobuf/CodedOutputStream.java282
1 files changed, 275 insertions, 7 deletions
diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
index 58dd150..fafe035 100644
--- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java
+++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -30,9 +30,10 @@
package com.google.protobuf;
-import java.io.OutputStream;
import java.io.IOException;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
/**
* Encodes and writes protocol message fields.
@@ -52,6 +53,7 @@ public final class CodedOutputStream {
private final byte[] buffer;
private final int limit;
private int position;
+ private int totalBytesWritten = 0;
private final OutputStream output;
@@ -128,6 +130,38 @@ public final class CodedOutputStream {
return new CodedOutputStream(flatArray, offset, length);
}
+ /**
+ * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
+ */
+ public static CodedOutputStream newInstance(ByteBuffer byteBuffer) {
+ return newInstance(byteBuffer, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Create a new {@code CodedOutputStream} that writes to the given ByteBuffer.
+ */
+ public static CodedOutputStream newInstance(ByteBuffer byteBuffer,
+ int bufferSize) {
+ return newInstance(new ByteBufferOutputStream(byteBuffer), bufferSize);
+ }
+
+ private static class ByteBufferOutputStream extends OutputStream {
+ private final ByteBuffer byteBuffer;
+ public ByteBufferOutputStream(ByteBuffer byteBuffer) {
+ this.byteBuffer = byteBuffer;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ byteBuffer.put((byte) b);
+ }
+
+ @Override
+ public void write(byte[] data, int offset, int length) throws IOException {
+ byteBuffer.put(data, offset, length);
+ }
+ }
+
// -----------------------------------------------------------------
/** Write a {@code double} field, including tag, to the stream. */
@@ -201,6 +235,7 @@ public final class CodedOutputStream {
writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
}
+
/**
* Write a group represented by an {@link UnknownFieldSet}.
*
@@ -221,6 +256,7 @@ public final class CodedOutputStream {
writeMessageNoTag(value);
}
+
/** Write a {@code bytes} field, including tag, to the stream. */
public void writeBytes(final int fieldNumber, final ByteString value)
throws IOException {
@@ -228,6 +264,39 @@ public final class CodedOutputStream {
writeBytesNoTag(value);
}
+ /** Write a {@code bytes} field, including tag, to the stream. */
+ public void writeByteArray(final int fieldNumber, final byte[] value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value);
+ }
+
+ /** Write a {@code bytes} field, including tag, to the stream. */
+ public void writeByteArray(final int fieldNumber,
+ final byte[] value,
+ final int offset,
+ final int length)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteArrayNoTag(value, offset, length);
+ }
+
+ /**
+ * 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())}.
+ */
+ public void writeByteBuffer(final int fieldNumber, final ByteBuffer value)
+ throws IOException {
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED);
+ writeByteBufferNoTag(value);
+ }
+
/** Write a {@code uint32} field, including tag, to the stream. */
public void writeUInt32(final int fieldNumber, final int value)
throws IOException {
@@ -361,6 +430,7 @@ public final class CodedOutputStream {
value.writeTo(this);
}
+
/**
* Write a group represented by an {@link UnknownFieldSet}.
*
@@ -379,11 +449,39 @@ public final class CodedOutputStream {
value.writeTo(this);
}
+
/** Write a {@code bytes} field to the stream. */
public void writeBytesNoTag(final ByteString value) throws IOException {
- final byte[] bytes = value.toByteArray();
- writeRawVarint32(bytes.length);
- writeRawBytes(bytes);
+ writeRawVarint32(value.size());
+ writeRawBytes(value);
+ }
+
+ /** Write a {@code bytes} field to the stream. */
+ public void writeByteArrayNoTag(final byte[] value) throws IOException {
+ writeRawVarint32(value.length);
+ writeRawBytes(value);
+ }
+
+ /** Write a {@code bytes} field to the stream. */
+ public void writeByteArrayNoTag(final byte[] value,
+ final int offset,
+ final int length) throws IOException {
+ writeRawVarint32(length);
+ writeRawBytes(value, offset, length);
+ }
+
+ /**
+ * Write a {@code bytes} field to the stream. This method will write all
+ * content of the ByteBuffer regardless of the current position and limit
+ * (i.e., the number of bytes to be written is value.capacity(), not
+ * value.remaining()). Furthermore, this method doesn't alter the state of
+ * the passed-in ByteBuffer. Its position, limit, mark, etc. will remain
+ * unchanged. If you only want to write the remaining bytes of a ByteBuffer,
+ * you can call {@code writeByteBufferNoTag(byteBuffer.slice())}.
+ */
+ public void writeByteBufferNoTag(final ByteBuffer value) throws IOException {
+ writeRawVarint32(value.capacity());
+ writeRawBytes(value);
}
/** Write a {@code uint32} field to the stream. */
@@ -396,7 +494,7 @@ public final class CodedOutputStream {
* for converting the enum value to its numeric value.
*/
public void writeEnumNoTag(final int value) throws IOException {
- writeRawVarint32(value);
+ writeInt32NoTag(value);
}
/** Write an {@code sfixed32} field to the stream. */
@@ -541,6 +639,33 @@ public final class CodedOutputStream {
/**
* Compute the number of bytes that would be needed to encode a
+ * {@code bytes} field, including tag.
+ */
+ public static int computeByteArraySize(final int fieldNumber,
+ final byte[] value) {
+ 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 a
* {@code uint32} field, including tag.
*/
public static int computeUInt32Size(final int fieldNumber, final int value) {
@@ -614,6 +739,18 @@ public final class CodedOutputStream {
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);
+ }
+
// -----------------------------------------------------------------
/**
@@ -730,6 +867,15 @@ public final class CodedOutputStream {
}
/**
+ * 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) {
+ final int size = value.getSerializedSize();
+ return computeRawVarint32Size(size) + size;
+ }
+
+ /**
* Compute the number of bytes that would be needed to encode a
* {@code bytes} field.
*/
@@ -740,6 +886,22 @@ public final class CodedOutputStream {
/**
* Compute the number of bytes that would be needed to encode a
+ * {@code bytes} field.
+ */
+ public static int computeByteArraySizeNoTag(final byte[] value) {
+ return computeRawVarint32Size(value.length) + 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 computeRawVarint32Size(value.capacity()) + value.capacity();
+ }
+
+ /**
+ * Compute the number of bytes that would be needed to encode a
* {@code uint32} field.
*/
public static int computeUInt32SizeNoTag(final int value) {
@@ -751,7 +913,7 @@ public final class CodedOutputStream {
* Caller is responsible for converting the enum value to its numeric value.
*/
public static int computeEnumSizeNoTag(final int value) {
- return computeRawVarint32Size(value);
+ return computeInt32SizeNoTag(value);
}
/**
@@ -856,6 +1018,15 @@ public final class CodedOutputStream {
}
}
+ /**
+ * 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 int getTotalBytesWritten() {
+ return totalBytesWritten;
+ }
+
/** Write a single byte. */
public void writeRawByte(final byte value) throws IOException {
if (position == limit) {
@@ -863,6 +1034,7 @@ public final class CodedOutputStream {
}
buffer[position++] = value;
+ ++totalBytesWritten;
}
/** Write a single byte, represented by an integer value. */
@@ -870,11 +1042,71 @@ public final class CodedOutputStream {
writeRawByte((byte) value);
}
+ /** Write a byte string. */
+ public void writeRawBytes(final ByteString value) throws IOException {
+ writeRawBytes(value, 0, value.size());
+ }
+
/** Write an array of bytes. */
public void writeRawBytes(final byte[] value) throws IOException {
writeRawBytes(value, 0, value.length);
}
+ /**
+ * Write a ByteBuffer. This method will write all content of the ByteBuffer
+ * regardless of the current position and limit (i.e., the number of bytes
+ * to be written is value.capacity(), not value.remaining()). Furthermore,
+ * this method doesn't alter the state of the passed-in ByteBuffer. Its
+ * position, limit, mark, etc. will remain unchanged. If you only want to
+ * write the remaining bytes of a ByteBuffer, you can call
+ * {@code writeRawBytes(byteBuffer.slice())}.
+ */
+ public void writeRawBytes(final ByteBuffer value) throws IOException {
+ if (value.hasArray()) {
+ writeRawBytes(value.array(), value.arrayOffset(), value.capacity());
+ } else {
+ ByteBuffer duplicated = value.duplicate();
+ duplicated.clear();
+ writeRawBytesInternal(duplicated);
+ }
+ }
+
+ /** Write a ByteBuffer that isn't backed by an array. */
+ private void writeRawBytesInternal(final 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;
+ refreshBuffer();
+
+ // 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.
+ // TODO(xiaofeng): Introduce ZeroCopyOutputStream to avoid this copy.
+ value.get(buffer, 0, limit);
+ output.write(buffer, 0, limit);
+ length -= limit;
+ totalBytesWritten += limit;
+ }
+ value.get(buffer, 0, length);
+ position = length;
+ totalBytesWritten += length;
+ }
+ }
+
/** Write part of an array of bytes. */
public void writeRawBytes(final byte[] value, int offset, int length)
throws IOException {
@@ -882,6 +1114,7 @@ public final class CodedOutputStream {
// 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.
@@ -890,6 +1123,7 @@ public final class CodedOutputStream {
offset += bytesWritten;
length -= bytesWritten;
position = limit;
+ totalBytesWritten += bytesWritten;
refreshBuffer();
// Now deal with the rest.
@@ -903,6 +1137,40 @@ public final class CodedOutputStream {
// Write is very big. Let's do it all at once.
output.write(value, offset, length);
}
+ totalBytesWritten += length;
+ }
+ }
+
+ /** Write part of a byte string. */
+ public void writeRawBytes(final ByteString value, int offset, int length)
+ throws IOException {
+ if (limit - position >= length) {
+ // We have room in the current buffer.
+ value.copyTo(buffer, offset, position, length);
+ position += length;
+ totalBytesWritten += length;
+ } else {
+ // Write extends past current buffer. Fill the rest of this buffer and
+ // flush.
+ final int bytesWritten = limit - position;
+ value.copyTo(buffer, offset, position, bytesWritten);
+ offset += bytesWritten;
+ length -= bytesWritten;
+ position = limit;
+ totalBytesWritten += bytesWritten;
+ refreshBuffer();
+
+ // Now deal with the rest.
+ // Since we have an output stream, this is our buffer
+ // and buffer offset == 0
+ if (length <= limit) {
+ // Fits in new buffer.
+ value.copyTo(buffer, offset, 0, length);
+ position = length;
+ } else {
+ value.writeTo(output, offset, length);
+ }
+ totalBytesWritten += length;
}
}