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