31 package com.google.protobuf;
33 import java.io.ByteArrayInputStream;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InvalidObjectException;
37 import java.io.ObjectInputStream;
38 import java.io.OutputStream;
39 import java.nio.ByteBuffer;
40 import java.nio.charset.Charset;
41 import java.util.ArrayDeque;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.NoSuchElementException;
68 final class RopeByteString
extends ByteString {
83 static final int[] minLengthByDepth = {
133 private final int totalLength;
134 private final ByteString
left;
135 private final ByteString right;
136 private final int leftLength;
137 private final int treeDepth;
146 private RopeByteString(ByteString
left, ByteString right) {
149 leftLength =
left.size();
150 totalLength = leftLength + right.size();
151 treeDepth = Math.max(
left.getTreeDepth(), right.getTreeDepth()) + 1;
167 static ByteString concatenate(ByteString
left, ByteString right) {
168 if (right.size() == 0) {
172 if (
left.size() == 0) {
176 final int newLength =
left.size() + right.size();
177 if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
180 return concatenateBytes(
left, right);
183 if (
left instanceof RopeByteString) {
184 final RopeByteString leftRope = (RopeByteString)
left;
185 if (leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
196 ByteString newRight = concatenateBytes(leftRope.right, right);
197 return new RopeByteString(leftRope.left, newRight);
200 if (leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
201 && leftRope.getTreeDepth() > right.getTreeDepth()) {
207 ByteString newRight =
new RopeByteString(leftRope.right, right);
208 return new RopeByteString(leftRope.left, newRight);
213 int newDepth = Math.max(
left.getTreeDepth(), right.getTreeDepth()) + 1;
214 if (newLength >= minLengthByDepth[newDepth]) {
216 return new RopeByteString(
left, right);
219 return new Balancer().balance(
left, right);
230 private static ByteString concatenateBytes(ByteString
left, ByteString right) {
231 int leftSize =
left.size();
232 int rightSize = right.size();
233 byte[]
bytes =
new byte[leftSize + rightSize];
235 right.copyTo(
bytes, 0, leftSize, rightSize);
236 return ByteString.wrap(
bytes);
249 static RopeByteString newInstanceForTest(ByteString
left, ByteString right) {
250 return new RopeByteString(
left, right);
263 public byte byteAt(
int index) {
264 checkIndex(
index, totalLength);
265 return internalByteAt(
index);
269 byte internalByteAt(
int index) {
271 if (
index < leftLength) {
275 return right.internalByteAt(
index - leftLength);
284 public ByteIterator iterator() {
285 return new AbstractByteIterator() {
286 final PieceIterator pieces =
new PieceIterator(RopeByteString.this);
287 ByteIterator current = nextPiece();
289 private ByteIterator nextPiece() {
292 return pieces.hasNext() ? pieces.next().iterator() :
null;
296 public boolean hasNext() {
297 return current !=
null;
301 public byte nextByte() {
302 if (current ==
null) {
303 throw new NoSuchElementException();
305 byte b = current.nextByte();
306 if (!current.hasNext()) {
307 current = nextPiece();
318 protected int getTreeDepth() {
330 protected boolean isBalanced() {
331 return totalLength >= minLengthByDepth[treeDepth];
348 public ByteString substring(
int beginIndex,
int endIndex) {
349 final int length = checkRange(beginIndex, endIndex, totalLength);
353 return ByteString.EMPTY;
356 if (
length == totalLength) {
362 if (endIndex <= leftLength) {
364 return left.substring(beginIndex, endIndex);
367 if (beginIndex >= leftLength) {
369 return right.substring(beginIndex - leftLength, endIndex - leftLength);
373 ByteString leftSub =
left.substring(beginIndex);
374 ByteString rightSub = right.substring(0, endIndex - leftLength);
378 return new RopeByteString(leftSub, rightSub);
385 protected void copyToInternal(
386 byte[]
target,
int sourceOffset,
int targetOffset,
int numberToCopy) {
387 if (sourceOffset + numberToCopy <= leftLength) {
388 left.copyToInternal(
target, sourceOffset, targetOffset, numberToCopy);
389 }
else if (sourceOffset >= leftLength) {
390 right.copyToInternal(
target, sourceOffset - leftLength, targetOffset, numberToCopy);
392 int leftLength = this.leftLength - sourceOffset;
393 left.copyToInternal(
target, sourceOffset, targetOffset, leftLength);
394 right.copyToInternal(
target, 0, targetOffset + leftLength, numberToCopy - leftLength);
399 public void copyTo(ByteBuffer
target) {
405 public ByteBuffer asReadOnlyByteBuffer() {
406 ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
407 return byteBuffer.asReadOnlyBuffer();
411 public List<ByteBuffer> asReadOnlyByteBufferList() {
414 List<ByteBuffer> result =
new ArrayList<ByteBuffer>();
415 PieceIterator pieces =
new PieceIterator(
this);
416 while (pieces.hasNext()) {
417 LeafByteString byteString = pieces.next();
418 result.add(byteString.asReadOnlyByteBuffer());
424 public void writeTo(OutputStream outputStream)
throws IOException {
425 left.writeTo(outputStream);
426 right.writeTo(outputStream);
430 void writeToInternal(OutputStream out,
int sourceOffset,
int numberToWrite)
throws IOException {
431 if (sourceOffset + numberToWrite <= leftLength) {
432 left.writeToInternal(out, sourceOffset, numberToWrite);
433 }
else if (sourceOffset >= leftLength) {
434 right.writeToInternal(out, sourceOffset - leftLength, numberToWrite);
436 int numberToWriteInLeft = leftLength - sourceOffset;
437 left.writeToInternal(out, sourceOffset, numberToWriteInLeft);
438 right.writeToInternal(out, 0, numberToWrite - numberToWriteInLeft);
443 void writeTo(ByteOutput
output)
throws IOException {
449 void writeToReverse(ByteOutput
output)
throws IOException {
450 right.writeToReverse(
output);
455 protected String toStringInternal(Charset charset) {
456 return new String(toByteArray(), charset);
463 public boolean isValidUtf8() {
464 int leftPartial =
left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength);
465 int state = right.partialIsValidUtf8(leftPartial, 0, right.size());
466 return state == Utf8.COMPLETE;
470 protected int partialIsValidUtf8(
int state,
int offset,
int length) {
472 if (toIndex <= leftLength) {
474 }
else if (
offset >= leftLength) {
475 return right.partialIsValidUtf8(state,
offset - leftLength,
length);
477 int leftLength = this.leftLength -
offset;
478 int leftPartial =
left.partialIsValidUtf8(state,
offset, leftLength);
479 return right.partialIsValidUtf8(leftPartial, 0,
length - leftLength);
487 public boolean equals(Object other) {
491 if (!(other instanceof ByteString)) {
495 ByteString otherByteString = (ByteString) other;
496 if (totalLength != otherByteString.size()) {
499 if (totalLength == 0) {
508 int thisHash = peekCachedHashCode();
509 int thatHash = otherByteString.peekCachedHashCode();
510 if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) {
514 return equalsFragments(otherByteString);
524 private boolean equalsFragments(ByteString other) {
526 Iterator<LeafByteString> thisIter =
new PieceIterator(
this);
527 LeafByteString thisString = thisIter.next();
530 Iterator<LeafByteString> thatIter =
new PieceIterator(other);
531 LeafByteString thatString = thatIter.next();
535 int thisRemaining = thisString.size() - thisOffset;
536 int thatRemaining = thatString.size() - thatOffset;
537 int bytesToCompare = Math.min(thisRemaining, thatRemaining);
542 ? thisString.equalsRange(thatString, thatOffset, bytesToCompare)
543 : thatString.equalsRange(thisString, thisOffset, bytesToCompare);
548 pos += bytesToCompare;
549 if (pos >= totalLength) {
550 if (pos == totalLength) {
553 throw new IllegalStateException();
556 if (bytesToCompare == thisRemaining) {
558 thisString = thisIter.next();
560 thisOffset += bytesToCompare;
562 if (bytesToCompare == thatRemaining) {
564 thatString = thatIter.next();
566 thatOffset += bytesToCompare;
574 if (toIndex <= leftLength) {
576 }
else if (
offset >= leftLength) {
579 int leftLength = this.leftLength -
offset;
580 int leftPartial =
left.partialHash(
h,
offset, leftLength);
581 return right.partialHash(leftPartial, 0,
length - leftLength);
589 public CodedInputStream newCodedInput() {
590 return CodedInputStream.newInstance(
new RopeInputStream());
594 public InputStream newInput() {
595 return new RopeInputStream();
621 partialString =
new RopeByteString(newLeft, partialString);
625 return partialString;
633 if (
root.isBalanced()) {
635 }
else if (
root instanceof RopeByteString) {
636 RopeByteString rbs = (RopeByteString)
root;
640 throw new IllegalArgumentException(
641 "Has a new type of ByteString been created? Found " +
root.getClass());
659 int binEnd = minLengthByDepth[depthBin + 1];
668 int binStart = minLengthByDepth[depthBin];
674 newTree =
new RopeByteString(
left, newTree);
678 newTree =
new RopeByteString(newTree, byteString);
683 binEnd = minLengthByDepth[depthBin + 1];
686 newTree =
new RopeByteString(
left, newTree);
696 int depth = Arrays.binarySearch(minLengthByDepth,
length);
700 int insertionPoint = -(
depth + 1);
701 depth = insertionPoint - 1;
715 private static final class PieceIterator implements Iterator<LeafByteString> {
720 if (
root instanceof RopeByteString) {
721 RopeByteString rbs = (RopeByteString)
root;
722 breadCrumbs =
new ArrayDeque<>(rbs.getTreeDepth());
733 while (pos instanceof RopeByteString) {
734 RopeByteString rbs = (RopeByteString) pos;
738 return (LeafByteString) pos;
749 if (!result.isEmpty()) {
769 throw new NoSuchElementException();
771 LeafByteString result =
next;
777 public void remove() {
778 throw new UnsupportedOperationException();
785 private static final long serialVersionUID = 1L;
787 Object writeReplace() {
791 private void readObject(@SuppressWarnings(
"unused") ObjectInputStream in)
throws IOException {
792 throw new InvalidObjectException(
"RopeByteStream instances are not to be serialized directly");
817 throw new NullPointerException();
819 throw new IndexOutOfBoundsException();
827 throw new IndexOutOfBoundsException();
828 }
else if (
length > Integer.MAX_VALUE) {
829 length = Integer.MAX_VALUE;
844 int bytesRemaining =
length;
845 while (bytesRemaining > 0) {
848 if (bytesRemaining ==
length) {
856 int count = Math.min(currentPieceRemaining, bytesRemaining);
862 bytesRemaining -=
count;
866 return length - bytesRemaining;
870 public int read() throws IOException {
882 return RopeByteString.this.size() - bytesRead;
891 public void mark(
int readAheadLimit) {