|
@@ -1,38 +1,13 @@
|
|
|
-// 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.
|
|
|
+// Copyright 2007 Google Inc. All rights reserved.
|
|
|
|
|
|
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;
|
|
@@ -41,6 +16,7 @@ import java.nio.charset.Charset;
|
|
|
import java.nio.charset.UnsupportedCharsetException;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collection;
|
|
|
+import java.util.Collections;
|
|
|
import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
import java.util.NoSuchElementException;
|
|
@@ -81,7 +57,7 @@ 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);
|
|
|
|
|
|
/**
|
|
|
* Cached hash value. Intentionally accessed via a data race, which
|
|
@@ -258,6 +234,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
|
|
@@ -1149,4 +1143,314 @@ 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
|
|
|
+ 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");
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|