001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.util; 037 038 039 040import java.io.ByteArrayInputStream; 041import java.io.File; 042import java.io.FileInputStream; 043import java.io.InputStream; 044import java.io.IOException; 045import java.io.OutputStream; 046import java.io.Serializable; 047import java.util.Arrays; 048 049import com.unboundid.asn1.ASN1OctetString; 050 051import static com.unboundid.util.UtilityMessages.*; 052 053 054 055/** 056 * This class provides a growable byte array to which data can be appended. 057 * Methods in this class are not synchronized. 058 */ 059@Mutable() 060@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 061public final class ByteStringBuffer 062 implements Serializable, Appendable 063{ 064 /** 065 * The default initial capacity for this buffer. 066 */ 067 private static final int DEFAULT_INITIAL_CAPACITY = 20; 068 069 070 071 /** 072 * The pre-allocated array that will be used for a boolean value of "false". 073 */ 074 @NotNull private static final byte[] FALSE_VALUE_BYTES = 075 StaticUtils.getBytes("false"); 076 077 078 079 /** 080 * The pre-allocated array that will be used for a boolean value of "true". 081 */ 082 @NotNull private static final byte[] TRUE_VALUE_BYTES = 083 StaticUtils.getBytes("true"); 084 085 086 087 /** 088 * A thread-local byte array that will be used for holding numeric values 089 * to append to the buffer. 090 */ 091 @NotNull private static final ThreadLocal<byte[]> TEMP_NUMBER_BUFFER = 092 new ThreadLocal<>(); 093 094 095 096 /** 097 * The serial version UID for this serializable class. 098 */ 099 private static final long serialVersionUID = 2899392249591230998L; 100 101 102 103 // The backing array for this buffer. 104 @NotNull private byte[] array; 105 106 // The length of the backing array. 107 private int capacity; 108 109 // The position at which to append the next data. 110 private int endPos; 111 112 113 114 /** 115 * Creates a new empty byte string buffer with a default initial capacity. 116 */ 117 public ByteStringBuffer() 118 { 119 this(DEFAULT_INITIAL_CAPACITY); 120 } 121 122 123 124 /** 125 * Creates a new byte string buffer with the specified capacity. 126 * 127 * @param initialCapacity The initial capacity to use for the buffer. It 128 * must be greater than or equal to zero. 129 */ 130 public ByteStringBuffer(final int initialCapacity) 131 { 132 array = new byte[initialCapacity]; 133 capacity = initialCapacity; 134 endPos = 0; 135 } 136 137 138 139 /** 140 * Appends the provided boolean value to this buffer. 141 * 142 * @param b The boolean value to be appended to this buffer. 143 * 144 * @return A reference to this buffer. 145 */ 146 @NotNull() 147 public ByteStringBuffer append(final boolean b) 148 { 149 if (b) 150 { 151 return append(TRUE_VALUE_BYTES, 0, 4); 152 } 153 else 154 { 155 return append(FALSE_VALUE_BYTES, 0, 5); 156 } 157 } 158 159 160 161 /** 162 * Appends the provided byte to this buffer. 163 * 164 * @param b The byte to be appended to this buffer. 165 * 166 * @return A reference to this buffer. 167 */ 168 @NotNull() 169 public ByteStringBuffer append(final byte b) 170 { 171 ensureCapacity(endPos + 1); 172 array[endPos++] = b; 173 return this; 174 } 175 176 177 178 /** 179 * Appends the contents of the provided byte array to this buffer. 180 * 181 * @param b The array whose contents should be appended to this buffer. It 182 * must not be {@code null}. 183 * 184 * @return A reference to this buffer. 185 * 186 * @throws NullPointerException If the provided array is {@code null}. 187 */ 188 @NotNull() 189 public ByteStringBuffer append(@NotNull final byte[] b) 190 throws NullPointerException 191 { 192 if (b == null) 193 { 194 final NullPointerException e = 195 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 196 Debug.debugCodingError(e); 197 throw e; 198 } 199 200 return append(b, 0, b.length); 201 } 202 203 204 205 /** 206 * Appends the specified portion of the provided byte array to this buffer. 207 * 208 * @param b The array whose contents should be appended to this buffer. 209 * @param off The offset within the array at which to begin copying data. 210 * @param len The number of bytes to copy. 211 * 212 * @return A reference to this buffer. 213 * 214 * @throws NullPointerException If the provided array is {@code null}. 215 * 216 * @throws IndexOutOfBoundsException If the offset or length are negative, 217 * if the offset plus the length is beyond 218 * the end of the provided array. 219 */ 220 @NotNull() 221 public ByteStringBuffer append(@NotNull final byte[] b, final int off, 222 final int len) 223 throws NullPointerException, IndexOutOfBoundsException 224 { 225 if (b == null) 226 { 227 final NullPointerException e = 228 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 229 Debug.debugCodingError(e); 230 throw e; 231 } 232 233 if ((off < 0) || (len < 0) || (off+len > b.length)) 234 { 235 final String message; 236 if (off < 0) 237 { 238 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 239 } 240 else if (len < 0) 241 { 242 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 243 } 244 else 245 { 246 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 247 b.length); 248 } 249 250 final IndexOutOfBoundsException e = 251 new IndexOutOfBoundsException(message); 252 Debug.debugCodingError(e); 253 throw e; 254 } 255 256 if (len > 0) 257 { 258 ensureCapacity(endPos + len); 259 System.arraycopy(b, off, array, endPos, len); 260 endPos += len; 261 } 262 263 return this; 264 } 265 266 267 268 /** 269 * Appends the provided byte string to this buffer. 270 * 271 * @param b The byte string to be appended to this buffer. 272 * 273 * @return A reference to this buffer. 274 * 275 * @throws NullPointerException If the provided byte string is {@code null}. 276 */ 277 @NotNull() 278 public ByteStringBuffer append(@NotNull final ByteString b) 279 throws NullPointerException 280 { 281 if (b == null) 282 { 283 final NullPointerException e = 284 new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); 285 Debug.debugCodingError(e); 286 throw e; 287 } 288 289 b.appendValueTo(this); 290 return this; 291 } 292 293 294 295 /** 296 * Appends the provided byte string buffer to this buffer. 297 * 298 * @param buffer The buffer whose contents should be appended to this 299 * buffer. 300 * 301 * @return A reference to this buffer. 302 * 303 * @throws NullPointerException If the provided buffer is {@code null}. 304 */ 305 @NotNull() 306 public ByteStringBuffer append(@NotNull final ByteStringBuffer buffer) 307 throws NullPointerException 308 { 309 if (buffer == null) 310 { 311 final NullPointerException e = 312 new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); 313 Debug.debugCodingError(e); 314 throw e; 315 } 316 317 return append(buffer.array, 0, buffer.endPos); 318 } 319 320 321 322 /** 323 * Appends the provided character to this buffer. 324 * 325 * @param c The character to be appended to this buffer. 326 * 327 * @return A reference to this buffer. 328 */ 329 @Override() 330 @NotNull() 331 public ByteStringBuffer append(final char c) 332 { 333 final byte b = (byte) (c & 0x7F); 334 if (b == c) 335 { 336 ensureCapacity(endPos + 1); 337 array[endPos++] = b; 338 } 339 else 340 { 341 append(String.valueOf(c)); 342 } 343 344 return this; 345 } 346 347 348 349 /** 350 * Appends the contents of the provided character array to this buffer. 351 * 352 * @param c The array whose contents should be appended to this buffer. 353 * 354 * @return A reference to this buffer. 355 * 356 * @throws NullPointerException If the provided array is {@code null}. 357 */ 358 @NotNull() 359 public ByteStringBuffer append(@NotNull final char[] c) 360 throws NullPointerException 361 { 362 if (c == null) 363 { 364 final NullPointerException e = 365 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 366 Debug.debugCodingError(e); 367 throw e; 368 } 369 370 return append(c, 0, c.length); 371 } 372 373 374 375 /** 376 * Appends the specified portion of the provided character array to this 377 * buffer. 378 * 379 * @param c The array whose contents should be appended to this buffer. 380 * @param off The offset within the array at which to begin copying data. 381 * @param len The number of characters to copy. 382 * 383 * @return A reference to this buffer. 384 * 385 * @throws NullPointerException If the provided array is {@code null}. 386 * 387 * @throws IndexOutOfBoundsException If the offset or length are negative, 388 * if the offset plus the length is beyond 389 * the end of the provided array. 390 */ 391 @NotNull() 392 public ByteStringBuffer append(@NotNull final char[] c, final int off, 393 final int len) 394 throws NullPointerException, IndexOutOfBoundsException 395 { 396 if (c == null) 397 { 398 final NullPointerException e = 399 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 400 Debug.debugCodingError(e); 401 throw e; 402 } 403 404 if ((off < 0) || (len < 0) || (off+len > c.length)) 405 { 406 final String message; 407 if (off < 0) 408 { 409 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 410 } 411 else if (len < 0) 412 { 413 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 414 } 415 else 416 { 417 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 418 c.length); 419 } 420 421 final IndexOutOfBoundsException e = 422 new IndexOutOfBoundsException(message); 423 Debug.debugCodingError(e); 424 throw e; 425 } 426 427 if (len > 0) 428 { 429 ensureCapacity(endPos + len); 430 431 int pos = off; 432 for (int i=0; i < len; i++, pos++) 433 { 434 final byte b = (byte) (c[pos] & 0x7F); 435 if (b == c[pos]) 436 { 437 array[endPos++] = b; 438 } 439 else 440 { 441 final String remainingString = 442 String.valueOf(c, pos, (off + len - pos)); 443 final byte[] remainingBytes = StaticUtils.getBytes(remainingString); 444 return append(remainingBytes); 445 } 446 } 447 } 448 449 return this; 450 } 451 452 453 454 /** 455 * Appends the provided character sequence to this buffer. 456 * 457 * @param s The character sequence to append to this buffer. 458 * 459 * @return A reference to this buffer. 460 * 461 * @throws NullPointerException If the provided character sequence is 462 * {@code null}. 463 */ 464 @Override() 465 @NotNull() 466 public ByteStringBuffer append(@NotNull final CharSequence s) 467 throws NullPointerException 468 { 469 final String str = s.toString(); 470 return append(str, 0, str.length()); 471 } 472 473 474 475 /** 476 * Appends the provided character sequence to this buffer. 477 * 478 * @param s The character sequence to append to this buffer. 479 * @param start The position in the sequence of the first character in the 480 * sequence to be appended to this buffer. 481 * @param end The position in the sequence immediately after the position 482 * of the last character to be appended. 483 * 484 * @return A reference to this buffer. 485 * 486 * @throws NullPointerException If the provided character sequence is 487 * {@code null}. 488 * 489 * @throws IndexOutOfBoundsException If the provided start or end positions 490 * are outside the bounds of the given 491 * character sequence. 492 */ 493 @Override() 494 @NotNull() 495 public ByteStringBuffer append(@NotNull final CharSequence s, final int start, 496 final int end) 497 throws NullPointerException, IndexOutOfBoundsException 498 { 499 if (s == null) 500 { 501 final NullPointerException e = 502 new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); 503 Debug.debugCodingError(e); 504 throw e; 505 } 506 507 final String string = s.toString(); 508 final int stringLength = string.length(); 509 if (start < 0) 510 { 511 throw new IndexOutOfBoundsException( 512 ERR_BS_BUFFER_START_NEGATIVE.get(start)); 513 } 514 else if (start > end) 515 { 516 throw new IndexOutOfBoundsException( 517 ERR_BS_BUFFER_START_BEYOND_END.get(start, end)); 518 } 519 else if (start > stringLength) 520 { 521 throw new IndexOutOfBoundsException( 522 ERR_BS_BUFFER_START_BEYOND_LENGTH.get(start, stringLength)); 523 } 524 else if (end > stringLength) 525 { 526 throw new IndexOutOfBoundsException( 527 ERR_BS_BUFFER_END_BEYOND_LENGTH.get(start, stringLength)); 528 } 529 else if (start < end) 530 { 531 ensureCapacity(endPos + (end - start)); 532 for (int pos=start; pos < end; pos++) 533 { 534 final char c = string.charAt(pos); 535 if (c <= 0x7F) 536 { 537 array[endPos++] = (byte) (c & 0x7F); 538 } 539 else 540 { 541 final String remainingString = string.substring(pos, end); 542 final byte[] remainingBytes = StaticUtils.getBytes(remainingString); 543 return append(remainingBytes); 544 } 545 } 546 } 547 548 return this; 549 } 550 551 552 553 /** 554 * Appends the provided integer value to this buffer. 555 * 556 * @param i The integer value to be appended to this buffer. 557 * 558 * @return A reference to this buffer. 559 */ 560 @NotNull() 561 public ByteStringBuffer append(final int i) 562 { 563 final int length = getBytes(i); 564 return append(TEMP_NUMBER_BUFFER.get(), 0, length); 565 } 566 567 568 569 /** 570 * Appends the provided long value to this buffer. 571 * 572 * @param l The long value to be appended to this buffer. 573 * 574 * @return A reference to this buffer. 575 */ 576 @NotNull() 577 public ByteStringBuffer append(final long l) 578 { 579 final int length = getBytes(l); 580 return append(TEMP_NUMBER_BUFFER.get(), 0, length); 581 } 582 583 584 585 /** 586 * Appends the provided code point to this buffer. 587 * 588 * @param codePoint The code point to append to this buffer. 589 * 590 * @return A reference to this buffer. 591 */ 592 @NotNull() 593 public ByteStringBuffer appendCodePoint(final int codePoint) 594 { 595 final int charByte = codePoint & 0x7F; 596 if (charByte == codePoint) 597 { 598 return append((byte) charByte); 599 } 600 else if (Character.isBmpCodePoint(codePoint)) 601 { 602 return append((char) codePoint); 603 } 604 else 605 { 606 return append(Character.toChars(codePoint)); 607 } 608 } 609 610 611 612 /** 613 * Inserts the provided boolean value to this buffer. 614 * 615 * @param pos The position at which the value is to be inserted. 616 * @param b The boolean value to be inserted into this buffer. 617 * 618 * @return A reference to this buffer. 619 * 620 * @throws IndexOutOfBoundsException If the specified position is negative 621 * or greater than the current length. 622 */ 623 @NotNull() 624 public ByteStringBuffer insert(final int pos, final boolean b) 625 throws IndexOutOfBoundsException 626 { 627 if (b) 628 { 629 return insert(pos, TRUE_VALUE_BYTES, 0, 4); 630 } 631 else 632 { 633 return insert(pos, FALSE_VALUE_BYTES, 0, 5); 634 } 635 } 636 637 638 639 /** 640 * Inserts the provided byte at the specified position in this buffer. 641 * 642 * @param pos The position at which the byte is to be inserted. 643 * @param b The byte to be inserted into this buffer. 644 * 645 * @return A reference to this buffer. 646 * 647 * @throws IndexOutOfBoundsException If the specified position is negative 648 * or greater than the current length. 649 */ 650 @NotNull () 651 public ByteStringBuffer insert(final int pos, final byte b) 652 throws IndexOutOfBoundsException 653 { 654 if ((pos < 0) || (pos > endPos)) 655 { 656 final String message; 657 if (pos < 0) 658 { 659 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 660 } 661 else 662 { 663 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 664 } 665 666 final IndexOutOfBoundsException e = 667 new IndexOutOfBoundsException(message); 668 Debug.debugCodingError(e); 669 throw e; 670 } 671 else if (pos == endPos) 672 { 673 return append(b); 674 } 675 676 ensureCapacity(endPos + 1); 677 System.arraycopy(array, pos, array, pos+1, (endPos-pos)); 678 array[pos] = b; 679 endPos++; 680 return this; 681 } 682 683 684 685 /** 686 * Inserts the contents of the provided byte array at the specified position 687 * in this buffer. 688 * 689 * @param pos The position at which the data is to be inserted. 690 * @param b The array whose contents should be inserted into this buffer. 691 * 692 * @return A reference to this buffer. 693 * 694 * @throws NullPointerException If the provided array is {@code null}. 695 * 696 * @throws IndexOutOfBoundsException If the specified position is negative 697 * or greater than the current length. 698 */ 699 @NotNull() 700 public ByteStringBuffer insert(final int pos, @NotNull final byte[] b) 701 throws NullPointerException, IndexOutOfBoundsException 702 { 703 if (b == null) 704 { 705 final NullPointerException e = 706 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 707 Debug.debugCodingError(e); 708 throw e; 709 } 710 711 return insert(pos, b, 0, b.length); 712 } 713 714 715 716 /** 717 * Inserts a portion of the data in the provided array at the specified 718 * position in this buffer. 719 * 720 * Appends the specified portion of the provided byte array to this buffer. 721 * 722 * @param pos The position at which the data is to be inserted. 723 * @param b The array whose contents should be inserted into this buffer. 724 * @param off The offset within the array at which to begin copying data. 725 * @param len The number of bytes to copy. 726 * 727 * @return A reference to this buffer. 728 * 729 * @throws NullPointerException If the provided array is {@code null}. 730 * 731 * @throws IndexOutOfBoundsException If the specified position is negative 732 * or greater than the current length, if 733 * the offset or length are negative, if 734 * the offset plus the length is beyond 735 * the end of the provided array. 736 */ 737 @NotNull() 738 public ByteStringBuffer insert(final int pos, @NotNull final byte[] b, 739 final int off, final int len) 740 throws NullPointerException, IndexOutOfBoundsException 741 { 742 if (b == null) 743 { 744 final NullPointerException e = 745 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 746 Debug.debugCodingError(e); 747 throw e; 748 } 749 750 if ((pos < 0) || (pos > endPos) || (off < 0) || (len < 0) || 751 (off+len > b.length)) 752 { 753 final String message; 754 if (pos < 0) 755 { 756 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 757 } 758 else if (pos > endPos) 759 { 760 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 761 } 762 else if (off < 0) 763 { 764 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 765 } 766 else if (len < 0) 767 { 768 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 769 } 770 else 771 { 772 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 773 b.length); 774 } 775 776 final IndexOutOfBoundsException e = 777 new IndexOutOfBoundsException(message); 778 Debug.debugCodingError(e); 779 throw e; 780 } 781 else if (len == 0) 782 { 783 return this; 784 } 785 else if (pos == endPos) 786 { 787 return append(b, off, len); 788 } 789 790 ensureCapacity(endPos + len); 791 System.arraycopy(array, pos, array, pos+len, (endPos-pos)); 792 System.arraycopy(b, off, array, pos, len); 793 endPos += len; 794 return this; 795 } 796 797 798 799 /** 800 * Inserts the provided byte string into this buffer at the specified 801 * position. 802 * 803 * @param pos The position at which the data is to be inserted. 804 * @param b The byte string to insert into this buffer. 805 * 806 * @return A reference to this buffer. 807 * 808 * @throws NullPointerException If the provided byte string is {@code null}. 809 * 810 * @throws IndexOutOfBoundsException If the specified position is negative 811 * or greater than the current length. 812 */ 813 @NotNull() 814 public ByteStringBuffer insert(final int pos, @NotNull final ByteString b) 815 throws NullPointerException, IndexOutOfBoundsException 816 { 817 if (b == null) 818 { 819 final NullPointerException e = 820 new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); 821 Debug.debugCodingError(e); 822 throw e; 823 } 824 825 return insert(pos, b.getValue()); 826 } 827 828 829 830 /** 831 * Inserts the provided byte string buffer into this buffer at the specified 832 * position. 833 * 834 * @param pos The position at which the data is to be inserted. 835 * @param buffer The buffer whose contents should be inserted into this 836 * buffer. 837 * 838 * @return A reference to this buffer. 839 * 840 * @throws NullPointerException If the provided buffer is {@code null}. 841 * 842 * @throws IndexOutOfBoundsException If the specified position is negative 843 * or greater than the current length. 844 */ 845 @NotNull() 846 public ByteStringBuffer insert(final int pos, 847 @NotNull final ByteStringBuffer buffer) 848 throws NullPointerException, IndexOutOfBoundsException 849 { 850 if (buffer == null) 851 { 852 final NullPointerException e = 853 new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); 854 Debug.debugCodingError(e); 855 throw e; 856 } 857 858 return insert(pos, buffer.array, 0, buffer.endPos); 859 } 860 861 862 863 /** 864 * Inserts the provided character into this buffer at the provided position. 865 * 866 * @param pos The position at which the character is to be inserted. 867 * @param c The character to be inserted into this buffer. 868 * 869 * @return A reference to this buffer. 870 * 871 * @throws IndexOutOfBoundsException If the specified position is negative 872 * or greater than the current length. 873 */ 874 @NotNull() 875 public ByteStringBuffer insert(final int pos, final char c) 876 throws IndexOutOfBoundsException 877 { 878 if ((pos < 0) || (pos > endPos)) 879 { 880 final String message; 881 if (pos < 0) 882 { 883 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 884 } 885 else 886 { 887 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 888 } 889 890 final IndexOutOfBoundsException e = 891 new IndexOutOfBoundsException(message); 892 Debug.debugCodingError(e); 893 throw e; 894 } 895 else if (pos == endPos) 896 { 897 return append(c); 898 } 899 900 final byte b = (byte) (c & 0x7F); 901 if (b == c) 902 { 903 ensureCapacity(endPos + 1); 904 System.arraycopy(array, pos, array, pos+1, (endPos-pos)); 905 array[pos] = b; 906 endPos++; 907 } 908 else 909 { 910 insert(pos, String.valueOf(c)); 911 } 912 913 return this; 914 } 915 916 917 918 /** 919 * Inserts the contents of the provided character array into this buffer at 920 * the specified position. 921 * 922 * @param pos The position at which the data is to be inserted. 923 * @param c The array whose contents should be inserted into this buffer. 924 * 925 * @return A reference to this buffer. 926 * 927 * @throws NullPointerException If the provided array is {@code null}. 928 * 929 * @throws IndexOutOfBoundsException If the specified position is negative 930 * or greater than the current length. 931 */ 932 @NotNull() 933 public ByteStringBuffer insert(final int pos, @NotNull final char[] c) 934 throws NullPointerException, IndexOutOfBoundsException 935 { 936 if (c == null) 937 { 938 final NullPointerException e = 939 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 940 Debug.debugCodingError(e); 941 throw e; 942 } 943 944 return insert(pos, new String(c, 0, c.length)); 945 } 946 947 948 949 /** 950 * Inserts the specified portion of the provided character array to this 951 * buffer at the specified position. 952 * 953 * @param pos The position at which the data is to be inserted. 954 * @param c The array whose contents should be inserted into this buffer. 955 * @param off The offset within the array at which to begin copying data. 956 * @param len The number of characters to copy. 957 * 958 * @return A reference to this buffer. 959 * 960 * @throws NullPointerException If the provided array is {@code null}. 961 * 962 * @throws IndexOutOfBoundsException If the specified position is negative 963 * or greater than the current length, if 964 * the offset or length are negative, if 965 * the offset plus the length is beyond 966 * the end of the provided array. 967 */ 968 @NotNull() 969 public ByteStringBuffer insert(final int pos, @NotNull final char[] c, 970 final int off, final int len) 971 throws NullPointerException, IndexOutOfBoundsException 972 { 973 if (c == null) 974 { 975 final NullPointerException e = 976 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 977 Debug.debugCodingError(e); 978 throw e; 979 } 980 981 return insert(pos, new String(c, off, len)); 982 } 983 984 985 986 /** 987 * Inserts the provided character sequence to this buffer at the specified 988 * position. 989 * 990 * @param pos The position at which the data is to be inserted. 991 * @param s The character sequence to insert into this buffer. 992 * 993 * @return A reference to this buffer. 994 * 995 * @throws NullPointerException If the provided character sequence is 996 * {@code null}. 997 * 998 * @throws IndexOutOfBoundsException If the specified position is negative 999 * or greater than the current length. 1000 */ 1001 @NotNull() 1002 public ByteStringBuffer insert(final int pos, @NotNull final CharSequence s) 1003 throws NullPointerException, IndexOutOfBoundsException 1004 { 1005 if (s == null) 1006 { 1007 final NullPointerException e = 1008 new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); 1009 Debug.debugCodingError(e); 1010 throw e; 1011 } 1012 1013 if ((pos < 0) || (pos > endPos)) 1014 { 1015 final String message; 1016 if (pos < 0) 1017 { 1018 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 1019 } 1020 else 1021 { 1022 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 1023 } 1024 1025 final IndexOutOfBoundsException e = 1026 new IndexOutOfBoundsException(message); 1027 Debug.debugCodingError(e); 1028 throw e; 1029 } 1030 else if (pos == endPos) 1031 { 1032 return append(s); 1033 } 1034 else 1035 { 1036 return insert(pos, StaticUtils.getBytes(s.toString())); 1037 } 1038 } 1039 1040 1041 1042 /** 1043 * Inserts the provided integer value to this buffer. 1044 * 1045 * @param pos The position at which the value is to be inserted. 1046 * @param i The integer value to be inserted into this buffer. 1047 * 1048 * @return A reference to this buffer. 1049 * 1050 * @throws IndexOutOfBoundsException If the specified position is negative 1051 * or greater than the current length. 1052 */ 1053 @NotNull() 1054 public ByteStringBuffer insert(final int pos, final int i) 1055 throws IndexOutOfBoundsException 1056 { 1057 final int length = getBytes(i); 1058 return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); 1059 } 1060 1061 1062 1063 /** 1064 * Inserts the provided long value to this buffer. 1065 * 1066 * @param pos The position at which the value is to be inserted. 1067 * @param l The long value to be inserted into this buffer. 1068 * 1069 * @return A reference to this buffer. 1070 * 1071 * @throws IndexOutOfBoundsException If the specified position is negative 1072 * or greater than the current length. 1073 */ 1074 @NotNull() 1075 public ByteStringBuffer insert(final int pos, final long l) 1076 throws IndexOutOfBoundsException 1077 { 1078 final int length = getBytes(l); 1079 return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); 1080 } 1081 1082 1083 1084 /** 1085 * Inserts the provided code point into this buffer. 1086 * 1087 * @param pos The position at which the code point is to be inserted. 1088 * @param codePoint The code point to be inserted. 1089 * 1090 * @return A reference to this buffer. 1091 * 1092 * @throws IndexOutOfBoundsException If the specified position is negative 1093 * or greater than the current length. 1094 */ 1095 @NotNull() 1096 public ByteStringBuffer insertCodePoint(final int pos, final int codePoint) 1097 throws IndexOutOfBoundsException 1098 { 1099 final int charByte = codePoint & 0x7F; 1100 if (charByte == codePoint) 1101 { 1102 return insert(pos, (byte) charByte); 1103 } 1104 else if (Character.isBmpCodePoint(codePoint)) 1105 { 1106 return insert(pos, (char) codePoint); 1107 } 1108 else 1109 { 1110 return insert(pos, Character.toChars(codePoint)); 1111 } 1112 } 1113 1114 1115 1116 /** 1117 * Deletes the specified number of bytes from the beginning of the buffer. 1118 * 1119 * @param len The number of bytes to delete. 1120 * 1121 * @return A reference to this buffer. 1122 * 1123 * @throws IndexOutOfBoundsException If the specified length is negative, 1124 * or if it is greater than the number of 1125 * bytes currently contained in this 1126 * buffer. 1127 */ 1128 @NotNull() 1129 public ByteStringBuffer delete(final int len) 1130 throws IndexOutOfBoundsException 1131 { 1132 return delete(0, len); 1133 } 1134 1135 1136 1137 /** 1138 * Deletes the indicated number of bytes from the specified location in the 1139 * buffer. 1140 * 1141 * @param off The position in the buffer at which the content to delete 1142 * begins. 1143 * @param len The number of bytes to remove from the buffer. 1144 * 1145 * @return A reference to this buffer. 1146 * 1147 * @throws IndexOutOfBoundsException If the offset or length is negative, or 1148 * if the combination of the offset and 1149 * length is greater than the end of the 1150 * content in the buffer. 1151 */ 1152 @NotNull() 1153 public ByteStringBuffer delete(final int off, final int len) 1154 throws IndexOutOfBoundsException 1155 { 1156 if (off < 0) 1157 { 1158 throw new IndexOutOfBoundsException( 1159 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off)); 1160 } 1161 else if (len < 0) 1162 { 1163 throw new IndexOutOfBoundsException( 1164 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len)); 1165 } 1166 else if ((off + len) > endPos) 1167 { 1168 throw new IndexOutOfBoundsException( 1169 ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, endPos)); 1170 } 1171 else if (len == 0) 1172 { 1173 return this; 1174 } 1175 else if (off == 0) 1176 { 1177 if (len == endPos) 1178 { 1179 endPos = 0; 1180 return this; 1181 } 1182 else 1183 { 1184 final int newEndPos = endPos - len; 1185 System.arraycopy(array, len, array, 0, newEndPos); 1186 endPos = newEndPos; 1187 return this; 1188 } 1189 } 1190 else 1191 { 1192 if ((off + len) == endPos) 1193 { 1194 endPos = off; 1195 return this; 1196 } 1197 else 1198 { 1199 final int bytesToCopy = endPos - (off+len); 1200 System.arraycopy(array, (off+len), array, off, bytesToCopy); 1201 endPos -= len; 1202 return this; 1203 } 1204 } 1205 } 1206 1207 1208 1209 /** 1210 * Sets the contents of this buffer to include only the provided boolean 1211 * value. 1212 * 1213 * @param b The boolean value to use as the content for this buffer. 1214 * 1215 * @return A reference to this buffer. 1216 */ 1217 @NotNull() 1218 public ByteStringBuffer set(final boolean b) 1219 { 1220 if (b) 1221 { 1222 return set(TRUE_VALUE_BYTES, 0, 4); 1223 } 1224 else 1225 { 1226 return set(FALSE_VALUE_BYTES, 0, 5); 1227 } 1228 } 1229 1230 1231 1232 /** 1233 * Sets the contents of this buffer to include only the provided byte. 1234 * 1235 * @param b The byte to use as the content for this buffer. 1236 * 1237 * @return A reference to this buffer. 1238 */ 1239 @NotNull() 1240 public ByteStringBuffer set(final byte b) 1241 { 1242 endPos = 0; 1243 return append(b); 1244 } 1245 1246 1247 1248 /** 1249 * Sets the contents of this buffer to the contents of the provided byte 1250 * array. 1251 * 1252 * @param b The byte array containing the content to use for this buffer. 1253 * 1254 * @throws NullPointerException If the provided array is {@code null}. 1255 * 1256 * @return A reference to this buffer. 1257 */ 1258 @NotNull() 1259 public ByteStringBuffer set(@NotNull final byte[] b) 1260 throws NullPointerException 1261 { 1262 if (b == null) 1263 { 1264 final NullPointerException e = 1265 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1266 Debug.debugCodingError(e); 1267 throw e; 1268 } 1269 1270 endPos = 0; 1271 return append(b, 0, b.length); 1272 } 1273 1274 1275 1276 /** 1277 * Sets the contents of this buffer to the specified portion of the provided 1278 * byte array. 1279 * 1280 * @param b The byte array containing the content to use for this buffer. 1281 * @param off The offset within the array at which to begin copying data. 1282 * @param len The number of bytes to copy. 1283 * 1284 * @return A reference to this buffer. 1285 * 1286 * @throws NullPointerException If the provided array is {@code null}. 1287 * 1288 * @throws IndexOutOfBoundsException If the offset or length are negative, 1289 * if the offset plus the length is beyond 1290 * the end of the provided array. 1291 */ 1292 @NotNull() 1293 public ByteStringBuffer set(@NotNull final byte[] b, final int off, 1294 final int len) 1295 throws NullPointerException, IndexOutOfBoundsException 1296 { 1297 if (b == null) 1298 { 1299 final NullPointerException e = 1300 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1301 Debug.debugCodingError(e); 1302 throw e; 1303 } 1304 1305 if ((off < 0) || (len < 0) || (off+len > b.length)) 1306 { 1307 final String message; 1308 if (off < 0) 1309 { 1310 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 1311 } 1312 else if (len < 0) 1313 { 1314 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 1315 } 1316 else 1317 { 1318 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 1319 b.length); 1320 } 1321 1322 final IndexOutOfBoundsException e = 1323 new IndexOutOfBoundsException(message); 1324 Debug.debugCodingError(e); 1325 throw e; 1326 } 1327 1328 endPos = 0; 1329 return append(b, off, len); 1330 } 1331 1332 1333 1334 /** 1335 * Sets the contents of this buffer to the contents of the provided byte 1336 * string. 1337 * 1338 * @param b The byte string that should be used as the content for this 1339 * buffer. 1340 * 1341 * @throws NullPointerException If the provided byte string is {@code null}. 1342 * 1343 * @return A reference to this buffer. 1344 */ 1345 @NotNull() 1346 public ByteStringBuffer set(@NotNull final ByteString b) 1347 throws NullPointerException 1348 { 1349 if (b == null) 1350 { 1351 final NullPointerException e = 1352 new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); 1353 Debug.debugCodingError(e); 1354 throw e; 1355 } 1356 1357 endPos = 0; 1358 b.appendValueTo(this); 1359 return this; 1360 } 1361 1362 1363 1364 /** 1365 * Sets the contents of this buffer to the contents of the provided byte 1366 * string buffer. 1367 * 1368 * @param buffer The buffer whose contents should be used as the content for 1369 * this buffer. 1370 * 1371 * @throws NullPointerException If the provided buffer is {@code null}. 1372 * 1373 * @return A reference to this buffer. 1374 */ 1375 @NotNull() 1376 public ByteStringBuffer set(@NotNull final ByteStringBuffer buffer) 1377 throws NullPointerException 1378 { 1379 if (buffer == null) 1380 { 1381 final NullPointerException e = 1382 new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); 1383 Debug.debugCodingError(e); 1384 throw e; 1385 } 1386 1387 endPos = 0; 1388 return append(buffer.array, 0, buffer.endPos); 1389 } 1390 1391 1392 1393 /** 1394 * Sets the contents of this buffer to include only the provided character. 1395 * 1396 * @param c The character use as the content for this buffer. 1397 * 1398 * @return A reference to this buffer. 1399 */ 1400 @NotNull() 1401 public ByteStringBuffer set(final char c) 1402 { 1403 endPos = 0; 1404 return append(c); 1405 } 1406 1407 1408 1409 /** 1410 * Sets the contents of this buffer to the contents of the provided character 1411 * array. 1412 * 1413 * @param c The character array containing the content to use for this 1414 * buffer. 1415 * 1416 * @throws NullPointerException If the provided array is {@code null}. 1417 * 1418 * @return A reference to this buffer. 1419 */ 1420 @NotNull() 1421 public ByteStringBuffer set(@NotNull final char[] c) 1422 throws NullPointerException 1423 { 1424 if (c == null) 1425 { 1426 final NullPointerException e = 1427 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1428 Debug.debugCodingError(e); 1429 throw e; 1430 } 1431 1432 endPos = 0; 1433 return append(c, 0, c.length); 1434 } 1435 1436 1437 1438 /** 1439 * Sets the contents of this buffer to the specified portion of the provided 1440 * character array. 1441 * 1442 * @param c The character array containing the content to use for this 1443 * buffer. 1444 * @param off The offset within the array at which to begin copying data. 1445 * @param len The number of characters to copy. 1446 * 1447 * @return A reference to this buffer. 1448 * 1449 * @throws NullPointerException If the provided array is {@code null}. 1450 * 1451 * @throws IndexOutOfBoundsException If the offset or length are negative, 1452 * if the offset plus the length is beyond 1453 * the end of the provided array. 1454 */ 1455 @NotNull() 1456 public ByteStringBuffer set(@NotNull final char[] c, final int off, 1457 final int len) 1458 throws NullPointerException, IndexOutOfBoundsException 1459 { 1460 if (c == null) 1461 { 1462 final NullPointerException e = 1463 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1464 Debug.debugCodingError(e); 1465 throw e; 1466 } 1467 1468 if ((off < 0) || (len < 0) || (off+len > c.length)) 1469 { 1470 final String message; 1471 if (off < 0) 1472 { 1473 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 1474 } 1475 else if (len < 0) 1476 { 1477 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 1478 } 1479 else 1480 { 1481 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 1482 c.length); 1483 } 1484 1485 final IndexOutOfBoundsException e = 1486 new IndexOutOfBoundsException(message); 1487 Debug.debugCodingError(e); 1488 throw e; 1489 } 1490 1491 endPos = 0; 1492 return append(c, off, len); 1493 } 1494 1495 1496 1497 /** 1498 * Sets the contents of this buffer to the specified portion of the provided 1499 * character sequence. 1500 * 1501 * @param s The character sequence to use as the content for this buffer. 1502 * 1503 * @throws NullPointerException If the provided character sequence is 1504 * {@code null}. 1505 * 1506 * @return A reference to this buffer. 1507 */ 1508 @NotNull() 1509 public ByteStringBuffer set(@NotNull final CharSequence s) 1510 throws NullPointerException 1511 { 1512 if (s == null) 1513 { 1514 final NullPointerException e = 1515 new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); 1516 Debug.debugCodingError(e); 1517 throw e; 1518 } 1519 1520 endPos = 0; 1521 return append(s); 1522 } 1523 1524 1525 1526 /** 1527 * Sets the contents of this buffer to include only the provided integer 1528 * value. 1529 * 1530 * @param i The integer value to use as the content for this buffer. 1531 * 1532 * @return A reference to this buffer. 1533 */ 1534 @NotNull() 1535 public ByteStringBuffer set(final int i) 1536 { 1537 final int length = getBytes(i); 1538 return set(TEMP_NUMBER_BUFFER.get(), 0, length); 1539 } 1540 1541 1542 1543 /** 1544 * Sets the contents of this buffer to include only the provided long value. 1545 * 1546 * @param l The long value to use as the content for this buffer. 1547 * 1548 * @return A reference to this buffer. 1549 */ 1550 @NotNull() 1551 public ByteStringBuffer set(final long l) 1552 { 1553 final int length = getBytes(l); 1554 return set(TEMP_NUMBER_BUFFER.get(), 0, length); 1555 } 1556 1557 1558 1559 /** 1560 * Clears the contents of this buffer. 1561 * 1562 * @return A reference to this buffer. 1563 */ 1564 @NotNull() 1565 public ByteStringBuffer clear() 1566 { 1567 endPos = 0; 1568 return this; 1569 } 1570 1571 1572 1573 /** 1574 * Clears the contents of this buffer. 1575 * 1576 * @param zero Indicates whether to overwrite the content of the backing 1577 * array with all zeros in order to wipe out any sensitive data 1578 * it may contain. 1579 * 1580 * @return A reference to this buffer. 1581 */ 1582 @NotNull() 1583 public ByteStringBuffer clear(final boolean zero) 1584 { 1585 endPos = 0; 1586 1587 if (zero) 1588 { 1589 Arrays.fill(array, (byte) 0x00); 1590 } 1591 1592 return this; 1593 } 1594 1595 1596 1597 /** 1598 * Retrieves the current backing array for this buffer. The data will begin 1599 * at position 0 and will contain {@link ByteStringBuffer#length} bytes. 1600 * 1601 * @return The current backing array for this buffer. 1602 */ 1603 @NotNull() 1604 public byte[] getBackingArray() 1605 { 1606 return array; 1607 } 1608 1609 1610 1611 /** 1612 * Indicates whether this buffer is currently empty. 1613 * 1614 * @return {@code true} if this buffer is currently empty, or {@code false} 1615 * if not. 1616 */ 1617 public boolean isEmpty() 1618 { 1619 return (endPos == 0); 1620 } 1621 1622 1623 1624 /** 1625 * Retrieves the number of bytes contained in this buffer. 1626 * 1627 * @return The number of bytes contained in this buffer. 1628 */ 1629 public int length() 1630 { 1631 return endPos; 1632 } 1633 1634 1635 1636 /** 1637 * Sets the length of this buffer to the specified value. If the new length 1638 * is greater than the current length, the value will be padded with zeroes. 1639 * 1640 * @param length The new length to use for the buffer. It must be greater 1641 * than or equal to zero. 1642 * 1643 * @throws IndexOutOfBoundsException If the provided length is negative. 1644 */ 1645 public void setLength(final int length) 1646 throws IndexOutOfBoundsException 1647 { 1648 if (length < 0) 1649 { 1650 final IndexOutOfBoundsException e = new IndexOutOfBoundsException( 1651 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length)); 1652 Debug.debugCodingError(e); 1653 throw e; 1654 } 1655 1656 if (length > endPos) 1657 { 1658 ensureCapacity(length); 1659 Arrays.fill(array, endPos, length, (byte) 0x00); 1660 endPos = length; 1661 } 1662 else 1663 { 1664 endPos = length; 1665 } 1666 } 1667 1668 1669 1670 /** 1671 * Returns the current capacity for this buffer. 1672 * 1673 * @return The current capacity for this buffer. 1674 */ 1675 public int capacity() 1676 { 1677 return capacity; 1678 } 1679 1680 1681 1682 /** 1683 * Ensures that the total capacity of this buffer is at least equal to the 1684 * specified size. 1685 * 1686 * @param minimumCapacity The minimum capacity for this buffer. 1687 */ 1688 public void ensureCapacity(final int minimumCapacity) 1689 { 1690 if (capacity < minimumCapacity) 1691 { 1692 final int newCapacity = Math.max(minimumCapacity, (2 * capacity) + 2); 1693 final byte[] newArray = new byte[newCapacity]; 1694 System.arraycopy(array, 0, newArray, 0, capacity); 1695 array = newArray; 1696 capacity = newCapacity; 1697 } 1698 } 1699 1700 1701 1702 /** 1703 * Sets the capacity equal to the specified value. If the provided capacity 1704 * is less than the current length, then the length will be reduced to the 1705 * new capacity. 1706 * 1707 * @param capacity The new capacity for this buffer. It must be greater 1708 * than or equal to zero. 1709 * 1710 * @throws IndexOutOfBoundsException If the provided capacity is negative. 1711 */ 1712 public void setCapacity(final int capacity) 1713 throws IndexOutOfBoundsException 1714 { 1715 if (capacity < 0) 1716 { 1717 final IndexOutOfBoundsException e = new IndexOutOfBoundsException( 1718 ERR_BS_BUFFER_CAPACITY_NEGATIVE.get(capacity)); 1719 Debug.debugCodingError(e); 1720 throw e; 1721 } 1722 1723 if (this.capacity == capacity) 1724 { 1725 return; 1726 } 1727 else if (this.capacity < capacity) 1728 { 1729 final byte[] newArray = new byte[capacity]; 1730 System.arraycopy(array, 0, newArray, 0, this.capacity); 1731 array = newArray; 1732 this.capacity = capacity; 1733 } 1734 else 1735 { 1736 final byte[] newArray = new byte[capacity]; 1737 System.arraycopy(array, 0, newArray, 0, capacity); 1738 array = newArray; 1739 endPos = Math.min(endPos, capacity); 1740 this.capacity = capacity; 1741 } 1742 } 1743 1744 1745 1746 /** 1747 * Trims the backing array to the minimal size required for this buffer. 1748 * 1749 * @return A reference to this buffer. 1750 */ 1751 @NotNull() 1752 public ByteStringBuffer trimToSize() 1753 { 1754 if (endPos != capacity) 1755 { 1756 final byte[] newArray = new byte[endPos]; 1757 System.arraycopy(array, 0, newArray, 0, endPos); 1758 array = newArray; 1759 capacity = endPos; 1760 } 1761 1762 return this; 1763 } 1764 1765 1766 1767 /** 1768 * Retrieves the byte at the specified offset in the buffer. 1769 * 1770 * @param offset The offset of the byte to read. It must be between greater 1771 * than or equal to zero and less than {@link #length}. 1772 * 1773 * @return The byte at the specified offset in the buffer. 1774 * 1775 * @throws IndexOutOfBoundsException If the provided offset is negative or 1776 * greater than or equal to the length of 1777 * the buffer. 1778 */ 1779 public byte byteAt(final int offset) 1780 throws IndexOutOfBoundsException 1781 { 1782 if (offset < 0) 1783 { 1784 throw new IndexOutOfBoundsException( 1785 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(offset)); 1786 } 1787 else if (offset >= endPos) 1788 { 1789 throw new IndexOutOfBoundsException( 1790 ERR_BS_BUFFER_OFFSET_TOO_LARGE.get(offset, endPos)); 1791 } 1792 else 1793 { 1794 return array[offset]; 1795 } 1796 } 1797 1798 1799 1800 /** 1801 * Retrieves the specified subset of bytes from the buffer. 1802 * 1803 * @param offset The offset of the first byte to retrieve. It must be 1804 * greater than or equal to zero and less than 1805 * {@link #length}. 1806 * @param length The number of bytes to retrieve. It must be greater than 1807 * or equal to zero, and the sum of {@code offset} and 1808 * {@code length} must be less than or equal to 1809 * {@link #length}. 1810 * 1811 * @return A byte array containing the specified subset of bytes from the 1812 * buffer. 1813 * 1814 * @throws IndexOutOfBoundsException If either the offset or the length is 1815 * negative, or if the offsset plus the 1816 * length is greater than or equal to the 1817 * length of the buffer. 1818 */ 1819 @NotNull() 1820 public byte[] bytesAt(final int offset, final int length) 1821 throws IndexOutOfBoundsException 1822 { 1823 if (offset < 0) 1824 { 1825 throw new IndexOutOfBoundsException( 1826 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(offset)); 1827 } 1828 1829 if (length < 0) 1830 { 1831 throw new IndexOutOfBoundsException( 1832 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length)); 1833 } 1834 1835 if ((offset + length) > endPos) 1836 { 1837 throw new IndexOutOfBoundsException( 1838 ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(offset, length, 1839 endPos)); 1840 } 1841 1842 final byte[] returnArray = new byte[length]; 1843 System.arraycopy(array, offset, returnArray, 0, length); 1844 return returnArray; 1845 } 1846 1847 1848 1849 /** 1850 * Indicates whether this buffer starts with the specified set of bytes. 1851 * 1852 * @param bytes The bytes for which to make the determination. 1853 * 1854 * @return {@code true} if this buffer starts with the specified set of 1855 * bytes, or {@code false} if not. 1856 */ 1857 public boolean startsWith(@NotNull final byte[] bytes) 1858 { 1859 if (bytes.length > endPos) 1860 { 1861 return false; 1862 } 1863 1864 for (int i=0; i < bytes.length; i++) 1865 { 1866 if (array[i] != bytes[i]) 1867 { 1868 return false; 1869 } 1870 } 1871 1872 return true; 1873 } 1874 1875 1876 1877 /** 1878 * Indicates whether this buffer ends with the specified set of bytes. 1879 * 1880 * @param bytes The bytes for which to make the determination. 1881 * 1882 * @return {@code true} if this buffer ends with the specified set of bytes, 1883 * or {@code false} if not. 1884 */ 1885 public boolean endsWith(@NotNull final byte[] bytes) 1886 { 1887 if (bytes.length > endPos) 1888 { 1889 return false; 1890 } 1891 1892 for (int i=0; i < bytes.length; i++) 1893 { 1894 if (array[endPos - bytes.length + i] != bytes[i]) 1895 { 1896 return false; 1897 } 1898 } 1899 1900 return true; 1901 } 1902 1903 1904 1905 /** 1906 * Returns a new byte array with the content from this buffer. 1907 * 1908 * @return A byte array containing the content from this buffer. 1909 */ 1910 @NotNull() 1911 public byte[] toByteArray() 1912 { 1913 final byte[] newArray = new byte[endPos]; 1914 System.arraycopy(array, 0, newArray, 0, endPos); 1915 return newArray; 1916 } 1917 1918 1919 1920 /** 1921 * Returns a new byte string with the content from this buffer. 1922 * 1923 * @return A byte string with the content from this buffer. 1924 */ 1925 @NotNull() 1926 public ByteString toByteString() 1927 { 1928 return new ASN1OctetString(toByteArray()); 1929 } 1930 1931 1932 1933 /** 1934 * Creates an input stream that may be used to read content from this buffer. 1935 * This buffer should not be altered while the input stream is being used. 1936 * 1937 * @return An input stream that may be used to read content from this buffer. 1938 */ 1939 @NotNull() 1940 public InputStream asInputStream() 1941 { 1942 return new ByteArrayInputStream(array, 0, endPos); 1943 } 1944 1945 1946 1947 /** 1948 * Reads the contents of the specified file into this buffer, appending it to 1949 * the end of the buffer. 1950 * 1951 * @param file The file to be read. 1952 * 1953 * @throws IOException If an unexpected problem occurs. 1954 */ 1955 public void readFrom(@NotNull final File file) 1956 throws IOException 1957 { 1958 try (FileInputStream inputStream = new FileInputStream(file)) 1959 { 1960 readFrom(inputStream); 1961 } 1962 } 1963 1964 1965 1966 /** 1967 * Reads data from the provided input stream into this buffer, appending it to 1968 * the end of the buffer. The entire content of the input stream will be 1969 * read, but the input stream will not be closed. 1970 * 1971 * @param inputStream The input stream from which data is to be read. 1972 * 1973 * @throws IOException If an unexpected problem occurs. 1974 */ 1975 public void readFrom(@NotNull final InputStream inputStream) 1976 throws IOException 1977 { 1978 final int initialEndPos = endPos; 1979 1980 try 1981 { 1982 while (true) 1983 { 1984 int remainingCapacity = capacity - endPos; 1985 if (remainingCapacity <= 100) 1986 { 1987 ensureCapacity(Math.max(100, (2*capacity))); 1988 remainingCapacity = capacity - endPos; 1989 } 1990 1991 final int bytesRead = 1992 inputStream.read(array, endPos, remainingCapacity); 1993 if (bytesRead < 0) 1994 { 1995 return; 1996 } 1997 1998 endPos += bytesRead; 1999 } 2000 } 2001 catch (final IOException e) 2002 { 2003 Debug.debugException(e); 2004 endPos = initialEndPos; 2005 throw e; 2006 } 2007 } 2008 2009 2010 2011 /** 2012 * Writes the contents of this byte string buffer to the provided output 2013 * stream. 2014 * 2015 * @param outputStream The output stream to which the data should be 2016 * written. 2017 * 2018 * @throws IOException If a problem occurs while writing to the provided 2019 * output stream. 2020 */ 2021 public void write(@NotNull final OutputStream outputStream) 2022 throws IOException 2023 { 2024 outputStream.write(array, 0, endPos); 2025 } 2026 2027 2028 2029 /** 2030 * Adds the bytes comprising the string representation of the provided long 2031 * value to the temporary number buffer. 2032 * 2033 * @param l The long value to be appended. 2034 * 2035 * @return The number of bytes in the string representation of the value. 2036 */ 2037 private static int getBytes(final long l) 2038 { 2039 // NOTE: This method is probably not as efficient as it could be, but it is 2040 // more important to avoid the need for memory allocation. 2041 byte[] b = TEMP_NUMBER_BUFFER.get(); 2042 if (b == null) 2043 { 2044 b = new byte[20]; 2045 TEMP_NUMBER_BUFFER.set(b); 2046 } 2047 2048 if (l == Long.MIN_VALUE) 2049 { 2050 b[0] = '-'; 2051 b[1] = '9'; 2052 b[2] = '2'; 2053 b[3] = '2'; 2054 b[4] = '3'; 2055 b[5] = '3'; 2056 b[6] = '7'; 2057 b[7] = '2'; 2058 b[8] = '0'; 2059 b[9] = '3'; 2060 b[10] = '6'; 2061 b[11] = '8'; 2062 b[12] = '5'; 2063 b[13] = '4'; 2064 b[14] = '7'; 2065 b[15] = '7'; 2066 b[16] = '5'; 2067 b[17] = '8'; 2068 b[18] = '0'; 2069 b[19] = '8'; 2070 return 20; 2071 } 2072 else if (l == 0L) 2073 { 2074 b[0] = '0'; 2075 return 1; 2076 } 2077 2078 int pos = 0; 2079 long v = l; 2080 if (l < 0) 2081 { 2082 b[0] = '-'; 2083 pos = 1; 2084 v = Math.abs(l); 2085 } 2086 2087 long divisor; 2088 if (v <= 9L) 2089 { 2090 divisor = 1L; 2091 } 2092 else if (v <= 99L) 2093 { 2094 divisor = 10L; 2095 } 2096 else if (v <= 999L) 2097 { 2098 divisor = 100L; 2099 } 2100 else if (v <= 9999L) 2101 { 2102 divisor = 1000L; 2103 } 2104 else if (v <= 99_999L) 2105 { 2106 divisor = 10_000L; 2107 } 2108 else if (v <= 999_999L) 2109 { 2110 divisor = 100_000L; 2111 } 2112 else if (v <= 9_999_999L) 2113 { 2114 divisor = 1_000_000L; 2115 } 2116 else if (v <= 99_999_999L) 2117 { 2118 divisor = 10_000_000L; 2119 } 2120 else if (v <= 999_999_999L) 2121 { 2122 divisor = 100_000_000L; 2123 } 2124 else if (v <= 9_999_999_999L) 2125 { 2126 divisor = 1_000_000_000L; 2127 } 2128 else if (v <= 99_999_999_999L) 2129 { 2130 divisor = 10_000_000_000L; 2131 } 2132 else if (v <= 999_999_999_999L) 2133 { 2134 divisor = 100_000_000_000L; 2135 } 2136 else if (v <= 9_999_999_999_999L) 2137 { 2138 divisor = 1_000_000_000_000L; 2139 } 2140 else if (v <= 99_999_999_999_999L) 2141 { 2142 divisor = 10_000_000_000_000L; 2143 } 2144 else if (v <= 999_999_999_999_999L) 2145 { 2146 divisor = 100_000_000_000_000L; 2147 } 2148 else if (v <= 9_999_999_999_999_999L) 2149 { 2150 divisor = 1_000_000_000_000_000L; 2151 } 2152 else if (v <= 99_999_999_999_999_999L) 2153 { 2154 divisor = 10_000_000_000_000_000L; 2155 } 2156 else if (v <= 999_999_999_999_999_999L) 2157 { 2158 divisor = 100_000_000_000_000_000L; 2159 } 2160 else 2161 { 2162 divisor = 1_000_000_000_000_000_000L; 2163 } 2164 2165 while (true) 2166 { 2167 final long digit = v / divisor; 2168 switch ((int) digit) 2169 { 2170 case 0: 2171 b[pos++] = '0'; 2172 break; 2173 case 1: 2174 b[pos++] = '1'; 2175 break; 2176 case 2: 2177 b[pos++] = '2'; 2178 break; 2179 case 3: 2180 b[pos++] = '3'; 2181 break; 2182 case 4: 2183 b[pos++] = '4'; 2184 break; 2185 case 5: 2186 b[pos++] = '5'; 2187 break; 2188 case 6: 2189 b[pos++] = '6'; 2190 break; 2191 case 7: 2192 b[pos++] = '7'; 2193 break; 2194 case 8: 2195 b[pos++] = '8'; 2196 break; 2197 case 9: 2198 b[pos++] = '9'; 2199 break; 2200 } 2201 2202 if (divisor == 1L) 2203 { 2204 break; 2205 } 2206 else 2207 { 2208 v -= (divisor * digit); 2209 if (v == 0) 2210 { 2211 while (divisor > 1L) 2212 { 2213 b[pos++] = '0'; 2214 divisor /= 10L; 2215 } 2216 2217 break; 2218 } 2219 2220 divisor /= 10L; 2221 } 2222 } 2223 2224 return pos; 2225 } 2226 2227 2228 2229 /** 2230 * Retrieves a hash code for this byte array. 2231 * 2232 * @return A hash code for this byte array. 2233 */ 2234 @Override() 2235 public int hashCode() 2236 { 2237 int hashCode = 0; 2238 2239 for (int i=0; i < endPos; i++) 2240 { 2241 hashCode += array[i]; 2242 } 2243 2244 return hashCode; 2245 } 2246 2247 2248 2249 /** 2250 * Indicates whether the provided object is a byte string buffer with contents 2251 * that are identical to that of this buffer. 2252 * 2253 * @param o The object for which to make the determination. 2254 * 2255 * @return {@code true} if the provided object is considered equal to this 2256 * buffer, or {@code false} if not. 2257 */ 2258 @Override() 2259 public boolean equals(@Nullable final Object o) 2260 { 2261 if (o == null) 2262 { 2263 return false; 2264 } 2265 2266 if (o == this) 2267 { 2268 return true; 2269 } 2270 2271 if (! (o instanceof ByteStringBuffer)) 2272 { 2273 return false; 2274 } 2275 2276 final ByteStringBuffer b = (ByteStringBuffer) o; 2277 if (endPos != b.endPos) 2278 { 2279 return false; 2280 } 2281 2282 for (int i=0; i < endPos; i++) 2283 { 2284 if (array[i] != b.array[i]) 2285 { 2286 return false; 2287 } 2288 } 2289 2290 return true; 2291 } 2292 2293 2294 2295 /** 2296 * Creates a duplicate of this byte string buffer. It will have identical 2297 * content but with a different backing array. Changes to this byte string 2298 * buffer will not impact the duplicate, and vice-versa. 2299 * 2300 * @return A duplicate of this byte string buffer. 2301 */ 2302 @NotNull() 2303 public ByteStringBuffer duplicate() 2304 { 2305 final ByteStringBuffer newBuffer = new ByteStringBuffer(endPos); 2306 return newBuffer.append(this); 2307 } 2308 2309 2310 2311 /** 2312 * Retrieves a string representation of the contents for this buffer. 2313 * 2314 * @return A string representation of the contents for this buffer. 2315 */ 2316 @Override() 2317 @NotNull() 2318 public String toString() 2319 { 2320 return StaticUtils.toUTF8String(array, 0, endPos); 2321 } 2322}