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