Index: third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java |
diff --git a/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java |
index ad174d0ff6049c73dfad44cc55ed02e29284fb06..3e32c2c536a87298eb8da64f3d149d944ab96df7 100644 |
--- a/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java |
+++ b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/CodedOutputStream.java |
@@ -30,18 +30,17 @@ |
package com.google.protobuf; |
+import static com.google.protobuf.WireFormat.FIXED_32_SIZE; |
+import static com.google.protobuf.WireFormat.FIXED_64_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.lang.reflect.Field; |
import java.nio.BufferOverflowException; |
import java.nio.ByteBuffer; |
import java.nio.ByteOrder; |
-import java.security.AccessController; |
-import java.security.PrivilegedExceptionAction; |
import java.util.logging.Level; |
import java.util.logging.Logger; |
@@ -59,13 +58,8 @@ import java.util.logging.Logger; |
*/ |
public abstract class CodedOutputStream extends ByteOutput { |
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName()); |
- private static final sun.misc.Unsafe UNSAFE = getUnsafe(); |
- private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = supportsUnsafeArrayOperations(); |
- private static final long ARRAY_BASE_OFFSET = byteArrayBaseOffset(); |
- |
- private static final int FIXED_32_SIZE = 4; |
- private static final int FIXED_64_SIZE = 8; |
- private static final int MAX_VARINT_SIZE = 10; |
+ private static final boolean HAS_UNSAFE_ARRAY_OPERATIONS = UnsafeUtil.hasUnsafeArrayOperations(); |
+ private static final long ARRAY_BASE_OFFSET = UnsafeUtil.getArrayBaseOffset(); |
/** |
* @deprecated Use {@link #computeFixed32SizeNoTag(int)} instead. |
@@ -138,15 +132,66 @@ public abstract class CodedOutputStream extends ByteOutput { |
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); |
+ } |
+ |
/** |
- * Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. |
+ * 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 static CodedOutputStream newInstance(ByteBuffer byteBuffer) { |
- if (byteBuffer.hasArray()) { |
- return new NioHeapEncoder(byteBuffer); |
- } |
- return new NioEncoder(byteBuffer); |
+ void useDeterministicSerialization() { |
+ serializationDeterministic = true; |
+ } |
+ |
+ boolean isSerializationDeterministic() { |
+ return serializationDeterministic; |
} |
+ private boolean serializationDeterministic; |
/** |
* Create a new {@code CodedOutputStream} that writes to the given {@link ByteBuffer}. |
@@ -869,7 +914,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
return computeLengthDelimitedFieldSize(value.getSerializedSize()); |
} |
- private static int computeLengthDelimitedFieldSize(int fieldLength) { |
+ static int computeLengthDelimitedFieldSize(int fieldLength) { |
return computeUInt32SizeNoTag(fieldLength) + fieldLength; |
} |
@@ -945,9 +990,17 @@ public abstract class CodedOutputStream extends ByteOutput { |
super(MESSAGE); |
} |
+ OutOfSpaceException(String explanationMessage) { |
+ super(MESSAGE + ": " + explanationMessage); |
+ } |
+ |
OutOfSpaceException(Throwable cause) { |
super(MESSAGE, cause); |
} |
+ |
+ OutOfSpaceException(String explanationMessage, Throwable cause) { |
+ super(MESSAGE + ": " + explanationMessage, cause); |
+ } |
} |
/** |
@@ -1250,8 +1303,8 @@ public abstract class CodedOutputStream extends ByteOutput { |
try { |
buffer[position++] = value; |
} catch (IndexOutOfBoundsException e) { |
- throw new OutOfSpaceException(new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); |
+ throw new OutOfSpaceException( |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); |
} |
} |
@@ -1271,11 +1324,11 @@ public abstract class CodedOutputStream extends ByteOutput { |
long pos = ARRAY_BASE_OFFSET + position; |
while (true) { |
if ((value & ~0x7F) == 0) { |
- UNSAFE.putByte(buffer, pos++, (byte) value); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) value); |
position++; |
return; |
} else { |
- UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); |
position++; |
value >>>= 7; |
} |
@@ -1293,8 +1346,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
} |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); |
} |
} |
} |
@@ -1308,8 +1360,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
buffer[position++] = (byte) ((value >> 24) & 0xFF); |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); |
} |
} |
@@ -1319,11 +1370,11 @@ public abstract class CodedOutputStream extends ByteOutput { |
long pos = ARRAY_BASE_OFFSET + position; |
while (true) { |
if ((value & ~0x7FL) == 0) { |
- UNSAFE.putByte(buffer, pos++, (byte) value); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) value); |
position++; |
return; |
} else { |
- UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); |
position++; |
value >>>= 7; |
} |
@@ -1341,8 +1392,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
} |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); |
} |
} |
} |
@@ -1360,8 +1410,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
buffer[position++] = (byte) ((int) (value >> 56) & 0xFF); |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, 1))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1), e); |
} |
} |
@@ -1372,8 +1421,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
position += length; |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, length))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e); |
} |
} |
@@ -1390,8 +1438,7 @@ public abstract class CodedOutputStream extends ByteOutput { |
position += length; |
} catch (IndexOutOfBoundsException e) { |
throw new OutOfSpaceException( |
- new IndexOutOfBoundsException( |
- String.format("Pos: %d, limit: %d, len: %d", position, limit, length))); |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length), e); |
} |
} |
@@ -1454,11 +1501,11 @@ public abstract class CodedOutputStream extends ByteOutput { |
* A {@link CodedOutputStream} that writes directly to a heap {@link ByteBuffer}. Writes are |
* done directly to the underlying array. The buffer position is only updated after a flush. |
*/ |
- private static final class NioHeapEncoder extends ArrayEncoder { |
+ private static final class HeapNioEncoder extends ArrayEncoder { |
private final ByteBuffer byteBuffer; |
private int initialPosition; |
- NioHeapEncoder(ByteBuffer byteBuffer) { |
+ HeapNioEncoder(ByteBuffer byteBuffer) { |
super(byteBuffer.array(), byteBuffer.arrayOffset() + byteBuffer.position(), |
byteBuffer.remaining()); |
this.byteBuffer = byteBuffer; |
@@ -1473,14 +1520,15 @@ public abstract class CodedOutputStream extends ByteOutput { |
} |
/** |
- * A {@link CodedOutputStream} that writes directly to a {@link ByteBuffer}. |
+ * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer}, using only |
+ * safe operations.. |
*/ |
- private static final class NioEncoder extends CodedOutputStream { |
+ private static final class SafeDirectNioEncoder extends CodedOutputStream { |
private final ByteBuffer originalBuffer; |
private final ByteBuffer buffer; |
private final int initialPosition; |
- NioEncoder(ByteBuffer buffer) { |
+ SafeDirectNioEncoder(ByteBuffer buffer) { |
this.originalBuffer = buffer; |
this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); |
initialPosition = buffer.position(); |
@@ -1783,6 +1831,356 @@ public abstract class CodedOutputStream extends ByteOutput { |
} |
/** |
+ * A {@link CodedOutputStream} that writes directly to a direct {@link ByteBuffer} using {@code |
+ * sun.misc.Unsafe}. |
+ */ |
+ private static final class UnsafeDirectNioEncoder extends CodedOutputStream { |
+ private final ByteBuffer originalBuffer; |
+ private final ByteBuffer buffer; |
+ private final long address; |
+ private final long initialPosition; |
+ private final long limit; |
+ private final long oneVarintLimit; |
+ private long position; |
+ |
+ UnsafeDirectNioEncoder(ByteBuffer buffer) { |
+ this.originalBuffer = buffer; |
+ this.buffer = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN); |
+ address = UnsafeUtil.addressOffset(buffer); |
+ initialPosition = address + buffer.position(); |
+ limit = address + buffer.limit(); |
+ oneVarintLimit = limit - MAX_VARINT_SIZE; |
+ position = initialPosition; |
+ } |
+ |
+ static boolean isSupported() { |
+ return UnsafeUtil.hasUnsafeByteBufferOperations(); |
+ } |
+ |
+ @Override |
+ public void writeTag(int fieldNumber, int wireType) throws IOException { |
+ writeUInt32NoTag(WireFormat.makeTag(fieldNumber, wireType)); |
+ } |
+ |
+ @Override |
+ public void writeInt32(int fieldNumber, int value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); |
+ writeInt32NoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeUInt32(int fieldNumber, int value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); |
+ writeUInt32NoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeFixed32(int fieldNumber, int value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); |
+ writeFixed32NoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeUInt64(int fieldNumber, long value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); |
+ writeUInt64NoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeFixed64(int fieldNumber, long value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); |
+ writeFixed64NoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeBool(int fieldNumber, boolean value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); |
+ write((byte) (value ? 1 : 0)); |
+ } |
+ |
+ @Override |
+ public void writeString(int fieldNumber, String value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); |
+ writeStringNoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeBytes(int fieldNumber, ByteString value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); |
+ writeBytesNoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeByteArray(int fieldNumber, byte[] value) throws IOException { |
+ writeByteArray(fieldNumber, value, 0, value.length); |
+ } |
+ |
+ @Override |
+ public void writeByteArray(int fieldNumber, byte[] value, int offset, int length) |
+ throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); |
+ writeByteArrayNoTag(value, offset, length); |
+ } |
+ |
+ @Override |
+ public void writeByteBuffer(int fieldNumber, ByteBuffer value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); |
+ writeUInt32NoTag(value.capacity()); |
+ writeRawBytes(value); |
+ } |
+ |
+ @Override |
+ public void writeMessage(int fieldNumber, MessageLite value) throws IOException { |
+ writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); |
+ writeMessageNoTag(value); |
+ } |
+ |
+ @Override |
+ public void writeMessageSetExtension(int fieldNumber, MessageLite value) throws IOException { |
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); |
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); |
+ writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); |
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); |
+ } |
+ |
+ @Override |
+ public void writeRawMessageSetExtension(int fieldNumber, ByteString value) throws IOException { |
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); |
+ writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); |
+ writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); |
+ writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); |
+ } |
+ |
+ @Override |
+ public void writeMessageNoTag(MessageLite value) throws IOException { |
+ writeUInt32NoTag(value.getSerializedSize()); |
+ value.writeTo(this); |
+ } |
+ |
+ @Override |
+ public void write(byte value) throws IOException { |
+ if (position >= limit) { |
+ throw new OutOfSpaceException( |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); |
+ } |
+ UnsafeUtil.putByte(position++, value); |
+ } |
+ |
+ @Override |
+ public void writeBytesNoTag(ByteString value) throws IOException { |
+ writeUInt32NoTag(value.size()); |
+ value.writeTo(this); |
+ } |
+ |
+ @Override |
+ public void writeByteArrayNoTag(byte[] value, int offset, int length) throws IOException { |
+ writeUInt32NoTag(length); |
+ write(value, offset, length); |
+ } |
+ |
+ @Override |
+ public void writeRawBytes(ByteBuffer value) throws IOException { |
+ if (value.hasArray()) { |
+ write(value.array(), value.arrayOffset(), value.capacity()); |
+ } else { |
+ ByteBuffer duplicated = value.duplicate(); |
+ duplicated.clear(); |
+ write(duplicated); |
+ } |
+ } |
+ |
+ @Override |
+ public void writeInt32NoTag(int value) throws IOException { |
+ if (value >= 0) { |
+ writeUInt32NoTag(value); |
+ } else { |
+ // Must sign-extend. |
+ writeUInt64NoTag(value); |
+ } |
+ } |
+ |
+ @Override |
+ public void writeUInt32NoTag(int value) throws IOException { |
+ if (position <= oneVarintLimit) { |
+ // Optimization to avoid bounds checks on each iteration. |
+ while (true) { |
+ if ((value & ~0x7F) == 0) { |
+ UnsafeUtil.putByte(position++, (byte) value); |
+ return; |
+ } else { |
+ UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); |
+ value >>>= 7; |
+ } |
+ } |
+ } else { |
+ while (position < limit) { |
+ if ((value & ~0x7F) == 0) { |
+ UnsafeUtil.putByte(position++, (byte) value); |
+ return; |
+ } else { |
+ UnsafeUtil.putByte(position++, (byte) ((value & 0x7F) | 0x80)); |
+ value >>>= 7; |
+ } |
+ } |
+ throw new OutOfSpaceException( |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); |
+ } |
+ } |
+ |
+ @Override |
+ public void writeFixed32NoTag(int value) throws IOException { |
+ buffer.putInt(bufferPos(position), value); |
+ position += FIXED_32_SIZE; |
+ } |
+ |
+ @Override |
+ public void writeUInt64NoTag(long value) throws IOException { |
+ if (position <= oneVarintLimit) { |
+ // Optimization to avoid bounds checks on each iteration. |
+ while (true) { |
+ if ((value & ~0x7FL) == 0) { |
+ UnsafeUtil.putByte(position++, (byte) value); |
+ return; |
+ } else { |
+ UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); |
+ value >>>= 7; |
+ } |
+ } |
+ } else { |
+ while (position < limit) { |
+ if ((value & ~0x7FL) == 0) { |
+ UnsafeUtil.putByte(position++, (byte) value); |
+ return; |
+ } else { |
+ UnsafeUtil.putByte(position++, (byte) (((int) value & 0x7F) | 0x80)); |
+ value >>>= 7; |
+ } |
+ } |
+ throw new OutOfSpaceException( |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, 1)); |
+ } |
+ } |
+ |
+ @Override |
+ public void writeFixed64NoTag(long value) throws IOException { |
+ buffer.putLong(bufferPos(position), value); |
+ position += FIXED_64_SIZE; |
+ } |
+ |
+ @Override |
+ public void write(byte[] value, int offset, int length) throws IOException { |
+ if (value == null |
+ || offset < 0 |
+ || length < 0 |
+ || (value.length - length) < offset |
+ || (limit - length) < position) { |
+ if (value == null) { |
+ throw new NullPointerException("value"); |
+ } |
+ throw new OutOfSpaceException( |
+ String.format("Pos: %d, limit: %d, len: %d", position, limit, length)); |
+ } |
+ |
+ UnsafeUtil.copyMemory( |
+ value, UnsafeUtil.getArrayBaseOffset() + offset, null, position, length); |
+ position += length; |
+ } |
+ |
+ @Override |
+ public void writeLazy(byte[] value, int offset, int length) throws IOException { |
+ write(value, offset, length); |
+ } |
+ |
+ @Override |
+ public void write(ByteBuffer value) throws IOException { |
+ try { |
+ int length = value.remaining(); |
+ repositionBuffer(position); |
+ buffer.put(value); |
+ position += length; |
+ } catch (BufferOverflowException e) { |
+ throw new OutOfSpaceException(e); |
+ } |
+ } |
+ |
+ @Override |
+ public void writeLazy(ByteBuffer value) throws IOException { |
+ write(value); |
+ } |
+ |
+ @Override |
+ public void writeStringNoTag(String value) throws IOException { |
+ long prevPos = position; |
+ try { |
+ // UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()), |
+ // and at most 3 times of it. We take advantage of this in both branches below. |
+ int maxEncodedSize = value.length() * Utf8.MAX_BYTES_PER_CHAR; |
+ int maxLengthVarIntSize = computeUInt32SizeNoTag(maxEncodedSize); |
+ int minLengthVarIntSize = computeUInt32SizeNoTag(value.length()); |
+ if (minLengthVarIntSize == maxLengthVarIntSize) { |
+ // Save the current position and increment past the length field. We'll come back |
+ // and write the length field after the encoding is complete. |
+ int stringStart = bufferPos(position) + minLengthVarIntSize; |
+ buffer.position(stringStart); |
+ |
+ // Encode the string. |
+ Utf8.encodeUtf8(value, buffer); |
+ |
+ // Write the length and advance the position. |
+ int length = buffer.position() - stringStart; |
+ writeUInt32NoTag(length); |
+ position += length; |
+ } else { |
+ // Calculate and write the encoded length. |
+ int length = Utf8.encodedLength(value); |
+ writeUInt32NoTag(length); |
+ |
+ // Write the string and advance the position. |
+ repositionBuffer(position); |
+ Utf8.encodeUtf8(value, buffer); |
+ position += length; |
+ } |
+ } catch (UnpairedSurrogateException e) { |
+ // Roll back the change and convert to an IOException. |
+ position = prevPos; |
+ repositionBuffer(position); |
+ |
+ // TODO(nathanmittler): We should throw an IOException here instead. |
+ inefficientWriteStringNoTag(value, e); |
+ } catch (IllegalArgumentException e) { |
+ // Thrown by buffer.position() if out of range. |
+ throw new OutOfSpaceException(e); |
+ } catch (IndexOutOfBoundsException e) { |
+ throw new OutOfSpaceException(e); |
+ } |
+ } |
+ |
+ @Override |
+ public void flush() { |
+ // Update the position of the original buffer. |
+ originalBuffer.position(bufferPos(position)); |
+ } |
+ |
+ @Override |
+ public int spaceLeft() { |
+ return (int) (limit - position); |
+ } |
+ |
+ @Override |
+ public int getTotalBytesWritten() { |
+ return (int) (position - initialPosition); |
+ } |
+ |
+ private void repositionBuffer(long pos) { |
+ buffer.position(bufferPos(pos)); |
+ } |
+ |
+ private int bufferPos(long pos) { |
+ return (int) (pos - address); |
+ } |
+ } |
+ |
+ /** |
* Abstract base class for buffered encoders. |
*/ |
private abstract static class AbstractBufferedEncoder extends CodedOutputStream { |
@@ -1855,10 +2253,10 @@ public abstract class CodedOutputStream extends ByteOutput { |
long pos = originalPos; |
while (true) { |
if ((value & ~0x7F) == 0) { |
- UNSAFE.putByte(buffer, pos++, (byte) value); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) value); |
break; |
} else { |
- UNSAFE.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) ((value & 0x7F) | 0x80)); |
value >>>= 7; |
} |
} |
@@ -1890,10 +2288,10 @@ public abstract class CodedOutputStream extends ByteOutput { |
long pos = originalPos; |
while (true) { |
if ((value & ~0x7FL) == 0) { |
- UNSAFE.putByte(buffer, pos++, (byte) value); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) value); |
break; |
} else { |
- UNSAFE.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); |
+ UnsafeUtil.putByte(buffer, pos++, (byte) (((int) value & 0x7F) | 0x80)); |
value >>>= 7; |
} |
} |
@@ -2600,65 +2998,4 @@ public abstract class CodedOutputStream extends ByteOutput { |
position = 0; |
} |
} |
- |
- /** |
- * Gets the {@code sun.misc.Unsafe} instance, or {@code null} if not available on this |
- * platform. |
- */ |
- private static sun.misc.Unsafe getUnsafe() { |
- sun.misc.Unsafe unsafe = null; |
- try { |
- unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction<sun.misc.Unsafe>() { |
- @Override |
- public sun.misc.Unsafe run() throws Exception { |
- Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class; |
- |
- for (Field f : k.getDeclaredFields()) { |
- f.setAccessible(true); |
- Object x = f.get(null); |
- if (k.isInstance(x)) { |
- return k.cast(x); |
- } |
- } |
- // The sun.misc.Unsafe field does not exist. |
- return null; |
- } |
- }); |
- } catch (Throwable e) { |
- // Catching Throwable here due to the fact that Google AppEngine raises NoClassDefFoundError |
- // for Unsafe. |
- } |
- |
- logger.log(Level.FINEST, "sun.misc.Unsafe: {}", |
- unsafe != null ? "available" : "unavailable"); |
- return unsafe; |
- } |
- |
- /** |
- * Indicates whether or not unsafe array operations are supported on this platform. |
- */ |
- // TODO(nathanmittler): Add support for Android's MemoryBlock. |
- private static boolean supportsUnsafeArrayOperations() { |
- boolean supported = false; |
- if (UNSAFE != null) { |
- try { |
- UNSAFE.getClass().getMethod("arrayBaseOffset", Class.class); |
- UNSAFE.getClass().getMethod("putByte", Object.class, long.class, byte.class); |
- supported = true; |
- } catch (Throwable e) { |
- // Do nothing. |
- } |
- } |
- logger.log(Level.FINEST, "Unsafe array operations: {}", |
- supported ? "available" : "unavailable"); |
- return supported; |
- } |
- |
- /** |
- * Get the base offset for byte arrays, or {@code -1} if {@code sun.misc.Unsafe} is not |
- * available. |
- */ |
- private static <T> int byteArrayBaseOffset() { |
- return HAS_UNSAFE_ARRAY_OPERATIONS ? UNSAFE.arrayBaseOffset(byte[].class) : -1; |
- } |
} |