001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009 Sun Microsystems, Inc. 015 * Portions copyright 2011-2015 ForgeRock AS. 016 * Portions Copyright 2022 Wren Security. 017 */ 018package org.forgerock.opendj.ldap; 019 020import java.io.DataInput; 021import java.io.EOFException; 022import java.io.IOException; 023import java.io.InputStream; 024import java.io.OutputStream; 025import java.io.UnsupportedEncodingException; 026import java.nio.ByteBuffer; 027import java.nio.CharBuffer; 028import java.nio.channels.WritableByteChannel; 029import java.nio.charset.Charset; 030import java.nio.charset.CharsetDecoder; 031 032import org.forgerock.util.Reject; 033 034import com.forgerock.opendj.util.PackedLong; 035 036/** A mutable sequence of bytes backed by a byte array. */ 037public final class ByteStringBuilder implements ByteSequence { 038 039 /** Maximum size in bytes of a compact encoded value. */ 040 public static final int MAX_COMPACT_SIZE = PackedLong.MAX_COMPACT_SIZE; 041 042 /** Output stream implementation. */ 043 private final class OutputStreamImpl extends OutputStream { 044 @Override 045 public void close() { 046 // Do nothing. 047 } 048 049 @Override 050 public void write(final byte[] bytes) { 051 appendBytes(bytes); 052 } 053 054 @Override 055 public void write(final byte[] bytes, final int i, final int i1) { 056 appendBytes(bytes, i, i1); 057 } 058 059 @Override 060 public void write(final int i) { 061 appendByte(i); 062 } 063 } 064 065 /** 066 * A sub-sequence of the parent byte string builder. The sub-sequence will 067 * be robust against all updates to the byte string builder except for 068 * invocations of the method {@code clear()}. 069 */ 070 private final class SubSequence implements ByteSequence { 071 072 /** The length of the sub-sequence. */ 073 private final int subLength; 074 075 /** The offset of the sub-sequence. */ 076 private final int subOffset; 077 078 /** 079 * Creates a new sub-sequence. 080 * 081 * @param offset 082 * The offset of the sub-sequence. 083 * @param length 084 * The length of the sub-sequence. 085 */ 086 private SubSequence(final int offset, final int length) { 087 this.subOffset = offset; 088 this.subLength = length; 089 } 090 091 @Override 092 public ByteSequenceReader asReader() { 093 return new ByteSequenceReader(this); 094 } 095 096 @Override 097 public byte byteAt(final int index) { 098 if (index >= subLength || index < 0) { 099 throw new IndexOutOfBoundsException(); 100 } 101 102 // Protect against reallocation: use builder's buffer. 103 return buffer[subOffset + index]; 104 } 105 106 @Override 107 public int compareTo(final byte[] b, final int offset, final int length) { 108 ByteString.checkArrayBounds(b, offset, length); 109 110 // Protect against reallocation: use builder's buffer. 111 return ByteString.compareTo(buffer, subOffset, subLength, b, offset, length); 112 } 113 114 @Override 115 public int compareTo(final ByteSequence o) { 116 if (this == o) { 117 return 0; 118 } 119 120 // Protect against reallocation: use builder's buffer. 121 return -o.compareTo(buffer, subOffset, subLength); 122 } 123 124 @Override 125 public byte[] copyTo(final byte[] b) { 126 copyTo(b, 0); 127 return b; 128 } 129 130 @Override 131 public byte[] copyTo(final byte[] b, final int offset) { 132 if (offset < 0) { 133 throw new IndexOutOfBoundsException(); 134 } 135 136 // Protect against reallocation: use builder's buffer. 137 System.arraycopy(buffer, subOffset, b, offset, Math.min(subLength, b.length - offset)); 138 return b; 139 } 140 141 @Override 142 public ByteBuffer copyTo(final ByteBuffer byteBuffer) { 143 byteBuffer.put(buffer, subOffset, subLength); 144 return byteBuffer; 145 } 146 147 @Override 148 public ByteStringBuilder copyTo(final ByteStringBuilder builder) { 149 // Protect against reallocation: use builder's buffer. 150 return builder.appendBytes(buffer, subOffset, subLength); 151 } 152 153 @Override 154 public boolean copyTo(CharBuffer charBuffer, CharsetDecoder decoder) { 155 return ByteString.copyTo(ByteBuffer.wrap(buffer, subOffset, subLength), charBuffer, decoder); 156 } 157 158 @Override 159 public OutputStream copyTo(final OutputStream stream) throws IOException { 160 // Protect against reallocation: use builder's buffer. 161 stream.write(buffer, subOffset, subLength); 162 return stream; 163 } 164 165 @Override 166 public boolean equals(final byte[] b, final int offset, final int length) { 167 ByteString.checkArrayBounds(b, offset, length); 168 169 // Protect against reallocation: use builder's buffer. 170 return ByteString.equals(buffer, subOffset, subLength, b, offset, length); 171 } 172 173 @Override 174 public boolean equals(final Object o) { 175 if (this == o) { 176 return true; 177 } else if (o instanceof ByteSequence) { 178 final ByteSequence other = (ByteSequence) o; 179 180 // Protect against reallocation: use builder's buffer. 181 return other.equals(buffer, subOffset, subLength); 182 } else { 183 return false; 184 } 185 } 186 187 @Override 188 public int hashCode() { 189 // Protect against reallocation: use builder's buffer. 190 return ByteString.hashCode(buffer, subOffset, subLength); 191 } 192 193 @Override 194 public boolean isEmpty() { 195 return length == 0; 196 } 197 198 @Override 199 public int length() { 200 return subLength; 201 } 202 203 @Override 204 public ByteSequence subSequence(final int start, final int end) { 205 if (start < 0 || start > end || end > subLength) { 206 throw new IndexOutOfBoundsException(); 207 } 208 209 return new SubSequence(subOffset + start, end - start); 210 } 211 212 @Override 213 public boolean startsWith(ByteSequence prefix) { 214 if (prefix == null || prefix.length() > length) { 215 return false; 216 } 217 return prefix.equals(buffer, 0, prefix.length()); 218 } 219 220 @Override 221 public String toBase64String() { 222 return Base64.encode(this); 223 } 224 225 @Override 226 public byte[] toByteArray() { 227 return copyTo(new byte[subLength]); 228 } 229 230 @Override 231 public ByteString toByteString() { 232 // Protect against reallocation: use builder's buffer. 233 final byte[] b = new byte[subLength]; 234 System.arraycopy(buffer, subOffset, b, 0, subLength); 235 return ByteString.wrap(b); 236 } 237 238 @Override 239 public String toString() { 240 // Protect against reallocation: use builder's buffer. 241 return ByteString.toString(buffer, subOffset, subLength); 242 } 243 } 244 245 // These are package private so that compression and crypto 246 // functionality may directly access the fields. 247 248 /** The buffer where data is stored. */ 249 byte[] buffer; 250 251 /** The number of bytes to expose from the buffer. */ 252 int length; 253 254 /** 255 * The lazily allocated output stream view of this builder. Synchronization 256 * is not necessary because the stream is stateless and race conditions can 257 * be tolerated. 258 */ 259 private OutputStreamImpl os; 260 261 /** Creates a new byte string builder with an initial capacity of 32 bytes. */ 262 public ByteStringBuilder() { 263 // Initially create a 32 byte buffer. 264 this(32); 265 } 266 267 /** 268 * Creates a new byte string builder with the specified initial capacity. 269 * 270 * @param capacity 271 * The initial capacity. 272 * @throws IllegalArgumentException 273 * If the {@code capacity} is negative. 274 */ 275 public ByteStringBuilder(final int capacity) { 276 Reject.ifFalse(capacity >= 0, "capacity must be >= 0"); 277 this.buffer = new byte[capacity]; 278 this.length = 0; 279 } 280 281 /** 282 * Creates a new byte string builder with the content of the provided 283 * ByteSequence. Its capacity is set to the length of the provided 284 * ByteSequence. 285 * 286 * @param bs 287 * The ByteSequence to copy 288 */ 289 public ByteStringBuilder(final ByteSequence bs) { 290 this(bs.length()); 291 bs.copyTo(this); 292 } 293 294 /** 295 * Appends the provided byte to this byte string builder. 296 * <p> 297 * Note: this method accepts an {@code int} for ease of reading and writing. 298 * <p> 299 * This method only keeps the lowest 8-bits of the provided {@code int}. 300 * Higher bits will be truncated. This method performs the equivalent of: 301 * 302 * <pre> 303 * int i = ...; 304 * int i8bits = i & 0xFF; 305 * // only use "i8bits" 306 * </pre> 307 * OR 308 * <pre> 309 * int i = ...; 310 * byte b = (byte) i; 311 * // only use "b" 312 * </pre> 313 * 314 * @param b 315 * The byte to be appended to this byte string builder. 316 * @return This byte string builder. 317 */ 318 public ByteStringBuilder appendByte(final int b) { 319 ensureAdditionalCapacity(1); 320 buffer[length++] = (byte) b; 321 return this; 322 } 323 324 /** 325 * Appends the provided byte array to this byte string builder. 326 * <p> 327 * An invocation of the form: 328 * 329 * <pre> 330 * src.append(bytes) 331 * </pre> 332 * 333 * Behaves in exactly the same way as the invocation: 334 * 335 * <pre> 336 * src.append(bytes, 0, bytes.length); 337 * </pre> 338 * 339 * @param bytes 340 * The byte array to be appended to this byte string builder. 341 * @return This byte string builder. 342 */ 343 public ByteStringBuilder appendBytes(final byte[] bytes) { 344 return appendBytes(bytes, 0, bytes.length); 345 } 346 347 /** 348 * Appends the provided byte array to this byte string builder. 349 * 350 * @param bytes 351 * The byte array to be appended to this byte string builder. 352 * @param offset 353 * The offset of the byte array to be used; must be non-negative 354 * and no larger than {@code bytes.length} . 355 * @param length 356 * The length of the byte array to be used; must be non-negative 357 * and no larger than {@code bytes.length - offset}. 358 * @return This byte string builder. 359 * @throws IndexOutOfBoundsException 360 * If {@code offset} is negative or if {@code length} is 361 * negative or if {@code offset + length} is greater than 362 * {@code bytes.length}. 363 */ 364 public ByteStringBuilder appendBytes(final byte[] bytes, final int offset, final int length) { 365 ByteString.checkArrayBounds(bytes, offset, length); 366 367 if (length != 0) { 368 ensureAdditionalCapacity(length); 369 System.arraycopy(bytes, offset, buffer, this.length, length); 370 this.length += length; 371 } 372 373 return this; 374 } 375 376 /** 377 * Appends the provided {@code ByteBuffer} to this byte string builder. 378 * 379 * @param buffer 380 * The byte buffer to be appended to this byte string builder. 381 * @param length 382 * The number of bytes to be appended from {@code buffer}. 383 * @return This byte string builder. 384 * @throws IndexOutOfBoundsException 385 * If {@code length} is less than zero or greater than 386 * {@code buffer.remaining()}. 387 */ 388 public ByteStringBuilder appendBytes(final ByteBuffer buffer, final int length) { 389 if (length < 0 || length > buffer.remaining()) { 390 throw new IndexOutOfBoundsException(); 391 } 392 393 if (length != 0) { 394 ensureAdditionalCapacity(length); 395 buffer.get(this.buffer, this.length, length); 396 this.length += length; 397 } 398 399 return this; 400 } 401 402 /** 403 * Appends the provided {@link ByteSequence} to this byte string builder. 404 * 405 * @param bytes 406 * The byte sequence to be appended to this byte string builder. 407 * @return This byte string builder. 408 */ 409 public ByteStringBuilder appendBytes(final ByteSequence bytes) { 410 return bytes.copyTo(this); 411 } 412 413 /** 414 * Appends the provided {@link ByteSequenceReader} to this byte string builder. 415 * 416 * @param reader 417 * The byte sequence reader to be appended to this byte string 418 * builder. 419 * @param length 420 * The number of bytes to be appended from {@code reader}. 421 * @return This byte string builder. 422 * @throws IndexOutOfBoundsException 423 * If {@code length} is less than zero or greater than 424 * {@code reader.remaining()}. 425 */ 426 public ByteStringBuilder appendBytes(final ByteSequenceReader reader, final int length) { 427 if (length < 0 || length > reader.remaining()) { 428 throw new IndexOutOfBoundsException(); 429 } 430 431 if (length != 0) { 432 ensureAdditionalCapacity(length); 433 reader.readBytes(buffer, this.length, length); 434 this.length += length; 435 } 436 437 return this; 438 } 439 440 /** 441 * Appends the UTF-8 encoded bytes of the provided char array to this byte 442 * string builder. 443 * 444 * @param chars 445 * The char array whose UTF-8 encoding is to be appended to this 446 * byte string builder. 447 * @return This byte string builder. 448 */ 449 public ByteStringBuilder appendUtf8(final char[] chars) { 450 if (chars == null) { 451 return this; 452 } 453 454 // Assume that each char is 1 byte 455 final int len = chars.length; 456 ensureAdditionalCapacity(len); 457 458 for (int i = 0; i < len; i++) { 459 final char c = chars[i]; 460 final byte b = (byte) (c & 0x0000007F); 461 462 if (c == b) { 463 buffer[this.length + i] = b; 464 } else { 465 // There is a multi-byte char. Defer to JDK. 466 final Charset utf8 = Charset.forName("UTF-8"); 467 final ByteBuffer byteBuffer = utf8.encode(CharBuffer.wrap(chars)); 468 final int remaining = byteBuffer.remaining(); 469 ensureAdditionalCapacity(remaining - len); 470 byteBuffer.get(buffer, this.length, remaining); 471 this.length += remaining; 472 return this; 473 } 474 } 475 476 // The 1 byte char assumption was correct 477 this.length += len; 478 return this; 479 } 480 481 /** 482 * Appends the provided {@code DataInput} to this byte string 483 * builder. 484 * 485 * @param stream 486 * The data input stream to be appended to this byte string 487 * builder. 488 * @param length 489 * The maximum number of bytes to be appended from {@code 490 * input}. 491 * @throws IndexOutOfBoundsException 492 * If {@code length} is less than zero. 493 * @throws EOFException 494 * If this stream reaches the end before reading all the bytes. 495 * @throws IOException 496 * If an I/O error occurs. 497 */ 498 public void appendBytes(DataInput stream, int length) throws EOFException, IOException { 499 if (length < 0) { 500 throw new IndexOutOfBoundsException(); 501 } 502 503 ensureAdditionalCapacity(length); 504 stream.readFully(buffer, this.length, length); 505 this.length += length; 506 } 507 508 /** 509 * Appends the provided {@code InputStream} to this byte string builder. 510 * 511 * @param stream 512 * The input stream to be appended to this byte string builder. 513 * @param length 514 * The maximum number of bytes to be appended from {@code buffer} 515 * . 516 * @return The number of bytes read from the input stream, or {@code -1} if 517 * the end of the input stream has been reached. 518 * @throws IndexOutOfBoundsException 519 * If {@code length} is less than zero. 520 * @throws IOException 521 * If an I/O error occurs. 522 */ 523 public int appendBytes(final InputStream stream, final int length) throws IOException { 524 if (length < 0) { 525 throw new IndexOutOfBoundsException(); 526 } 527 528 ensureAdditionalCapacity(length); 529 final int bytesRead = stream.read(buffer, this.length, length); 530 if (bytesRead > 0) { 531 this.length += bytesRead; 532 } 533 534 return bytesRead; 535 } 536 537 /** 538 * Appends the big-endian encoded bytes of the provided integer to this byte 539 * string builder. 540 * 541 * @param i 542 * The integer whose big-endian encoding is to be appended to 543 * this byte string builder. 544 * @return This byte string builder. 545 */ 546 public ByteStringBuilder appendInt(int i) { 547 ensureAdditionalCapacity(4); 548 for (int j = length + 3; j >= length; j--) { 549 buffer[j] = (byte) i; 550 i >>>= 8; 551 } 552 length += 4; 553 return this; 554 } 555 556 /** 557 * Appends the big-endian encoded bytes of the provided long to this byte 558 * string builder. 559 * 560 * @param l 561 * The long whose big-endian encoding is to be appended to this 562 * byte string builder. 563 * @return This byte string builder. 564 */ 565 public ByteStringBuilder appendLong(long l) { 566 ensureAdditionalCapacity(8); 567 for (int i = length + 7; i >= length; i--) { 568 buffer[i] = (byte) l; 569 l >>>= 8; 570 } 571 length += 8; 572 return this; 573 } 574 575 /** 576 * Appends the compact encoded bytes of the provided unsigned long to this byte 577 * string builder. This method allows to encode unsigned long up to 56 bits using 578 * fewer bytes (from 1 to 8) than append(long). The encoding has the important 579 * property that it preserves ordering, so it can be used for keys. 580 * 581 * @param value 582 * The long whose compact encoding is to be appended to this 583 * byte string builder. 584 * @return This byte string builder. 585 */ 586 public ByteStringBuilder appendCompactUnsigned(long value) { 587 Reject.ifFalse(value >= 0, "value must be >= 0"); 588 try { 589 PackedLong.writeCompactUnsigned(asOutputStream(), value); 590 } catch (IOException e) { 591 throw new IllegalStateException(e); 592 } 593 return this; 594 } 595 596 /** 597 * Appends the byte string representation of the provided object to this 598 * byte string builder. The object is converted to a byte string as follows: 599 * <ul> 600 * <li>if the object is an instance of {@code ByteSequence} then this method 601 * is equivalent to calling {@link #appendBytes(ByteSequence)} 602 * <li>if the object is a {@code byte[]} then this method is equivalent to 603 * calling {@link #appendBytes(byte[])} 604 * <li>if the object is a {@code char[]} then this method is equivalent to 605 * calling {@link #appendUtf8(char[])} 606 * <li>for all other types of object this method is equivalent to calling 607 * {@link #appendUtf8(String)} with the {@code toString()} representation of the 608 * provided object. 609 * </ul> 610 * <b>Note:</b> this method treats {@code Long} and {@code Integer} objects 611 * like any other type of {@code Object}. More specifically, the following 612 * invocations are not equivalent: 613 * <ul> 614 * <li>{@code append(0)} is not equivalent to {@code append((Object) 0)} 615 * <li>{@code append(0L)} is not equivalent to {@code append((Object) 0L)} 616 * </ul> 617 * 618 * @param o 619 * The object to be appended to this byte string builder. 620 * @return This byte string builder. 621 */ 622 public ByteStringBuilder appendObject(final Object o) { 623 if (o == null) { 624 return this; 625 } else if (o instanceof ByteSequence) { 626 return appendBytes((ByteSequence) o); 627 } else if (o instanceof byte[]) { 628 return appendBytes((byte[]) o); 629 } else if (o instanceof char[]) { 630 return appendUtf8((char[]) o); 631 } else { 632 return appendUtf8(o.toString()); 633 } 634 } 635 636 /** 637 * Appends the big-endian encoded bytes of the provided short to this byte 638 * string builder. 639 * <p> 640 * Note: this method accepts an {@code int} for ease of reading and writing. 641 * <p> 642 * This method only keeps the lowest 16-bits of the provided {@code int}. 643 * Higher bits will be truncated. This method performs the equivalent of: 644 * 645 * <pre> 646 * int i = ...; 647 * int i16bits = i & 0xFFFF; 648 * // only use "i16bits" 649 * </pre> 650 * OR 651 * <pre> 652 * int i = ...; 653 * short s = (short) i; 654 * // only use "s" 655 * </pre> 656 * 657 * @param i 658 * The short whose big-endian encoding is to be appended to this 659 * byte string builder. 660 * @return This byte string builder. 661 */ 662 public ByteStringBuilder appendShort(int i) { 663 ensureAdditionalCapacity(2); 664 for (int j = length + 1; j >= length; j--) { 665 buffer[j] = (byte) i; 666 i >>>= 8; 667 } 668 length += 2; 669 return this; 670 } 671 672 /** 673 * Appends the UTF-8 encoded bytes of the provided string to this byte 674 * string builder. 675 * 676 * @param s 677 * The string whose UTF-8 encoding is to be appended to this byte 678 * string builder. 679 * @return This byte string builder. 680 */ 681 public ByteStringBuilder appendUtf8(final String s) { 682 if (s == null) { 683 return this; 684 } 685 686 // Assume that each char is 1 byte 687 final int len = s.length(); 688 ensureAdditionalCapacity(len); 689 690 for (int i = 0; i < len; i++) { 691 final char c = s.charAt(i); 692 final byte b = (byte) (c & 0x0000007F); 693 694 if (c == b) { 695 buffer[this.length + i] = b; 696 } else { 697 // There is a multi-byte char. Defer to JDK 698 try { 699 return appendBytes(s.getBytes("UTF-8")); 700 } catch (final UnsupportedEncodingException e) { 701 // TODO: I18N 702 throw new RuntimeException("Unable to encode String '" + s + "' to UTF-8 bytes", e); 703 } 704 } 705 } 706 707 // The 1 byte char assumption was correct 708 this.length += len; 709 return this; 710 } 711 712 /** 713 * Appends the ASN.1 BER length encoding representation of the provided 714 * integer to this byte string builder. 715 * 716 * @param length 717 * The value to encode using the BER length encoding rules. 718 * @return This byte string builder. 719 */ 720 public ByteStringBuilder appendBERLength(final int length) { 721 if ((length & 0x0000007F) == length) { 722 ensureAdditionalCapacity(1); 723 724 buffer[this.length++] = (byte) length; 725 } else if ((length & 0x000000FF) == length) { 726 ensureAdditionalCapacity(2); 727 728 buffer[this.length++] = (byte) 0x81; 729 buffer[this.length++] = (byte) length; 730 } else if ((length & 0x0000FFFF) == length) { 731 ensureAdditionalCapacity(3); 732 733 buffer[this.length++] = (byte) 0x82; 734 buffer[this.length++] = (byte) (length >> 8); 735 buffer[this.length++] = (byte) length; 736 } else if ((length & 0x00FFFFFF) == length) { 737 ensureAdditionalCapacity(4); 738 739 buffer[this.length++] = (byte) 0x83; 740 buffer[this.length++] = (byte) (length >> 16); 741 buffer[this.length++] = (byte) (length >> 8); 742 buffer[this.length++] = (byte) length; 743 } else { 744 ensureAdditionalCapacity(5); 745 746 buffer[this.length++] = (byte) 0x84; 747 buffer[this.length++] = (byte) (length >> 24); 748 buffer[this.length++] = (byte) (length >> 16); 749 buffer[this.length++] = (byte) (length >> 8); 750 buffer[this.length++] = (byte) length; 751 } 752 return this; 753 } 754 755 /** 756 * Returns an {@link OutputStream} whose write operations append data to 757 * this byte string builder. The returned output stream will never throw an 758 * {@link IOException} and its {@link OutputStream#close() close} method 759 * does not do anything. 760 * 761 * @return An {@link OutputStream} whose write operations append data to 762 * this byte string builder. 763 */ 764 public OutputStream asOutputStream() { 765 if (os == null) { 766 os = new OutputStreamImpl(); 767 } 768 return os; 769 } 770 771 /** 772 * Returns a {@link ByteSequenceReader} which can be used to incrementally 773 * read and decode data from this byte string builder. 774 * <p> 775 * <b>NOTE:</b> all concurrent updates to this byte string builder are 776 * supported with the exception of {@link #clear()}. Any invocations of 777 * {@link #clear()} must be accompanied by a subsequent call to 778 * {@code ByteSequenceReader.rewind()}. 779 * 780 * @return The {@link ByteSequenceReader} which can be used to incrementally 781 * read and decode data from this byte string builder. 782 * @see #clear() 783 */ 784 @Override 785 public ByteSequenceReader asReader() { 786 return new ByteSequenceReader(this); 787 } 788 789 @Override 790 public byte byteAt(final int index) { 791 if (index >= length || index < 0) { 792 throw new IndexOutOfBoundsException(); 793 } 794 return buffer[index]; 795 } 796 797 /** 798 * Returns the current capacity of this byte string builder. The capacity 799 * may increase as more data is appended. 800 * 801 * @return The current capacity of this byte string builder. 802 */ 803 public int capacity() { 804 return buffer.length; 805 } 806 807 /** 808 * Sets the length of this byte string builder to zero. 809 * <p> 810 * <b>NOTE:</b> if this method is called, then 811 * {@code ByteSequenceReader.rewind()} must also be called on any associated 812 * byte sequence readers in order for them to remain valid. 813 * 814 * @return This byte string builder. 815 * @see #asReader() 816 */ 817 public ByteStringBuilder clear() { 818 length = 0; 819 return this; 820 } 821 822 /** 823 * Sets the length of this byte string builder to zero, and resets the 824 * capacity to the specified size if above provided threshold. 825 * <p> 826 * <b>NOTE:</b> if this method is called, then 827 * {@code ByteSequenceReader.rewind()} must also be called on any associated 828 * byte sequence readers in order for them to remain valid. 829 * 830 * @param thresholdCapacity 831 * The threshold capacity triggering a truncate 832 * @param newCapacity 833 * The new capacity. 834 * @return This byte string builder. 835 * @throws IllegalArgumentException 836 * If the {@code newCapacity} is negative or the {@code newCapacity} 837 * is bigger than the {@code thresholdCapacity}. 838 * @see #asReader() 839 */ 840 public ByteStringBuilder clearAndTruncate(int thresholdCapacity, int newCapacity) { 841 if (newCapacity > thresholdCapacity) { 842 throw new IllegalArgumentException("new capacity '" + newCapacity 843 + "' cannot be bigger than threshold capacity '" + thresholdCapacity + "'"); 844 } 845 if (newCapacity < 0) { 846 throw new IllegalArgumentException("new capacity '" + newCapacity + "' cannot be negative."); 847 } 848 if (buffer.length > thresholdCapacity) { 849 // garbage collect excessively large buffers 850 buffer = new byte[newCapacity]; 851 } 852 length = 0; 853 return this; 854 } 855 856 @Override 857 public int compareTo(final byte[] bytes, final int offset, final int length) { 858 ByteString.checkArrayBounds(bytes, offset, length); 859 return ByteString.compareTo(this.buffer, 0, this.length, bytes, offset, length); 860 } 861 862 @Override 863 public int compareTo(final ByteSequence o) { 864 if (this == o) { 865 return 0; 866 } 867 return -o.compareTo(buffer, 0, length); 868 } 869 870 @Override 871 public byte[] copyTo(final byte[] bytes) { 872 copyTo(bytes, 0); 873 return bytes; 874 } 875 876 @Override 877 public byte[] copyTo(final byte[] bytes, final int offset) { 878 if (offset < 0) { 879 throw new IndexOutOfBoundsException(); 880 } 881 System.arraycopy(buffer, 0, bytes, offset, Math.min(length, bytes.length - offset)); 882 return bytes; 883 } 884 885 @Override 886 public ByteBuffer copyTo(final ByteBuffer byteBuffer) { 887 byteBuffer.put(buffer, 0, length); 888 return byteBuffer; 889 } 890 891 @Override 892 public ByteStringBuilder copyTo(final ByteStringBuilder builder) { 893 builder.appendBytes(buffer, 0, length); 894 return builder; 895 } 896 897 @Override 898 public boolean copyTo(CharBuffer charBuffer, CharsetDecoder decoder) { 899 return ByteString.copyTo(ByteBuffer.wrap(buffer, 0, length), charBuffer, decoder); 900 } 901 902 @Override 903 public OutputStream copyTo(final OutputStream stream) throws IOException { 904 stream.write(buffer, 0, length); 905 return stream; 906 } 907 908 /** 909 * Copies the entire contents of this byte string to the provided 910 * {@code WritableByteChannel}. 911 * 912 * @param channel 913 * The {@code WritableByteChannel} to copy to. 914 * @return The number of bytes written, possibly zero 915 * @throws IOException 916 * If some other I/O error occurs 917 * @see WritableByteChannel#write(java.nio.ByteBuffer) 918 */ 919 public int copyTo(WritableByteChannel channel) throws IOException { 920 return channel.write(ByteBuffer.wrap(buffer, 0, length)); 921 } 922 923 /** 924 * Ensures that the specified number of additional bytes will fit in this 925 * byte string builder and resizes it if necessary. 926 * 927 * @param size 928 * The number of additional bytes. 929 * @return This byte string builder. 930 */ 931 public ByteStringBuilder ensureAdditionalCapacity(final int size) { 932 final int newCount = this.length + size; 933 if (newCount > buffer.length) { 934 final byte[] newbuffer = new byte[Math.max(buffer.length << 1, newCount)]; 935 System.arraycopy(buffer, 0, newbuffer, 0, buffer.length); 936 buffer = newbuffer; 937 } 938 return this; 939 } 940 941 @Override 942 public boolean equals(final byte[] bytes, final int offset, final int length) { 943 ByteString.checkArrayBounds(bytes, offset, length); 944 return ByteString.equals(this.buffer, 0, this.length, bytes, offset, length); 945 } 946 947 /** 948 * Indicates whether the provided object is equal to this byte string 949 * builder. In order for it to be considered equal, the provided object must 950 * be a byte sequence containing the same bytes in the same order. 951 * 952 * @param o 953 * The object for which to make the determination. 954 * @return {@code true} if the provided object is a byte sequence whose 955 * content is equal to that of this byte string builder, or 956 * {@code false} if not. 957 */ 958 @Override 959 public boolean equals(final Object o) { 960 if (this == o) { 961 return true; 962 } else if (o instanceof ByteSequence) { 963 final ByteSequence other = (ByteSequence) o; 964 return other.equals(buffer, 0, length); 965 } else { 966 return false; 967 } 968 } 969 970 /** 971 * Returns the byte array that backs this byte string builder. Modifications 972 * to this byte string builder's content may cause the returned array's 973 * content to be modified, and vice versa. 974 * <p> 975 * Note that the length of the returned array is only guaranteed to be the 976 * same as the length of this byte string builder immediately after a call 977 * to {@link #trimToSize()}. 978 * <p> 979 * In addition, subsequent modifications to this byte string builder may 980 * cause the backing byte array to be reallocated thus decoupling the 981 * returned byte array from this byte string builder. 982 * 983 * @return The byte array that backs this byte string builder. 984 */ 985 public byte[] getBackingArray() { 986 return buffer; 987 } 988 989 /** 990 * Returns a hash code for this byte string builder. It will be the sum of 991 * all of the bytes contained in the byte string builder. 992 * <p> 993 * <b>NOTE:</b> subsequent changes to this byte string builder will 994 * invalidate the returned hash code. 995 * 996 * @return A hash code for this byte string builder. 997 */ 998 @Override 999 public int hashCode() { 1000 return ByteString.hashCode(buffer, 0, length); 1001 } 1002 1003 @Override 1004 public boolean isEmpty() { 1005 return length == 0; 1006 } 1007 1008 @Override 1009 public int length() { 1010 return length; 1011 } 1012 1013 /** 1014 * Sets the byte value at the specified index. 1015 * <p> 1016 * An index ranges from zero to {@code length() - 1}. The first byte value 1017 * of the sequence is at index zero, the next at index one, and so on, as 1018 * for array indexing. 1019 * 1020 * @param index 1021 * The index of the byte to be set. 1022 * @param b 1023 * The byte to set on this byte string builder. 1024 * @throws IndexOutOfBoundsException 1025 * If the index argument is negative or not less than length(). 1026 */ 1027 public void setByte(final int index, final byte b) { 1028 if (index >= length || index < 0) { 1029 throw new IndexOutOfBoundsException(); 1030 } 1031 buffer[index] = b; 1032 } 1033 1034 /** 1035 * Sets the length of this byte string builder. 1036 * <p> 1037 * If the <code>newLength</code> argument is less than the current length, 1038 * the length is changed to the specified length. 1039 * <p> 1040 * If the <code>newLength</code> argument is greater than or equal to the 1041 * current length, then the capacity is increased and sufficient null bytes 1042 * are appended so that length becomes the <code>newLength</code> argument. 1043 * <p> 1044 * The <code>newLength</code> argument must be greater than or equal to 1045 * <code>0</code>. 1046 * 1047 * @param newLength 1048 * The new length. 1049 * @return This byte string builder. 1050 * @throws IndexOutOfBoundsException 1051 * If the <code>newLength</code> argument is negative. 1052 */ 1053 public ByteStringBuilder setLength(final int newLength) { 1054 if (newLength < 0) { 1055 throw new IndexOutOfBoundsException("Negative newLength: " + newLength); 1056 } 1057 1058 if (newLength > length) { 1059 ensureAdditionalCapacity(newLength - length); 1060 1061 // Pad with zeros. 1062 for (int i = length; i < newLength; i++) { 1063 buffer[i] = 0; 1064 } 1065 } 1066 length = newLength; 1067 1068 return this; 1069 } 1070 1071 /** 1072 * Returns a new byte sequence that is a subsequence of this byte sequence. 1073 * <p> 1074 * The subsequence starts with the byte value at the specified {@code start} 1075 * index and ends with the byte value at index {@code end - 1}. The length 1076 * (in bytes) of the returned sequence is {@code end - start}, so if 1077 * {@code start 1078 * == end} then an empty sequence is returned. 1079 * <p> 1080 * <b>NOTE:</b> the returned sub-sequence will be robust against all updates 1081 * to the byte string builder except for invocations of the method 1082 * {@link #clear()}. If a permanent immutable byte sequence is required then 1083 * callers should invoke {@code toByteString()} on the returned byte 1084 * sequence. 1085 * 1086 * @param start 1087 * The start index, inclusive. 1088 * @param end 1089 * The end index, exclusive. 1090 * @return The newly created byte subsequence. 1091 */ 1092 @Override 1093 public ByteSequence subSequence(final int start, final int end) { 1094 if (start < 0 || start > end || end > length) { 1095 throw new IndexOutOfBoundsException(); 1096 } 1097 1098 return new SubSequence(start, end - start); 1099 } 1100 1101 @Override 1102 public boolean startsWith(ByteSequence prefix) { 1103 if (prefix == null || prefix.length() > length) { 1104 return false; 1105 } 1106 return prefix.equals(buffer, 0, prefix.length()); 1107 } 1108 1109 @Override 1110 public String toBase64String() { 1111 return Base64.encode(this); 1112 } 1113 1114 @Override 1115 public byte[] toByteArray() { 1116 return copyTo(new byte[length]); 1117 } 1118 1119 /** 1120 * Returns the {@link ByteString} representation of this byte string 1121 * builder. Subsequent changes to this byte string builder will not modify 1122 * the returned {@link ByteString}. 1123 * 1124 * @return The {@link ByteString} representation of this byte sequence. 1125 */ 1126 @Override 1127 public ByteString toByteString() { 1128 final byte[] b = new byte[length]; 1129 System.arraycopy(buffer, 0, b, 0, length); 1130 return ByteString.wrap(b); 1131 } 1132 1133 @Override 1134 public String toString() { 1135 return ByteString.toString(buffer, 0, length); 1136 } 1137 1138 /** 1139 * Attempts to reduce storage used for this byte string builder. If the 1140 * buffer is larger than necessary to hold its current sequence of bytes, 1141 * then it may be resized to become more space efficient. 1142 * 1143 * @return This byte string builder. 1144 */ 1145 public ByteStringBuilder trimToSize() { 1146 if (buffer.length > length) { 1147 final byte[] newBuffer = new byte[length]; 1148 System.arraycopy(buffer, 0, newBuffer, 0, length); 1149 buffer = newBuffer; 1150 } 1151 return this; 1152 } 1153}