001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2015 ForgeRock AS
026 */
027package org.forgerock.opendj.ldap;
028
029import static com.forgerock.opendj.ldap.CoreMessages.*;
030import static com.forgerock.opendj.util.StaticUtils.*;
031
032import java.io.IOException;
033import java.io.OutputStream;
034import java.io.UnsupportedEncodingException;
035import java.nio.ByteBuffer;
036import java.nio.CharBuffer;
037import java.nio.charset.Charset;
038import java.nio.charset.CharsetDecoder;
039import java.nio.charset.CoderResult;
040import java.util.Arrays;
041
042import org.forgerock.i18n.LocalizedIllegalArgumentException;
043
044import com.forgerock.opendj.util.StaticUtils;
045
046/**
047 * An immutable sequence of bytes backed by a byte array.
048 */
049public final class ByteString implements ByteSequence {
050
051    /** Singleton empty byte string. */
052    private static final ByteString EMPTY = wrap(new byte[0]);
053
054    /**
055     * Returns an empty byte string.
056     *
057     * @return An empty byte string.
058     */
059    public static ByteString empty() {
060        return EMPTY;
061    }
062
063    /**
064     * Returns a byte string containing the big-endian encoded bytes of the
065     * provided integer.
066     *
067     * @param i
068     *            The integer to encode.
069     * @return The byte string containing the big-endian encoded bytes of the
070     *         provided integer.
071     */
072    public static ByteString valueOfInt(int i) {
073        final byte[] bytes = new byte[4];
074        for (int j = 3; j >= 0; j--) {
075            bytes[j] = (byte) i;
076            i >>>= 8;
077        }
078        return wrap(bytes);
079    }
080
081    /**
082     * Returns a byte string containing the big-endian encoded bytes of the
083     * provided long.
084     *
085     * @param l
086     *            The long to encode.
087     * @return The byte string containing the big-endian encoded bytes of the
088     *         provided long.
089     */
090    public static ByteString valueOfLong(long l) {
091        final byte[] bytes = new byte[8];
092        for (int i = 7; i >= 0; i--) {
093            bytes[i] = (byte) l;
094            l >>>= 8;
095        }
096        return wrap(bytes);
097    }
098
099    /**
100     * Returns a byte string representation of the provided object. The object
101     * is converted to a byte string as follows:
102     * <ul>
103     * <li>if the object is an instance of {@code ByteSequence} then this method
104     * is equivalent to calling {@code o.toByteString()}
105     * <li>if the object is a {@code byte[]} then this method is equivalent to
106     * calling {@link #valueOfBytes(byte[])}
107     * <li>if the object is a {@code char[]} then this method is equivalent to
108     * calling {@link #valueOfUtf8(char[])}
109     * <li>for all other types of object this method is equivalent to calling
110     * {@link #valueOfUtf8(CharSequence)} with the {@code toString()} representation of
111     * the provided object.
112     * </ul>
113     * <b>Note:</b> this method treats {@code Long} and {@code Integer} objects
114     * like any other type of {@code Object}. More specifically, the following
115     * invocations are not equivalent:
116     * <ul>
117     * <li>{@code valueOf(0)} is not equivalent to {@code valueOf((Object) 0)}
118     * <li>{@code valueOf(0L)} is not equivalent to {@code valueOf((Object) 0L)}
119     * </ul>
120     *
121     * @param o
122     *            The object to use.
123     * @return The byte string containing the provided object.
124     */
125    public static ByteString valueOfObject(final Object o) {
126        if (o instanceof ByteSequence) {
127            return ((ByteSequence) o).toByteString();
128        } else if (o instanceof byte[]) {
129            return valueOfBytes((byte[]) o);
130        } else if (o instanceof char[]) {
131            return valueOfUtf8((char[]) o);
132        } else {
133            return valueOfUtf8(o.toString());
134        }
135    }
136
137    /**
138     * Returns a byte string containing the UTF-8 encoded bytes of the provided
139     * char sequence.
140     *
141     * @param s
142     *            The char sequence to use.
143     * @return The byte string with the encoded bytes of the provided string.
144     */
145    public static ByteString valueOfUtf8(final CharSequence s) {
146        if (s.length() == 0) {
147            return EMPTY;
148        }
149        return wrap(StaticUtils.getBytes(s));
150    }
151
152    /**
153     * Returns a byte string containing the Base64 decoded bytes of the provided
154     * string.
155     *
156     * @param s
157     *            The string to use.
158     * @return The byte string containing the Base64 decoded bytes of the
159     *         provided string.
160     * @throws LocalizedIllegalArgumentException
161     *             If the provided string does not contain valid Base64 encoded
162     *             content.
163     * @see #toBase64String()
164     */
165    public static ByteString valueOfBase64(final String s) {
166        if (s.length() == 0) {
167            return EMPTY;
168        }
169        return Base64.decode(s);
170    }
171
172    /**
173     * Returns a byte string containing the bytes of the provided hexadecimal string.
174     *
175     * @param hexString
176     *            The hexadecimal string to convert to a byte array.
177     * @return The byte string containing the binary representation of the
178     *         provided hex string.
179     * @throws LocalizedIllegalArgumentException
180     *             If the provided string contains invalid hexadecimal digits or
181     *             does not contain an even number of digits.
182     */
183    public static ByteString valueOfHex(final String hexString) {
184        if (hexString == null || hexString.length() == 0) {
185            return EMPTY;
186        }
187
188        final int length = hexString.length();
189        if (length % 2 != 0) {
190            throw new LocalizedIllegalArgumentException(ERR_HEX_DECODE_INVALID_LENGTH.get(hexString));
191        }
192        final int arrayLength = length / 2;
193        final byte[] bytes = new byte[arrayLength];
194        for (int i = 0; i < arrayLength; i++) {
195            bytes[i] = hexToByte(hexString, hexString.charAt(i * 2), hexString.charAt(i * 2 + 1));
196        }
197        return valueOfBytes(bytes);
198    }
199
200    /**
201     * Returns a byte string containing the contents of the provided byte array.
202     * <p>
203     * This method differs from {@link #wrap(byte[])} in that it defensively
204     * copies the provided byte array.
205     *
206     * @param bytes
207     *            The byte array to use.
208     * @return A byte string containing a copy of the provided byte array.
209     */
210    public static ByteString valueOfBytes(final byte[] bytes) {
211        if (bytes.length == 0) {
212            return EMPTY;
213        }
214        return wrap(Arrays.copyOf(bytes, bytes.length));
215    }
216
217    /**
218     * Returns a byte string containing a subsequence of the contents of the
219     * provided byte array.
220     * <p>
221     * This method differs from {@link #wrap(byte[], int, int)} in that it
222     * defensively copies the provided byte array.
223     *
224     * @param bytes
225     *            The byte array to use.
226     * @param offset
227     *            The offset of the byte array to be used; must be non-negative
228     *            and no larger than {@code bytes.length} .
229     * @param length
230     *            The length of the byte array to be used; must be non-negative
231     *            and no larger than {@code bytes.length - offset}.
232     * @return A byte string containing a copy of the subsequence of the
233     *         provided byte array.
234     */
235    public static ByteString valueOfBytes(final byte[] bytes, final int offset, final int length) {
236        checkArrayBounds(bytes, offset, length);
237        if (offset == length) {
238            return EMPTY;
239        }
240        return wrap(Arrays.copyOfRange(bytes, offset, offset + length));
241    }
242
243    /**
244     * Returns a byte string containing the UTF-8 encoded bytes of the provided
245     * char array.
246     *
247     * @param chars
248     *            The char array to use.
249     * @return A byte string containing the UTF-8 encoded bytes of the provided
250     *         char array.
251     */
252    public static ByteString valueOfUtf8(final char[] chars) {
253        if (chars.length == 0) {
254            return EMPTY;
255        }
256        return wrap(StaticUtils.getBytes(chars));
257    }
258
259    /**
260     * Returns a byte string that wraps the provided byte array.
261     * <p>
262     * <b>NOTE:</b> this method takes ownership of the provided byte array and,
263     * therefore, the byte array MUST NOT be altered directly after this method
264     * returns.
265     *
266     * @param bytes
267     *            The byte array to wrap.
268     * @return The byte string that wraps the given byte array.
269     */
270    public static ByteString wrap(final byte[] bytes) {
271        return new ByteString(bytes, 0, bytes.length);
272    }
273
274    /**
275     * Returns a byte string that wraps a subsequence of the provided byte
276     * array.
277     * <p>
278     * <b>NOTE:</b> this method takes ownership of the provided byte array and,
279     * therefore, the byte array MUST NOT be altered directly after this method
280     * returns.
281     *
282     * @param bytes
283     *            The byte array to wrap.
284     * @param offset
285     *            The offset of the byte array to be used; must be non-negative
286     *            and no larger than {@code bytes.length} .
287     * @param length
288     *            The length of the byte array to be used; must be non-negative
289     *            and no larger than {@code bytes.length - offset}.
290     * @return The byte string that wraps the given byte array.
291     * @throws IndexOutOfBoundsException
292     *             If {@code offset} is negative or if {@code length} is
293     *             negative or if {@code offset + length} is greater than
294     *             {@code bytes.length}.
295     */
296    public static ByteString wrap(final byte[] bytes, final int offset, final int length) {
297        checkArrayBounds(bytes, offset, length);
298        return new ByteString(bytes, offset, length);
299    }
300
301    /**
302     * Checks the array bounds of the provided byte array sub-sequence, throwing
303     * an {@code IndexOutOfBoundsException} if they are illegal.
304     *
305     * @param b
306     *            The byte array.
307     * @param offset
308     *            The offset of the byte array to be checked; must be
309     *            non-negative and no larger than {@code b.length}.
310     * @param length
311     *            The length of the byte array to be checked; must be
312     *            non-negative and no larger than {@code b.length - offset}.
313     * @throws IndexOutOfBoundsException
314     *             If {@code offset} is negative or if {@code length} is
315     *             negative or if {@code offset + length} is greater than
316     *             {@code b.length}.
317     */
318    static void checkArrayBounds(final byte[] b, final int offset, final int length) {
319        if (offset < 0 || offset > b.length || length < 0 || offset + length > b.length
320                || offset + length < 0) {
321            throw new IndexOutOfBoundsException();
322        }
323    }
324
325    /**
326     * Compares two byte array sub-sequences and returns a value that indicates
327     * their relative order.
328     *
329     * @param b1
330     *            The byte array containing the first sub-sequence.
331     * @param offset1
332     *            The offset of the first byte array sub-sequence.
333     * @param length1
334     *            The length of the first byte array sub-sequence.
335     * @param b2
336     *            The byte array containing the second sub-sequence.
337     * @param offset2
338     *            The offset of the second byte array sub-sequence.
339     * @param length2
340     *            The length of the second byte array sub-sequence.
341     * @return A negative integer if first byte array sub-sequence should come
342     *         before the second byte array sub-sequence in ascending order, a
343     *         positive integer if the first byte array sub-sequence should come
344     *         after the byte array sub-sequence in ascending order, or zero if
345     *         there is no difference between the two byte array sub-sequences
346     *         with regard to ordering.
347     */
348    static int compareTo(final byte[] b1, final int offset1, final int length1, final byte[] b2,
349            final int offset2, final int length2) {
350        int count = Math.min(length1, length2);
351        int i = offset1;
352        int j = offset2;
353        while (count-- != 0) {
354            final int firstByte = 0xFF & b1[i++];
355            final int secondByte = 0xFF & b2[j++];
356            if (firstByte != secondByte) {
357                return firstByte - secondByte;
358            }
359        }
360        return length1 - length2;
361    }
362
363    /**
364     * Indicates whether two byte array sub-sequences are equal. In order for
365     * them to be considered equal, they must contain the same bytes in the same
366     * order.
367     *
368     * @param b1
369     *            The byte array containing the first sub-sequence.
370     * @param offset1
371     *            The offset of the first byte array sub-sequence.
372     * @param length1
373     *            The length of the first byte array sub-sequence.
374     * @param b2
375     *            The byte array containing the second sub-sequence.
376     * @param offset2
377     *            The offset of the second byte array sub-sequence.
378     * @param length2
379     *            The length of the second byte array sub-sequence.
380     * @return {@code true} if the two byte array sub-sequences have the same
381     *         content, or {@code false} if not.
382     */
383    static boolean equals(final byte[] b1, final int offset1, final int length1, final byte[] b2,
384            final int offset2, final int length2) {
385        if (length1 != length2) {
386            return false;
387        }
388
389        int i = offset1;
390        int j = offset2;
391        int count = length1;
392        while (count-- != 0) {
393            if (b1[i++] != b2[j++]) {
394                return false;
395            }
396        }
397        return true;
398    }
399
400    /**
401     * Returns a hash code for the provided byte array sub-sequence.
402     *
403     * @param b
404     *            The byte array.
405     * @param offset
406     *            The offset of the byte array sub-sequence.
407     * @param length
408     *            The length of the byte array sub-sequence.
409     * @return A hash code for the provided byte array sub-sequence.
410     */
411    static int hashCode(final byte[] b, final int offset, final int length) {
412        int hashCode = 1;
413        int i = offset;
414        int count = length;
415        while (count-- != 0) {
416            hashCode = 31 * hashCode + b[i++];
417        }
418        return hashCode;
419    }
420
421    /**
422     * Returns the UTF-8 decoded string representation of the provided byte
423     * array sub-sequence. If UTF-8 decoding fails, the platform's default
424     * encoding will be used.
425     *
426     * @param b
427     *            The byte array.
428     * @param offset
429     *            The offset of the byte array sub-sequence.
430     * @param length
431     *            The length of the byte array sub-sequence.
432     * @return The string representation of the byte array sub-sequence.
433     */
434    static String toString(final byte[] b, final int offset, final int length) {
435        if (length == 0) {
436            return "";
437        }
438        try {
439            return new String(b, offset, length, "UTF-8");
440        } catch (final UnsupportedEncodingException e) {
441            // TODO: I18N
442            throw new RuntimeException("Unable to decode bytes as UTF-8 string", e);
443        }
444    }
445
446    /**
447     * Returns a 7-bit ASCII string representation.
448     * Non-ASCII characters will be expanded to percent (%) hexadecimal value.
449     * @return a 7-bit ASCII string representation
450     */
451    public String toASCIIString() {
452        if (length == 0) {
453            return "";
454        }
455
456        StringBuilder sb = new StringBuilder();
457        for (int i = 0; i < length; i++) {
458            byte b = buffer[offset + i];
459            if (StaticUtils.isPrintable(b)) {
460                sb.append((char) b);
461            } else {
462                sb.append('%');
463                sb.append(StaticUtils.byteToHex(b));
464            }
465        }
466        return sb.toString();
467    }
468
469    private static byte hexToByte(final String value, final char c1, final char c2) {
470        byte b;
471        switch (c1) {
472        case '0':
473            b = 0x00;
474            break;
475        case '1':
476            b = 0x10;
477            break;
478        case '2':
479            b = 0x20;
480            break;
481        case '3':
482            b = 0x30;
483            break;
484        case '4':
485            b = 0x40;
486            break;
487        case '5':
488            b = 0x50;
489            break;
490        case '6':
491            b = 0x60;
492            break;
493        case '7':
494            b = 0x70;
495            break;
496        case '8':
497            b = (byte) 0x80;
498            break;
499        case '9':
500            b = (byte) 0x90;
501            break;
502        case 'A':
503        case 'a':
504            b = (byte) 0xA0;
505            break;
506        case 'B':
507        case 'b':
508            b = (byte) 0xB0;
509            break;
510        case 'C':
511        case 'c':
512            b = (byte) 0xC0;
513            break;
514        case 'D':
515        case 'd':
516            b = (byte) 0xD0;
517            break;
518        case 'E':
519        case 'e':
520            b = (byte) 0xE0;
521            break;
522        case 'F':
523        case 'f':
524            b = (byte) 0xF0;
525            break;
526        default:
527            throw new LocalizedIllegalArgumentException(ERR_HEX_DECODE_INVALID_CHARACTER.get(value, c1));
528        }
529
530        switch (c2) {
531        case '0':
532            // No action required.
533            break;
534        case '1':
535            b |= 0x01;
536            break;
537        case '2':
538            b |= 0x02;
539            break;
540        case '3':
541            b |= 0x03;
542            break;
543        case '4':
544            b |= 0x04;
545            break;
546        case '5':
547            b |= 0x05;
548            break;
549        case '6':
550            b |= 0x06;
551            break;
552        case '7':
553            b |= 0x07;
554            break;
555        case '8':
556            b |= 0x08;
557            break;
558        case '9':
559            b |= 0x09;
560            break;
561        case 'A':
562        case 'a':
563            b |= 0x0A;
564            break;
565        case 'B':
566        case 'b':
567            b |= 0x0B;
568            break;
569        case 'C':
570        case 'c':
571            b |= 0x0C;
572            break;
573        case 'D':
574        case 'd':
575            b |= 0x0D;
576            break;
577        case 'E':
578        case 'e':
579            b |= 0x0E;
580            break;
581        case 'F':
582        case 'f':
583            b |= 0x0F;
584            break;
585        default:
586            throw new LocalizedIllegalArgumentException(ERR_HEX_DECODE_INVALID_CHARACTER.get(value, c2));
587        }
588
589        return b;
590    }
591
592    // These are package private so that compression and crypto
593    // functionality may directly access the fields.
594
595    /** The buffer where data is stored. */
596    final byte[] buffer;
597
598    /** The number of bytes to expose from the buffer. */
599    final int length;
600
601    /** The start index of the range of bytes to expose through this byte string. */
602    final int offset;
603
604    /**
605     * Creates a new byte string that wraps a subsequence of the provided byte
606     * array.
607     * <p>
608     * <b>NOTE:</b> this method takes ownership of the provided byte array and,
609     * therefore, the byte array MUST NOT be altered directly after this method
610     * returns.
611     *
612     * @param b
613     *            The byte array to wrap.
614     * @param offset
615     *            The offset of the byte array to be used; must be non-negative
616     *            and no larger than {@code b.length} .
617     * @param length
618     *            The length of the byte array to be used; must be non-negative
619     *            and no larger than {@code b.length - offset}.
620     */
621    private ByteString(final byte[] b, final int offset, final int length) {
622        this.buffer = b;
623        this.offset = offset;
624        this.length = length;
625    }
626
627    /**
628     * Returns a {@link ByteSequenceReader} which can be used to incrementally
629     * read and decode data from this byte string.
630     *
631     * @return The {@link ByteSequenceReader} which can be used to incrementally
632     *         read and decode data from this byte string.
633     */
634    @Override
635    public ByteSequenceReader asReader() {
636        return new ByteSequenceReader(this);
637    }
638
639    /** {@inheritDoc} */
640    @Override
641    public byte byteAt(final int index) {
642        if (index >= length || index < 0) {
643            throw new IndexOutOfBoundsException();
644        }
645        return buffer[offset + index];
646    }
647
648    /** {@inheritDoc} */
649    @Override
650    public int compareTo(final byte[] bytes, final int offset, final int length) {
651        checkArrayBounds(bytes, offset, length);
652        return compareTo(this.buffer, this.offset, this.length, bytes, offset, length);
653    }
654
655    /** {@inheritDoc} */
656    @Override
657    public int compareTo(final ByteSequence o) {
658        if (this == o) {
659            return 0;
660        }
661        return -o.compareTo(buffer, offset, length);
662    }
663
664    /** {@inheritDoc} */
665    @Override
666    public byte[] copyTo(final byte[] bytes) {
667        copyTo(bytes, 0);
668        return bytes;
669    }
670
671    /** {@inheritDoc} */
672    @Override
673    public byte[] copyTo(final byte[] bytes, final int offset) {
674        if (offset < 0) {
675            throw new IndexOutOfBoundsException();
676        }
677        System.arraycopy(buffer, this.offset, bytes, offset, Math.min(length, bytes.length - offset));
678        return bytes;
679    }
680
681    @Override
682    public ByteBuffer copyTo(final ByteBuffer byteBuffer) {
683        byteBuffer.put(buffer, offset, length);
684        return byteBuffer;
685    }
686
687    @Override
688    public ByteStringBuilder copyTo(final ByteStringBuilder builder) {
689        builder.appendBytes(buffer, offset, length);
690        return builder;
691    }
692
693
694    @Override
695    public boolean copyTo(CharBuffer charBuffer, CharsetDecoder decoder) {
696        return copyTo(ByteBuffer.wrap(buffer, offset, length), charBuffer, decoder);
697    }
698
699    /**
700     * Convenience method to copy from a byte buffer to a char buffer using provided decoder to decode
701     * bytes into characters.
702     * <p>
703     * It should not be used directly, prefer instance method of ByteString or ByteStringBuilder instead.
704     */
705    static boolean copyTo(ByteBuffer inBuffer, CharBuffer outBuffer, CharsetDecoder decoder) {
706        final CoderResult result = decoder.decode(inBuffer, outBuffer, true);
707        decoder.flush(outBuffer);
708        return !result.isError() && !result.isOverflow();
709    }
710
711    /** {@inheritDoc} */
712    @Override
713    public OutputStream copyTo(final OutputStream stream) throws IOException {
714        stream.write(buffer, offset, length);
715        return stream;
716    }
717
718    /** {@inheritDoc} */
719    @Override
720    public boolean equals(final byte[] bytes, final int offset, final int length) {
721        checkArrayBounds(bytes, offset, length);
722        return equals(this.buffer, this.offset, this.length, bytes, offset, length);
723    }
724
725    /**
726     * Indicates whether the provided object is equal to this byte string. In
727     * order for it to be considered equal, the provided object must be a byte
728     * sequence containing the same bytes in the same order.
729     *
730     * @param o
731     *            The object for which to make the determination.
732     * @return {@code true} if the provided object is a byte sequence whose
733     *         content is equal to that of this byte string, or {@code false} if
734     *         not.
735     */
736    @Override
737    public boolean equals(final Object o) {
738        if (this == o) {
739            return true;
740        } else if (o instanceof ByteSequence) {
741            final ByteSequence other = (ByteSequence) o;
742            return other.equals(buffer, offset, length);
743        } else {
744            return false;
745        }
746    }
747
748    /**
749     * Returns a hash code for this byte string. It will be the sum of all of
750     * the bytes contained in the byte string.
751     *
752     * @return A hash code for this byte string.
753     */
754    @Override
755    public int hashCode() {
756        return hashCode(buffer, offset, length);
757    }
758
759    @Override
760    public boolean isEmpty() {
761        return length == 0;
762    }
763
764    /** {@inheritDoc} */
765    @Override
766    public int length() {
767        return length;
768    }
769
770    /** {@inheritDoc} */
771    @Override
772    public ByteString subSequence(final int start, final int end) {
773        if (start < 0 || start > end || end > length) {
774            throw new IndexOutOfBoundsException();
775        }
776        return new ByteString(buffer, offset + start, end - start);
777    }
778
779    /** {@inheritDoc} */
780    @Override
781    public boolean startsWith(ByteSequence prefix) {
782        if (prefix == null || prefix.length() > length) {
783            return false;
784        }
785        return prefix.equals(buffer, 0, prefix.length());
786    }
787
788    /** {@inheritDoc} */
789    @Override
790    public String toBase64String() {
791        return Base64.encode(this);
792    }
793
794    /**
795     * Returns a string representation of the contents of this byte sequence
796     * using hexadecimal characters and a space between each byte.
797     *
798     * @return A string representation of the contents of this byte sequence
799     *         using hexadecimal characters.
800     */
801    public String toHexString() {
802        if (isEmpty()) {
803            return "";
804        }
805        StringBuilder builder = new StringBuilder((length - 1) * 3 + 2);
806        builder.append(StaticUtils.byteToHex(buffer[offset]));
807        for (int i = 1; i < length; i++) {
808            builder.append(' ');
809            builder.append(StaticUtils.byteToHex(buffer[offset + i]));
810        }
811        return builder.toString();
812    }
813
814    /**
815     * Returns a string representation of the contents of this byte sequence
816     * using hexadecimal characters and a percent prefix (%) before each char.
817     *
818     * @return A string representation of the contents of this byte sequence
819     *         using percent + hexadecimal characters.
820     */
821    public String toPercentHexString() {
822        if (isEmpty()) {
823            return "";
824        }
825        StringBuilder builder = new StringBuilder(length * 3);
826        for (int i = 0; i < length; i++) {
827            builder.append('%');
828            builder.append(StaticUtils.byteToHex(buffer[offset + i]));
829        }
830        return builder.toString();
831    }
832
833    /**
834     * Returns a string representation of the data in this byte sequence using
835     * the specified indent.
836     * <p>
837     * The data will be formatted with sixteen hex bytes in a row followed by
838     * the ASCII representation, then wrapping to a new line as necessary. The
839     * state of the byte buffer is not changed.
840     *
841     * @param indent
842     *            The number of spaces to indent the output.
843     * @return the string representation of this byte string
844     */
845    public String toHexPlusAsciiString(int indent) {
846        StringBuilder builder = new StringBuilder();
847        StringBuilder indentBuf = new StringBuilder(indent);
848        for (int i = 0; i < indent; i++) {
849            indentBuf.append(' ');
850        }
851        int pos = 0;
852        while (length - pos >= 16) {
853            StringBuilder asciiBuf = new StringBuilder(17);
854            byte currentByte = buffer[offset + pos];
855            builder.append(indentBuf);
856            builder.append(byteToHex(currentByte));
857            asciiBuf.append(byteToASCII(currentByte));
858            pos++;
859
860            for (int i = 1; i < 16; i++, pos++) {
861                currentByte = buffer[offset + pos];
862                builder.append(' ');
863                builder.append(byteToHex(currentByte));
864                asciiBuf.append(byteToASCII(currentByte));
865
866                if (i == 7) {
867                    builder.append("  ");
868                    asciiBuf.append(' ');
869                }
870            }
871
872            builder.append("  ");
873            builder.append(asciiBuf);
874            builder.append(EOL);
875        }
876
877        int remaining = length - pos;
878        if (remaining > 0) {
879            StringBuilder asciiBuf = new StringBuilder(remaining + 1);
880
881            byte currentByte = buffer[offset + pos];
882            builder.append(indentBuf);
883            builder.append(byteToHex(currentByte));
884            asciiBuf.append(byteToASCII(currentByte));
885            pos++;
886
887            for (int i = 1; i < 16; i++, pos++) {
888                builder.append(' ');
889
890                if (i < remaining) {
891                    currentByte = buffer[offset + pos];
892                    builder.append(byteToHex(currentByte));
893                    asciiBuf.append(byteToASCII(currentByte));
894                } else {
895                    builder.append("  ");
896                }
897
898                if (i == 7) {
899                    builder.append("  ");
900
901                    if (i < remaining) {
902                        asciiBuf.append(' ');
903                    }
904                }
905            }
906
907            builder.append("  ");
908            builder.append(asciiBuf);
909            builder.append(EOL);
910        }
911        return builder.toString();
912    }
913
914    /** {@inheritDoc} */
915    @Override
916    public byte[] toByteArray() {
917        return copyTo(new byte[length]);
918    }
919
920    /** {@inheritDoc} */
921    @Override
922    public ByteString toByteString() {
923        return this;
924    }
925
926    /**
927     * Returns the UTF-8 decoded char array representation of this byte
928     * sequence.
929     *
930     * @return The UTF-8 decoded char array representation of this byte
931     *         sequence.
932     */
933    public char[] toCharArray() {
934        Charset utf8 = Charset.forName("UTF-8");
935        CharBuffer charBuffer = utf8.decode(ByteBuffer.wrap(buffer, offset, length));
936        char[] chars = new char[charBuffer.remaining()];
937        charBuffer.get(chars);
938        return chars;
939    }
940
941    /**
942     * Returns the integer value represented by the first four bytes of this
943     * byte string in big-endian order.
944     *
945     * @return The integer value represented by the first four bytes of this
946     *         byte string in big-endian order.
947     * @throws IndexOutOfBoundsException
948     *             If this byte string has less than four bytes.
949     */
950    public int toInt() {
951        if (length < 4) {
952            throw new IndexOutOfBoundsException();
953        }
954
955        int v = 0;
956        for (int i = 0; i < 4; i++) {
957            v <<= 8;
958            v |= buffer[offset + i] & 0xFF;
959        }
960        return v;
961    }
962
963    /**
964     * Returns the long value represented by the first eight bytes of this byte
965     * string in big-endian order.
966     *
967     * @return The long value represented by the first eight bytes of this byte
968     *         string in big-endian order.
969     * @throws IndexOutOfBoundsException
970     *             If this byte string has less than eight bytes.
971     */
972    public long toLong() {
973        if (length < 8) {
974            throw new IndexOutOfBoundsException();
975        }
976
977        long v = 0;
978        for (int i = 0; i < 8; i++) {
979            v <<= 8;
980            v |= buffer[offset + i] & 0xFF;
981        }
982        return v;
983    }
984
985    /** {@inheritDoc} */
986    @Override
987    public String toString() {
988        return toString(buffer, offset, length);
989    }
990}