001/* 002 * Copyright 2008-2021 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2021 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-2021 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 * Inserts the provided boolean value to this buffer. 587 * 588 * @param pos The position at which the value is to be inserted. 589 * @param b The boolean value to be inserted into this buffer. 590 * 591 * @return A reference to this buffer. 592 * 593 * @throws IndexOutOfBoundsException If the specified position is negative 594 * or greater than the current length. 595 */ 596 @NotNull() 597 public ByteStringBuffer insert(final int pos, final boolean b) 598 throws IndexOutOfBoundsException 599 { 600 if (b) 601 { 602 return insert(pos, TRUE_VALUE_BYTES, 0, 4); 603 } 604 else 605 { 606 return insert(pos, FALSE_VALUE_BYTES, 0, 5); 607 } 608 } 609 610 611 612 /** 613 * Inserts the provided byte at the specified position in this buffer. 614 * 615 * @param pos The position at which the byte is to be inserted. 616 * @param b The byte 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 byte b) 625 throws IndexOutOfBoundsException 626 { 627 if ((pos < 0) || (pos > endPos)) 628 { 629 final String message; 630 if (pos < 0) 631 { 632 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 633 } 634 else 635 { 636 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 637 } 638 639 final IndexOutOfBoundsException e = 640 new IndexOutOfBoundsException(message); 641 Debug.debugCodingError(e); 642 throw e; 643 } 644 else if (pos == endPos) 645 { 646 return append(b); 647 } 648 649 ensureCapacity(endPos + 1); 650 System.arraycopy(array, pos, array, pos+1, (endPos-pos)); 651 array[pos] = b; 652 endPos++; 653 return this; 654 } 655 656 657 658 /** 659 * Inserts the contents of the provided byte array at the specified position 660 * in this buffer. 661 * 662 * @param pos The position at which the data is to be inserted. 663 * @param b The array whose contents should be inserted into this buffer. 664 * 665 * @return A reference to this buffer. 666 * 667 * @throws NullPointerException If the provided array is {@code null}. 668 * 669 * @throws IndexOutOfBoundsException If the specified position is negative 670 * or greater than the current length. 671 */ 672 @NotNull() 673 public ByteStringBuffer insert(final int pos, @NotNull final byte[] b) 674 throws NullPointerException, IndexOutOfBoundsException 675 { 676 if (b == null) 677 { 678 final NullPointerException e = 679 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 680 Debug.debugCodingError(e); 681 throw e; 682 } 683 684 return insert(pos, b, 0, b.length); 685 } 686 687 688 689 /** 690 * Inserts a portion of the data in the provided array at the specified 691 * position in this buffer. 692 * 693 * Appends the specified portion of the provided byte array to this buffer. 694 * 695 * @param pos The position at which the data is to be inserted. 696 * @param b The array whose contents should be inserted into this buffer. 697 * @param off The offset within the array at which to begin copying data. 698 * @param len The number of bytes to copy. 699 * 700 * @return A reference to this buffer. 701 * 702 * @throws NullPointerException If the provided array is {@code null}. 703 * 704 * @throws IndexOutOfBoundsException If the specified position is negative 705 * or greater than the current length, if 706 * the offset or length are negative, if 707 * the offset plus the length is beyond 708 * the end of the provided array. 709 */ 710 @NotNull() 711 public ByteStringBuffer insert(final int pos, @NotNull final byte[] b, 712 final int off, final int len) 713 throws NullPointerException, IndexOutOfBoundsException 714 { 715 if (b == null) 716 { 717 final NullPointerException e = 718 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 719 Debug.debugCodingError(e); 720 throw e; 721 } 722 723 if ((pos < 0) || (pos > endPos) || (off < 0) || (len < 0) || 724 (off+len > b.length)) 725 { 726 final String message; 727 if (pos < 0) 728 { 729 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 730 } 731 else if (pos > endPos) 732 { 733 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 734 } 735 else if (off < 0) 736 { 737 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 738 } 739 else if (len < 0) 740 { 741 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 742 } 743 else 744 { 745 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 746 b.length); 747 } 748 749 final IndexOutOfBoundsException e = 750 new IndexOutOfBoundsException(message); 751 Debug.debugCodingError(e); 752 throw e; 753 } 754 else if (len == 0) 755 { 756 return this; 757 } 758 else if (pos == endPos) 759 { 760 return append(b, off, len); 761 } 762 763 ensureCapacity(endPos + len); 764 System.arraycopy(array, pos, array, pos+len, (endPos-pos)); 765 System.arraycopy(b, off, array, pos, len); 766 endPos += len; 767 return this; 768 } 769 770 771 772 /** 773 * Inserts the provided byte string into this buffer at the specified 774 * position. 775 * 776 * @param pos The position at which the data is to be inserted. 777 * @param b The byte string to insert into this buffer. 778 * 779 * @return A reference to this buffer. 780 * 781 * @throws NullPointerException If the provided byte string is {@code null}. 782 * 783 * @throws IndexOutOfBoundsException If the specified position is negative 784 * or greater than the current length. 785 */ 786 @NotNull() 787 public ByteStringBuffer insert(final int pos, @NotNull final ByteString b) 788 throws NullPointerException, IndexOutOfBoundsException 789 { 790 if (b == null) 791 { 792 final NullPointerException e = 793 new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); 794 Debug.debugCodingError(e); 795 throw e; 796 } 797 798 return insert(pos, b.getValue()); 799 } 800 801 802 803 /** 804 * Inserts the provided byte string buffer into this buffer at the specified 805 * position. 806 * 807 * @param pos The position at which the data is to be inserted. 808 * @param buffer The buffer whose contents should be inserted into this 809 * buffer. 810 * 811 * @return A reference to this buffer. 812 * 813 * @throws NullPointerException If the provided buffer is {@code null}. 814 * 815 * @throws IndexOutOfBoundsException If the specified position is negative 816 * or greater than the current length. 817 */ 818 @NotNull() 819 public ByteStringBuffer insert(final int pos, 820 @NotNull final ByteStringBuffer buffer) 821 throws NullPointerException, IndexOutOfBoundsException 822 { 823 if (buffer == null) 824 { 825 final NullPointerException e = 826 new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); 827 Debug.debugCodingError(e); 828 throw e; 829 } 830 831 return insert(pos, buffer.array, 0, buffer.endPos); 832 } 833 834 835 836 /** 837 * Inserts the provided character into this buffer at the provided position. 838 * 839 * @param pos The position at which the character is to be inserted. 840 * @param c The character to be inserted into this buffer. 841 * 842 * @return A reference to this buffer. 843 * 844 * @throws IndexOutOfBoundsException If the specified position is negative 845 * or greater than the current length. 846 */ 847 @NotNull() 848 public ByteStringBuffer insert(final int pos, final char c) 849 throws IndexOutOfBoundsException 850 { 851 if ((pos < 0) || (pos > endPos)) 852 { 853 final String message; 854 if (pos < 0) 855 { 856 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 857 } 858 else 859 { 860 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 861 } 862 863 final IndexOutOfBoundsException e = 864 new IndexOutOfBoundsException(message); 865 Debug.debugCodingError(e); 866 throw e; 867 } 868 else if (pos == endPos) 869 { 870 return append(c); 871 } 872 873 final byte b = (byte) (c & 0x7F); 874 if (b == c) 875 { 876 ensureCapacity(endPos + 1); 877 System.arraycopy(array, pos, array, pos+1, (endPos-pos)); 878 array[pos] = b; 879 endPos++; 880 } 881 else 882 { 883 insert(pos, String.valueOf(c)); 884 } 885 886 return this; 887 } 888 889 890 891 /** 892 * Inserts the contents of the provided character array into this buffer at 893 * the specified position. 894 * 895 * @param pos The position at which the data is to be inserted. 896 * @param c The array whose contents should be inserted into this buffer. 897 * 898 * @return A reference to this buffer. 899 * 900 * @throws NullPointerException If the provided array is {@code null}. 901 * 902 * @throws IndexOutOfBoundsException If the specified position is negative 903 * or greater than the current length. 904 */ 905 @NotNull() 906 public ByteStringBuffer insert(final int pos, @NotNull final char[] c) 907 throws NullPointerException, IndexOutOfBoundsException 908 { 909 if (c == null) 910 { 911 final NullPointerException e = 912 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 913 Debug.debugCodingError(e); 914 throw e; 915 } 916 917 return insert(pos, new String(c, 0, c.length)); 918 } 919 920 921 922 /** 923 * Inserts the specified portion of the provided character array to this 924 * buffer at the specified position. 925 * 926 * @param pos The position at which the data is to be inserted. 927 * @param c The array whose contents should be inserted into this buffer. 928 * @param off The offset within the array at which to begin copying data. 929 * @param len The number of characters to copy. 930 * 931 * @return A reference to this buffer. 932 * 933 * @throws NullPointerException If the provided array is {@code null}. 934 * 935 * @throws IndexOutOfBoundsException If the specified position is negative 936 * or greater than the current length, if 937 * the offset or length are negative, if 938 * the offset plus the length is beyond 939 * the end of the provided array. 940 */ 941 @NotNull() 942 public ByteStringBuffer insert(final int pos, @NotNull final char[] c, 943 final int off, final int len) 944 throws NullPointerException, IndexOutOfBoundsException 945 { 946 if (c == null) 947 { 948 final NullPointerException e = 949 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 950 Debug.debugCodingError(e); 951 throw e; 952 } 953 954 return insert(pos, new String(c, off, len)); 955 } 956 957 958 959 /** 960 * Inserts the provided character sequence to this buffer at the specified 961 * position. 962 * 963 * @param pos The position at which the data is to be inserted. 964 * @param s The character sequence to insert into this buffer. 965 * 966 * @return A reference to this buffer. 967 * 968 * @throws NullPointerException If the provided character sequence is 969 * {@code null}. 970 * 971 * @throws IndexOutOfBoundsException If the specified position is negative 972 * or greater than the current length. 973 */ 974 @NotNull() 975 public ByteStringBuffer insert(final int pos, @NotNull final CharSequence s) 976 throws NullPointerException, IndexOutOfBoundsException 977 { 978 if (s == null) 979 { 980 final NullPointerException e = 981 new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); 982 Debug.debugCodingError(e); 983 throw e; 984 } 985 986 if ((pos < 0) || (pos > endPos)) 987 { 988 final String message; 989 if (pos < 0) 990 { 991 message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos); 992 } 993 else 994 { 995 message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos); 996 } 997 998 final IndexOutOfBoundsException e = 999 new IndexOutOfBoundsException(message); 1000 Debug.debugCodingError(e); 1001 throw e; 1002 } 1003 else if (pos == endPos) 1004 { 1005 return append(s); 1006 } 1007 else 1008 { 1009 return insert(pos, StaticUtils.getBytes(s.toString())); 1010 } 1011 } 1012 1013 1014 1015 /** 1016 * Inserts the provided integer value to this buffer. 1017 * 1018 * @param pos The position at which the value is to be inserted. 1019 * @param i The integer value to be inserted into this buffer. 1020 * 1021 * @return A reference to this buffer. 1022 * 1023 * @throws IndexOutOfBoundsException If the specified position is negative 1024 * or greater than the current length. 1025 */ 1026 @NotNull() 1027 public ByteStringBuffer insert(final int pos, final int i) 1028 throws IndexOutOfBoundsException 1029 { 1030 final int length = getBytes(i); 1031 return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); 1032 } 1033 1034 1035 1036 /** 1037 * Inserts the provided long value to this buffer. 1038 * 1039 * @param pos The position at which the value is to be inserted. 1040 * @param l The long value to be inserted into this buffer. 1041 * 1042 * @return A reference to this buffer. 1043 * 1044 * @throws IndexOutOfBoundsException If the specified position is negative 1045 * or greater than the current length. 1046 */ 1047 @NotNull() 1048 public ByteStringBuffer insert(final int pos, final long l) 1049 throws IndexOutOfBoundsException 1050 { 1051 final int length = getBytes(l); 1052 return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length); 1053 } 1054 1055 1056 1057 /** 1058 * Deletes the specified number of bytes from the beginning of the buffer. 1059 * 1060 * @param len The number of bytes to delete. 1061 * 1062 * @return A reference to this buffer. 1063 * 1064 * @throws IndexOutOfBoundsException If the specified length is negative, 1065 * or if it is greater than the number of 1066 * bytes currently contained in this 1067 * buffer. 1068 */ 1069 @NotNull() 1070 public ByteStringBuffer delete(final int len) 1071 throws IndexOutOfBoundsException 1072 { 1073 return delete(0, len); 1074 } 1075 1076 1077 1078 /** 1079 * Deletes the indicated number of bytes from the specified location in the 1080 * buffer. 1081 * 1082 * @param off The position in the buffer at which the content to delete 1083 * begins. 1084 * @param len The number of bytes to remove from the buffer. 1085 * 1086 * @return A reference to this buffer. 1087 * 1088 * @throws IndexOutOfBoundsException If the offset or length is negative, or 1089 * if the combination of the offset and 1090 * length is greater than the end of the 1091 * content in the buffer. 1092 */ 1093 @NotNull() 1094 public ByteStringBuffer delete(final int off, final int len) 1095 throws IndexOutOfBoundsException 1096 { 1097 if (off < 0) 1098 { 1099 throw new IndexOutOfBoundsException( 1100 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off)); 1101 } 1102 else if (len < 0) 1103 { 1104 throw new IndexOutOfBoundsException( 1105 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len)); 1106 } 1107 else if ((off + len) > endPos) 1108 { 1109 throw new IndexOutOfBoundsException( 1110 ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, endPos)); 1111 } 1112 else if (len == 0) 1113 { 1114 return this; 1115 } 1116 else if (off == 0) 1117 { 1118 if (len == endPos) 1119 { 1120 endPos = 0; 1121 return this; 1122 } 1123 else 1124 { 1125 final int newEndPos = endPos - len; 1126 System.arraycopy(array, len, array, 0, newEndPos); 1127 endPos = newEndPos; 1128 return this; 1129 } 1130 } 1131 else 1132 { 1133 if ((off + len) == endPos) 1134 { 1135 endPos = off; 1136 return this; 1137 } 1138 else 1139 { 1140 final int bytesToCopy = endPos - (off+len); 1141 System.arraycopy(array, (off+len), array, off, bytesToCopy); 1142 endPos -= len; 1143 return this; 1144 } 1145 } 1146 } 1147 1148 1149 1150 /** 1151 * Sets the contents of this buffer to include only the provided boolean 1152 * value. 1153 * 1154 * @param b The boolean value to use as the content for this buffer. 1155 * 1156 * @return A reference to this buffer. 1157 */ 1158 @NotNull() 1159 public ByteStringBuffer set(final boolean b) 1160 { 1161 if (b) 1162 { 1163 return set(TRUE_VALUE_BYTES, 0, 4); 1164 } 1165 else 1166 { 1167 return set(FALSE_VALUE_BYTES, 0, 5); 1168 } 1169 } 1170 1171 1172 1173 /** 1174 * Sets the contents of this buffer to include only the provided byte. 1175 * 1176 * @param b The byte to use as the content for this buffer. 1177 * 1178 * @return A reference to this buffer. 1179 */ 1180 @NotNull() 1181 public ByteStringBuffer set(final byte b) 1182 { 1183 endPos = 0; 1184 return append(b); 1185 } 1186 1187 1188 1189 /** 1190 * Sets the contents of this buffer to the contents of the provided byte 1191 * array. 1192 * 1193 * @param b The byte array containing the content to use for this buffer. 1194 * 1195 * @throws NullPointerException If the provided array is {@code null}. 1196 * 1197 * @return A reference to this buffer. 1198 */ 1199 @NotNull() 1200 public ByteStringBuffer set(@NotNull final byte[] b) 1201 throws NullPointerException 1202 { 1203 if (b == null) 1204 { 1205 final NullPointerException e = 1206 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1207 Debug.debugCodingError(e); 1208 throw e; 1209 } 1210 1211 endPos = 0; 1212 return append(b, 0, b.length); 1213 } 1214 1215 1216 1217 /** 1218 * Sets the contents of this buffer to the specified portion of the provided 1219 * byte array. 1220 * 1221 * @param b The byte array containing the content to use for this buffer. 1222 * @param off The offset within the array at which to begin copying data. 1223 * @param len The number of bytes to copy. 1224 * 1225 * @return A reference to this buffer. 1226 * 1227 * @throws NullPointerException If the provided array is {@code null}. 1228 * 1229 * @throws IndexOutOfBoundsException If the offset or length are negative, 1230 * if the offset plus the length is beyond 1231 * the end of the provided array. 1232 */ 1233 @NotNull() 1234 public ByteStringBuffer set(@NotNull final byte[] b, final int off, 1235 final int len) 1236 throws NullPointerException, IndexOutOfBoundsException 1237 { 1238 if (b == null) 1239 { 1240 final NullPointerException e = 1241 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1242 Debug.debugCodingError(e); 1243 throw e; 1244 } 1245 1246 if ((off < 0) || (len < 0) || (off+len > b.length)) 1247 { 1248 final String message; 1249 if (off < 0) 1250 { 1251 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 1252 } 1253 else if (len < 0) 1254 { 1255 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 1256 } 1257 else 1258 { 1259 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 1260 b.length); 1261 } 1262 1263 final IndexOutOfBoundsException e = 1264 new IndexOutOfBoundsException(message); 1265 Debug.debugCodingError(e); 1266 throw e; 1267 } 1268 1269 endPos = 0; 1270 return append(b, off, len); 1271 } 1272 1273 1274 1275 /** 1276 * Sets the contents of this buffer to the contents of the provided byte 1277 * string. 1278 * 1279 * @param b The byte string that should be used as the content for this 1280 * buffer. 1281 * 1282 * @throws NullPointerException If the provided byte string is {@code null}. 1283 * 1284 * @return A reference to this buffer. 1285 */ 1286 @NotNull() 1287 public ByteStringBuffer set(@NotNull final ByteString b) 1288 throws NullPointerException 1289 { 1290 if (b == null) 1291 { 1292 final NullPointerException e = 1293 new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get()); 1294 Debug.debugCodingError(e); 1295 throw e; 1296 } 1297 1298 endPos = 0; 1299 b.appendValueTo(this); 1300 return this; 1301 } 1302 1303 1304 1305 /** 1306 * Sets the contents of this buffer to the contents of the provided byte 1307 * string buffer. 1308 * 1309 * @param buffer The buffer whose contents should be used as the content for 1310 * this buffer. 1311 * 1312 * @throws NullPointerException If the provided buffer is {@code null}. 1313 * 1314 * @return A reference to this buffer. 1315 */ 1316 @NotNull() 1317 public ByteStringBuffer set(@NotNull final ByteStringBuffer buffer) 1318 throws NullPointerException 1319 { 1320 if (buffer == null) 1321 { 1322 final NullPointerException e = 1323 new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get()); 1324 Debug.debugCodingError(e); 1325 throw e; 1326 } 1327 1328 endPos = 0; 1329 return append(buffer.array, 0, buffer.endPos); 1330 } 1331 1332 1333 1334 /** 1335 * Sets the contents of this buffer to include only the provided character. 1336 * 1337 * @param c The character use as the content for this buffer. 1338 * 1339 * @return A reference to this buffer. 1340 */ 1341 @NotNull() 1342 public ByteStringBuffer set(final char c) 1343 { 1344 endPos = 0; 1345 return append(c); 1346 } 1347 1348 1349 1350 /** 1351 * Sets the contents of this buffer to the contents of the provided character 1352 * array. 1353 * 1354 * @param c The character array containing the content to use for this 1355 * buffer. 1356 * 1357 * @throws NullPointerException If the provided array is {@code null}. 1358 * 1359 * @return A reference to this buffer. 1360 */ 1361 @NotNull() 1362 public ByteStringBuffer set(@NotNull final char[] c) 1363 throws NullPointerException 1364 { 1365 if (c == null) 1366 { 1367 final NullPointerException e = 1368 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1369 Debug.debugCodingError(e); 1370 throw e; 1371 } 1372 1373 endPos = 0; 1374 return append(c, 0, c.length); 1375 } 1376 1377 1378 1379 /** 1380 * Sets the contents of this buffer to the specified portion of the provided 1381 * character array. 1382 * 1383 * @param c The character array containing the content to use for this 1384 * buffer. 1385 * @param off The offset within the array at which to begin copying data. 1386 * @param len The number of characters to copy. 1387 * 1388 * @return A reference to this buffer. 1389 * 1390 * @throws NullPointerException If the provided array is {@code null}. 1391 * 1392 * @throws IndexOutOfBoundsException If the offset or length are negative, 1393 * if the offset plus the length is beyond 1394 * the end of the provided array. 1395 */ 1396 @NotNull() 1397 public ByteStringBuffer set(@NotNull final char[] c, final int off, 1398 final int len) 1399 throws NullPointerException, IndexOutOfBoundsException 1400 { 1401 if (c == null) 1402 { 1403 final NullPointerException e = 1404 new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get()); 1405 Debug.debugCodingError(e); 1406 throw e; 1407 } 1408 1409 if ((off < 0) || (len < 0) || (off+len > c.length)) 1410 { 1411 final String message; 1412 if (off < 0) 1413 { 1414 message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off); 1415 } 1416 else if (len < 0) 1417 { 1418 message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len); 1419 } 1420 else 1421 { 1422 message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, 1423 c.length); 1424 } 1425 1426 final IndexOutOfBoundsException e = 1427 new IndexOutOfBoundsException(message); 1428 Debug.debugCodingError(e); 1429 throw e; 1430 } 1431 1432 endPos = 0; 1433 return append(c, off, len); 1434 } 1435 1436 1437 1438 /** 1439 * Sets the contents of this buffer to the specified portion of the provided 1440 * character sequence. 1441 * 1442 * @param s The character sequence to use as the content for this buffer. 1443 * 1444 * @throws NullPointerException If the provided character sequence is 1445 * {@code null}. 1446 * 1447 * @return A reference to this buffer. 1448 */ 1449 @NotNull() 1450 public ByteStringBuffer set(@NotNull final CharSequence s) 1451 throws NullPointerException 1452 { 1453 if (s == null) 1454 { 1455 final NullPointerException e = 1456 new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get()); 1457 Debug.debugCodingError(e); 1458 throw e; 1459 } 1460 1461 endPos = 0; 1462 return append(s); 1463 } 1464 1465 1466 1467 /** 1468 * Sets the contents of this buffer to include only the provided integer 1469 * value. 1470 * 1471 * @param i The integer value to use as the content for this buffer. 1472 * 1473 * @return A reference to this buffer. 1474 */ 1475 @NotNull() 1476 public ByteStringBuffer set(final int i) 1477 { 1478 final int length = getBytes(i); 1479 return set(TEMP_NUMBER_BUFFER.get(), 0, length); 1480 } 1481 1482 1483 1484 /** 1485 * Sets the contents of this buffer to include only the provided long value. 1486 * 1487 * @param l The long value to use as the content for this buffer. 1488 * 1489 * @return A reference to this buffer. 1490 */ 1491 @NotNull() 1492 public ByteStringBuffer set(final long l) 1493 { 1494 final int length = getBytes(l); 1495 return set(TEMP_NUMBER_BUFFER.get(), 0, length); 1496 } 1497 1498 1499 1500 /** 1501 * Clears the contents of this buffer. 1502 * 1503 * @return A reference to this buffer. 1504 */ 1505 @NotNull() 1506 public ByteStringBuffer clear() 1507 { 1508 endPos = 0; 1509 return this; 1510 } 1511 1512 1513 1514 /** 1515 * Clears the contents of this buffer. 1516 * 1517 * @param zero Indicates whether to overwrite the content of the backing 1518 * array with all zeros in order to wipe out any sensitive data 1519 * it may contain. 1520 * 1521 * @return A reference to this buffer. 1522 */ 1523 @NotNull() 1524 public ByteStringBuffer clear(final boolean zero) 1525 { 1526 endPos = 0; 1527 1528 if (zero) 1529 { 1530 Arrays.fill(array, (byte) 0x00); 1531 } 1532 1533 return this; 1534 } 1535 1536 1537 1538 /** 1539 * Retrieves the current backing array for this buffer. The data will begin 1540 * at position 0 and will contain {@link ByteStringBuffer#length} bytes. 1541 * 1542 * @return The current backing array for this buffer. 1543 */ 1544 @NotNull() 1545 public byte[] getBackingArray() 1546 { 1547 return array; 1548 } 1549 1550 1551 1552 /** 1553 * Indicates whether this buffer is currently empty. 1554 * 1555 * @return {@code true} if this buffer is currently empty, or {@code false} 1556 * if not. 1557 */ 1558 public boolean isEmpty() 1559 { 1560 return (endPos == 0); 1561 } 1562 1563 1564 1565 /** 1566 * Retrieves the number of bytes contained in this buffer. 1567 * 1568 * @return The number of bytes contained in this buffer. 1569 */ 1570 public int length() 1571 { 1572 return endPos; 1573 } 1574 1575 1576 1577 /** 1578 * Sets the length of this buffer to the specified value. If the new length 1579 * is greater than the current length, the value will be padded with zeroes. 1580 * 1581 * @param length The new length to use for the buffer. It must be greater 1582 * than or equal to zero. 1583 * 1584 * @throws IndexOutOfBoundsException If the provided length is negative. 1585 */ 1586 public void setLength(final int length) 1587 throws IndexOutOfBoundsException 1588 { 1589 if (length < 0) 1590 { 1591 final IndexOutOfBoundsException e = new IndexOutOfBoundsException( 1592 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length)); 1593 Debug.debugCodingError(e); 1594 throw e; 1595 } 1596 1597 if (length > endPos) 1598 { 1599 ensureCapacity(length); 1600 Arrays.fill(array, endPos, length, (byte) 0x00); 1601 endPos = length; 1602 } 1603 else 1604 { 1605 endPos = length; 1606 } 1607 } 1608 1609 1610 1611 /** 1612 * Returns the current capacity for this buffer. 1613 * 1614 * @return The current capacity for this buffer. 1615 */ 1616 public int capacity() 1617 { 1618 return capacity; 1619 } 1620 1621 1622 1623 /** 1624 * Ensures that the total capacity of this buffer is at least equal to the 1625 * specified size. 1626 * 1627 * @param minimumCapacity The minimum capacity for this buffer. 1628 */ 1629 public void ensureCapacity(final int minimumCapacity) 1630 { 1631 if (capacity < minimumCapacity) 1632 { 1633 final int newCapacity = Math.max(minimumCapacity, (2 * capacity) + 2); 1634 final byte[] newArray = new byte[newCapacity]; 1635 System.arraycopy(array, 0, newArray, 0, capacity); 1636 array = newArray; 1637 capacity = newCapacity; 1638 } 1639 } 1640 1641 1642 1643 /** 1644 * Sets the capacity equal to the specified value. If the provided capacity 1645 * is less than the current length, then the length will be reduced to the 1646 * new capacity. 1647 * 1648 * @param capacity The new capacity for this buffer. It must be greater 1649 * than or equal to zero. 1650 * 1651 * @throws IndexOutOfBoundsException If the provided capacity is negative. 1652 */ 1653 public void setCapacity(final int capacity) 1654 throws IndexOutOfBoundsException 1655 { 1656 if (capacity < 0) 1657 { 1658 final IndexOutOfBoundsException e = new IndexOutOfBoundsException( 1659 ERR_BS_BUFFER_CAPACITY_NEGATIVE.get(capacity)); 1660 Debug.debugCodingError(e); 1661 throw e; 1662 } 1663 1664 if (this.capacity == capacity) 1665 { 1666 return; 1667 } 1668 else if (this.capacity < capacity) 1669 { 1670 final byte[] newArray = new byte[capacity]; 1671 System.arraycopy(array, 0, newArray, 0, this.capacity); 1672 array = newArray; 1673 this.capacity = capacity; 1674 } 1675 else 1676 { 1677 final byte[] newArray = new byte[capacity]; 1678 System.arraycopy(array, 0, newArray, 0, capacity); 1679 array = newArray; 1680 endPos = Math.min(endPos, capacity); 1681 this.capacity = capacity; 1682 } 1683 } 1684 1685 1686 1687 /** 1688 * Trims the backing array to the minimal size required for this buffer. 1689 * 1690 * @return A reference to this buffer. 1691 */ 1692 @NotNull() 1693 public ByteStringBuffer trimToSize() 1694 { 1695 if (endPos != capacity) 1696 { 1697 final byte[] newArray = new byte[endPos]; 1698 System.arraycopy(array, 0, newArray, 0, endPos); 1699 array = newArray; 1700 capacity = endPos; 1701 } 1702 1703 return this; 1704 } 1705 1706 1707 1708 /** 1709 * Retrieves the byte at the specified offset in the buffer. 1710 * 1711 * @param offset The offset of the byte to read. It must be between greater 1712 * than or equal to zero and less than {@link #length}. 1713 * 1714 * @return The byte at the specified offset in the buffer. 1715 * 1716 * @throws IndexOutOfBoundsException If the provided offset is negative or 1717 * greater than or equal to the length of 1718 * the buffer. 1719 */ 1720 public byte byteAt(final int offset) 1721 throws IndexOutOfBoundsException 1722 { 1723 if (offset < 0) 1724 { 1725 throw new IndexOutOfBoundsException( 1726 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(offset)); 1727 } 1728 else if (offset >= endPos) 1729 { 1730 throw new IndexOutOfBoundsException( 1731 ERR_BS_BUFFER_OFFSET_TOO_LARGE.get(offset, endPos)); 1732 } 1733 else 1734 { 1735 return array[offset]; 1736 } 1737 } 1738 1739 1740 1741 /** 1742 * Retrieves the specified subset of bytes from the buffer. 1743 * 1744 * @param offset The offset of the first byte to retrieve. It must be 1745 * greater than or equal to zero and less than 1746 * {@link #length}. 1747 * @param length The number of bytes to retrieve. It must be greater than 1748 * or equal to zero, and the sum of {@code offset} and 1749 * {@code length} must be less than or equal to 1750 * {@link #length}. 1751 * 1752 * @return A byte array containing the specified subset of bytes from the 1753 * buffer. 1754 * 1755 * @throws IndexOutOfBoundsException If either the offset or the length is 1756 * negative, or if the offsset plus the 1757 * length is greater than or equal to the 1758 * length of the buffer. 1759 */ 1760 @NotNull() 1761 public byte[] bytesAt(final int offset, final int length) 1762 throws IndexOutOfBoundsException 1763 { 1764 if (offset < 0) 1765 { 1766 throw new IndexOutOfBoundsException( 1767 ERR_BS_BUFFER_OFFSET_NEGATIVE.get(offset)); 1768 } 1769 1770 if (length < 0) 1771 { 1772 throw new IndexOutOfBoundsException( 1773 ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length)); 1774 } 1775 1776 if ((offset + length) > endPos) 1777 { 1778 throw new IndexOutOfBoundsException( 1779 ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(offset, length, 1780 endPos)); 1781 } 1782 1783 final byte[] returnArray = new byte[length]; 1784 System.arraycopy(array, offset, returnArray, 0, length); 1785 return returnArray; 1786 } 1787 1788 1789 1790 /** 1791 * Indicates whether this buffer starts with the specified set of bytes. 1792 * 1793 * @param bytes The bytes for which to make the determination. 1794 * 1795 * @return {@code true} if this buffer starts with the specified set of 1796 * bytes, or {@code false} if not. 1797 */ 1798 public boolean startsWith(@NotNull final byte[] bytes) 1799 { 1800 if (bytes.length > endPos) 1801 { 1802 return false; 1803 } 1804 1805 for (int i=0; i < bytes.length; i++) 1806 { 1807 if (array[i] != bytes[i]) 1808 { 1809 return false; 1810 } 1811 } 1812 1813 return true; 1814 } 1815 1816 1817 1818 /** 1819 * Indicates whether this buffer ends with the specified set of bytes. 1820 * 1821 * @param bytes The bytes for which to make the determination. 1822 * 1823 * @return {@code true} if this buffer ends with the specified set of bytes, 1824 * or {@code false} if not. 1825 */ 1826 public boolean endsWith(@NotNull final byte[] bytes) 1827 { 1828 if (bytes.length > endPos) 1829 { 1830 return false; 1831 } 1832 1833 for (int i=0; i < bytes.length; i++) 1834 { 1835 if (array[endPos - bytes.length + i] != bytes[i]) 1836 { 1837 return false; 1838 } 1839 } 1840 1841 return true; 1842 } 1843 1844 1845 1846 /** 1847 * Returns a new byte array with the content from this buffer. 1848 * 1849 * @return A byte array containing the content from this buffer. 1850 */ 1851 @NotNull() 1852 public byte[] toByteArray() 1853 { 1854 final byte[] newArray = new byte[endPos]; 1855 System.arraycopy(array, 0, newArray, 0, endPos); 1856 return newArray; 1857 } 1858 1859 1860 1861 /** 1862 * Returns a new byte string with the content from this buffer. 1863 * 1864 * @return A byte string with the content from this buffer. 1865 */ 1866 @NotNull() 1867 public ByteString toByteString() 1868 { 1869 return new ASN1OctetString(toByteArray()); 1870 } 1871 1872 1873 1874 /** 1875 * Creates an input stream that may be used to read content from this buffer. 1876 * This buffer should not be altered while the input stream is being used. 1877 * 1878 * @return An input stream that may be used to read content from this buffer. 1879 */ 1880 @NotNull() 1881 public InputStream asInputStream() 1882 { 1883 return new ByteArrayInputStream(array, 0, endPos); 1884 } 1885 1886 1887 1888 /** 1889 * Reads the contents of the specified file into this buffer, appending it to 1890 * the end of the buffer. 1891 * 1892 * @param file The file to be read. 1893 * 1894 * @throws IOException If an unexpected problem occurs. 1895 */ 1896 public void readFrom(@NotNull final File file) 1897 throws IOException 1898 { 1899 try (FileInputStream inputStream = new FileInputStream(file)) 1900 { 1901 readFrom(inputStream); 1902 } 1903 } 1904 1905 1906 1907 /** 1908 * Reads data from the provided input stream into this buffer, appending it to 1909 * the end of the buffer. The entire content of the input stream will be 1910 * read, but the input stream will not be closed. 1911 * 1912 * @param inputStream The input stream from which data is to be read. 1913 * 1914 * @throws IOException If an unexpected problem occurs. 1915 */ 1916 public void readFrom(@NotNull final InputStream inputStream) 1917 throws IOException 1918 { 1919 final int initialEndPos = endPos; 1920 1921 try 1922 { 1923 while (true) 1924 { 1925 int remainingCapacity = capacity - endPos; 1926 if (remainingCapacity <= 100) 1927 { 1928 ensureCapacity(Math.max(100, (2*capacity))); 1929 remainingCapacity = capacity - endPos; 1930 } 1931 1932 final int bytesRead = 1933 inputStream.read(array, endPos, remainingCapacity); 1934 if (bytesRead < 0) 1935 { 1936 return; 1937 } 1938 1939 endPos += bytesRead; 1940 } 1941 } 1942 catch (final IOException e) 1943 { 1944 Debug.debugException(e); 1945 endPos = initialEndPos; 1946 throw e; 1947 } 1948 } 1949 1950 1951 1952 /** 1953 * Writes the contents of this byte string buffer to the provided output 1954 * stream. 1955 * 1956 * @param outputStream The output stream to which the data should be 1957 * written. 1958 * 1959 * @throws IOException If a problem occurs while writing to the provided 1960 * output stream. 1961 */ 1962 public void write(@NotNull final OutputStream outputStream) 1963 throws IOException 1964 { 1965 outputStream.write(array, 0, endPos); 1966 } 1967 1968 1969 1970 /** 1971 * Adds the bytes comprising the string representation of the provided long 1972 * value to the temporary number buffer. 1973 * 1974 * @param l The long value to be appended. 1975 * 1976 * @return The number of bytes in the string representation of the value. 1977 */ 1978 private static int getBytes(final long l) 1979 { 1980 // NOTE: This method is probably not as efficient as it could be, but it is 1981 // more important to avoid the need for memory allocation. 1982 byte[] b = TEMP_NUMBER_BUFFER.get(); 1983 if (b == null) 1984 { 1985 b = new byte[20]; 1986 TEMP_NUMBER_BUFFER.set(b); 1987 } 1988 1989 if (l == Long.MIN_VALUE) 1990 { 1991 b[0] = '-'; 1992 b[1] = '9'; 1993 b[2] = '2'; 1994 b[3] = '2'; 1995 b[4] = '3'; 1996 b[5] = '3'; 1997 b[6] = '7'; 1998 b[7] = '2'; 1999 b[8] = '0'; 2000 b[9] = '3'; 2001 b[10] = '6'; 2002 b[11] = '8'; 2003 b[12] = '5'; 2004 b[13] = '4'; 2005 b[14] = '7'; 2006 b[15] = '7'; 2007 b[16] = '5'; 2008 b[17] = '8'; 2009 b[18] = '0'; 2010 b[19] = '8'; 2011 return 20; 2012 } 2013 else if (l == 0L) 2014 { 2015 b[0] = '0'; 2016 return 1; 2017 } 2018 2019 int pos = 0; 2020 long v = l; 2021 if (l < 0) 2022 { 2023 b[0] = '-'; 2024 pos = 1; 2025 v = Math.abs(l); 2026 } 2027 2028 long divisor; 2029 if (v <= 9L) 2030 { 2031 divisor = 1L; 2032 } 2033 else if (v <= 99L) 2034 { 2035 divisor = 10L; 2036 } 2037 else if (v <= 999L) 2038 { 2039 divisor = 100L; 2040 } 2041 else if (v <= 9999L) 2042 { 2043 divisor = 1000L; 2044 } 2045 else if (v <= 99_999L) 2046 { 2047 divisor = 10_000L; 2048 } 2049 else if (v <= 999_999L) 2050 { 2051 divisor = 100_000L; 2052 } 2053 else if (v <= 9_999_999L) 2054 { 2055 divisor = 1_000_000L; 2056 } 2057 else if (v <= 99_999_999L) 2058 { 2059 divisor = 10_000_000L; 2060 } 2061 else if (v <= 999_999_999L) 2062 { 2063 divisor = 100_000_000L; 2064 } 2065 else if (v <= 9_999_999_999L) 2066 { 2067 divisor = 1_000_000_000L; 2068 } 2069 else if (v <= 99_999_999_999L) 2070 { 2071 divisor = 10_000_000_000L; 2072 } 2073 else if (v <= 999_999_999_999L) 2074 { 2075 divisor = 100_000_000_000L; 2076 } 2077 else if (v <= 9_999_999_999_999L) 2078 { 2079 divisor = 1_000_000_000_000L; 2080 } 2081 else if (v <= 99_999_999_999_999L) 2082 { 2083 divisor = 10_000_000_000_000L; 2084 } 2085 else if (v <= 999_999_999_999_999L) 2086 { 2087 divisor = 100_000_000_000_000L; 2088 } 2089 else if (v <= 9_999_999_999_999_999L) 2090 { 2091 divisor = 1_000_000_000_000_000L; 2092 } 2093 else if (v <= 99_999_999_999_999_999L) 2094 { 2095 divisor = 10_000_000_000_000_000L; 2096 } 2097 else if (v <= 999_999_999_999_999_999L) 2098 { 2099 divisor = 100_000_000_000_000_000L; 2100 } 2101 else 2102 { 2103 divisor = 1_000_000_000_000_000_000L; 2104 } 2105 2106 while (true) 2107 { 2108 final long digit = v / divisor; 2109 switch ((int) digit) 2110 { 2111 case 0: 2112 b[pos++] = '0'; 2113 break; 2114 case 1: 2115 b[pos++] = '1'; 2116 break; 2117 case 2: 2118 b[pos++] = '2'; 2119 break; 2120 case 3: 2121 b[pos++] = '3'; 2122 break; 2123 case 4: 2124 b[pos++] = '4'; 2125 break; 2126 case 5: 2127 b[pos++] = '5'; 2128 break; 2129 case 6: 2130 b[pos++] = '6'; 2131 break; 2132 case 7: 2133 b[pos++] = '7'; 2134 break; 2135 case 8: 2136 b[pos++] = '8'; 2137 break; 2138 case 9: 2139 b[pos++] = '9'; 2140 break; 2141 } 2142 2143 if (divisor == 1L) 2144 { 2145 break; 2146 } 2147 else 2148 { 2149 v -= (divisor * digit); 2150 if (v == 0) 2151 { 2152 while (divisor > 1L) 2153 { 2154 b[pos++] = '0'; 2155 divisor /= 10L; 2156 } 2157 2158 break; 2159 } 2160 2161 divisor /= 10L; 2162 } 2163 } 2164 2165 return pos; 2166 } 2167 2168 2169 2170 /** 2171 * Retrieves a hash code for this byte array. 2172 * 2173 * @return A hash code for this byte array. 2174 */ 2175 @Override() 2176 public int hashCode() 2177 { 2178 int hashCode = 0; 2179 2180 for (int i=0; i < endPos; i++) 2181 { 2182 hashCode += array[i]; 2183 } 2184 2185 return hashCode; 2186 } 2187 2188 2189 2190 /** 2191 * Indicates whether the provided object is a byte string buffer with contents 2192 * that are identical to that of this buffer. 2193 * 2194 * @param o The object for which to make the determination. 2195 * 2196 * @return {@code true} if the provided object is considered equal to this 2197 * buffer, or {@code false} if not. 2198 */ 2199 @Override() 2200 public boolean equals(@Nullable final Object o) 2201 { 2202 if (o == null) 2203 { 2204 return false; 2205 } 2206 2207 if (o == this) 2208 { 2209 return true; 2210 } 2211 2212 if (! (o instanceof ByteStringBuffer)) 2213 { 2214 return false; 2215 } 2216 2217 final ByteStringBuffer b = (ByteStringBuffer) o; 2218 if (endPos != b.endPos) 2219 { 2220 return false; 2221 } 2222 2223 for (int i=0; i < endPos; i++) 2224 { 2225 if (array[i] != b.array[i]) 2226 { 2227 return false; 2228 } 2229 } 2230 2231 return true; 2232 } 2233 2234 2235 2236 /** 2237 * Creates a duplicate of this byte string buffer. It will have identical 2238 * content but with a different backing array. Changes to this byte string 2239 * buffer will not impact the duplicate, and vice-versa. 2240 * 2241 * @return A duplicate of this byte string buffer. 2242 */ 2243 @NotNull() 2244 public ByteStringBuffer duplicate() 2245 { 2246 final ByteStringBuffer newBuffer = new ByteStringBuffer(endPos); 2247 return newBuffer.append(this); 2248 } 2249 2250 2251 2252 /** 2253 * Retrieves a string representation of the contents for this buffer. 2254 * 2255 * @return A string representation of the contents for this buffer. 2256 */ 2257 @Override() 2258 @NotNull() 2259 public String toString() 2260 { 2261 return StaticUtils.toUTF8String(array, 0, endPos); 2262 } 2263}