31 package com.google.protobuf;
33 import java.io.ByteArrayInputStream;
34 import java.io.ByteArrayOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.InvalidObjectException;
38 import java.io.ObjectInputStream;
39 import java.io.OutputStream;
40 import java.io.Serializable;
41 import java.io.UnsupportedEncodingException;
42 import java.nio.ByteBuffer;
43 import java.nio.charset.Charset;
44 import java.nio.charset.UnsupportedCharsetException;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.Comparator;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.NoSuchElementException;
67 public abstract class ByteString implements Iterable<Byte>, Serializable {
74 static final int CONCATENATE_BY_COPY_SIZE = 128;
80 static final int MIN_READ_FROM_CHUNK_SIZE = 0x100;
82 static final int MAX_READ_FROM_CHUNK_SIZE = 0x2000;
107 byte[] copy =
new byte[
size];
156 abstract byte internalByteAt(
int index);
166 return new AbstractByteIterator() {
168 private final int limit =
size();
171 public boolean hasNext() {
176 public byte nextByte() {
178 if (currentPos >= limit) {
179 throw new NoSuchElementException();
182 return internalByteAt(currentPos);
200 abstract static class AbstractByteIterator
implements ByteIterator {
202 public final Byte
next() {
208 public final void remove() {
209 throw new UnsupportedOperationException();
218 public abstract int size();
254 new Comparator<ByteString>() {
260 while (formerBytes.hasNext() && latterBytes.hasNext()) {
270 return Integer.compare(former.
size(), latter.
size());
286 public static Comparator<ByteString> unsignedLexicographicalComparator() {
300 public final ByteString substring(
int beginIndex) {
301 return substring(beginIndex,
size());
313 public abstract ByteString substring(
int beginIndex,
int endIndex);
323 public final boolean startsWith(ByteString
prefix) {
335 public final boolean endsWith(ByteString suffix) {
336 return size() >= suffix.size() && substring(
size() - suffix.size()).equals(suffix);
351 public static ByteString copyFrom(
byte[]
bytes,
int offset,
int size) {
362 public static ByteString copyFrom(
byte[]
bytes) {
367 static ByteString wrap(ByteBuffer
buffer) {
372 return new NioByteString(
buffer);
380 static ByteString wrap(
byte[]
bytes) {
382 return new LiteralByteString(
bytes);
402 public static ByteString copyFrom(ByteBuffer
bytes,
int size) {
404 byte[] copy =
new byte[
size];
406 return new LiteralByteString(copy);
415 public static ByteString copyFrom(ByteBuffer
bytes) {
428 public static ByteString copyFrom(String text, String charsetName)
429 throws UnsupportedEncodingException {
430 return new LiteralByteString(text.getBytes(charsetName));
441 public static ByteString copyFrom(String text, Charset charset) {
442 return new LiteralByteString(text.getBytes(charset));
452 public static ByteString copyFromUtf8(String text) {
453 return new LiteralByteString(text.getBytes(Internal.UTF_8));
475 public static ByteString readFrom(InputStream streamToDrain)
throws IOException {
476 return readFrom(streamToDrain, MIN_READ_FROM_CHUNK_SIZE, MAX_READ_FROM_CHUNK_SIZE);
495 public static ByteString readFrom(InputStream streamToDrain,
int chunkSize)
throws IOException {
496 return readFrom(streamToDrain, chunkSize, chunkSize);
500 public static ByteString readFrom(InputStream streamToDrain,
int minChunkSize,
int maxChunkSize)
502 Collection<ByteString>
results =
new ArrayList<ByteString>();
506 int chunkSize = minChunkSize;
508 ByteString chunk = readChunk(streamToDrain, chunkSize);
513 chunkSize = Math.min(chunkSize * 2, maxChunkSize);
516 return ByteString.copyFrom(
results);
527 private static ByteString readChunk(InputStream in,
final int chunkSize)
throws IOException {
528 final byte[]
buf =
new byte[chunkSize];
530 while (bytesRead < chunkSize) {
531 final int count = in.read(
buf, bytesRead, chunkSize - bytesRead);
538 if (bytesRead == 0) {
543 return ByteString.copyFrom(
buf, 0, bytesRead);
559 public final ByteString concat(ByteString other) {
560 if (Integer.MAX_VALUE -
size() < other.size()) {
561 throw new IllegalArgumentException(
562 "ByteString would be too long: " +
size() +
"+" + other.size());
565 return RopeByteString.concatenate(
this, other);
579 public static ByteString copyFrom(Iterable<ByteString> byteStrings) {
582 if (!(byteStrings instanceof Collection)) {
584 for (Iterator<ByteString> iter = byteStrings.iterator();
586 iter.next(), ++tempSize) {}
589 size = ((Collection<ByteString>) byteStrings).size();
596 return balancedConcat(byteStrings.iterator(),
size);
602 private static ByteString balancedConcat(Iterator<ByteString>
iterator,
int length) {
604 throw new IllegalArgumentException(String.format(
"length (%s) must be >= 1",
length));
610 int halfLength =
length >>> 1;
613 result =
left.concat(right);
647 public final void copyTo(
byte[]
target,
int sourceOffset,
int targetOffset,
int numberToCopy) {
648 checkRange(sourceOffset, sourceOffset + numberToCopy,
size());
649 checkRange(targetOffset, targetOffset + numberToCopy,
target.length);
650 if (numberToCopy > 0) {
651 copyToInternal(
target, sourceOffset, targetOffset, numberToCopy);
659 protected abstract void copyToInternal(
660 byte[]
target,
int sourceOffset,
int targetOffset,
int numberToCopy);
673 public abstract void copyTo(ByteBuffer
target);
680 public final byte[] toByteArray() {
683 return Internal.EMPTY_BYTE_ARRAY;
685 byte[] result =
new byte[
size];
686 copyToInternal(result, 0, 0,
size);
696 public abstract void writeTo(OutputStream out)
throws IOException;
707 final void writeTo(OutputStream out,
int sourceOffset,
int numberToWrite)
throws IOException {
708 checkRange(sourceOffset, sourceOffset + numberToWrite,
size());
709 if (numberToWrite > 0) {
710 writeToInternal(out, sourceOffset, numberToWrite);
718 abstract void writeToInternal(OutputStream out,
int sourceOffset,
int numberToWrite)
733 abstract void writeTo(ByteOutput byteOutput)
throws IOException;
744 abstract void writeToReverse(ByteOutput byteOutput)
throws IOException;
752 public abstract ByteBuffer asReadOnlyByteBuffer();
764 public abstract List<ByteBuffer> asReadOnlyByteBufferList();
773 public final String toString(String charsetName)
throws UnsupportedEncodingException {
775 return toString(Charset.forName(charsetName));
776 }
catch (UnsupportedCharsetException e) {
777 UnsupportedEncodingException exception =
new UnsupportedEncodingException(charsetName);
778 exception.initCause(e);
790 public final String toString(Charset charset) {
791 return size() == 0 ?
"" : toStringInternal(charset);
800 protected abstract String toStringInternal(Charset charset);
810 public final String toStringUtf8() {
811 return toString(Internal.UTF_8);
838 public abstract boolean isValidUtf8();
855 protected abstract int partialIsValidUtf8(
int state,
int offset,
int length);
861 public abstract boolean equals(Object o);
864 abstract static class LeafByteString
extends ByteString {
866 protected final int getTreeDepth() {
871 protected final boolean isBalanced() {
876 void writeToReverse(ByteOutput byteOutput)
throws IOException {
889 abstract boolean equalsRange(ByteString other,
int offset,
int length);
898 public final int hashCode() {
928 public abstract InputStream newInput();
937 public abstract CodedInputStream newCodedInput();
953 public static Output newOutput(
int initialCapacity) {
954 return new Output(initialCapacity);
967 public static Output newOutput() {
968 return new Output(CONCATENATE_BY_COPY_SIZE);
975 public static final class Output extends OutputStream {
982 private static final byte[] EMPTY_BYTE_ARRAY =
new byte[0];
984 private final int initialCapacity;
986 private final ArrayList<ByteString> flushedBuffers;
988 private int flushedBuffersTotalBytes;
992 private int bufferPos;
999 Output(
int initialCapacity) {
1000 if (initialCapacity < 0) {
1001 throw new IllegalArgumentException(
"Buffer size < 0");
1003 this.initialCapacity = initialCapacity;
1004 this.flushedBuffers =
new ArrayList<ByteString>();
1005 this.buffer =
new byte[initialCapacity];
1009 public synchronized void write(
int b) {
1017 public synchronized void write(
byte[]
b,
int offset,
int length) {
1042 public synchronized ByteString toByteString() {
1044 return ByteString.copyFrom(flushedBuffers);
1049 byte[] result =
new byte[
length];
1061 public void writeTo(OutputStream out)
throws IOException {
1062 ByteString[] cachedFlushBuffers;
1063 byte[] cachedBuffer;
1064 int cachedBufferPos;
1065 synchronized (
this) {
1068 cachedFlushBuffers = flushedBuffers.toArray(
new ByteString[flushedBuffers.size()]);
1070 cachedBufferPos = bufferPos;
1072 for (ByteString byteString : cachedFlushBuffers) {
1073 byteString.writeTo(out);
1076 out.write(copyArray(cachedBuffer, cachedBufferPos));
1084 public synchronized int size() {
1085 return flushedBuffersTotalBytes + bufferPos;
1092 public synchronized void reset() {
1093 flushedBuffers.clear();
1094 flushedBuffersTotalBytes = 0;
1099 public String toString() {
1100 return String.format(
1101 "<ByteString.Output@%s size=%d>",
1102 Integer.toHexString(System.identityHashCode(
this)),
size());
1109 private void flushFullBuffer(
int minSize) {
1110 flushedBuffers.add(
new LiteralByteString(
buffer));
1115 int newSize = Math.max(initialCapacity, Math.max(minSize, flushedBuffersTotalBytes >>> 1));
1116 buffer =
new byte[newSize];
1124 private void flushLastBuffer() {
1126 if (bufferPos > 0) {
1127 byte[] bufferCopy = copyArray(
buffer, bufferPos);
1128 flushedBuffers.add(
new LiteralByteString(bufferCopy));
1133 flushedBuffers.add(
new LiteralByteString(
buffer));
1139 buffer = EMPTY_BYTE_ARRAY;
1141 flushedBuffersTotalBytes += bufferPos;
1158 static CodedBuilder newCodedBuilder(
int size) {
1159 return new CodedBuilder(
size);
1163 static final class CodedBuilder {
1164 private final CodedOutputStream
output;
1165 private final byte[]
buffer;
1167 private CodedBuilder(
int size) {
1172 public ByteString build() {
1173 output.checkNoSpaceLeft();
1178 return new LiteralByteString(
buffer);
1181 public CodedOutputStream getCodedOutput() {
1196 protected abstract int getTreeDepth();
1204 protected abstract boolean isBalanced();
1211 protected final int peekCachedHashCode() {
1225 protected abstract int partialHash(
int h,
int offset,
int length);
1234 static void checkIndex(
int index,
int size) {
1237 throw new ArrayIndexOutOfBoundsException(
"Index < 0: " +
index);
1239 throw new ArrayIndexOutOfBoundsException(
"Index > length: " +
index +
", " +
size);
1252 static int checkRange(
int startIndex,
int endIndex,
int size) {
1253 final int length = endIndex - startIndex;
1254 if ((startIndex | endIndex |
length | (
size - endIndex)) < 0) {
1255 if (startIndex < 0) {
1256 throw new IndexOutOfBoundsException(
"Beginning index: " + startIndex +
" < 0");
1258 if (endIndex < startIndex) {
1259 throw new IndexOutOfBoundsException(
1260 "Beginning index larger than ending index: " + startIndex +
", " + endIndex);
1263 throw new IndexOutOfBoundsException(
"End index: " + endIndex +
" >= " +
size);
1269 public final String toString() {
1270 return String.format(
1271 "<ByteString@%s size=%d>", Integer.toHexString(System.identityHashCode(
this)),
size());
1284 private static class LiteralByteString
extends ByteString.LeafByteString {
1285 private static final long serialVersionUID = 1L;
1287 protected final byte[]
bytes;
1294 LiteralByteString(
byte[]
bytes) {
1295 if (
bytes ==
null) {
1296 throw new NullPointerException();
1310 byte internalByteAt(
int index) {
1316 return bytes.length;
1323 public final ByteString substring(
int beginIndex,
int endIndex) {
1324 final int length = checkRange(beginIndex, endIndex,
size());
1327 return ByteString.EMPTY;
1330 return new BoundedByteString(
bytes, getOffsetIntoBytes() + beginIndex,
length);
1337 protected void copyToInternal(
1338 byte[]
target,
int sourceOffset,
int targetOffset,
int numberToCopy) {
1342 System.arraycopy(
bytes, sourceOffset,
target, targetOffset, numberToCopy);
1346 public final void copyTo(ByteBuffer
target) {
1351 public final ByteBuffer asReadOnlyByteBuffer() {
1352 return ByteBuffer.wrap(
bytes, getOffsetIntoBytes(),
size()).asReadOnlyBuffer();
1356 public final List<ByteBuffer> asReadOnlyByteBufferList() {
1357 return Collections.singletonList(asReadOnlyByteBuffer());
1361 public final void writeTo(OutputStream outputStream)
throws IOException {
1362 outputStream.write(toByteArray());
1366 final void writeToInternal(OutputStream outputStream,
int sourceOffset,
int numberToWrite)
1367 throws IOException {
1368 outputStream.write(
bytes, getOffsetIntoBytes() + sourceOffset, numberToWrite);
1372 final void writeTo(ByteOutput
output)
throws IOException {
1377 protected final String toStringInternal(Charset charset) {
1378 return new String(
bytes, getOffsetIntoBytes(),
size(), charset);
1385 public final boolean isValidUtf8() {
1386 int offset = getOffsetIntoBytes();
1391 protected final int partialIsValidUtf8(
int state,
int offset,
int length) {
1400 public final boolean equals(Object other) {
1401 if (other ==
this) {
1404 if (!(other instanceof ByteString)) {
1408 if (
size() != ((ByteString) other).
size()) {
1415 if (other instanceof LiteralByteString) {
1416 LiteralByteString otherAsLiteral = (LiteralByteString) other;
1419 int thisHash = peekCachedHashCode();
1420 int thatHash = otherAsLiteral.peekCachedHashCode();
1421 if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) {
1425 return equalsRange((LiteralByteString) other, 0,
size());
1428 return other.equals(
this);
1442 final boolean equalsRange(ByteString other,
int offset,
int length) {
1443 if (
length > other.size()) {
1444 throw new IllegalArgumentException(
"Length too large: " +
length +
size());
1447 throw new IllegalArgumentException(
1448 "Ran off end of other: " +
offset +
", " +
length +
", " + other.size());
1451 if (other instanceof LiteralByteString) {
1452 LiteralByteString lbsOther = (LiteralByteString) other;
1453 byte[] thisBytes =
bytes;
1454 byte[] otherBytes = lbsOther.bytes;
1455 int thisLimit = getOffsetIntoBytes() +
length;
1456 for (
int thisIndex = getOffsetIntoBytes(),
1457 otherIndex = lbsOther.getOffsetIntoBytes() +
offset;
1458 (thisIndex < thisLimit);
1459 ++thisIndex, ++otherIndex) {
1460 if (thisBytes[thisIndex] != otherBytes[otherIndex]) {
1471 protected final int partialHash(
int h,
int offset,
int length) {
1479 public final InputStream newInput() {
1480 return new ByteArrayInputStream(
bytes, getOffsetIntoBytes(),
size());
1484 public final CodedInputStream newCodedInput() {
1487 return CodedInputStream.newInstance(
1488 bytes, getOffsetIntoBytes(),
size(),
true);
1499 protected int getOffsetIntoBytes() {
1516 private static final class BoundedByteString
extends LiteralByteString {
1518 private final int bytesOffset;
1519 private final int bytesLength;
1534 this.bytesOffset =
offset;
1535 this.bytesLength =
length;
1556 byte internalByteAt(
int index) {
1566 protected int getOffsetIntoBytes() {
1574 protected void copyToInternal(
1575 byte[]
target,
int sourceOffset,
int targetOffset,
int numberToCopy) {
1577 bytes, getOffsetIntoBytes() + sourceOffset,
target, targetOffset, numberToCopy);
1583 private static final long serialVersionUID = 1L;
1585 Object writeReplace() {
1586 return ByteString.wrap(toByteArray());
1589 private void readObject(@SuppressWarnings(
"unused") ObjectInputStream in)
throws IOException {
1590 throw new InvalidObjectException(
1591 "BoundedByteStream instances are not to be serialized directly");