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}