001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2009 Sun Microsystems, Inc.
015 * Portions copyright 2011-2015 ForgeRock AS.
016 * Portions Copyright 2022 Wren Security.
017 */
018package org.forgerock.opendj.ldap;
019
020import java.io.DataInput;
021import java.io.EOFException;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.io.UnsupportedEncodingException;
026import java.nio.ByteBuffer;
027import java.nio.CharBuffer;
028import java.nio.channels.WritableByteChannel;
029import java.nio.charset.Charset;
030import java.nio.charset.CharsetDecoder;
031
032import org.forgerock.util.Reject;
033
034import com.forgerock.opendj.util.PackedLong;
035
036/** A mutable sequence of bytes backed by a byte array. */
037public final class ByteStringBuilder implements ByteSequence {
038
039    /** Maximum size in bytes of a compact encoded value. */
040    public static final int MAX_COMPACT_SIZE = PackedLong.MAX_COMPACT_SIZE;
041
042    /** Output stream implementation. */
043    private final class OutputStreamImpl extends OutputStream {
044        @Override
045        public void close() {
046            // Do nothing.
047        }
048
049        @Override
050        public void write(final byte[] bytes) {
051            appendBytes(bytes);
052        }
053
054        @Override
055        public void write(final byte[] bytes, final int i, final int i1) {
056            appendBytes(bytes, i, i1);
057        }
058
059        @Override
060        public void write(final int i) {
061            appendByte(i);
062        }
063    }
064
065    /**
066     * A sub-sequence of the parent byte string builder. The sub-sequence will
067     * be robust against all updates to the byte string builder except for
068     * invocations of the method {@code clear()}.
069     */
070    private final class SubSequence implements ByteSequence {
071
072        /** The length of the sub-sequence. */
073        private final int subLength;
074
075        /** The offset of the sub-sequence. */
076        private final int subOffset;
077
078        /**
079         * Creates a new sub-sequence.
080         *
081         * @param offset
082         *            The offset of the sub-sequence.
083         * @param length
084         *            The length of the sub-sequence.
085         */
086        private SubSequence(final int offset, final int length) {
087            this.subOffset = offset;
088            this.subLength = length;
089        }
090
091        @Override
092        public ByteSequenceReader asReader() {
093            return new ByteSequenceReader(this);
094        }
095
096        @Override
097        public byte byteAt(final int index) {
098            if (index >= subLength || index < 0) {
099                throw new IndexOutOfBoundsException();
100            }
101
102            // Protect against reallocation: use builder's buffer.
103            return buffer[subOffset + index];
104        }
105
106        @Override
107        public int compareTo(final byte[] b, final int offset, final int length) {
108            ByteString.checkArrayBounds(b, offset, length);
109
110            // Protect against reallocation: use builder's buffer.
111            return ByteString.compareTo(buffer, subOffset, subLength, b, offset, length);
112        }
113
114        @Override
115        public int compareTo(final ByteSequence o) {
116            if (this == o) {
117                return 0;
118            }
119
120            // Protect against reallocation: use builder's buffer.
121            return -o.compareTo(buffer, subOffset, subLength);
122        }
123
124        @Override
125        public byte[] copyTo(final byte[] b) {
126            copyTo(b, 0);
127            return b;
128        }
129
130        @Override
131        public byte[] copyTo(final byte[] b, final int offset) {
132            if (offset < 0) {
133                throw new IndexOutOfBoundsException();
134            }
135
136            // Protect against reallocation: use builder's buffer.
137            System.arraycopy(buffer, subOffset, b, offset, Math.min(subLength, b.length - offset));
138            return b;
139        }
140
141        @Override
142        public ByteBuffer copyTo(final ByteBuffer byteBuffer) {
143            byteBuffer.put(buffer, subOffset, subLength);
144            return byteBuffer;
145        }
146
147        @Override
148        public ByteStringBuilder copyTo(final ByteStringBuilder builder) {
149            // Protect against reallocation: use builder's buffer.
150            return builder.appendBytes(buffer, subOffset, subLength);
151        }
152
153        @Override
154        public boolean copyTo(CharBuffer charBuffer, CharsetDecoder decoder) {
155            return ByteString.copyTo(ByteBuffer.wrap(buffer, subOffset, subLength), charBuffer, decoder);
156        }
157
158        @Override
159        public OutputStream copyTo(final OutputStream stream) throws IOException {
160            // Protect against reallocation: use builder's buffer.
161            stream.write(buffer, subOffset, subLength);
162            return stream;
163        }
164
165        @Override
166        public boolean equals(final byte[] b, final int offset, final int length) {
167            ByteString.checkArrayBounds(b, offset, length);
168
169            // Protect against reallocation: use builder's buffer.
170            return ByteString.equals(buffer, subOffset, subLength, b, offset, length);
171        }
172
173        @Override
174        public boolean equals(final Object o) {
175            if (this == o) {
176                return true;
177            } else if (o instanceof ByteSequence) {
178                final ByteSequence other = (ByteSequence) o;
179
180                // Protect against reallocation: use builder's buffer.
181                return other.equals(buffer, subOffset, subLength);
182            } else {
183                return false;
184            }
185        }
186
187        @Override
188        public int hashCode() {
189            // Protect against reallocation: use builder's buffer.
190            return ByteString.hashCode(buffer, subOffset, subLength);
191        }
192
193        @Override
194        public boolean isEmpty() {
195            return length == 0;
196        }
197
198        @Override
199        public int length() {
200            return subLength;
201        }
202
203        @Override
204        public ByteSequence subSequence(final int start, final int end) {
205            if (start < 0 || start > end || end > subLength) {
206                throw new IndexOutOfBoundsException();
207            }
208
209            return new SubSequence(subOffset + start, end - start);
210        }
211
212        @Override
213        public boolean startsWith(ByteSequence prefix) {
214            if (prefix == null || prefix.length() > length) {
215                return false;
216            }
217            return prefix.equals(buffer, 0, prefix.length());
218        }
219
220        @Override
221        public String toBase64String() {
222            return Base64.encode(this);
223        }
224
225        @Override
226        public byte[] toByteArray() {
227            return copyTo(new byte[subLength]);
228        }
229
230        @Override
231        public ByteString toByteString() {
232            // Protect against reallocation: use builder's buffer.
233            final byte[] b = new byte[subLength];
234            System.arraycopy(buffer, subOffset, b, 0, subLength);
235            return ByteString.wrap(b);
236        }
237
238        @Override
239        public String toString() {
240            // Protect against reallocation: use builder's buffer.
241            return ByteString.toString(buffer, subOffset, subLength);
242        }
243    }
244
245    // These are package private so that compression and crypto
246    // functionality may directly access the fields.
247
248    /** The buffer where data is stored. */
249    byte[] buffer;
250
251    /** The number of bytes to expose from the buffer. */
252    int length;
253
254    /**
255     * The lazily allocated output stream view of this builder. Synchronization
256     * is not necessary because the stream is stateless and race conditions can
257     * be tolerated.
258     */
259    private OutputStreamImpl os;
260
261    /** Creates a new byte string builder with an initial capacity of 32 bytes. */
262    public ByteStringBuilder() {
263        // Initially create a 32 byte buffer.
264        this(32);
265    }
266
267    /**
268     * Creates a new byte string builder with the specified initial capacity.
269     *
270     * @param capacity
271     *            The initial capacity.
272     * @throws IllegalArgumentException
273     *             If the {@code capacity} is negative.
274     */
275    public ByteStringBuilder(final int capacity) {
276        Reject.ifFalse(capacity >= 0, "capacity must be >= 0");
277        this.buffer = new byte[capacity];
278        this.length = 0;
279    }
280
281    /**
282     * Creates a new byte string builder with the content of the provided
283     * ByteSequence. Its capacity is set to the length of the provided
284     * ByteSequence.
285     *
286     * @param bs
287     *            The ByteSequence to copy
288     */
289    public ByteStringBuilder(final ByteSequence bs) {
290        this(bs.length());
291        bs.copyTo(this);
292    }
293
294    /**
295     * Appends the provided byte to this byte string builder.
296     * <p>
297     * Note: this method accepts an {@code int} for ease of reading and writing.
298     * <p>
299     * This method only keeps the lowest 8-bits of the provided {@code int}.
300     * Higher bits will be truncated. This method performs the equivalent of:
301     *
302     * <pre>
303     * int i = ...;
304     * int i8bits = i &amp; 0xFF;
305     * // only use "i8bits"
306     * </pre>
307     * OR
308     * <pre>
309     * int i = ...;
310     * byte b = (byte) i;
311     * // only use "b"
312     * </pre>
313     *
314     * @param b
315     *            The byte to be appended to this byte string builder.
316     * @return This byte string builder.
317     */
318    public ByteStringBuilder appendByte(final int b) {
319        ensureAdditionalCapacity(1);
320        buffer[length++] = (byte) b;
321        return this;
322    }
323
324    /**
325     * Appends the provided byte array to this byte string builder.
326     * <p>
327     * An invocation of the form:
328     *
329     * <pre>
330     * src.append(bytes)
331     * </pre>
332     *
333     * Behaves in exactly the same way as the invocation:
334     *
335     * <pre>
336     * src.append(bytes, 0, bytes.length);
337     * </pre>
338     *
339     * @param bytes
340     *            The byte array to be appended to this byte string builder.
341     * @return This byte string builder.
342     */
343    public ByteStringBuilder appendBytes(final byte[] bytes) {
344        return appendBytes(bytes, 0, bytes.length);
345    }
346
347    /**
348     * Appends the provided byte array to this byte string builder.
349     *
350     * @param bytes
351     *            The byte array to be appended to this byte string builder.
352     * @param offset
353     *            The offset of the byte array to be used; must be non-negative
354     *            and no larger than {@code bytes.length} .
355     * @param length
356     *            The length of the byte array to be used; must be non-negative
357     *            and no larger than {@code bytes.length - offset}.
358     * @return This byte string builder.
359     * @throws IndexOutOfBoundsException
360     *             If {@code offset} is negative or if {@code length} is
361     *             negative or if {@code offset + length} is greater than
362     *             {@code bytes.length}.
363     */
364    public ByteStringBuilder appendBytes(final byte[] bytes, final int offset, final int length) {
365        ByteString.checkArrayBounds(bytes, offset, length);
366
367        if (length != 0) {
368            ensureAdditionalCapacity(length);
369            System.arraycopy(bytes, offset, buffer, this.length, length);
370            this.length += length;
371        }
372
373        return this;
374    }
375
376    /**
377     * Appends the provided {@code ByteBuffer} to this byte string builder.
378     *
379     * @param buffer
380     *            The byte buffer to be appended to this byte string builder.
381     * @param length
382     *            The number of bytes to be appended from {@code buffer}.
383     * @return This byte string builder.
384     * @throws IndexOutOfBoundsException
385     *             If {@code length} is less than zero or greater than
386     *             {@code buffer.remaining()}.
387     */
388    public ByteStringBuilder appendBytes(final ByteBuffer buffer, final int length) {
389        if (length < 0 || length > buffer.remaining()) {
390            throw new IndexOutOfBoundsException();
391        }
392
393        if (length != 0) {
394            ensureAdditionalCapacity(length);
395            buffer.get(this.buffer, this.length, length);
396            this.length += length;
397        }
398
399        return this;
400    }
401
402    /**
403     * Appends the provided {@link ByteSequence} to this byte string builder.
404     *
405     * @param bytes
406     *            The byte sequence to be appended to this byte string builder.
407     * @return This byte string builder.
408     */
409    public ByteStringBuilder appendBytes(final ByteSequence bytes) {
410        return bytes.copyTo(this);
411    }
412
413    /**
414     * Appends the provided {@link ByteSequenceReader} to this byte string builder.
415     *
416     * @param reader
417     *            The byte sequence reader to be appended to this byte string
418     *            builder.
419     * @param length
420     *            The number of bytes to be appended from {@code reader}.
421     * @return This byte string builder.
422     * @throws IndexOutOfBoundsException
423     *             If {@code length} is less than zero or greater than
424     *             {@code reader.remaining()}.
425     */
426    public ByteStringBuilder appendBytes(final ByteSequenceReader reader, final int length) {
427        if (length < 0 || length > reader.remaining()) {
428            throw new IndexOutOfBoundsException();
429        }
430
431        if (length != 0) {
432            ensureAdditionalCapacity(length);
433            reader.readBytes(buffer, this.length, length);
434            this.length += length;
435        }
436
437        return this;
438    }
439
440    /**
441     * Appends the UTF-8 encoded bytes of the provided char array to this byte
442     * string builder.
443     *
444     * @param chars
445     *            The char array whose UTF-8 encoding is to be appended to this
446     *            byte string builder.
447     * @return This byte string builder.
448     */
449    public ByteStringBuilder appendUtf8(final char[] chars) {
450        if (chars == null) {
451            return this;
452        }
453
454        // Assume that each char is 1 byte
455        final int len = chars.length;
456        ensureAdditionalCapacity(len);
457
458        for (int i = 0; i < len; i++) {
459            final char c = chars[i];
460            final byte b = (byte) (c & 0x0000007F);
461
462            if (c == b) {
463                buffer[this.length + i] = b;
464            } else {
465                // There is a multi-byte char. Defer to JDK.
466                final Charset utf8 = Charset.forName("UTF-8");
467                final ByteBuffer byteBuffer = utf8.encode(CharBuffer.wrap(chars));
468                final int remaining = byteBuffer.remaining();
469                ensureAdditionalCapacity(remaining - len);
470                byteBuffer.get(buffer, this.length, remaining);
471                this.length += remaining;
472                return this;
473            }
474        }
475
476        // The 1 byte char assumption was correct
477        this.length += len;
478        return this;
479    }
480
481    /**
482     * Appends the provided {@code DataInput} to this byte string
483     * builder.
484     *
485     * @param stream
486     *          The data input stream to be appended to this byte string
487     *          builder.
488     * @param length
489     *          The maximum number of bytes to be appended from {@code
490     *          input}.
491     * @throws IndexOutOfBoundsException
492     *           If {@code length} is less than zero.
493     * @throws EOFException
494     *           If this stream reaches the end before reading all the bytes.
495     * @throws IOException
496     *           If an I/O error occurs.
497     */
498    public void appendBytes(DataInput stream, int length) throws EOFException, IOException {
499        if (length < 0) {
500            throw new IndexOutOfBoundsException();
501        }
502
503        ensureAdditionalCapacity(length);
504        stream.readFully(buffer, this.length, length);
505        this.length += length;
506    }
507
508    /**
509     * Appends the provided {@code InputStream} to this byte string builder.
510     *
511     * @param stream
512     *            The input stream to be appended to this byte string builder.
513     * @param length
514     *            The maximum number of bytes to be appended from {@code buffer}
515     *            .
516     * @return The number of bytes read from the input stream, or {@code -1} if
517     *         the end of the input stream has been reached.
518     * @throws IndexOutOfBoundsException
519     *             If {@code length} is less than zero.
520     * @throws IOException
521     *             If an I/O error occurs.
522     */
523    public int appendBytes(final InputStream stream, final int length) throws IOException {
524        if (length < 0) {
525            throw new IndexOutOfBoundsException();
526        }
527
528        ensureAdditionalCapacity(length);
529        final int bytesRead = stream.read(buffer, this.length, length);
530        if (bytesRead > 0) {
531            this.length += bytesRead;
532        }
533
534        return bytesRead;
535    }
536
537    /**
538     * Appends the big-endian encoded bytes of the provided integer to this byte
539     * string builder.
540     *
541     * @param i
542     *            The integer whose big-endian encoding is to be appended to
543     *            this byte string builder.
544     * @return This byte string builder.
545     */
546    public ByteStringBuilder appendInt(int i) {
547        ensureAdditionalCapacity(4);
548        for (int j = length + 3; j >= length; j--) {
549            buffer[j] = (byte) i;
550            i >>>= 8;
551        }
552        length += 4;
553        return this;
554    }
555
556    /**
557     * Appends the big-endian encoded bytes of the provided long to this byte
558     * string builder.
559     *
560     * @param l
561     *            The long whose big-endian encoding is to be appended to this
562     *            byte string builder.
563     * @return This byte string builder.
564     */
565    public ByteStringBuilder appendLong(long l) {
566        ensureAdditionalCapacity(8);
567        for (int i = length + 7; i >= length; i--) {
568            buffer[i] = (byte) l;
569            l >>>= 8;
570        }
571        length += 8;
572        return this;
573    }
574
575    /**
576     * Appends the compact encoded bytes of the provided unsigned long to this byte
577     * string builder. This method allows to encode unsigned long up to 56 bits using
578     * fewer bytes (from 1 to 8) than append(long). The encoding has the important
579     * property that it preserves ordering, so it can be used for keys.
580     *
581     * @param value
582     *            The long whose compact encoding is to be appended to this
583     *            byte string builder.
584     * @return This byte string builder.
585     */
586    public ByteStringBuilder appendCompactUnsigned(long value) {
587        Reject.ifFalse(value >= 0, "value must be >= 0");
588        try {
589            PackedLong.writeCompactUnsigned(asOutputStream(), value);
590        } catch (IOException e) {
591            throw new IllegalStateException(e);
592        }
593        return this;
594    }
595
596    /**
597     * Appends the byte string representation of the provided object to this
598     * byte string builder. The object is converted to a byte string as follows:
599     * <ul>
600     * <li>if the object is an instance of {@code ByteSequence} then this method
601     * is equivalent to calling {@link #appendBytes(ByteSequence)}
602     * <li>if the object is a {@code byte[]} then this method is equivalent to
603     * calling {@link #appendBytes(byte[])}
604     * <li>if the object is a {@code char[]} then this method is equivalent to
605     * calling {@link #appendUtf8(char[])}
606     * <li>for all other types of object this method is equivalent to calling
607     * {@link #appendUtf8(String)} with the {@code toString()} representation of the
608     * provided object.
609     * </ul>
610     * <b>Note:</b> this method treats {@code Long} and {@code Integer} objects
611     * like any other type of {@code Object}. More specifically, the following
612     * invocations are not equivalent:
613     * <ul>
614     * <li>{@code append(0)} is not equivalent to {@code append((Object) 0)}
615     * <li>{@code append(0L)} is not equivalent to {@code append((Object) 0L)}
616     * </ul>
617     *
618     * @param o
619     *            The object to be appended to this byte string builder.
620     * @return This byte string builder.
621     */
622    public ByteStringBuilder appendObject(final Object o) {
623        if (o == null) {
624            return this;
625        } else if (o instanceof ByteSequence) {
626            return appendBytes((ByteSequence) o);
627        } else if (o instanceof byte[]) {
628            return appendBytes((byte[]) o);
629        } else if (o instanceof char[]) {
630            return appendUtf8((char[]) o);
631        } else {
632            return appendUtf8(o.toString());
633        }
634    }
635
636    /**
637     * Appends the big-endian encoded bytes of the provided short to this byte
638     * string builder.
639     * <p>
640     * Note: this method accepts an {@code int} for ease of reading and writing.
641     * <p>
642     * This method only keeps the lowest 16-bits of the provided {@code int}.
643     * Higher bits will be truncated. This method performs the equivalent of:
644     *
645     * <pre>
646     * int i = ...;
647     * int i16bits = i &amp; 0xFFFF;
648     * // only use "i16bits"
649     * </pre>
650     * OR
651     * <pre>
652     * int i = ...;
653     * short s = (short) i;
654     * // only use "s"
655     * </pre>
656     *
657     * @param i
658     *            The short whose big-endian encoding is to be appended to this
659     *            byte string builder.
660     * @return This byte string builder.
661     */
662    public ByteStringBuilder appendShort(int i) {
663        ensureAdditionalCapacity(2);
664        for (int j = length + 1; j >= length; j--) {
665            buffer[j] = (byte) i;
666            i >>>= 8;
667        }
668        length += 2;
669        return this;
670    }
671
672    /**
673     * Appends the UTF-8 encoded bytes of the provided string to this byte
674     * string builder.
675     *
676     * @param s
677     *            The string whose UTF-8 encoding is to be appended to this byte
678     *            string builder.
679     * @return This byte string builder.
680     */
681    public ByteStringBuilder appendUtf8(final String s) {
682        if (s == null) {
683            return this;
684        }
685
686        // Assume that each char is 1 byte
687        final int len = s.length();
688        ensureAdditionalCapacity(len);
689
690        for (int i = 0; i < len; i++) {
691            final char c = s.charAt(i);
692            final byte b = (byte) (c & 0x0000007F);
693
694            if (c == b) {
695                buffer[this.length + i] = b;
696            } else {
697                // There is a multi-byte char. Defer to JDK
698                try {
699                    return appendBytes(s.getBytes("UTF-8"));
700                } catch (final UnsupportedEncodingException e) {
701                    // TODO: I18N
702                    throw new RuntimeException("Unable to encode String '" + s + "' to UTF-8 bytes", e);
703                }
704            }
705        }
706
707        // The 1 byte char assumption was correct
708        this.length += len;
709        return this;
710    }
711
712    /**
713     * Appends the ASN.1 BER length encoding representation of the provided
714     * integer to this byte string builder.
715     *
716     * @param length
717     *            The value to encode using the BER length encoding rules.
718     * @return This byte string builder.
719     */
720    public ByteStringBuilder appendBERLength(final int length) {
721        if ((length & 0x0000007F) == length) {
722            ensureAdditionalCapacity(1);
723
724            buffer[this.length++] = (byte) length;
725        } else if ((length & 0x000000FF) == length) {
726            ensureAdditionalCapacity(2);
727
728            buffer[this.length++] = (byte) 0x81;
729            buffer[this.length++] = (byte) length;
730        } else if ((length & 0x0000FFFF) == length) {
731            ensureAdditionalCapacity(3);
732
733            buffer[this.length++] = (byte) 0x82;
734            buffer[this.length++] = (byte) (length >> 8);
735            buffer[this.length++] = (byte) length;
736        } else if ((length & 0x00FFFFFF) == length) {
737            ensureAdditionalCapacity(4);
738
739            buffer[this.length++] = (byte) 0x83;
740            buffer[this.length++] = (byte) (length >> 16);
741            buffer[this.length++] = (byte) (length >> 8);
742            buffer[this.length++] = (byte) length;
743        } else {
744            ensureAdditionalCapacity(5);
745
746            buffer[this.length++] = (byte) 0x84;
747            buffer[this.length++] = (byte) (length >> 24);
748            buffer[this.length++] = (byte) (length >> 16);
749            buffer[this.length++] = (byte) (length >> 8);
750            buffer[this.length++] = (byte) length;
751        }
752        return this;
753    }
754
755    /**
756     * Returns an {@link OutputStream} whose write operations append data to
757     * this byte string builder. The returned output stream will never throw an
758     * {@link IOException} and its {@link OutputStream#close() close} method
759     * does not do anything.
760     *
761     * @return An {@link OutputStream} whose write operations append data to
762     *         this byte string builder.
763     */
764    public OutputStream asOutputStream() {
765        if (os == null) {
766            os = new OutputStreamImpl();
767        }
768        return os;
769    }
770
771    /**
772     * Returns a {@link ByteSequenceReader} which can be used to incrementally
773     * read and decode data from this byte string builder.
774     * <p>
775     * <b>NOTE:</b> all concurrent updates to this byte string builder are
776     * supported with the exception of {@link #clear()}. Any invocations of
777     * {@link #clear()} must be accompanied by a subsequent call to
778     * {@code ByteSequenceReader.rewind()}.
779     *
780     * @return The {@link ByteSequenceReader} which can be used to incrementally
781     *         read and decode data from this byte string builder.
782     * @see #clear()
783     */
784    @Override
785    public ByteSequenceReader asReader() {
786        return new ByteSequenceReader(this);
787    }
788
789    @Override
790    public byte byteAt(final int index) {
791        if (index >= length || index < 0) {
792            throw new IndexOutOfBoundsException();
793        }
794        return buffer[index];
795    }
796
797    /**
798     * Returns the current capacity of this byte string builder. The capacity
799     * may increase as more data is appended.
800     *
801     * @return The current capacity of this byte string builder.
802     */
803    public int capacity() {
804        return buffer.length;
805    }
806
807    /**
808     * Sets the length of this byte string builder to zero.
809     * <p>
810     * <b>NOTE:</b> if this method is called, then
811     * {@code ByteSequenceReader.rewind()} must also be called on any associated
812     * byte sequence readers in order for them to remain valid.
813     *
814     * @return This byte string builder.
815     * @see #asReader()
816     */
817    public ByteStringBuilder clear() {
818        length = 0;
819        return this;
820    }
821
822    /**
823     * Sets the length of this byte string builder to zero, and resets the
824     * capacity to the specified size if above provided threshold.
825     * <p>
826     * <b>NOTE:</b> if this method is called, then
827     * {@code ByteSequenceReader.rewind()} must also be called on any associated
828     * byte sequence readers in order for them to remain valid.
829     *
830     * @param thresholdCapacity
831     *             The threshold capacity triggering a truncate
832     * @param newCapacity
833     *            The new capacity.
834     * @return This byte string builder.
835     * @throws IllegalArgumentException
836     *             If the {@code newCapacity} is negative or the {@code newCapacity}
837     *             is bigger than the {@code thresholdCapacity}.
838     * @see #asReader()
839     */
840    public ByteStringBuilder clearAndTruncate(int thresholdCapacity, int newCapacity) {
841        if (newCapacity > thresholdCapacity) {
842            throw new IllegalArgumentException("new capacity '" + newCapacity
843                    + "' cannot be bigger than threshold capacity '" + thresholdCapacity + "'");
844        }
845        if (newCapacity < 0) {
846            throw new IllegalArgumentException("new capacity '" + newCapacity + "' cannot be negative.");
847        }
848        if (buffer.length > thresholdCapacity) {
849            // garbage collect excessively large buffers
850            buffer = new byte[newCapacity];
851        }
852        length = 0;
853        return this;
854    }
855
856    @Override
857    public int compareTo(final byte[] bytes, final int offset, final int length) {
858        ByteString.checkArrayBounds(bytes, offset, length);
859        return ByteString.compareTo(this.buffer, 0, this.length, bytes, offset, length);
860    }
861
862    @Override
863    public int compareTo(final ByteSequence o) {
864        if (this == o) {
865            return 0;
866        }
867        return -o.compareTo(buffer, 0, length);
868    }
869
870    @Override
871    public byte[] copyTo(final byte[] bytes) {
872        copyTo(bytes, 0);
873        return bytes;
874    }
875
876    @Override
877    public byte[] copyTo(final byte[] bytes, final int offset) {
878        if (offset < 0) {
879            throw new IndexOutOfBoundsException();
880        }
881        System.arraycopy(buffer, 0, bytes, offset, Math.min(length, bytes.length - offset));
882        return bytes;
883    }
884
885    @Override
886    public ByteBuffer copyTo(final ByteBuffer byteBuffer) {
887        byteBuffer.put(buffer, 0, length);
888        return byteBuffer;
889    }
890
891    @Override
892    public ByteStringBuilder copyTo(final ByteStringBuilder builder) {
893        builder.appendBytes(buffer, 0, length);
894        return builder;
895    }
896
897    @Override
898    public boolean copyTo(CharBuffer charBuffer, CharsetDecoder decoder) {
899        return ByteString.copyTo(ByteBuffer.wrap(buffer, 0, length), charBuffer, decoder);
900    }
901
902    @Override
903    public OutputStream copyTo(final OutputStream stream) throws IOException {
904        stream.write(buffer, 0, length);
905        return stream;
906    }
907
908    /**
909     * Copies the entire contents of this byte string to the provided
910     * {@code WritableByteChannel}.
911     *
912     * @param channel
913     *            The {@code WritableByteChannel} to copy to.
914     * @return The number of bytes written, possibly zero
915     * @throws IOException
916     *             If some other I/O error occurs
917     * @see WritableByteChannel#write(java.nio.ByteBuffer)
918     */
919    public int copyTo(WritableByteChannel channel) throws IOException {
920        return channel.write(ByteBuffer.wrap(buffer, 0, length));
921    }
922
923    /**
924     * Ensures that the specified number of additional bytes will fit in this
925     * byte string builder and resizes it if necessary.
926     *
927     * @param size
928     *            The number of additional bytes.
929     * @return This byte string builder.
930     */
931    public ByteStringBuilder ensureAdditionalCapacity(final int size) {
932        final int newCount = this.length + size;
933        if (newCount > buffer.length) {
934            final byte[] newbuffer = new byte[Math.max(buffer.length << 1, newCount)];
935            System.arraycopy(buffer, 0, newbuffer, 0, buffer.length);
936            buffer = newbuffer;
937        }
938        return this;
939    }
940
941    @Override
942    public boolean equals(final byte[] bytes, final int offset, final int length) {
943        ByteString.checkArrayBounds(bytes, offset, length);
944        return ByteString.equals(this.buffer, 0, this.length, bytes, offset, length);
945    }
946
947    /**
948     * Indicates whether the provided object is equal to this byte string
949     * builder. In order for it to be considered equal, the provided object must
950     * be a byte sequence containing the same bytes in the same order.
951     *
952     * @param o
953     *            The object for which to make the determination.
954     * @return {@code true} if the provided object is a byte sequence whose
955     *         content is equal to that of this byte string builder, or
956     *         {@code false} if not.
957     */
958    @Override
959    public boolean equals(final Object o) {
960        if (this == o) {
961            return true;
962        } else if (o instanceof ByteSequence) {
963            final ByteSequence other = (ByteSequence) o;
964            return other.equals(buffer, 0, length);
965        } else {
966            return false;
967        }
968    }
969
970    /**
971     * Returns the byte array that backs this byte string builder. Modifications
972     * to this byte string builder's content may cause the returned array's
973     * content to be modified, and vice versa.
974     * <p>
975     * Note that the length of the returned array is only guaranteed to be the
976     * same as the length of this byte string builder immediately after a call
977     * to {@link #trimToSize()}.
978     * <p>
979     * In addition, subsequent modifications to this byte string builder may
980     * cause the backing byte array to be reallocated thus decoupling the
981     * returned byte array from this byte string builder.
982     *
983     * @return The byte array that backs this byte string builder.
984     */
985    public byte[] getBackingArray() {
986        return buffer;
987    }
988
989    /**
990     * Returns a hash code for this byte string builder. It will be the sum of
991     * all of the bytes contained in the byte string builder.
992     * <p>
993     * <b>NOTE:</b> subsequent changes to this byte string builder will
994     * invalidate the returned hash code.
995     *
996     * @return A hash code for this byte string builder.
997     */
998    @Override
999    public int hashCode() {
1000        return ByteString.hashCode(buffer, 0, length);
1001    }
1002
1003    @Override
1004    public boolean isEmpty() {
1005        return length == 0;
1006    }
1007
1008    @Override
1009    public int length() {
1010        return length;
1011    }
1012
1013    /**
1014     * Sets the byte value at the specified index.
1015     * <p>
1016     * An index ranges from zero to {@code length() - 1}. The first byte value
1017     * of the sequence is at index zero, the next at index one, and so on, as
1018     * for array indexing.
1019     *
1020     * @param index
1021     *            The index of the byte to be set.
1022     * @param b
1023     *            The byte to set on this byte string builder.
1024     * @throws IndexOutOfBoundsException
1025     *             If the index argument is negative or not less than length().
1026     */
1027    public void setByte(final int index, final byte b) {
1028        if (index >= length || index < 0) {
1029            throw new IndexOutOfBoundsException();
1030        }
1031        buffer[index] = b;
1032    }
1033
1034    /**
1035     * Sets the length of this byte string builder.
1036     * <p>
1037     * If the <code>newLength</code> argument is less than the current length,
1038     * the length is changed to the specified length.
1039     * <p>
1040     * If the <code>newLength</code> argument is greater than or equal to the
1041     * current length, then the capacity is increased and sufficient null bytes
1042     * are appended so that length becomes the <code>newLength</code> argument.
1043     * <p>
1044     * The <code>newLength</code> argument must be greater than or equal to
1045     * <code>0</code>.
1046     *
1047     * @param newLength
1048     *            The new length.
1049     * @return This byte string builder.
1050     * @throws IndexOutOfBoundsException
1051     *             If the <code>newLength</code> argument is negative.
1052     */
1053    public ByteStringBuilder setLength(final int newLength) {
1054        if (newLength < 0) {
1055            throw new IndexOutOfBoundsException("Negative newLength: " + newLength);
1056        }
1057
1058        if (newLength > length) {
1059            ensureAdditionalCapacity(newLength - length);
1060
1061            // Pad with zeros.
1062            for (int i = length; i < newLength; i++) {
1063                buffer[i] = 0;
1064            }
1065        }
1066        length = newLength;
1067
1068        return this;
1069    }
1070
1071    /**
1072     * Returns a new byte sequence that is a subsequence of this byte sequence.
1073     * <p>
1074     * The subsequence starts with the byte value at the specified {@code start}
1075     * index and ends with the byte value at index {@code end - 1}. The length
1076     * (in bytes) of the returned sequence is {@code end - start}, so if
1077     * {@code start
1078     * == end} then an empty sequence is returned.
1079     * <p>
1080     * <b>NOTE:</b> the returned sub-sequence will be robust against all updates
1081     * to the byte string builder except for invocations of the method
1082     * {@link #clear()}. If a permanent immutable byte sequence is required then
1083     * callers should invoke {@code toByteString()} on the returned byte
1084     * sequence.
1085     *
1086     * @param start
1087     *            The start index, inclusive.
1088     * @param end
1089     *            The end index, exclusive.
1090     * @return The newly created byte subsequence.
1091     */
1092    @Override
1093    public ByteSequence subSequence(final int start, final int end) {
1094        if (start < 0 || start > end || end > length) {
1095            throw new IndexOutOfBoundsException();
1096        }
1097
1098        return new SubSequence(start, end - start);
1099    }
1100
1101    @Override
1102    public boolean startsWith(ByteSequence prefix) {
1103        if (prefix == null || prefix.length() > length) {
1104            return false;
1105        }
1106        return prefix.equals(buffer, 0, prefix.length());
1107    }
1108
1109    @Override
1110    public String toBase64String() {
1111        return Base64.encode(this);
1112    }
1113
1114    @Override
1115    public byte[] toByteArray() {
1116        return copyTo(new byte[length]);
1117    }
1118
1119    /**
1120     * Returns the {@link ByteString} representation of this byte string
1121     * builder. Subsequent changes to this byte string builder will not modify
1122     * the returned {@link ByteString}.
1123     *
1124     * @return The {@link ByteString} representation of this byte sequence.
1125     */
1126    @Override
1127    public ByteString toByteString() {
1128        final byte[] b = new byte[length];
1129        System.arraycopy(buffer, 0, b, 0, length);
1130        return ByteString.wrap(b);
1131    }
1132
1133    @Override
1134    public String toString() {
1135        return ByteString.toString(buffer, 0, length);
1136    }
1137
1138    /**
1139     * Attempts to reduce storage used for this byte string builder. If the
1140     * buffer is larger than necessary to hold its current sequence of bytes,
1141     * then it may be resized to become more space efficient.
1142     *
1143     * @return This byte string builder.
1144     */
1145    public ByteStringBuilder trimToSize() {
1146        if (buffer.length > length) {
1147            final byte[] newBuffer = new byte[length];
1148            System.arraycopy(buffer, 0, newBuffer, 0, length);
1149            buffer = newBuffer;
1150        }
1151        return this;
1152    }
1153}