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