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}