• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
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.ArrayList;
42 import java.util.Arrays;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.NoSuchElementException;
46 import java.util.Stack;
47 
48 /**
49  * Class to represent {@code ByteStrings} formed by concatenation of other
50  * ByteStrings, without copying the data in the pieces. The concatenation is
51  * represented as a tree whose leaf nodes are each a
52  * {@link com.google.protobuf.ByteString.LeafByteString}.
53  *
54  * <p>Most of the operation here is inspired by the now-famous paper <a
55  * href="https://web.archive.org/web/20060202015456/http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
56  * BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
57  * michael plass
58  *
59  * <p>The algorithms described in the paper have been implemented for character
60  * strings in {@code com.google.common.string.Rope} and in the c++ class {@code
61  * cord.cc}.
62  *
63  * <p>Fundamentally the Rope algorithm represents the collection of pieces as a
64  * binary tree. BAP95 uses a Fibonacci bound relating depth to a minimum
65  * sequence length, sequences that are too short relative to their depth cause a
66  * tree rebalance.  More precisely, a tree of depth d is "balanced" in the
67  * terminology of BAP95 if its length is at least F(d+2), where F(n) is the
68  * n-the Fibonacci number. Thus for depths 0, 1, 2, 3, 4, 5,... we have minimum
69  * lengths 1, 2, 3, 5, 8, 13,...
70  *
71  * @author carlanton@google.com (Carl Haverl)
72  */
73 final class RopeByteString extends ByteString {
74 
75   /**
76    * BAP95. Let Fn be the nth Fibonacci number. A {@link RopeByteString} of
77    * depth n is "balanced", i.e flat enough, if its length is at least Fn+2,
78    * e.g. a "balanced" {@link RopeByteString} of depth 1 must have length at
79    * least 2, of depth 4 must have length >= 8, etc.
80    *
81    * <p>There's nothing special about using the Fibonacci numbers for this, but
82    * they are a reasonable sequence for encapsulating the idea that we are OK
83    * with longer strings being encoded in deeper binary trees.
84    *
85    * <p>For 32-bit integers, this array has length 46.
86    */
87   private static final int[] minLengthByDepth;
88 
89   static {
90     // Dynamically generate the list of Fibonacci numbers the first time this
91     // class is accessed.
92     List<Integer> numbers = new ArrayList<Integer>();
93 
94     // we skip the first Fibonacci number (1).  So instead of: 1 1 2 3 5 8 ...
95     // we have: 1 2 3 5 8 ...
96     int f1 = 1;
97     int f2 = 1;
98 
99     // get all the values until we roll over.
100     while (f2 > 0) {
101       numbers.add(f2);
102       int temp = f1 + f2;
103       f1 = f2;
104       f2 = temp;
105     }
106 
107     // we include this here so that we can index this array to [x + 1] in the
108     // loops below.
109     numbers.add(Integer.MAX_VALUE);
110     minLengthByDepth = new int[numbers.size()];
111     for (int i = 0; i < minLengthByDepth.length; i++) {
112       // unbox all the values
113       minLengthByDepth[i] = numbers.get(i);
114     }
115   }
116 
117   private final int totalLength;
118   private final ByteString left;
119   private final ByteString right;
120   private final int leftLength;
121   private final int treeDepth;
122 
123   /**
124    * Create a new RopeByteString, which can be thought of as a new tree node, by
125    * recording references to the two given strings.
126    *
127    * @param left  string on the left of this node, should have {@code size() >
128    *              0}
129    * @param right string on the right of this node, should have {@code size() >
130    *              0}
131    */
RopeByteString(ByteString left, ByteString right)132   private RopeByteString(ByteString left, ByteString right) {
133     this.left = left;
134     this.right = right;
135     leftLength = left.size();
136     totalLength = leftLength + right.size();
137     treeDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
138   }
139 
140   /**
141    * Concatenate the given strings while performing various optimizations to
142    * slow the growth rate of tree depth and tree node count. The result is
143    * either a {@link com.google.protobuf.ByteString.LeafByteString} or a
144    * {@link RopeByteString} depending on which optimizations, if any, were
145    * applied.
146    *
147    * <p>Small pieces of length less than {@link
148    * ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
149    * BAP95.  Large pieces are referenced without copy.
150    *
151    * @param left  string on the left
152    * @param right string on the right
153    * @return concatenation representing the same sequence as the given strings
154    */
concatenate(ByteString left, ByteString right)155   static ByteString concatenate(ByteString left, ByteString right) {
156     if (right.size() == 0) {
157       return left;
158     }
159 
160     if (left.size() == 0) {
161       return right;
162     }
163 
164     final int newLength = left.size() + right.size();
165     if (newLength < ByteString.CONCATENATE_BY_COPY_SIZE) {
166       // Optimization from BAP95: For short (leaves in paper, but just short
167       // here) total length, do a copy of data to a new leaf.
168       return concatenateBytes(left, right);
169     }
170 
171     if (left instanceof RopeByteString) {
172       final RopeByteString leftRope = (RopeByteString) left;
173       if (leftRope.right.size() + right.size() < CONCATENATE_BY_COPY_SIZE) {
174         // Optimization from BAP95: As an optimization of the case where the
175         // ByteString is constructed by repeated concatenate, recognize the case
176         // where a short string is concatenated to a left-hand node whose
177         // right-hand branch is short.  In the paper this applies to leaves, but
178         // we just look at the length here. This has the advantage of shedding
179         // references to unneeded data when substrings have been taken.
180         //
181         // When we recognize this case, we do a copy of the data and create a
182         // new parent node so that the depth of the result is the same as the
183         // given left tree.
184         ByteString newRight = concatenateBytes(leftRope.right, right);
185         return new RopeByteString(leftRope.left, newRight);
186       }
187 
188       if (leftRope.left.getTreeDepth() > leftRope.right.getTreeDepth()
189           && leftRope.getTreeDepth() > right.getTreeDepth()) {
190         // Typically for concatenate-built strings the left-side is deeper than
191         // the right.  This is our final attempt to concatenate without
192         // increasing the tree depth.  We'll redo the node on the RHS.  This
193         // is yet another optimization for building the string by repeatedly
194         // concatenating on the right.
195         ByteString newRight = new RopeByteString(leftRope.right, right);
196         return new RopeByteString(leftRope.left, newRight);
197       }
198     }
199 
200     // Fine, we'll add a node and increase the tree depth--unless we rebalance ;^)
201     int newDepth = Math.max(left.getTreeDepth(), right.getTreeDepth()) + 1;
202     if (newLength >= minLengthByDepth[newDepth]) {
203       // The tree is shallow enough, so don't rebalance
204       return new RopeByteString(left, right);
205     }
206 
207     return new Balancer().balance(left, right);
208   }
209 
210   /**
211    * Concatenates two strings by copying data values. This is called in a few
212    * cases in order to reduce the growth of the number of tree nodes.
213    *
214    * @param left  string on the left
215    * @param right string on the right
216    * @return string formed by copying data bytes
217    */
concatenateBytes(ByteString left, ByteString right)218   private static ByteString concatenateBytes(ByteString left,
219       ByteString right) {
220     int leftSize = left.size();
221     int rightSize = right.size();
222     byte[] bytes = new byte[leftSize + rightSize];
223     left.copyTo(bytes, 0, 0, leftSize);
224     right.copyTo(bytes, 0, leftSize, rightSize);
225     return ByteString.wrap(bytes);  // Constructor wraps bytes
226   }
227 
228   /**
229    * Create a new RopeByteString for testing only while bypassing all the
230    * defenses of {@link #concatenate(ByteString, ByteString)}. This allows
231    * testing trees of specific structure. We are also able to insert empty
232    * leaves, though these are dis-allowed, so that we can make sure the
233    * implementation can withstand their presence.
234    *
235    * @param left  string on the left of this node
236    * @param right string on the right of this node
237    * @return an unsafe instance for testing only
238    */
newInstanceForTest(ByteString left, ByteString right)239   static RopeByteString newInstanceForTest(ByteString left, ByteString right) {
240     return new RopeByteString(left, right);
241   }
242 
243   /**
244    * Gets the byte at the given index.
245    * Throws {@link ArrayIndexOutOfBoundsException} for backwards-compatibility
246    * reasons although it would more properly be {@link
247    * IndexOutOfBoundsException}.
248    *
249    * @param index index of byte
250    * @return the value
251    * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
252    */
253   @Override
byteAt(int index)254   public byte byteAt(int index) {
255     checkIndex(index, totalLength);
256 
257     // Find the relevant piece by recursive descent
258     if (index < leftLength) {
259       return left.byteAt(index);
260     }
261 
262     return right.byteAt(index - leftLength);
263   }
264 
265   @Override
size()266   public int size() {
267     return totalLength;
268   }
269 
270   // =================================================================
271   // Pieces
272 
273   @Override
getTreeDepth()274   protected int getTreeDepth() {
275     return treeDepth;
276   }
277 
278   /**
279    * Determines if the tree is balanced according to BAP95, which means the tree
280    * is flat-enough with respect to the bounds. Note that this definition of
281    * balanced is one where sub-trees of balanced trees are not necessarily
282    * balanced.
283    *
284    * @return true if the tree is balanced
285    */
286   @Override
isBalanced()287   protected boolean isBalanced() {
288     return totalLength >= minLengthByDepth[treeDepth];
289   }
290 
291   /**
292    * Takes a substring of this one. This involves recursive descent along the
293    * left and right edges of the substring, and referencing any wholly contained
294    * segments in between. Any leaf nodes entirely uninvolved in the substring
295    * will not be referenced by the substring.
296    *
297    * <p>Substrings of {@code length < 2} should result in at most a single
298    * recursive call chain, terminating at a leaf node. Thus the result will be a
299    * {@link com.google.protobuf.ByteString.LeafByteString}.
300    *
301    * @param beginIndex start at this index
302    * @param endIndex   the last character is the one before this index
303    * @return substring leaf node or tree
304    */
305   @Override
substring(int beginIndex, int endIndex)306   public ByteString substring(int beginIndex, int endIndex) {
307     final int length = checkRange(beginIndex, endIndex, totalLength);
308 
309     if (length == 0) {
310       // Empty substring
311       return ByteString.EMPTY;
312     }
313 
314     if (length == totalLength) {
315       // The whole string
316       return this;
317     }
318 
319     // Proper substring
320     if (endIndex <= leftLength) {
321       // Substring on the left
322       return left.substring(beginIndex, endIndex);
323     }
324 
325     if (beginIndex >= leftLength) {
326       // Substring on the right
327       return right.substring(beginIndex - leftLength, endIndex - leftLength);
328     }
329 
330     // Split substring
331     ByteString leftSub = left.substring(beginIndex);
332     ByteString rightSub = right.substring(0, endIndex - leftLength);
333     // Intentionally not rebalancing, since in many cases these two
334     // substrings will already be less deep than the top-level
335     // RopeByteString we're taking a substring of.
336     return new RopeByteString(leftSub, rightSub);
337   }
338 
339   // =================================================================
340   // ByteString -> byte[]
341 
342   @Override
copyToInternal(byte[] target, int sourceOffset, int targetOffset, int numberToCopy)343   protected void copyToInternal(byte[] target, int sourceOffset,
344       int targetOffset, int numberToCopy) {
345    if (sourceOffset + numberToCopy <= leftLength) {
346       left.copyToInternal(target, sourceOffset, targetOffset, numberToCopy);
347     } else if (sourceOffset >= leftLength) {
348       right.copyToInternal(target, sourceOffset - leftLength, targetOffset,
349           numberToCopy);
350     } else {
351       int leftLength = this.leftLength - sourceOffset;
352       left.copyToInternal(target, sourceOffset, targetOffset, leftLength);
353       right.copyToInternal(target, 0, targetOffset + leftLength,
354           numberToCopy - leftLength);
355     }
356   }
357 
358   @Override
copyTo(ByteBuffer target)359   public void copyTo(ByteBuffer target) {
360     left.copyTo(target);
361     right.copyTo(target);
362   }
363 
364   @Override
asReadOnlyByteBuffer()365   public ByteBuffer asReadOnlyByteBuffer() {
366     ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
367     return byteBuffer.asReadOnlyBuffer();
368   }
369 
370   @Override
asReadOnlyByteBufferList()371   public List<ByteBuffer> asReadOnlyByteBufferList() {
372     // Walk through the list of LeafByteString's that make up this
373     // rope, and add each one as a read-only ByteBuffer.
374     List<ByteBuffer> result = new ArrayList<ByteBuffer>();
375     PieceIterator pieces = new PieceIterator(this);
376     while (pieces.hasNext()) {
377       LeafByteString byteString = pieces.next();
378       result.add(byteString.asReadOnlyByteBuffer());
379     }
380     return result;
381   }
382 
383   @Override
writeTo(OutputStream outputStream)384   public void writeTo(OutputStream outputStream) throws IOException {
385     left.writeTo(outputStream);
386     right.writeTo(outputStream);
387   }
388 
389   @Override
writeToInternal(OutputStream out, int sourceOffset, int numberToWrite)390   void writeToInternal(OutputStream out, int sourceOffset,
391       int numberToWrite) throws IOException {
392     if (sourceOffset + numberToWrite <= leftLength) {
393       left.writeToInternal(out, sourceOffset, numberToWrite);
394     } else if (sourceOffset >= leftLength) {
395       right.writeToInternal(out, sourceOffset - leftLength, numberToWrite);
396     } else {
397       int numberToWriteInLeft = leftLength - sourceOffset;
398       left.writeToInternal(out, sourceOffset, numberToWriteInLeft);
399       right.writeToInternal(out, 0, numberToWrite - numberToWriteInLeft);
400     }
401   }
402 
403   @Override
writeTo(ByteOutput output)404   void writeTo(ByteOutput output) throws IOException {
405     left.writeTo(output);
406     right.writeTo(output);
407   }
408 
409   @Override
toStringInternal(Charset charset)410   protected String toStringInternal(Charset charset) {
411     return new String(toByteArray(), charset);
412   }
413 
414   // =================================================================
415   // UTF-8 decoding
416 
417   @Override
isValidUtf8()418   public boolean isValidUtf8() {
419     int leftPartial = left.partialIsValidUtf8(Utf8.COMPLETE, 0, leftLength);
420     int state = right.partialIsValidUtf8(leftPartial, 0, right.size());
421     return state == Utf8.COMPLETE;
422   }
423 
424   @Override
partialIsValidUtf8(int state, int offset, int length)425   protected int partialIsValidUtf8(int state, int offset, int length) {
426     int toIndex = offset + length;
427     if (toIndex <= leftLength) {
428       return left.partialIsValidUtf8(state, offset, length);
429     } else if (offset >= leftLength) {
430       return right.partialIsValidUtf8(state, offset - leftLength, length);
431     } else {
432       int leftLength = this.leftLength - offset;
433       int leftPartial = left.partialIsValidUtf8(state, offset, leftLength);
434       return right.partialIsValidUtf8(leftPartial, 0, length - leftLength);
435     }
436   }
437 
438   // =================================================================
439   // equals() and hashCode()
440 
441   @Override
equals(Object other)442   public boolean equals(Object other) {
443     if (other == this) {
444       return true;
445     }
446     if (!(other instanceof ByteString)) {
447       return false;
448     }
449 
450     ByteString otherByteString = (ByteString) other;
451     if (totalLength != otherByteString.size()) {
452       return false;
453     }
454     if (totalLength == 0) {
455       return true;
456     }
457 
458     // You don't really want to be calling equals on long strings, but since
459     // we cache the hashCode, we effectively cache inequality. We use the cached
460     // hashCode if it's already computed.  It's arguable we should compute the
461     // hashCode here, and if we're going to be testing a bunch of byteStrings,
462     // it might even make sense.
463     int thisHash = peekCachedHashCode();
464     int thatHash = otherByteString.peekCachedHashCode();
465     if (thisHash != 0 && thatHash != 0 && thisHash != thatHash) {
466       return false;
467     }
468 
469     return equalsFragments(otherByteString);
470   }
471 
472   /**
473    * Determines if this string is equal to another of the same length by
474    * iterating over the leaf nodes. On each step of the iteration, the
475    * overlapping segments of the leaves are compared.
476    *
477    * @param other string of the same length as this one
478    * @return true if the values of this string equals the value of the given
479    *         one
480    */
equalsFragments(ByteString other)481   private boolean equalsFragments(ByteString other) {
482     int thisOffset = 0;
483     Iterator<LeafByteString> thisIter = new PieceIterator(this);
484     LeafByteString thisString = thisIter.next();
485 
486     int thatOffset = 0;
487     Iterator<LeafByteString> thatIter = new PieceIterator(other);
488     LeafByteString thatString = thatIter.next();
489 
490     int pos = 0;
491     while (true) {
492       int thisRemaining = thisString.size() - thisOffset;
493       int thatRemaining = thatString.size() - thatOffset;
494       int bytesToCompare = Math.min(thisRemaining, thatRemaining);
495 
496       // At least one of the offsets will be zero
497       boolean stillEqual = (thisOffset == 0)
498           ? thisString.equalsRange(thatString, thatOffset, bytesToCompare)
499           : thatString.equalsRange(thisString, thisOffset, bytesToCompare);
500       if (!stillEqual) {
501         return false;
502       }
503 
504       pos += bytesToCompare;
505       if (pos >= totalLength) {
506         if (pos == totalLength) {
507           return true;
508         }
509         throw new IllegalStateException();
510       }
511       // We always get to the end of at least one of the pieces
512       if (bytesToCompare == thisRemaining) { // If reached end of this
513         thisOffset = 0;
514         thisString = thisIter.next();
515       } else {
516         thisOffset += bytesToCompare;
517       }
518       if (bytesToCompare == thatRemaining) { // If reached end of that
519         thatOffset = 0;
520         thatString = thatIter.next();
521       } else {
522         thatOffset += bytesToCompare;
523       }
524     }
525   }
526 
527   @Override
partialHash(int h, int offset, int length)528   protected int partialHash(int h, int offset, int length) {
529     int toIndex = offset + length;
530     if (toIndex <= leftLength) {
531       return left.partialHash(h, offset, length);
532     } else if (offset >= leftLength) {
533       return right.partialHash(h, offset - leftLength, length);
534     } else {
535       int leftLength = this.leftLength - offset;
536       int leftPartial = left.partialHash(h, offset, leftLength);
537       return right.partialHash(leftPartial, 0, length - leftLength);
538     }
539   }
540 
541   // =================================================================
542   // Input stream
543 
544   @Override
newCodedInput()545   public CodedInputStream newCodedInput() {
546     return CodedInputStream.newInstance(new RopeInputStream());
547   }
548 
549   @Override
newInput()550   public InputStream newInput() {
551     return new RopeInputStream();
552   }
553 
554   /**
555    * This class implements the balancing algorithm of BAP95. In the paper the
556    * authors use an array to keep track of pieces, while here we use a stack.
557    * The tree is balanced by traversing subtrees in left to right order, and the
558    * stack always contains the part of the string we've traversed so far.
559    *
560    * <p>One surprising aspect of the algorithm is the result of balancing is not
561    * necessarily balanced, though it is nearly balanced.  For details, see
562    * BAP95.
563    */
564   private static class Balancer {
565     // Stack containing the part of the string, starting from the left, that
566     // we've already traversed.  The final string should be the equivalent of
567     // concatenating the strings on the stack from bottom to top.
568     private final Stack<ByteString> prefixesStack = new Stack<ByteString>();
569 
balance(ByteString left, ByteString right)570     private ByteString balance(ByteString left, ByteString right) {
571       doBalance(left);
572       doBalance(right);
573 
574       // Sweep stack to gather the result
575       ByteString partialString = prefixesStack.pop();
576       while (!prefixesStack.isEmpty()) {
577         ByteString newLeft = prefixesStack.pop();
578         partialString = new RopeByteString(newLeft, partialString);
579       }
580       // We should end up with a RopeByteString since at a minimum we will
581       // create one from concatenating left and right
582       return partialString;
583     }
584 
doBalance(ByteString root)585     private void doBalance(ByteString root) {
586       // BAP95: Insert balanced subtrees whole. This means the result might not
587       // be balanced, leading to repeated rebalancings on concatenate. However,
588       // these rebalancings are shallow due to ignoring balanced subtrees, and
589       // relatively few calls to insert() result.
590       if (root.isBalanced()) {
591         insert(root);
592       } else if (root instanceof RopeByteString) {
593         RopeByteString rbs = (RopeByteString) root;
594         doBalance(rbs.left);
595         doBalance(rbs.right);
596       } else {
597         throw new IllegalArgumentException(
598             "Has a new type of ByteString been created? Found " +
599                 root.getClass());
600       }
601     }
602 
603     /**
604      * Push a string on the balance stack (BAP95).  BAP95 uses an array and
605      * calls the elements in the array 'bins'.  We instead use a stack, so the
606      * 'bins' of lengths are represented by differences between the elements of
607      * minLengthByDepth.
608      *
609      * <p>If the length bin for our string, and all shorter length bins, are
610      * empty, we just push it on the stack.  Otherwise, we need to start
611      * concatenating, putting the given string in the "middle" and continuing
612      * until we land in an empty length bin that matches the length of our
613      * concatenation.
614      *
615      * @param byteString string to place on the balance stack
616      */
insert(ByteString byteString)617     private void insert(ByteString byteString) {
618       int depthBin = getDepthBinForLength(byteString.size());
619       int binEnd = minLengthByDepth[depthBin + 1];
620 
621       // BAP95: Concatenate all trees occupying bins representing the length of
622       // our new piece or of shorter pieces, to the extent that is possible.
623       // The goal is to clear the bin which our piece belongs in, but that may
624       // not be entirely possible if there aren't enough longer bins occupied.
625       if (prefixesStack.isEmpty() || prefixesStack.peek().size() >= binEnd) {
626         prefixesStack.push(byteString);
627       } else {
628         int binStart = minLengthByDepth[depthBin];
629 
630         // Concatenate the subtrees of shorter length
631         ByteString newTree = prefixesStack.pop();
632         while (!prefixesStack.isEmpty()
633             && prefixesStack.peek().size() < binStart) {
634           ByteString left = prefixesStack.pop();
635           newTree = new RopeByteString(left, newTree);
636         }
637 
638         // Concatenate the given string
639         newTree = new RopeByteString(newTree, byteString);
640 
641         // Continue concatenating until we land in an empty bin
642         while (!prefixesStack.isEmpty()) {
643           depthBin = getDepthBinForLength(newTree.size());
644           binEnd = minLengthByDepth[depthBin + 1];
645           if (prefixesStack.peek().size() < binEnd) {
646             ByteString left = prefixesStack.pop();
647             newTree = new RopeByteString(left, newTree);
648           } else {
649             break;
650           }
651         }
652         prefixesStack.push(newTree);
653       }
654     }
655 
getDepthBinForLength(int length)656     private int getDepthBinForLength(int length) {
657       int depth = Arrays.binarySearch(minLengthByDepth, length);
658       if (depth < 0) {
659         // It wasn't an exact match, so convert to the index of the containing
660         // fragment, which is one less even than the insertion point.
661         int insertionPoint = -(depth + 1);
662         depth = insertionPoint - 1;
663       }
664 
665       return depth;
666     }
667   }
668 
669   /**
670    * This class is a continuable tree traversal, which keeps the state
671    * information which would exist on the stack in a recursive traversal instead
672    * on a stack of "Bread Crumbs". The maximum depth of the stack in this
673    * iterator is the same as the depth of the tree being traversed.
674    *
675    * <p>This iterator is used to implement
676    * {@link RopeByteString#equalsFragments(ByteString)}.
677    */
678   private static class PieceIterator implements Iterator<LeafByteString> {
679 
680     private final Stack<RopeByteString> breadCrumbs =
681         new Stack<RopeByteString>();
682     private LeafByteString next;
683 
PieceIterator(ByteString root)684     private PieceIterator(ByteString root) {
685       next = getLeafByLeft(root);
686     }
687 
getLeafByLeft(ByteString root)688     private LeafByteString getLeafByLeft(ByteString root) {
689       ByteString pos = root;
690       while (pos instanceof RopeByteString) {
691         RopeByteString rbs = (RopeByteString) pos;
692         breadCrumbs.push(rbs);
693         pos = rbs.left;
694       }
695       return (LeafByteString) pos;
696     }
697 
getNextNonEmptyLeaf()698     private LeafByteString getNextNonEmptyLeaf() {
699       while (true) {
700         // Almost always, we go through this loop exactly once.  However, if
701         // we discover an empty string in the rope, we toss it and try again.
702         if (breadCrumbs.isEmpty()) {
703           return null;
704         } else {
705           LeafByteString result = getLeafByLeft(breadCrumbs.pop().right);
706           if (!result.isEmpty()) {
707             return result;
708           }
709         }
710       }
711     }
712 
713     @Override
hasNext()714     public boolean hasNext() {
715       return next != null;
716     }
717 
718     /**
719      * Returns the next item and advances one
720      * {@link com.google.protobuf.ByteString.LeafByteString}.
721      *
722      * @return next non-empty LeafByteString or {@code null}
723      */
724     @Override
next()725     public LeafByteString next() {
726       if (next == null) {
727         throw new NoSuchElementException();
728       }
729       LeafByteString result = next;
730       next = getNextNonEmptyLeaf();
731       return result;
732     }
733 
734     @Override
remove()735     public void remove() {
736       throw new UnsupportedOperationException();
737     }
738   }
739 
740   // =================================================================
741   // Serializable
742 
743   private static final long serialVersionUID = 1L;
744 
writeReplace()745   Object writeReplace() {
746     return ByteString.wrap(toByteArray());
747   }
748 
readObject(@uppressWarnings"unused") ObjectInputStream in)749   private void readObject(@SuppressWarnings("unused") ObjectInputStream in) throws IOException {
750     throw new InvalidObjectException(
751         "RopeByteStream instances are not to be serialized directly");
752   }
753 
754   /**
755    * This class is the {@link RopeByteString} equivalent for
756    * {@link ByteArrayInputStream}.
757    */
758   private class RopeInputStream extends InputStream {
759     // Iterates through the pieces of the rope
760     private PieceIterator pieceIterator;
761     // The current piece
762     private LeafByteString currentPiece;
763     // The size of the current piece
764     private int currentPieceSize;
765     // The index of the next byte to read in the current piece
766     private int currentPieceIndex;
767     // The offset of the start of the current piece in the rope byte string
768     private int currentPieceOffsetInRope;
769     // Offset in the buffer at which user called mark();
770     private int mark;
771 
RopeInputStream()772     public RopeInputStream() {
773       initialize();
774     }
775 
776     @Override
read(byte b[], int offset, int length)777     public int read(byte b[], int offset, int length)  {
778       if (b == null) {
779         throw new NullPointerException();
780       } else if (offset < 0 || length < 0 || length > b.length - offset) {
781         throw new IndexOutOfBoundsException();
782       }
783       return readSkipInternal(b, offset, length);
784     }
785 
786     @Override
skip(long length)787     public long skip(long length) {
788       if (length < 0) {
789         throw new IndexOutOfBoundsException();
790       } else if (length > Integer.MAX_VALUE) {
791         length = Integer.MAX_VALUE;
792       }
793       return readSkipInternal(null, 0, (int) length);
794     }
795 
796     /**
797      * Internal implementation of read and skip.  If b != null, then read the
798      * next {@code length} bytes into the buffer {@code b} at
799      * offset {@code offset}.  If b == null, then skip the next {@code length}
800      * bytes.
801      * <p>
802      * This method assumes that all error checking has already happened.
803      * <p>
804      * Returns the actual number of bytes read or skipped.
805      */
readSkipInternal(byte b[], int offset, int length)806     private int readSkipInternal(byte b[], int offset, int length)  {
807       int bytesRemaining = length;
808       while (bytesRemaining > 0) {
809         advanceIfCurrentPieceFullyRead();
810         if (currentPiece == null) {
811           if (bytesRemaining == length) {
812              // We didn't manage to read anything
813              return -1;
814            }
815           break;
816         } else {
817           // Copy the bytes from this piece.
818           int currentPieceRemaining = currentPieceSize - currentPieceIndex;
819           int count = Math.min(currentPieceRemaining, bytesRemaining);
820           if (b != null) {
821             currentPiece.copyTo(b, currentPieceIndex, offset, count);
822             offset += count;
823           }
824           currentPieceIndex += count;
825           bytesRemaining -= count;
826         }
827       }
828        // Return the number of bytes read.
829       return length - bytesRemaining;
830     }
831 
832     @Override
read()833     public int read() throws IOException {
834       advanceIfCurrentPieceFullyRead();
835       if (currentPiece == null) {
836         return -1;
837       } else {
838         return currentPiece.byteAt(currentPieceIndex++) & 0xFF;
839       }
840     }
841 
842     @Override
available()843     public int available() throws IOException {
844       int bytesRead = currentPieceOffsetInRope + currentPieceIndex;
845       return RopeByteString.this.size() - bytesRead;
846     }
847 
848     @Override
markSupported()849     public boolean markSupported() {
850       return true;
851     }
852 
853     @Override
mark(int readAheadLimit)854     public void mark(int readAheadLimit) {
855       // Set the mark to our position in the byte string
856       mark = currentPieceOffsetInRope + currentPieceIndex;
857     }
858 
859     @Override
reset()860     public synchronized void reset() {
861       // Just reinitialize and skip the specified number of bytes.
862       initialize();
863       readSkipInternal(null, 0, mark);
864     }
865 
866     /** Common initialization code used by both the constructor and reset() */
initialize()867     private void initialize() {
868       pieceIterator = new PieceIterator(RopeByteString.this);
869       currentPiece = pieceIterator.next();
870       currentPieceSize = currentPiece.size();
871       currentPieceIndex = 0;
872       currentPieceOffsetInRope = 0;
873     }
874 
875     /**
876      * Skips to the next piece if we have read all the data in the current
877      * piece.  Sets currentPiece to null if we have reached the end of the
878      * input.
879      */
advanceIfCurrentPieceFullyRead()880     private void advanceIfCurrentPieceFullyRead() {
881       if (currentPiece != null && currentPieceIndex == currentPieceSize) {
882         // Generally, we can only go through this loop at most once, since
883         // empty strings can't end up in a rope.  But better to test.
884         currentPieceOffsetInRope += currentPieceSize;
885         currentPieceIndex = 0;
886         if (pieceIterator.hasNext()) {
887           currentPiece = pieceIterator.next();
888           currentPieceSize = currentPiece.size();
889         } else {
890           currentPiece = null;
891           currentPieceSize = 0;
892         }
893       }
894     }
895   }
896 }
897