Home | History | Annotate | Download | only in protobuf
      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 (at) 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    */
    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    */
    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    */
    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    */
    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
    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
    266   public int size() {
    267     return totalLength;
    268   }
    269 
    270   // =================================================================
    271   // Pieces
    272 
    273   @Override
    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
    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
    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
    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
    359   public void copyTo(ByteBuffer target) {
    360     left.copyTo(target);
    361     right.copyTo(target);
    362   }
    363 
    364   @Override
    365   public ByteBuffer asReadOnlyByteBuffer() {
    366     ByteBuffer byteBuffer = ByteBuffer.wrap(toByteArray());
    367     return byteBuffer.asReadOnlyBuffer();
    368   }
    369 
    370   @Override
    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
    384   public void writeTo(OutputStream outputStream) throws IOException {
    385     left.writeTo(outputStream);
    386     right.writeTo(outputStream);
    387   }
    388 
    389   @Override
    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
    404   void writeTo(ByteOutput output) throws IOException {
    405     left.writeTo(output);
    406     right.writeTo(output);
    407   }
    408 
    409   @Override
    410   protected String toStringInternal(Charset charset) {
    411     return new String(toByteArray(), charset);
    412   }
    413 
    414   // =================================================================
    415   // UTF-8 decoding
    416 
    417   @Override
    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
    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
    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    */
    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
    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
    545   public CodedInputStream newCodedInput() {
    546     return CodedInputStream.newInstance(new RopeInputStream());
    547   }
    548 
    549   @Override
    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 
    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 
    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      */
    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 
    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 
    684     private PieceIterator(ByteString root) {
    685       next = getLeafByLeft(root);
    686     }
    687 
    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 
    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
    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
    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
    735     public void remove() {
    736       throw new UnsupportedOperationException();
    737     }
    738   }
    739 
    740   // =================================================================
    741   // Serializable
    742 
    743   private static final long serialVersionUID = 1L;
    744 
    745   Object writeReplace() {
    746     return ByteString.wrap(toByteArray());
    747   }
    748 
    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 
    772     public RopeInputStream() {
    773       initialize();
    774     }
    775 
    776     @Override
    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
    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      */
    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
    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
    843     public int available() throws IOException {
    844       int bytesRead = currentPieceOffsetInRope + currentPieceIndex;
    845       return RopeByteString.this.size() - bytesRead;
    846     }
    847 
    848     @Override
    849     public boolean markSupported() {
    850       return true;
    851     }
    852 
    853     @Override
    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
    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() */
    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      */
    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