mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
25.33.2012 263d085885df024dca9250cc03c807912b0a7662
opendj3/opendj-ldap-sdk/src/main/java/org/forgerock/opendj/ldap/ByteStringBuilder.java
@@ -6,17 +6,16 @@
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
@@ -27,8 +26,6 @@
 */
package org.forgerock.opendj.ldap;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -39,1146 +36,907 @@
import com.forgerock.opendj.util.StaticUtils;
/**
 * A mutable sequence of bytes backed by a byte array.
 */
public final class ByteStringBuilder implements ByteSequence
{
  /**
   * A sub-sequence of the parent byte string builder. The sub-sequence will be
   * robust against all updates to the byte string builder except for
   * invocations of the method {@code clear()}.
   */
  private final class SubSequence implements ByteSequence
  {
    // The length of the sub-sequence.
    private final int subLength;
    // The offset of the sub-sequence.
    private final int subOffset;
public final class ByteStringBuilder implements ByteSequence {
    /**
     * Creates a new sub-sequence.
     * A sub-sequence of the parent byte string builder. The sub-sequence will
     * be robust against all updates to the byte string builder except for
     * invocations of the method {@code clear()}.
     */
    private final class SubSequence implements ByteSequence {
        // The length of the sub-sequence.
        private final int subLength;
        // The offset of the sub-sequence.
        private final int subOffset;
        /**
         * Creates a new sub-sequence.
         *
         * @param offset
         *            The offset of the sub-sequence.
         * @param length
         *            The length of the sub-sequence.
         */
        private SubSequence(final int offset, final int length) {
            this.subOffset = offset;
            this.subLength = length;
        }
        /**
         * {@inheritDoc}
         */
        public ByteSequenceReader asReader() {
            return new ByteSequenceReader(this);
        }
        /**
         * {@inheritDoc}
         */
        public byte byteAt(final int index) {
            if (index >= subLength || index < 0) {
                throw new IndexOutOfBoundsException();
            }
            // Protect against reallocation: use builder's buffer.
            return buffer[subOffset + index];
        }
        /**
         * {@inheritDoc}
         */
        public int compareTo(final byte[] b, final int offset, final int length) {
            ByteString.checkArrayBounds(b, offset, length);
            // Protect against reallocation: use builder's buffer.
            return ByteString.compareTo(buffer, subOffset, subLength, b, offset, length);
        }
        /**
         * {@inheritDoc}
         */
        public int compareTo(final ByteSequence o) {
            if (this == o) {
                return 0;
            }
            // Protect against reallocation: use builder's buffer.
            return -o.compareTo(buffer, subOffset, subLength);
        }
        /**
         * {@inheritDoc}
         */
        public byte[] copyTo(final byte[] b) {
            copyTo(b, 0);
            return b;
        }
        /**
         * {@inheritDoc}
         */
        public byte[] copyTo(final byte[] b, final int offset) {
            if (offset < 0) {
                throw new IndexOutOfBoundsException();
            }
            // Protect against reallocation: use builder's buffer.
            System.arraycopy(buffer, subOffset, b, offset, Math.min(subLength, b.length - offset));
            return b;
        }
        /**
         * {@inheritDoc}
         */
        public ByteStringBuilder copyTo(final ByteStringBuilder builder) {
            // Protect against reallocation: use builder's buffer.
            return builder.append(buffer, subOffset, subLength);
        }
        /**
         * {@inheritDoc}
         */
        public OutputStream copyTo(final OutputStream stream) throws IOException {
            // Protect against reallocation: use builder's buffer.
            stream.write(buffer, subOffset, subLength);
            return stream;
        }
        /**
         * {@inheritDoc}
         */
        public boolean equals(final byte[] b, final int offset, final int length) {
            ByteString.checkArrayBounds(b, offset, length);
            // Protect against reallocation: use builder's buffer.
            return ByteString.equals(buffer, subOffset, subLength, b, offset, length);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            } else if (o instanceof ByteSequence) {
                final ByteSequence other = (ByteSequence) o;
                // Protect against reallocation: use builder's buffer.
                return other.equals(buffer, subOffset, subLength);
            } else {
                return false;
            }
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public int hashCode() {
            // Protect against reallocation: use builder's buffer.
            return ByteString.hashCode(buffer, subOffset, subLength);
        }
        /**
         * {@inheritDoc}
         */
        public int length() {
            return subLength;
        }
        /**
         * {@inheritDoc}
         */
        public ByteSequence subSequence(final int start, final int end) {
            if (start < 0 || start > end || end > subLength) {
                throw new IndexOutOfBoundsException();
            }
            return new SubSequence(subOffset + start, end - start);
        }
        /**
         * {@inheritDoc}
         */
        public byte[] toByteArray() {
            return copyTo(new byte[subLength]);
        }
        /**
         * {@inheritDoc}
         */
        public ByteString toByteString() {
            // Protect against reallocation: use builder's buffer.
            final byte[] b = new byte[subLength];
            System.arraycopy(buffer, subOffset, b, 0, subLength);
            return ByteString.wrap(b);
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            // Protect against reallocation: use builder's buffer.
            return ByteString.toString(buffer, subOffset, subLength);
        }
    }
    // These are package private so that compression and crypto
    // functionality may directly access the fields.
    // The buffer where data is stored.
    byte[] buffer;
    // The number of bytes to expose from the buffer.
    int length;
    /**
     * Creates a new byte string builder with an initial capacity of 32 bytes.
     */
    public ByteStringBuilder() {
        // Initially create a 32 byte buffer.
        this(32);
    }
    /**
     * Creates a new byte string builder with the specified initial capacity.
     *
     * @param offset
     *          The offset of the sub-sequence.
     * @param length
     *          The length of the sub-sequence.
     * @param capacity
     *            The initial capacity.
     * @throws IllegalArgumentException
     *             If the {@code capacity} is negative.
     */
    private SubSequence(final int offset, final int length)
    {
      this.subOffset = offset;
      this.subLength = length;
    public ByteStringBuilder(final int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException();
        }
        this.buffer = new byte[capacity];
        this.length = 0;
    }
    /**
     * {@inheritDoc}
     * Appends the provided byte to this byte string builder.
     *
     * @param b
     *            The byte to be appended to this byte string builder.
     * @return This byte string builder.
     */
    public ByteSequenceReader asReader()
    {
      return new ByteSequenceReader(this);
    }
    /**
     * {@inheritDoc}
     */
    public byte byteAt(final int index)
    {
      if (index >= subLength || index < 0)
      {
        throw new IndexOutOfBoundsException();
      }
      // Protect against reallocation: use builder's buffer.
      return buffer[subOffset + index];
    }
    /**
     * {@inheritDoc}
     */
    public int compareTo(final byte[] b, final int offset, final int length)
    {
      ByteString.checkArrayBounds(b, offset, length);
      // Protect against reallocation: use builder's buffer.
      return ByteString.compareTo(buffer, subOffset, subLength, b, offset,
          length);
    }
    /**
     * {@inheritDoc}
     */
    public int compareTo(final ByteSequence o)
    {
      if (this == o)
      {
        return 0;
      }
      // Protect against reallocation: use builder's buffer.
      return -o.compareTo(buffer, subOffset, subLength);
    }
    /**
     * {@inheritDoc}
     */
    public byte[] copyTo(final byte[] b)
    {
      copyTo(b, 0);
      return b;
    }
    /**
     * {@inheritDoc}
     */
    public byte[] copyTo(final byte[] b, final int offset)
    {
      if (offset < 0)
      {
        throw new IndexOutOfBoundsException();
      }
      // Protect against reallocation: use builder's buffer.
      System.arraycopy(buffer, subOffset, b, offset, Math.min(subLength,
          b.length - offset));
      return b;
    }
    /**
     * {@inheritDoc}
     */
    public ByteStringBuilder copyTo(final ByteStringBuilder builder)
    {
      // Protect against reallocation: use builder's buffer.
      return builder.append(buffer, subOffset, subLength);
    }
    /**
     * {@inheritDoc}
     */
    public OutputStream copyTo(final OutputStream stream) throws IOException
    {
      // Protect against reallocation: use builder's buffer.
      stream.write(buffer, subOffset, subLength);
      return stream;
    }
    /**
     * {@inheritDoc}
     */
    public boolean equals(final byte[] b, final int offset, final int length)
    {
      ByteString.checkArrayBounds(b, offset, length);
      // Protect against reallocation: use builder's buffer.
      return ByteString.equals(buffer, subOffset, subLength, b, offset, length);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o)
    {
      if (this == o)
      {
        return true;
      }
      else if (o instanceof ByteSequence)
      {
        final ByteSequence other = (ByteSequence) o;
        // Protect against reallocation: use builder's buffer.
        return other.equals(buffer, subOffset, subLength);
      }
      else
      {
        return false;
      }
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode()
    {
      // Protect against reallocation: use builder's buffer.
      return ByteString.hashCode(buffer, subOffset, subLength);
    }
    /**
     * {@inheritDoc}
     */
    public int length()
    {
      return subLength;
    }
    /**
     * {@inheritDoc}
     */
    public ByteSequence subSequence(final int start, final int end)
    {
      if (start < 0 || start > end || end > subLength)
      {
        throw new IndexOutOfBoundsException();
      }
      return new SubSequence(subOffset + start, end - start);
    }
    /**
     * {@inheritDoc}
     */
    public byte[] toByteArray()
    {
      return copyTo(new byte[subLength]);
    }
    /**
     * {@inheritDoc}
     */
    public ByteString toByteString()
    {
      // Protect against reallocation: use builder's buffer.
      final byte[] b = new byte[subLength];
      System.arraycopy(buffer, subOffset, b, 0, subLength);
      return ByteString.wrap(b);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
      // Protect against reallocation: use builder's buffer.
      return ByteString.toString(buffer, subOffset, subLength);
    }
  }
  // These are package private so that compression and crypto
  // functionality may directly access the fields.
  // The buffer where data is stored.
  byte[] buffer;
  // The number of bytes to expose from the buffer.
  int length;
  /**
   * Creates a new byte string builder with an initial capacity of 32 bytes.
   */
  public ByteStringBuilder()
  {
    // Initially create a 32 byte buffer.
    this(32);
  }
  /**
   * Creates a new byte string builder with the specified initial capacity.
   *
   * @param capacity
   *          The initial capacity.
   * @throws IllegalArgumentException
   *           If the {@code capacity} is negative.
   */
  public ByteStringBuilder(final int capacity)
  {
    if (capacity < 0)
    {
      throw new IllegalArgumentException();
    }
    this.buffer = new byte[capacity];
    this.length = 0;
  }
  /**
   * Appends the provided byte to this byte string builder.
   *
   * @param b
   *          The byte to be appended to this byte string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final byte b)
  {
    ensureAdditionalCapacity(1);
    buffer[length++] = b;
    return this;
  }
  /**
   * Appends the provided byte array to this byte string builder.
   * <p>
   * An invocation of the form:
   *
   * <pre>
   * src.append(bytes)
   * </pre>
   *
   * Behaves in exactly the same way as the invocation:
   *
   * <pre>
   * src.append(bytes, 0, bytes.length);
   * </pre>
   *
   * @param bytes
   *          The byte array to be appended to this byte string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final byte[] bytes)
  {
    return append(bytes, 0, bytes.length);
  }
  /**
   * Appends the provided byte array to this byte string builder.
   *
   * @param bytes
   *          The byte array to be appended to this byte string builder.
   * @param offset
   *          The offset of the byte array to be used; must be non-negative and
   *          no larger than {@code bytes.length} .
   * @param length
   *          The length of the byte array to be used; must be non-negative and
   *          no larger than {@code bytes.length - offset}.
   * @return This byte string builder.
   * @throws IndexOutOfBoundsException
   *           If {@code offset} is negative or if {@code length} is negative or
   *           if {@code offset + length} is greater than {@code bytes.length}.
   */
  public ByteStringBuilder append(final byte[] bytes, final int offset,
      final int length)
  {
    ByteString.checkArrayBounds(bytes, offset, length);
    if (length != 0)
    {
      ensureAdditionalCapacity(length);
      System.arraycopy(bytes, offset, buffer, this.length, length);
      this.length += length;
    }
    return this;
  }
  /**
   * Appends the provided {@code ByteBuffer} to this byte string builder.
   *
   * @param buffer
   *          The byte buffer to be appended to this byte string builder.
   * @param length
   *          The number of bytes to be appended from {@code buffer}.
   * @return This byte string builder.
   * @throws IndexOutOfBoundsException
   *           If {@code length} is less than zero or greater than {@code
   *           buffer.remaining()}.
   */
  public ByteStringBuilder append(final ByteBuffer buffer, final int length)
  {
    if (length < 0 || length > buffer.remaining())
    {
      throw new IndexOutOfBoundsException();
    }
    if (length != 0)
    {
      ensureAdditionalCapacity(length);
      buffer.get(this.buffer, this.length, length);
      this.length += length;
    }
    return this;
  }
  /**
   * Appends the provided {@link ByteSequence} to this byte string builder.
   *
   * @param bytes
   *          The byte sequence to be appended to this byte string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final ByteSequence bytes)
  {
    return bytes.copyTo(this);
  }
  /**
   * Appends the provided {@link ByteSequenceReader} to this byte string
   * builder.
   *
   * @param reader
   *          The byte sequence reader to be appended to this byte string
   *          builder.
   * @param length
   *          The number of bytes to be appended from {@code reader}.
   * @return This byte string builder.
   * @throws IndexOutOfBoundsException
   *           If {@code length} is less than zero or greater than {@code
   *           reader.remaining()}.
   */
  public ByteStringBuilder append(final ByteSequenceReader reader,
      final int length)
  {
    if (length < 0 || length > reader.remaining())
    {
      throw new IndexOutOfBoundsException();
    }
    if (length != 0)
    {
      ensureAdditionalCapacity(length);
      reader.get(buffer, this.length, length);
      this.length += length;
    }
    return this;
  }
  /**
   * Appends the UTF-8 encoded bytes of the provided
   * char array to this byte string
   * builder.
   *
   * @param chars
   *          The char array whose UTF-8 encoding is to be appended to this byte
   *          string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final char[] chars)
  {
    if (chars == null)
    {
      return this;
    }
    // Assume that each char is 1 byte
    final int len = chars.length;
    ensureAdditionalCapacity(len);
    for (int i = 0; i < len; i++)
    {
      final char c = chars[i];
      final byte b = (byte) (c & 0x0000007F);
      if (c == b)
      {
        buffer[this.length + i] = b;
      }
      else
      {
        // There is a multi-byte char. Defer to JDK.
        final Charset utf8 = Charset.forName("UTF-8");
        final ByteBuffer byteBuffer = utf8.encode(CharBuffer.wrap(chars));
        final int remaining = byteBuffer.remaining();
        ensureAdditionalCapacity(remaining - len);
        byteBuffer.get(buffer, this.length, remaining);
        this.length += remaining;
    public ByteStringBuilder append(final byte b) {
        ensureAdditionalCapacity(1);
        buffer[length++] = b;
        return this;
      }
    }
    // The 1 byte char assumption was correct
    this.length += len;
    return this;
  }
  /**
   * Appends the provided {@code InputStream} to this byte string builder.
   *
   * @param stream
   *          The input stream to be appended to this byte string builder.
   * @param length
   *          The maximum number of bytes to be appended from {@code buffer}.
   * @return The number of bytes read from the input stream, or {@code -1} if
   *         the end of the input stream has been reached.
   * @throws IndexOutOfBoundsException
   *           If {@code length} is less than zero.
   * @throws IOException
   *           If an I/O error occurs.
   */
  public int append(final InputStream stream, final int length)
      throws IOException
  {
    if (length < 0)
    {
      throw new IndexOutOfBoundsException();
    /**
     * Appends the provided byte array to this byte string builder.
     * <p>
     * An invocation of the form:
     *
     * <pre>
     * src.append(bytes)
     * </pre>
     *
     * Behaves in exactly the same way as the invocation:
     *
     * <pre>
     * src.append(bytes, 0, bytes.length);
     * </pre>
     *
     * @param bytes
     *            The byte array to be appended to this byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(final byte[] bytes) {
        return append(bytes, 0, bytes.length);
    }
    ensureAdditionalCapacity(length);
    final int bytesRead = stream.read(buffer, this.length, length);
    if (bytesRead > 0)
    {
      this.length += bytesRead;
    }
    /**
     * Appends the provided byte array to this byte string builder.
     *
     * @param bytes
     *            The byte array to be appended to this byte string builder.
     * @param offset
     *            The offset of the byte array to be used; must be non-negative
     *            and no larger than {@code bytes.length} .
     * @param length
     *            The length of the byte array to be used; must be non-negative
     *            and no larger than {@code bytes.length - offset}.
     * @return This byte string builder.
     * @throws IndexOutOfBoundsException
     *             If {@code offset} is negative or if {@code length} is
     *             negative or if {@code offset + length} is greater than
     *             {@code bytes.length}.
     */
    public ByteStringBuilder append(final byte[] bytes, final int offset, final int length) {
        ByteString.checkArrayBounds(bytes, offset, length);
    return bytesRead;
  }
  /**
   * Appends the big-endian encoded bytes of the provided integer to this byte
   * string builder.
   *
   * @param i
   *          The integer whose big-endian encoding is to be appended to this
   *          byte string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(int i)
  {
    ensureAdditionalCapacity(4);
    for (int j = length + 3; j >= length; j--)
    {
      buffer[j] = (byte) (i & 0xFF);
      i >>>= 8;
    }
    length += 4;
    return this;
  }
  /**
   * Appends the big-endian encoded bytes of the provided long to this byte
   * string builder.
   *
   * @param l
   *          The long whose big-endian encoding is to be appended to this byte
   *          string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(long l)
  {
    ensureAdditionalCapacity(8);
    for (int i = length + 7; i >= length; i--)
    {
      buffer[i] = (byte) (l & 0xFF);
      l >>>= 8;
    }
    length += 8;
    return this;
  }
  /**
   * Appends the byte string representation of the provided object to this byte
   * string builder. The object is converted to a byte string as follows:
   * <ul>
   * <li>if the object is an instance of {@code ByteSequence} then this method
   * is equivalent to calling {@link #append(ByteSequence)}
   * <li>if the object is a {@code byte[]} then this method is equivalent to
   * calling {@link #append(byte[])}
   * <li>if the object is a {@code char[]} then this method is equivalent to
   * calling {@link #append(char[])}
   * <li>for all other types of object this method is equivalent to calling
   * {@link #append(String)} with the {@code toString()} representation of the
   * provided object.
   * </ul>
   * <b>Note:</b> this method treats {@code Long} and {@code Integer} objects
   * like any other type of {@code Object}. More specifically, the following
   * invocations are not equivalent:
   * <ul>
   * <li>{@code append(0)} is not equivalent to {@code append((Object) 0)}
   * <li>{@code append(0L)} is not equivalent to {@code append((Object) 0L)}
   * </ul>
   *
   * @param o
   *          The object to be appended to this byte string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final Object o)
  {
    if (o == null)
    {
      return this;
    }
    else if (o instanceof ByteSequence)
    {
      return append((ByteSequence) o);
    }
    else if (o instanceof byte[])
    {
      return append((byte[]) o);
    }
    else if (o instanceof char[])
    {
      return append((char[]) o);
    }
    else
    {
      return append(o.toString());
    }
  }
  /**
   * Appends the big-endian encoded bytes of the provided short to this byte
   * string builder.
   *
   * @param i
   *          The short whose big-endian encoding is to be appended to this byte
   *          string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(short i)
  {
    ensureAdditionalCapacity(2);
    for (int j = length + 1; j >= length; j--)
    {
      buffer[j] = (byte) (i & 0xFF);
      i >>>= 8;
    }
    length += 2;
    return this;
  }
  /**
   * Appends the UTF-8 encoded bytes of the provided string to this byte string
   * builder.
   *
   * @param s
   *          The string whose UTF-8 encoding is to be appended to this byte
   *          string builder.
   * @return This byte string builder.
   */
  public ByteStringBuilder append(final String s)
  {
    if (s == null)
    {
      return this;
    }
    // Assume that each char is 1 byte
    final int len = s.length();
    ensureAdditionalCapacity(len);
    for (int i = 0; i < len; i++)
    {
      final char c = s.charAt(i);
      final byte b = (byte) (c & 0x0000007F);
      if (c == b)
      {
        buffer[this.length + i] = b;
      }
      else
      {
        // There is a multi-byte char. Defer to JDK
        try
        {
          return append(s.getBytes("UTF-8"));
        if (length != 0) {
            ensureAdditionalCapacity(length);
            System.arraycopy(bytes, offset, buffer, this.length, length);
            this.length += length;
        }
        catch (final Exception e)
        {
          if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
          {
            StaticUtils.DEBUG_LOG.warning("Unable to encode String "
                + "to UTF-8 bytes: " + e.toString());
          }
          return append(s.getBytes());
        return this;
    }
    /**
     * Appends the provided {@code ByteBuffer} to this byte string builder.
     *
     * @param buffer
     *            The byte buffer to be appended to this byte string builder.
     * @param length
     *            The number of bytes to be appended from {@code buffer}.
     * @return This byte string builder.
     * @throws IndexOutOfBoundsException
     *             If {@code length} is less than zero or greater than
     *             {@code buffer.remaining()}.
     */
    public ByteStringBuilder append(final ByteBuffer buffer, final int length) {
        if (length < 0 || length > buffer.remaining()) {
            throw new IndexOutOfBoundsException();
        }
      }
        if (length != 0) {
            ensureAdditionalCapacity(length);
            buffer.get(this.buffer, this.length, length);
            this.length += length;
        }
        return this;
    }
    // The 1 byte char assumption was correct
    this.length += len;
    return this;
  }
  /**
   * Appends the ASN.1 BER length encoding representation of the provided
   * integer to this byte string builder.
   *
   * @param length
   *          The value to encode using the BER length encoding rules.
   * @return This byte string builder.
   */
  public ByteStringBuilder appendBERLength(final int length)
  {
    if ((length & 0x0000007F) == length)
    {
      ensureAdditionalCapacity(1);
      buffer[this.length++] = (byte) (length & 0xFF);
    }
    else if ((length & 0x000000FF) == length)
    {
      ensureAdditionalCapacity(2);
      buffer[this.length++] = (byte) 0x81;
      buffer[this.length++] = (byte) (length & 0xFF);
    }
    else if ((length & 0x0000FFFF) == length)
    {
      ensureAdditionalCapacity(3);
      buffer[this.length++] = (byte) 0x82;
      buffer[this.length++] = (byte) (length >> 8 & 0xFF);
      buffer[this.length++] = (byte) (length & 0xFF);
    }
    else if ((length & 0x00FFFFFF) == length)
    {
      ensureAdditionalCapacity(4);
      buffer[this.length++] = (byte) 0x83;
      buffer[this.length++] = (byte) (length >> 16 & 0xFF);
      buffer[this.length++] = (byte) (length >> 8 & 0xFF);
      buffer[this.length++] = (byte) (length & 0xFF);
    }
    else
    {
      ensureAdditionalCapacity(5);
      buffer[this.length++] = (byte) 0x84;
      buffer[this.length++] = (byte) (length >> 24 & 0xFF);
      buffer[this.length++] = (byte) (length >> 16 & 0xFF);
      buffer[this.length++] = (byte) (length >> 8 & 0xFF);
      buffer[this.length++] = (byte) (length & 0xFF);
    }
    return this;
  }
  /**
   * Returns a {@link ByteSequenceReader} which can be used to incrementally
   * read and decode data from this byte string builder.
   * <p>
   * <b>NOTE:</b> all concurrent updates to this byte string builder are
   * supported with the exception of {@link #clear()}. Any invocations of
   * {@link #clear()} must be accompanied by a subsequent call to {@code
   * ByteSequenceReader.rewind()}.
   *
   * @return The {@link ByteSequenceReader} which can be used to incrementally
   *         read and decode data from this byte string builder.
   * @see #clear()
   */
  public ByteSequenceReader asReader()
  {
    return new ByteSequenceReader(this);
  }
  /**
   * {@inheritDoc}
   */
  public byte byteAt(final int index)
  {
    if (index >= length || index < 0)
    {
      throw new IndexOutOfBoundsException();
    }
    return buffer[index];
  }
  /**
   * Sets the length of this byte string builder to zero.
   * <p>
   * <b>NOTE:</b> if this method is called, then {@code
   * ByteSequenceReader.rewind()} must also be called on any associated byte
   * sequence readers in order for them to remain valid.
   *
   * @return This byte string builder.
   * @see #asReader()
   */
  public ByteStringBuilder clear()
  {
    length = 0;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public int compareTo(final byte[] bytes, final int offset, final int length)
  {
    ByteString.checkArrayBounds(bytes, offset, length);
    return ByteString.compareTo(this.buffer, 0, this.length, bytes, offset,
        length);
  }
  /**
   * {@inheritDoc}
   */
  public int compareTo(final ByteSequence o)
  {
    if (this == o)
    {
      return 0;
    }
    return -o.compareTo(buffer, 0, length);
  }
  /**
   * {@inheritDoc}
   */
  public byte[] copyTo(final byte[] bytes)
  {
    copyTo(bytes, 0);
    return bytes;
  }
  /**
   * {@inheritDoc}
   */
  public byte[] copyTo(final byte[] bytes, final int offset)
  {
    if (offset < 0)
    {
      throw new IndexOutOfBoundsException();
    }
    System.arraycopy(buffer, 0, bytes, offset,
        Math.min(length, bytes.length - offset));
    return bytes;
  }
  /**
   * {@inheritDoc}
   */
  public ByteStringBuilder copyTo(final ByteStringBuilder builder)
  {
    builder.append(buffer, 0, length);
    return builder;
  }
  /**
   * {@inheritDoc}
   */
  public OutputStream copyTo(final OutputStream stream) throws IOException
  {
    stream.write(buffer, 0, length);
    return stream;
  }
  /**
   * Ensures that the specified number of additional bytes will fit in this byte
   * string builder and resizes it if necessary.
   *
   * @param size
   *          The number of additional bytes.
   * @return This byte string builder.
   */
  public ByteStringBuilder ensureAdditionalCapacity(final int size)
  {
    final int newCount = this.length + size;
    if (newCount > buffer.length)
    {
      final byte[] newbuffer = new byte[Math.max(buffer.length << 1, newCount)];
      System.arraycopy(buffer, 0, newbuffer, 0, buffer.length);
      buffer = newbuffer;
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public boolean equals(final byte[] bytes, final int offset, final int length)
  {
    ByteString.checkArrayBounds(bytes, offset, length);
    return ByteString
        .equals(this.buffer, 0, this.length, bytes, offset, length);
  }
  /**
   * Indicates whether the provided object is equal to this byte string builder.
   * In order for it to be considered equal, the provided object must be a byte
   * sequence containing the same bytes in the same order.
   *
   * @param o
   *          The object for which to make the determination.
   * @return {@code true} if the provided object is a byte sequence whose
   *         content is equal to that of this byte string builder, or {@code
   *         false} if not.
   */
  @Override
  public boolean equals(final Object o)
  {
    if (this == o)
    {
      return true;
    }
    else if (o instanceof ByteSequence)
    {
      final ByteSequence other = (ByteSequence) o;
      return other.equals(buffer, 0, length);
    }
    else
    {
      return false;
    }
  }
  /**
   * Returns the byte array that backs this byte string builder. Modifications
   * to this byte string builder's content may cause the returned array's
   * content to be modified, and vice versa.
   * <p>
   * Note that the length of the returned array is only guaranteed to be the
   * same as the length of this byte string builder immediately after a call to
   * {@link #trimToSize()}.
   * <p>
   * In addition, subsequent modifications to this byte string builder may cause
   * the backing byte array to be reallocated thus decoupling the returned byte
   * array from this byte string builder.
   *
   * @return The byte array that backs this byte string builder.
   */
  public byte[] getBackingArray()
  {
    return buffer;
  }
  /**
   * Returns a hash code for this byte string builder. It will be the sum of all
   * of the bytes contained in the byte string builder.
   * <p>
   * <b>NOTE:</b> subsequent changes to this byte string builder will invalidate
   * the returned hash code.
   *
   * @return A hash code for this byte string builder.
   */
  @Override
  public int hashCode()
  {
    return ByteString.hashCode(buffer, 0, length);
  }
  /**
   * {@inheritDoc}
   */
  public int length()
  {
    return length;
  }
  /**
   * Sets the length of this byte string builder.
   * <p>
   * If the <code>newLength</code> argument is less than the current length, the
   * length is changed to the specified length.
   * <p>
   * If the <code>newLength</code> argument is greater than or equal to the
   * current length, then the capacity is increased and sufficient null bytes
   * are appended so that length becomes the <code>newLength</code> argument.
   * <p>
   * The <code>newLength</code> argument must be greater than or equal to
   * <code>0</code>.
   *
   * @param newLength
   *          The new length.
   * @return This byte string builder.
   * @throws IndexOutOfBoundsException
   *           If the <code>newLength</code> argument is negative.
   */
  public ByteStringBuilder setLength(final int newLength)
  {
    if (newLength < 0)
    {
      throw new IndexOutOfBoundsException("Negative newLength: " + newLength);
    /**
     * Appends the provided {@link ByteSequence} to this byte string builder.
     *
     * @param bytes
     *            The byte sequence to be appended to this byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(final ByteSequence bytes) {
        return bytes.copyTo(this);
    }
    if (newLength > length)
    {
      ensureAdditionalCapacity(newLength - length);
    /**
     * Appends the provided {@link ByteSequenceReader} to this byte string
     * builder.
     *
     * @param reader
     *            The byte sequence reader to be appended to this byte string
     *            builder.
     * @param length
     *            The number of bytes to be appended from {@code reader}.
     * @return This byte string builder.
     * @throws IndexOutOfBoundsException
     *             If {@code length} is less than zero or greater than
     *             {@code reader.remaining()}.
     */
    public ByteStringBuilder append(final ByteSequenceReader reader, final int length) {
        if (length < 0 || length > reader.remaining()) {
            throw new IndexOutOfBoundsException();
        }
      // Pad with zeros.
      for (int i = length; i < newLength; i++)
      {
        buffer[i] = 0;
      }
    }
    length = newLength;
        if (length != 0) {
            ensureAdditionalCapacity(length);
            reader.get(buffer, this.length, length);
            this.length += length;
        }
    return this;
  }
  /**
   * Returns a new byte sequence that is a subsequence of this byte sequence.
   * <p>
   * The subsequence starts with the byte value at the specified {@code start}
   * index and ends with the byte value at index {@code end - 1}. The length (in
   * bytes) of the returned sequence is {@code end - start}, so if {@code start
   * == end} then an empty sequence is returned.
   * <p>
   * <b>NOTE:</b> the returned sub-sequence will be robust against all updates
   * to the byte string builder except for invocations of the method
   * {@link #clear()}. If a permanent immutable byte sequence is required then
   * callers should invoke {@code toByteString()} on the returned byte sequence.
   *
   * @param start
   *          The start index, inclusive.
   * @param end
   *          The end index, exclusive.
   * @return The newly created byte subsequence.
   */
  public ByteSequence subSequence(final int start, final int end)
  {
    if (start < 0 || start > end || end > length)
    {
      throw new IndexOutOfBoundsException();
        return this;
    }
    return new SubSequence(start, end - start);
  }
    /**
     * Appends the UTF-8 encoded bytes of the provided char array to this byte
     * string builder.
     *
     * @param chars
     *            The char array whose UTF-8 encoding is to be appended to this
     *            byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(final char[] chars) {
        if (chars == null) {
            return this;
        }
        // Assume that each char is 1 byte
        final int len = chars.length;
        ensureAdditionalCapacity(len);
        for (int i = 0; i < len; i++) {
            final char c = chars[i];
            final byte b = (byte) (c & 0x0000007F);
  /**
   * {@inheritDoc}
   */
  public byte[] toByteArray()
  {
    return copyTo(new byte[length]);
  }
            if (c == b) {
                buffer[this.length + i] = b;
            } else {
                // There is a multi-byte char. Defer to JDK.
                final Charset utf8 = Charset.forName("UTF-8");
                final ByteBuffer byteBuffer = utf8.encode(CharBuffer.wrap(chars));
                final int remaining = byteBuffer.remaining();
                ensureAdditionalCapacity(remaining - len);
                byteBuffer.get(buffer, this.length, remaining);
                this.length += remaining;
                return this;
            }
        }
        // The 1 byte char assumption was correct
        this.length += len;
        return this;
  /**
   * Returns the {@link ByteString} representation of this byte string builder.
   * Subsequent changes to this byte string builder will not modify the returned
   * {@link ByteString}.
   *
   * @return The {@link ByteString} representation of this byte sequence.
   */
  public ByteString toByteString()
  {
    final byte[] b = new byte[length];
    System.arraycopy(buffer, 0, b, 0, length);
    return ByteString.wrap(b);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    return ByteString.toString(buffer, 0, length);
  }
  /**
   * Attempts to reduce storage used for this byte string builder. If the buffer
   * is larger than necessary to hold its current sequence of bytes, then it may
   * be resized to become more space efficient.
   *
   * @return This byte string builder.
   */
  public ByteStringBuilder trimToSize()
  {
    if (buffer.length > length)
    {
      final byte[] newBuffer = new byte[length];
      System.arraycopy(buffer, 0, newBuffer, 0, length);
      buffer = newBuffer;
    }
    return this;
  }
    /**
     * Appends the provided {@code InputStream} to this byte string builder.
     *
     * @param stream
     *            The input stream to be appended to this byte string builder.
     * @param length
     *            The maximum number of bytes to be appended from {@code buffer}
     *            .
     * @return The number of bytes read from the input stream, or {@code -1} if
     *         the end of the input stream has been reached.
     * @throws IndexOutOfBoundsException
     *             If {@code length} is less than zero.
     * @throws IOException
     *             If an I/O error occurs.
     */
    public int append(final InputStream stream, final int length) throws IOException {
        if (length < 0) {
            throw new IndexOutOfBoundsException();
        }
        ensureAdditionalCapacity(length);
        final int bytesRead = stream.read(buffer, this.length, length);
        if (bytesRead > 0) {
            this.length += bytesRead;
        }
        return bytesRead;
    }
    /**
     * Appends the big-endian encoded bytes of the provided integer to this byte
     * string builder.
     *
     * @param i
     *            The integer whose big-endian encoding is to be appended to
     *            this byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(int i) {
        ensureAdditionalCapacity(4);
        for (int j = length + 3; j >= length; j--) {
            buffer[j] = (byte) (i & 0xFF);
            i >>>= 8;
        }
        length += 4;
        return this;
    }
    /**
     * Appends the big-endian encoded bytes of the provided long to this byte
     * string builder.
     *
     * @param l
     *            The long whose big-endian encoding is to be appended to this
     *            byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(long l) {
        ensureAdditionalCapacity(8);
        for (int i = length + 7; i >= length; i--) {
            buffer[i] = (byte) (l & 0xFF);
            l >>>= 8;
        }
        length += 8;
        return this;
    }
    /**
     * Appends the byte string representation of the provided object to this
     * byte string builder. The object is converted to a byte string as follows:
     * <ul>
     * <li>if the object is an instance of {@code ByteSequence} then this method
     * is equivalent to calling {@link #append(ByteSequence)}
     * <li>if the object is a {@code byte[]} then this method is equivalent to
     * calling {@link #append(byte[])}
     * <li>if the object is a {@code char[]} then this method is equivalent to
     * calling {@link #append(char[])}
     * <li>for all other types of object this method is equivalent to calling
     * {@link #append(String)} with the {@code toString()} representation of the
     * provided object.
     * </ul>
     * <b>Note:</b> this method treats {@code Long} and {@code Integer} objects
     * like any other type of {@code Object}. More specifically, the following
     * invocations are not equivalent:
     * <ul>
     * <li>{@code append(0)} is not equivalent to {@code append((Object) 0)}
     * <li>{@code append(0L)} is not equivalent to {@code append((Object) 0L)}
     * </ul>
     *
     * @param o
     *            The object to be appended to this byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(final Object o) {
        if (o == null) {
            return this;
        } else if (o instanceof ByteSequence) {
            return append((ByteSequence) o);
        } else if (o instanceof byte[]) {
            return append((byte[]) o);
        } else if (o instanceof char[]) {
            return append((char[]) o);
        } else {
            return append(o.toString());
        }
    }
    /**
     * Appends the big-endian encoded bytes of the provided short to this byte
     * string builder.
     *
     * @param i
     *            The short whose big-endian encoding is to be appended to this
     *            byte string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(short i) {
        ensureAdditionalCapacity(2);
        for (int j = length + 1; j >= length; j--) {
            buffer[j] = (byte) (i & 0xFF);
            i >>>= 8;
        }
        length += 2;
        return this;
    }
    /**
     * Appends the UTF-8 encoded bytes of the provided string to this byte
     * string builder.
     *
     * @param s
     *            The string whose UTF-8 encoding is to be appended to this byte
     *            string builder.
     * @return This byte string builder.
     */
    public ByteStringBuilder append(final String s) {
        if (s == null) {
            return this;
        }
        // Assume that each char is 1 byte
        final int len = s.length();
        ensureAdditionalCapacity(len);
        for (int i = 0; i < len; i++) {
            final char c = s.charAt(i);
            final byte b = (byte) (c & 0x0000007F);
            if (c == b) {
                buffer[this.length + i] = b;
            } else {
                // There is a multi-byte char. Defer to JDK
                try {
                    return append(s.getBytes("UTF-8"));
                } catch (final Exception e) {
                    if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING)) {
                        StaticUtils.DEBUG_LOG.warning("Unable to encode String "
                                + "to UTF-8 bytes: " + e.toString());
                    }
                    return append(s.getBytes());
                }
            }
        }
        // The 1 byte char assumption was correct
        this.length += len;
        return this;
    }
    /**
     * Appends the ASN.1 BER length encoding representation of the provided
     * integer to this byte string builder.
     *
     * @param length
     *            The value to encode using the BER length encoding rules.
     * @return This byte string builder.
     */
    public ByteStringBuilder appendBERLength(final int length) {
        if ((length & 0x0000007F) == length) {
            ensureAdditionalCapacity(1);
            buffer[this.length++] = (byte) (length & 0xFF);
        } else if ((length & 0x000000FF) == length) {
            ensureAdditionalCapacity(2);
            buffer[this.length++] = (byte) 0x81;
            buffer[this.length++] = (byte) (length & 0xFF);
        } else if ((length & 0x0000FFFF) == length) {
            ensureAdditionalCapacity(3);
            buffer[this.length++] = (byte) 0x82;
            buffer[this.length++] = (byte) (length >> 8 & 0xFF);
            buffer[this.length++] = (byte) (length & 0xFF);
        } else if ((length & 0x00FFFFFF) == length) {
            ensureAdditionalCapacity(4);
            buffer[this.length++] = (byte) 0x83;
            buffer[this.length++] = (byte) (length >> 16 & 0xFF);
            buffer[this.length++] = (byte) (length >> 8 & 0xFF);
            buffer[this.length++] = (byte) (length & 0xFF);
        } else {
            ensureAdditionalCapacity(5);
            buffer[this.length++] = (byte) 0x84;
            buffer[this.length++] = (byte) (length >> 24 & 0xFF);
            buffer[this.length++] = (byte) (length >> 16 & 0xFF);
            buffer[this.length++] = (byte) (length >> 8 & 0xFF);
            buffer[this.length++] = (byte) (length & 0xFF);
        }
        return this;
    }
    /**
     * Returns a {@link ByteSequenceReader} which can be used to incrementally
     * read and decode data from this byte string builder.
     * <p>
     * <b>NOTE:</b> all concurrent updates to this byte string builder are
     * supported with the exception of {@link #clear()}. Any invocations of
     * {@link #clear()} must be accompanied by a subsequent call to
     * {@code ByteSequenceReader.rewind()}.
     *
     * @return The {@link ByteSequenceReader} which can be used to incrementally
     *         read and decode data from this byte string builder.
     * @see #clear()
     */
    public ByteSequenceReader asReader() {
        return new ByteSequenceReader(this);
    }
    /**
     * {@inheritDoc}
     */
    public byte byteAt(final int index) {
        if (index >= length || index < 0) {
            throw new IndexOutOfBoundsException();
        }
        return buffer[index];
    }
    /**
     * Sets the length of this byte string builder to zero.
     * <p>
     * <b>NOTE:</b> if this method is called, then
     * {@code ByteSequenceReader.rewind()} must also be called on any associated
     * byte sequence readers in order for them to remain valid.
     *
     * @return This byte string builder.
     * @see #asReader()
     */
    public ByteStringBuilder clear() {
        length = 0;
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public int compareTo(final byte[] bytes, final int offset, final int length) {
        ByteString.checkArrayBounds(bytes, offset, length);
        return ByteString.compareTo(this.buffer, 0, this.length, bytes, offset, length);
    }
    /**
     * {@inheritDoc}
     */
    public int compareTo(final ByteSequence o) {
        if (this == o) {
            return 0;
        }
        return -o.compareTo(buffer, 0, length);
    }
    /**
     * {@inheritDoc}
     */
    public byte[] copyTo(final byte[] bytes) {
        copyTo(bytes, 0);
        return bytes;
    }
    /**
     * {@inheritDoc}
     */
    public byte[] copyTo(final byte[] bytes, final int offset) {
        if (offset < 0) {
            throw new IndexOutOfBoundsException();
        }
        System.arraycopy(buffer, 0, bytes, offset, Math.min(length, bytes.length - offset));
        return bytes;
    }
    /**
     * {@inheritDoc}
     */
    public ByteStringBuilder copyTo(final ByteStringBuilder builder) {
        builder.append(buffer, 0, length);
        return builder;
    }
    /**
     * {@inheritDoc}
     */
    public OutputStream copyTo(final OutputStream stream) throws IOException {
        stream.write(buffer, 0, length);
        return stream;
    }
    /**
     * Ensures that the specified number of additional bytes will fit in this
     * byte string builder and resizes it if necessary.
     *
     * @param size
     *            The number of additional bytes.
     * @return This byte string builder.
     */
    public ByteStringBuilder ensureAdditionalCapacity(final int size) {
        final int newCount = this.length + size;
        if (newCount > buffer.length) {
            final byte[] newbuffer = new byte[Math.max(buffer.length << 1, newCount)];
            System.arraycopy(buffer, 0, newbuffer, 0, buffer.length);
            buffer = newbuffer;
        }
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public boolean equals(final byte[] bytes, final int offset, final int length) {
        ByteString.checkArrayBounds(bytes, offset, length);
        return ByteString.equals(this.buffer, 0, this.length, bytes, offset, length);
    }
    /**
     * Indicates whether the provided object is equal to this byte string
     * builder. In order for it to be considered equal, the provided object must
     * be a byte sequence containing the same bytes in the same order.
     *
     * @param o
     *            The object for which to make the determination.
     * @return {@code true} if the provided object is a byte sequence whose
     *         content is equal to that of this byte string builder, or
     *         {@code false} if not.
     */
    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        } else if (o instanceof ByteSequence) {
            final ByteSequence other = (ByteSequence) o;
            return other.equals(buffer, 0, length);
        } else {
            return false;
        }
    }
    /**
     * Returns the byte array that backs this byte string builder. Modifications
     * to this byte string builder's content may cause the returned array's
     * content to be modified, and vice versa.
     * <p>
     * Note that the length of the returned array is only guaranteed to be the
     * same as the length of this byte string builder immediately after a call
     * to {@link #trimToSize()}.
     * <p>
     * In addition, subsequent modifications to this byte string builder may
     * cause the backing byte array to be reallocated thus decoupling the
     * returned byte array from this byte string builder.
     *
     * @return The byte array that backs this byte string builder.
     */
    public byte[] getBackingArray() {
        return buffer;
    }
    /**
     * Returns a hash code for this byte string builder. It will be the sum of
     * all of the bytes contained in the byte string builder.
     * <p>
     * <b>NOTE:</b> subsequent changes to this byte string builder will
     * invalidate the returned hash code.
     *
     * @return A hash code for this byte string builder.
     */
    @Override
    public int hashCode() {
        return ByteString.hashCode(buffer, 0, length);
    }
    /**
     * {@inheritDoc}
     */
    public int length() {
        return length;
    }
    /**
     * Sets the length of this byte string builder.
     * <p>
     * If the <code>newLength</code> argument is less than the current length,
     * the length is changed to the specified length.
     * <p>
     * If the <code>newLength</code> argument is greater than or equal to the
     * current length, then the capacity is increased and sufficient null bytes
     * are appended so that length becomes the <code>newLength</code> argument.
     * <p>
     * The <code>newLength</code> argument must be greater than or equal to
     * <code>0</code>.
     *
     * @param newLength
     *            The new length.
     * @return This byte string builder.
     * @throws IndexOutOfBoundsException
     *             If the <code>newLength</code> argument is negative.
     */
    public ByteStringBuilder setLength(final int newLength) {
        if (newLength < 0) {
            throw new IndexOutOfBoundsException("Negative newLength: " + newLength);
        }
        if (newLength > length) {
            ensureAdditionalCapacity(newLength - length);
            // Pad with zeros.
            for (int i = length; i < newLength; i++) {
                buffer[i] = 0;
            }
        }
        length = newLength;
        return this;
    }
    /**
     * Returns a new byte sequence that is a subsequence of this byte sequence.
     * <p>
     * The subsequence starts with the byte value at the specified {@code start}
     * index and ends with the byte value at index {@code end - 1}. The length
     * (in bytes) of the returned sequence is {@code end - start}, so if
     * {@code start
     * == end} then an empty sequence is returned.
     * <p>
     * <b>NOTE:</b> the returned sub-sequence will be robust against all updates
     * to the byte string builder except for invocations of the method
     * {@link #clear()}. If a permanent immutable byte sequence is required then
     * callers should invoke {@code toByteString()} on the returned byte
     * sequence.
     *
     * @param start
     *            The start index, inclusive.
     * @param end
     *            The end index, exclusive.
     * @return The newly created byte subsequence.
     */
    public ByteSequence subSequence(final int start, final int end) {
        if (start < 0 || start > end || end > length) {
            throw new IndexOutOfBoundsException();
        }
        return new SubSequence(start, end - start);
    }
    /**
     * {@inheritDoc}
     */
    public byte[] toByteArray() {
        return copyTo(new byte[length]);
    }
    /**
     * Returns the {@link ByteString} representation of this byte string
     * builder. Subsequent changes to this byte string builder will not modify
     * the returned {@link ByteString}.
     *
     * @return The {@link ByteString} representation of this byte sequence.
     */
    public ByteString toByteString() {
        final byte[] b = new byte[length];
        System.arraycopy(buffer, 0, b, 0, length);
        return ByteString.wrap(b);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return ByteString.toString(buffer, 0, length);
    }
    /**
     * Attempts to reduce storage used for this byte string builder. If the
     * buffer is larger than necessary to hold its current sequence of bytes,
     * then it may be resized to become more space efficient.
     *
     * @return This byte string builder.
     */
    public ByteStringBuilder trimToSize() {
        if (buffer.length > length) {
            final byte[] newBuffer = new byte[length];
            System.arraycopy(buffer, 0, newBuffer, 0, length);
            buffer = newBuffer;
        }
        return this;
    }
}