Index: third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteString.java |
diff --git a/third_party/protobuf/java/src/main/java/com/google/protobuf/ByteString.java b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteString.java |
similarity index 72% |
rename from third_party/protobuf/java/src/main/java/com/google/protobuf/ByteString.java |
rename to third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteString.java |
index 68f20d5199b61ecd085a16d153e4a2b21fa1a9ca..62c945085f6195c5e8ae5878859c1ed769ca8c9b 100644 |
--- a/third_party/protobuf/java/src/main/java/com/google/protobuf/ByteString.java |
+++ b/third_party/protobuf/java/core/src/main/java/com/google/protobuf/ByteString.java |
@@ -30,9 +30,12 @@ |
package com.google.protobuf; |
+import java.io.ByteArrayInputStream; |
import java.io.ByteArrayOutputStream; |
import java.io.IOException; |
import java.io.InputStream; |
+import java.io.InvalidObjectException; |
+import java.io.ObjectInputStream; |
import java.io.OutputStream; |
import java.io.Serializable; |
import java.io.UnsupportedEncodingException; |
@@ -40,7 +43,9 @@ import java.nio.ByteBuffer; |
import java.nio.charset.Charset; |
import java.nio.charset.UnsupportedCharsetException; |
import java.util.ArrayList; |
+import java.util.Arrays; |
import java.util.Collection; |
+import java.util.Collections; |
import java.util.Iterator; |
import java.util.List; |
import java.util.NoSuchElementException; |
@@ -81,7 +86,55 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
/** |
* Empty {@code ByteString}. |
*/ |
- public static final ByteString EMPTY = new LiteralByteString(new byte[0]); |
+ public static final ByteString EMPTY = new LiteralByteString(Internal.EMPTY_BYTE_ARRAY); |
+ |
+ /** |
+ * An interface to efficiently copy {@code byte[]}. |
+ * |
+ * <p>One of the noticable costs of copying a byte[] into a new array using |
+ * {@code System.arraycopy} is nullification of a new buffer before the copy. It has been shown |
+ * the Hotspot VM is capable to intrisicfy {@code Arrays.copyOfRange} operation to avoid this |
+ * expensive nullification and provide substantial performance gain. Unfortunately this does not |
+ * hold on Android runtimes and could make the copy slightly slower due to additional code in |
+ * the {@code Arrays.copyOfRange}. Thus we provide two different implementation for array copier |
+ * for Hotspot and Android runtimes. |
+ */ |
+ private interface ByteArrayCopier { |
+ /** |
+ * Copies the specified range of the specified array into a new array |
+ */ |
+ byte[] copyFrom(byte[] bytes, int offset, int size); |
+ } |
+ |
+ /** Implementation of {@code ByteArrayCopier} which uses {@link System#arraycopy}. */ |
+ private static final class SystemByteArrayCopier implements ByteArrayCopier { |
+ @Override |
+ public byte[] copyFrom(byte[] bytes, int offset, int size) { |
+ byte[] copy = new byte[size]; |
+ System.arraycopy(bytes, offset, copy, 0, size); |
+ return copy; |
+ } |
+ } |
+ |
+ /** Implementation of {@code ByteArrayCopier} which uses {@link Arrays#copyOfRange}. */ |
+ private static final class ArraysByteArrayCopier implements ByteArrayCopier { |
+ @Override |
+ public byte[] copyFrom(byte[] bytes, int offset, int size) { |
+ return Arrays.copyOfRange(bytes, offset, offset + size); |
+ } |
+ } |
+ |
+ private static final ByteArrayCopier byteArrayCopier; |
+ static { |
+ boolean isAndroid = true; |
+ try { |
+ Class.forName("android.content.Context"); |
+ } catch (ClassNotFoundException e) { |
+ isAndroid = false; |
+ } |
+ |
+ byteArrayCopier = isAndroid ? new SystemByteArrayCopier() : new ArraysByteArrayCopier(); |
+ } |
/** |
* Cached hash value. Intentionally accessed via a data race, which |
@@ -101,7 +154,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
* |
* @param index index of byte |
* @return the value |
- * @throws ArrayIndexOutOfBoundsException {@code index < 0 or index >= size} |
+ * @throws IndexOutOfBoundsException {@code index < 0 or index >= size} |
*/ |
public abstract byte byteAt(int index); |
@@ -133,7 +186,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
public byte nextByte() { |
try { |
return byteAt(position++); |
- } catch (ArrayIndexOutOfBoundsException e) { |
+ } catch (IndexOutOfBoundsException e) { |
throw new NoSuchElementException(e.getMessage()); |
} |
} |
@@ -244,9 +297,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
* @return new {@code ByteString} |
*/ |
public static ByteString copyFrom(byte[] bytes, int offset, int size) { |
- byte[] copy = new byte[size]; |
- System.arraycopy(bytes, offset, copy, 0, size); |
- return new LiteralByteString(copy); |
+ return new LiteralByteString(byteArrayCopier.copyFrom(bytes, offset, size)); |
} |
/** |
@@ -258,6 +309,24 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
public static ByteString copyFrom(byte[] bytes) { |
return copyFrom(bytes, 0, bytes.length); |
} |
+ |
+ /** |
+ * Wraps the given bytes into a {@code ByteString}. Intended for internal only |
+ * usage to force a classload of ByteString before LiteralByteString. |
+ */ |
+ static ByteString wrap(byte[] bytes) { |
+ // TODO(dweis): Return EMPTY when bytes are empty to reduce allocations? |
+ return new LiteralByteString(bytes); |
+ } |
+ |
+ /** |
+ * Wraps the given bytes into a {@code ByteString}. Intended for internal only |
+ * usage to force a classload of ByteString before BoundedByteString and |
+ * LiteralByteString. |
+ */ |
+ static ByteString wrap(byte[] bytes, int offset, int length) { |
+ return new BoundedByteString(bytes, offset, length); |
+ } |
/** |
* Copies the next {@code size} bytes from a {@code java.nio.ByteBuffer} into |
@@ -565,12 +634,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
} |
/** |
- * Writes the complete contents of this byte string to |
- * the specified output stream argument. |
- * |
- * <p>It is assumed that the {@link OutputStream} will not modify the contents passed it |
- * it. It may be possible for a malicious {@link OutputStream} to corrupt |
- * the data underlying the {@link ByteString}. |
+ * Writes a copy of the contents of this byte string to the specified output stream argument. |
* |
* @param out the output stream to which to write the data. |
* @throws IOException if an I/O error occurs. |
@@ -584,8 +648,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
* @param sourceOffset offset within these bytes |
* @param numberToWrite number of bytes to write |
* @throws IOException if an I/O error occurs. |
- * @throws IndexOutOfBoundsException if an offset or size is negative or too |
- * large |
+ * @throws IndexOutOfBoundsException if an offset or size is negative or too large |
*/ |
final void writeTo(OutputStream out, int sourceOffset, int numberToWrite) |
throws IOException { |
@@ -603,6 +666,20 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
throws IOException; |
/** |
+ * Writes this {@link ByteString} to the provided {@link ByteOutput}. Calling |
+ * this method may result in multiple operations on the target {@link ByteOutput}. |
+ * |
+ * <p>This method may expose internal backing buffers of the {@link ByteString} to the {@link |
+ * ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious |
+ * {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution! |
+ * |
+ * @param byteOutput the output target to receive the bytes |
+ * @throws IOException if an I/O error occurs |
+ * @see UnsafeByteOperations#unsafeWriteTo(ByteString, ByteOutput) |
+ */ |
+ abstract void writeTo(ByteOutput byteOutput) throws IOException; |
+ |
+ /** |
* Constructs a read-only {@code java.nio.ByteBuffer} whose content |
* is equal to the contents of this byte string. |
* The result uses the same backing array as the byte string, if possible. |
@@ -1108,7 +1185,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
* |
* @param index the index position to be tested |
* @param size the length of the array |
- * @throws ArrayIndexOutOfBoundsException if the index does not fall within the array. |
+ * @throws IndexOutOfBoundsException if the index does not fall within the array. |
*/ |
static void checkIndex(int index, int size) { |
if ((index | (size - (index + 1))) < 0) { |
@@ -1126,7 +1203,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
* @param endIndex the end index of the range (exclusive) |
* @param size the size of the array. |
* @return the length of the range. |
- * @throws ArrayIndexOutOfBoundsException some or all of the range falls outside of the array. |
+ * @throws IndexOutOfBoundsException some or all of the range falls outside of the array. |
*/ |
static int checkRange(int startIndex, int endIndex, int size) { |
final int length = endIndex - startIndex; |
@@ -1149,4 +1226,319 @@ public abstract class ByteString implements Iterable<Byte>, Serializable { |
return String.format("<ByteString@%s size=%d>", |
Integer.toHexString(System.identityHashCode(this)), size()); |
} |
+ |
+ /** |
+ * This class implements a {@link com.google.protobuf.ByteString} backed by a |
+ * single array of bytes, contiguous in memory. It supports substring by |
+ * pointing to only a sub-range of the underlying byte array, meaning that a |
+ * substring will reference the full byte-array of the string it's made from, |
+ * exactly as with {@link String}. |
+ * |
+ * @author carlanton@google.com (Carl Haverl) |
+ */ |
+ // Keep this class private to avoid deadlocks in classloading across threads as ByteString's |
+ // static initializer loads LiteralByteString and another thread loads LiteralByteString. |
+ private static class LiteralByteString extends ByteString.LeafByteString { |
+ private static final long serialVersionUID = 1L; |
+ |
+ protected final byte[] bytes; |
+ |
+ /** |
+ * Creates a {@code LiteralByteString} backed by the given array, without |
+ * copying. |
+ * |
+ * @param bytes array to wrap |
+ */ |
+ LiteralByteString(byte[] bytes) { |
+ this.bytes = bytes; |
+ } |
+ |
+ @Override |
+ public byte byteAt(int index) { |
+ // Unlike most methods in this class, this one is a direct implementation |
+ // ignoring the potential offset because we need to do range-checking in the |
+ // substring case anyway. |
+ return bytes[index]; |
+ } |
+ |
+ @Override |
+ public int size() { |
+ return bytes.length; |
+ } |
+ |
+ // ================================================================= |
+ // ByteString -> substring |
+ |
+ @Override |
+ public final ByteString substring(int beginIndex, int endIndex) { |
+ final int length = checkRange(beginIndex, endIndex, size()); |
+ |
+ if (length == 0) { |
+ return ByteString.EMPTY; |
+ } |
+ |
+ return new BoundedByteString(bytes, getOffsetIntoBytes() + beginIndex, length); |
+ } |
+ |
+ // ================================================================= |
+ // ByteString -> byte[] |
+ |
+ @Override |
+ protected void copyToInternal( |
+ byte[] target, int sourceOffset, int targetOffset, int numberToCopy) { |
+ // Optimized form, not for subclasses, since we don't call |
+ // getOffsetIntoBytes() or check the 'numberToCopy' parameter. |
+ // TODO(nathanmittler): Is not calling getOffsetIntoBytes really saving that much? |
+ System.arraycopy(bytes, sourceOffset, target, targetOffset, numberToCopy); |
+ } |
+ |
+ @Override |
+ public final void copyTo(ByteBuffer target) { |
+ target.put(bytes, getOffsetIntoBytes(), size()); // Copies bytes |
+ } |
+ |
+ @Override |
+ public final ByteBuffer asReadOnlyByteBuffer() { |
+ return ByteBuffer.wrap(bytes, getOffsetIntoBytes(), size()).asReadOnlyBuffer(); |
+ } |
+ |
+ @Override |
+ public final List<ByteBuffer> asReadOnlyByteBufferList() { |
+ return Collections.singletonList(asReadOnlyByteBuffer()); |
+ } |
+ |
+ @Override |
+ public final void writeTo(OutputStream outputStream) throws IOException { |
+ outputStream.write(toByteArray()); |
+ } |
+ |
+ @Override |
+ final void writeToInternal(OutputStream outputStream, int sourceOffset, int numberToWrite) |
+ throws IOException { |
+ outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset, numberToWrite); |
+ } |
+ |
+ @Override |
+ final void writeTo(ByteOutput output) throws IOException { |
+ output.writeLazy(bytes, getOffsetIntoBytes(), size()); |
+ } |
+ |
+ @Override |
+ protected final String toStringInternal(Charset charset) { |
+ return new String(bytes, getOffsetIntoBytes(), size(), charset); |
+ } |
+ |
+ // ================================================================= |
+ // UTF-8 decoding |
+ |
+ @Override |
+ public final boolean isValidUtf8() { |
+ int offset = getOffsetIntoBytes(); |
+ return Utf8.isValidUtf8(bytes, offset, offset + size()); |
+ } |
+ |
+ @Override |
+ protected final int partialIsValidUtf8(int state, int offset, int length) { |
+ int index = getOffsetIntoBytes() + offset; |
+ return Utf8.partialIsValidUtf8(state, bytes, index, index + length); |
+ } |
+ |
+ // ================================================================= |
+ // equals() and hashCode() |
+ |
+ @Override |
+ public final boolean equals(Object other) { |
+ if (other == this) { |
+ return true; |
+ } |
+ if (!(other instanceof ByteString)) { |
+ return false; |
+ } |
+ |
+ if (size() != ((ByteString) other).size()) { |
+ return false; |
+ } |
+ if (size() == 0) { |
+ return true; |
+ } |
+ |
+ if (other instanceof LiteralByteString) { |
+ LiteralByteString otherAsLiteral = (LiteralByteString) other; |
+ // If we know the hash codes and they are not equal, we know the byte |
+ // strings are not equal. |
+ int thisHash = peekCachedHashCode(); |
+ int thatHash = otherAsLiteral.peekCachedHashCode(); |
+ if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) { |
+ return false; |
+ } |
+ |
+ return equalsRange((LiteralByteString) other, 0, size()); |
+ } else { |
+ // RopeByteString and NioByteString. |
+ return other.equals(this); |
+ } |
+ } |
+ |
+ /** |
+ * Check equality of the substring of given length of this object starting at |
+ * zero with another {@code LiteralByteString} substring starting at offset. |
+ * |
+ * @param other what to compare a substring in |
+ * @param offset offset into other |
+ * @param length number of bytes to compare |
+ * @return true for equality of substrings, else false. |
+ */ |
+ @Override |
+ final boolean equalsRange(ByteString other, int offset, int length) { |
+ if (length > other.size()) { |
+ throw new IllegalArgumentException("Length too large: " + length + size()); |
+ } |
+ if (offset + length > other.size()) { |
+ throw new IllegalArgumentException( |
+ "Ran off end of other: " + offset + ", " + length + ", " + other.size()); |
+ } |
+ |
+ if (other instanceof LiteralByteString) { |
+ LiteralByteString lbsOther = (LiteralByteString) other; |
+ byte[] thisBytes = bytes; |
+ byte[] otherBytes = lbsOther.bytes; |
+ int thisLimit = getOffsetIntoBytes() + length; |
+ for ( |
+ int thisIndex = getOffsetIntoBytes(), |
+ otherIndex = lbsOther.getOffsetIntoBytes() + offset; |
+ (thisIndex < thisLimit); ++thisIndex, ++otherIndex) { |
+ if (thisBytes[thisIndex] != otherBytes[otherIndex]) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ return other.substring(offset, offset + length).equals(substring(0, length)); |
+ } |
+ |
+ @Override |
+ protected final int partialHash(int h, int offset, int length) { |
+ return Internal.partialHash(h, bytes, getOffsetIntoBytes() + offset, length); |
+ } |
+ |
+ // ================================================================= |
+ // Input stream |
+ |
+ @Override |
+ public final InputStream newInput() { |
+ return new ByteArrayInputStream(bytes, getOffsetIntoBytes(), size()); // No copy |
+ } |
+ |
+ @Override |
+ public final CodedInputStream newCodedInput() { |
+ // We trust CodedInputStream not to modify the bytes, or to give anyone |
+ // else access to them. |
+ return CodedInputStream.newInstance( |
+ bytes, getOffsetIntoBytes(), size(), true /* bufferIsImmutable */); |
+ } |
+ |
+ // ================================================================= |
+ // Internal methods |
+ |
+ /** |
+ * Offset into {@code bytes[]} to use, non-zero for substrings. |
+ * |
+ * @return always 0 for this class |
+ */ |
+ protected int getOffsetIntoBytes() { |
+ return 0; |
+ } |
+ } |
+ |
+ /** |
+ * This class is used to represent the substring of a {@link ByteString} over a |
+ * single byte array. In terms of the public API of {@link ByteString}, you end |
+ * up here by calling {@link ByteString#copyFrom(byte[])} followed by {@link |
+ * ByteString#substring(int, int)}. |
+ * |
+ * <p>This class contains most of the overhead involved in creating a substring |
+ * from a {@link LiteralByteString}. The overhead involves some range-checking |
+ * and two extra fields. |
+ * |
+ * @author carlanton@google.com (Carl Haverl) |
+ */ |
+ // Keep this class private to avoid deadlocks in classloading across threads as ByteString's |
+ // static initializer loads LiteralByteString and another thread loads BoundedByteString. |
+ private static final class BoundedByteString extends LiteralByteString { |
+ |
+ private final int bytesOffset; |
+ private final int bytesLength; |
+ |
+ /** |
+ * Creates a {@code BoundedByteString} backed by the sub-range of given array, |
+ * without copying. |
+ * |
+ * @param bytes array to wrap |
+ * @param offset index to first byte to use in bytes |
+ * @param length number of bytes to use from bytes |
+ * @throws IllegalArgumentException if {@code offset < 0}, {@code length < 0}, |
+ * or if {@code offset + length > |
+ * bytes.length}. |
+ */ |
+ BoundedByteString(byte[] bytes, int offset, int length) { |
+ super(bytes); |
+ checkRange(offset, offset + length, bytes.length); |
+ |
+ this.bytesOffset = offset; |
+ this.bytesLength = length; |
+ } |
+ |
+ /** |
+ * Gets the byte at the given index. |
+ * Throws {@link ArrayIndexOutOfBoundsException} |
+ * for backwards-compatibility reasons although it would more properly be |
+ * {@link IndexOutOfBoundsException}. |
+ * |
+ * @param index index of byte |
+ * @return the value |
+ * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size |
+ */ |
+ @Override |
+ public byte byteAt(int index) { |
+ // We must check the index ourselves as we cannot rely on Java array index |
+ // checking for substrings. |
+ checkIndex(index, size()); |
+ return bytes[bytesOffset + index]; |
+ } |
+ |
+ @Override |
+ public int size() { |
+ return bytesLength; |
+ } |
+ |
+ @Override |
+ protected int getOffsetIntoBytes() { |
+ return bytesOffset; |
+ } |
+ |
+ // ================================================================= |
+ // ByteString -> byte[] |
+ |
+ @Override |
+ protected void copyToInternal(byte[] target, int sourceOffset, int targetOffset, |
+ int numberToCopy) { |
+ System.arraycopy(bytes, getOffsetIntoBytes() + sourceOffset, target, |
+ targetOffset, numberToCopy); |
+ } |
+ |
+ // ================================================================= |
+ // Serializable |
+ |
+ private static final long serialVersionUID = 1L; |
+ |
+ Object writeReplace() { |
+ return ByteString.wrap(toByteArray()); |
+ } |
+ |
+ private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException { |
+ throw new InvalidObjectException( |
+ "BoundedByteStream instances are not to be serialized directly"); |
+ } |
+ } |
} |