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