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