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    return append(s, 0, s.length());
439  }
440
441
442
443  /**
444   * Appends the provided character sequence to this buffer.
445   *
446   * @param  s      The character sequence to append to this buffer.
447   * @param  start  The position in the sequence of the first character in the
448   *                sequence to be appended to this buffer.
449   * @param  end    The position in the sequence immediately after the position
450   *                of the last character to be appended.
451   *
452   * @return  A reference to this buffer.
453   *
454   * @throws  NullPointerException  If the provided character sequence is
455   *                                {@code null}.
456   *
457   * @throws  IndexOutOfBoundsException  If the provided start or end positions
458   *                                     are outside the bounds of the given
459   *                                     character sequence.
460   */
461  @Override()
462  public ByteStringBuffer append(final CharSequence s, final int start,
463                                 final int end)
464         throws NullPointerException, IndexOutOfBoundsException
465  {
466    if (s == null)
467    {
468      final NullPointerException e =
469           new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get());
470      Debug.debugCodingError(e);
471      throw e;
472    }
473
474    final char[] chars;
475    if (s instanceof String)
476    {
477      chars = ((String) s).toCharArray();
478    }
479    else
480    {
481      chars = s.toString().toCharArray();
482    }
483
484    return append(chars, start, end);
485  }
486
487
488
489  /**
490   * Appends the provided integer value to this buffer.
491   *
492   * @param  i  The integer value to be appended to this buffer.
493   *
494   * @return  A reference to this buffer.
495   */
496  public ByteStringBuffer append(final int i)
497  {
498    final int length = getBytes(i);
499    return append(TEMP_NUMBER_BUFFER.get(), 0, length);
500  }
501
502
503
504  /**
505   * Appends the provided long value to this buffer.
506   *
507   * @param  l  The long value to be appended to this buffer.
508   *
509   * @return  A reference to this buffer.
510   */
511  public ByteStringBuffer append(final long l)
512  {
513    final int length = getBytes(l);
514    return append(TEMP_NUMBER_BUFFER.get(), 0, length);
515  }
516
517
518
519  /**
520   * Inserts the provided boolean value to this buffer.
521   *
522   * @param  pos  The position at which the value is to be inserted.
523   * @param  b    The boolean value to be inserted into this buffer.
524   *
525   * @return  A reference to this buffer.
526   *
527   * @throws  IndexOutOfBoundsException  If the specified position is negative
528   *                                     or greater than the current length.
529   */
530  public ByteStringBuffer insert(final int pos, final boolean b)
531         throws  IndexOutOfBoundsException
532  {
533    if (b)
534    {
535      return insert(pos, TRUE_VALUE_BYTES, 0, 4);
536    }
537    else
538    {
539      return insert(pos, FALSE_VALUE_BYTES, 0, 5);
540    }
541  }
542
543
544
545  /**
546   * Inserts the provided byte at the specified position in this buffer.
547   *
548   * @param  pos  The position at which the byte is to be inserted.
549   * @param  b    The byte to be inserted into this buffer.
550   *
551   * @return  A reference to this buffer.
552   *
553   * @throws  IndexOutOfBoundsException  If the specified position is negative
554   *                                     or greater than the current length.
555   */
556  public ByteStringBuffer insert(final int pos, final byte b)
557         throws IndexOutOfBoundsException
558  {
559    if ((pos < 0) || (pos > endPos))
560    {
561      final String message;
562      if (pos < 0)
563      {
564        message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos);
565      }
566      else
567      {
568        message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos);
569      }
570
571      final IndexOutOfBoundsException e =
572           new IndexOutOfBoundsException(message);
573      Debug.debugCodingError(e);
574      throw e;
575    }
576    else if (pos == endPos)
577    {
578      return append(b);
579    }
580
581    ensureCapacity(endPos + 1);
582    System.arraycopy(array, pos, array, pos+1, (endPos-pos));
583    array[pos] = b;
584    endPos++;
585    return this;
586  }
587
588
589
590  /**
591   * Inserts the contents of the provided byte array at the specified position
592   * in this buffer.
593   *
594   * @param  pos  The position at which the data is to be inserted.
595   * @param  b    The array whose contents should be inserted into this buffer.
596   *
597   * @return  A reference to this buffer.
598   *
599   * @throws  NullPointerException  If the provided array is {@code null}.
600   *
601   * @throws  IndexOutOfBoundsException  If the specified position is negative
602   *                                     or greater than the current length.
603   */
604  public ByteStringBuffer insert(final int pos, final byte[] b)
605         throws NullPointerException, IndexOutOfBoundsException
606  {
607    if (b == null)
608    {
609      final NullPointerException e =
610           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
611      Debug.debugCodingError(e);
612      throw e;
613    }
614
615    return insert(pos, b, 0, b.length);
616  }
617
618
619
620  /**
621   * Inserts a portion of the data in the provided array at the specified
622   * position in this buffer.
623   *
624   * Appends the specified portion of the provided byte array to 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   * @param  off  The offset within the array at which to begin copying data.
629   * @param  len  The number of bytes to copy.
630   *
631   * @return  A reference to this buffer.
632   *
633   * @throws  NullPointerException  If the provided array is {@code null}.
634   *
635   * @throws  IndexOutOfBoundsException  If the specified position is negative
636   *                                     or greater than the current length, if
637   *                                     the offset or length are negative, if
638   *                                     the offset plus the length is beyond
639   *                                     the end of the provided array.
640   */
641  public ByteStringBuffer insert(final int pos, final byte[] b, final int off,
642                                 final int len)
643         throws NullPointerException, IndexOutOfBoundsException
644  {
645    if (b == null)
646    {
647      final NullPointerException e =
648           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
649      Debug.debugCodingError(e);
650      throw e;
651    }
652
653    if ((pos < 0) || (pos > endPos) || (off < 0) || (len < 0) ||
654        (off+len > b.length))
655    {
656      final String message;
657      if (pos < 0)
658      {
659        message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos);
660      }
661      else if (pos > endPos)
662      {
663        message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos);
664      }
665      else if (off < 0)
666      {
667        message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off);
668      }
669      else if (len < 0)
670      {
671        message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len);
672      }
673      else
674      {
675        message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len,
676                                                                 b.length);
677      }
678
679      final IndexOutOfBoundsException e =
680           new IndexOutOfBoundsException(message);
681      Debug.debugCodingError(e);
682      throw e;
683    }
684    else if (len == 0)
685    {
686      return this;
687    }
688    else if (pos == endPos)
689    {
690      return append(b, off, len);
691    }
692
693    ensureCapacity(endPos + len);
694    System.arraycopy(array, pos, array, pos+len, (endPos-pos));
695    System.arraycopy(b, off, array, pos, len);
696    endPos += len;
697    return this;
698  }
699
700
701
702  /**
703   * Inserts the provided byte string into this buffer at the specified
704   * position.
705   *
706   * @param  pos  The position at which the data is to be inserted.
707   * @param  b    The byte string to insert into this buffer.
708   *
709   * @return  A reference to this buffer.
710   *
711   * @throws  NullPointerException  If the provided buffer is {@code null}.
712   *
713   * @throws  IndexOutOfBoundsException  If the specified position is negative
714   *                                     or greater than the current length.
715   */
716  public ByteStringBuffer insert(final int pos, final ByteString b)
717         throws NullPointerException, IndexOutOfBoundsException
718  {
719    if (b == null)
720    {
721      final NullPointerException e =
722           new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get());
723      Debug.debugCodingError(e);
724      throw e;
725    }
726
727    return insert(pos, b.getValue());
728  }
729
730
731
732  /**
733   * Inserts the provided byte string buffer into this buffer at the specified
734   * position.
735   *
736   * @param  pos     The position at which the data is to be inserted.
737   * @param  buffer  The buffer whose contents should be inserted into this
738   *                 buffer.
739   *
740   * @return  A reference to this buffer.
741   *
742   * @throws  NullPointerException  If the provided buffer is {@code null}.
743   *
744   * @throws  IndexOutOfBoundsException  If the specified position is negative
745   *                                     or greater than the current length.
746   */
747  public ByteStringBuffer insert(final int pos, final ByteStringBuffer buffer)
748         throws NullPointerException, IndexOutOfBoundsException
749  {
750    if (buffer == null)
751    {
752      final NullPointerException e =
753           new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get());
754      Debug.debugCodingError(e);
755      throw e;
756    }
757
758    return insert(pos, buffer.array, 0, buffer.endPos);
759  }
760
761
762
763  /**
764   * Inserts the provided character into this buffer at the provided position.
765   *
766   * @param  pos  The position at which the character is to be inserted.
767   * @param  c    The character to be inserted into this buffer.
768   *
769   * @return  A reference to this buffer.
770   *
771   * @throws  IndexOutOfBoundsException  If the specified position is negative
772   *                                     or greater than the current length.
773   */
774  public ByteStringBuffer insert(final int pos, final char c)
775         throws IndexOutOfBoundsException
776  {
777    if ((pos < 0) || (pos > endPos))
778    {
779      final String message;
780      if (pos < 0)
781      {
782        message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos);
783      }
784      else
785      {
786        message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos);
787      }
788
789      final IndexOutOfBoundsException e =
790           new IndexOutOfBoundsException(message);
791      Debug.debugCodingError(e);
792      throw e;
793    }
794    else if (pos == endPos)
795    {
796      return append(c);
797    }
798
799    final byte b = (byte) (c & 0x7F);
800    if (b == c)
801    {
802      ensureCapacity(endPos + 1);
803      System.arraycopy(array, pos, array, pos+1, (endPos-pos));
804      array[pos] = b;
805      endPos++;
806    }
807    else
808    {
809      insert(pos, String.valueOf(c));
810    }
811
812    return this;
813  }
814
815
816
817  /**
818   * Inserts the contents of the provided character array into this buffer at
819   * the specified position.
820   *
821   * @param  pos  The position at which the data is to be inserted.
822   * @param  c    The array whose contents should be inserted into this buffer.
823   *
824   * @return  A reference to this buffer.
825   *
826   * @throws  NullPointerException  If the provided array is {@code null}.
827   *
828   * @throws  IndexOutOfBoundsException  If the specified position is negative
829   *                                     or greater than the current length.
830   */
831  public ByteStringBuffer insert(final int pos, final char[] c)
832         throws NullPointerException, IndexOutOfBoundsException
833  {
834    if (c == null)
835    {
836      final NullPointerException e =
837           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
838      Debug.debugCodingError(e);
839      throw e;
840    }
841
842    return insert(pos, new String(c, 0, c.length));
843  }
844
845
846
847  /**
848   * Inserts the specified portion of the provided character array to this
849   * buffer at the specified position.
850   *
851   * @param  pos  The position at which the data is to be inserted.
852   * @param  c    The array whose contents should be inserted into this buffer.
853   * @param  off  The offset within the array at which to begin copying data.
854   * @param  len  The number of characters to copy.
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, if
862   *                                     the offset or length are negative, if
863   *                                     the offset plus the length is beyond
864   *                                     the end of the provided array.
865   */
866  public ByteStringBuffer insert(final int pos, final char[] c, final int off,
867                                 final int len)
868         throws NullPointerException, IndexOutOfBoundsException
869  {
870    if (c == null)
871    {
872      final NullPointerException e =
873           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
874      Debug.debugCodingError(e);
875      throw e;
876    }
877
878    return insert(pos, new String(c, off, len));
879  }
880
881
882
883  /**
884   * Inserts the provided character sequence to this buffer at the specified
885   * position.
886   *
887   * @param  pos  The position at which the data is to be inserted.
888   * @param  s    The character sequence to insert into this buffer.
889   *
890   * @return  A reference to this buffer.
891   *
892   * @throws  NullPointerException  If the provided character sequence is
893   *                                {@code null}.
894   *
895   * @throws  IndexOutOfBoundsException  If the specified position is negative
896   *                                     or greater than the current length.
897   */
898  public ByteStringBuffer insert(final int pos, final CharSequence s)
899         throws NullPointerException, IndexOutOfBoundsException
900  {
901    if (s == null)
902    {
903      final NullPointerException e =
904           new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get());
905      Debug.debugCodingError(e);
906      throw e;
907    }
908
909    if ((pos < 0) || (pos > endPos))
910    {
911      final String message;
912      if (pos < 0)
913      {
914        message = ERR_BS_BUFFER_POS_NEGATIVE.get(pos);
915      }
916      else
917      {
918        message = ERR_BS_BUFFER_POS_TOO_LARGE.get(pos, endPos);
919      }
920
921      final IndexOutOfBoundsException e =
922           new IndexOutOfBoundsException(message);
923      Debug.debugCodingError(e);
924      throw e;
925    }
926    else if (pos == endPos)
927    {
928      return append(s);
929    }
930    else
931    {
932      return insert(pos, StaticUtils.getBytes(s.toString()));
933    }
934  }
935
936
937
938  /**
939   * Inserts the provided integer value to this buffer.
940   *
941   * @param  pos  The position at which the value is to be inserted.
942   * @param  i    The integer value to be inserted into this buffer.
943   *
944   * @return  A reference to this buffer.
945   *
946   * @throws  IndexOutOfBoundsException  If the specified position is negative
947   *                                     or greater than the current length.
948   */
949  public ByteStringBuffer insert(final int pos, final int i)
950         throws IndexOutOfBoundsException
951  {
952    final int length = getBytes(i);
953    return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length);
954  }
955
956
957
958  /**
959   * Inserts the provided long value to this buffer.
960   *
961   * @param  pos  The position at which the value is to be inserted.
962   * @param  l    The long value to be inserted into this buffer.
963   *
964   * @return  A reference to this buffer.
965   *
966   * @throws  IndexOutOfBoundsException  If the specified position is negative
967   *                                     or greater than the current length.
968   */
969  public ByteStringBuffer insert(final int pos, final long l)
970         throws IndexOutOfBoundsException
971  {
972    final int length = getBytes(l);
973    return insert(pos, TEMP_NUMBER_BUFFER.get(), 0, length);
974  }
975
976
977
978  /**
979   * Deletes the specified number of bytes from the beginning of the buffer.
980   *
981   * @param  len  The number of bytes to delete.
982   *
983   * @return  A reference to this buffer.
984   *
985   * @throws  IndexOutOfBoundsException  If the specified length is negative,
986   *                                     or if it is greater than the number of
987   *                                     bytes currently contained in this
988   *                                     buffer.
989   */
990  public ByteStringBuffer delete(final int len)
991         throws IndexOutOfBoundsException
992  {
993    return delete(0, len);
994  }
995
996
997
998  /**
999   * Deletes the indicated number of bytes from the specified location in the
1000   * buffer.
1001   *
1002   * @param  off  The position in the buffer at which the content to delete
1003   *              begins.
1004   * @param  len  The number of bytes to remove from the buffer.
1005   *
1006   * @return  A reference to this buffer.
1007   *
1008   * @throws  IndexOutOfBoundsException  If the offset or length is negative, or
1009   *                                     if the combination of the offset and
1010   *                                     length is greater than the end of the
1011   *                                     content in the buffer.
1012   */
1013  public ByteStringBuffer delete(final int off, final int len)
1014         throws IndexOutOfBoundsException
1015  {
1016    if (off < 0)
1017    {
1018      throw new IndexOutOfBoundsException(
1019           ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off));
1020    }
1021    else if (len < 0)
1022    {
1023      throw new IndexOutOfBoundsException(
1024           ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len));
1025    }
1026    else if ((off + len) > endPos)
1027    {
1028      throw new IndexOutOfBoundsException(
1029           ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len, endPos));
1030    }
1031    else if (len == 0)
1032    {
1033      return this;
1034    }
1035    else if (off == 0)
1036    {
1037      if (len == endPos)
1038      {
1039        endPos = 0;
1040        return this;
1041      }
1042      else
1043      {
1044        final int newEndPos = endPos - len;
1045        System.arraycopy(array, len, array, 0, newEndPos);
1046        endPos = newEndPos;
1047        return this;
1048      }
1049    }
1050    else
1051    {
1052      if ((off + len) == endPos)
1053      {
1054        endPos = off;
1055        return this;
1056      }
1057      else
1058      {
1059        final int bytesToCopy = endPos - (off+len);
1060        System.arraycopy(array, (off+len), array, off, bytesToCopy);
1061        endPos -= len;
1062        return this;
1063      }
1064    }
1065  }
1066
1067
1068
1069  /**
1070   * Sets the contents of this buffer to include only the provided boolean
1071   * value.
1072   *
1073   * @param  b  The boolean value to use as the content for this buffer.
1074   *
1075   * @return  A reference to this buffer.
1076   */
1077  public ByteStringBuffer set(final boolean b)
1078  {
1079    if (b)
1080    {
1081      return set(TRUE_VALUE_BYTES, 0, 4);
1082    }
1083    else
1084    {
1085      return set(FALSE_VALUE_BYTES, 0, 5);
1086    }
1087  }
1088
1089
1090
1091  /**
1092   * Sets the contents of this buffer to include only the provided byte.
1093   *
1094   * @param  b  The byte to use as the content for this buffer.
1095   *
1096   * @return  A reference to this buffer.
1097   */
1098  public ByteStringBuffer set(final byte b)
1099  {
1100    endPos = 0;
1101    return append(b);
1102  }
1103
1104
1105
1106  /**
1107   * Sets the contents of this buffer to the contents of the provided byte
1108   * array.
1109   *
1110   * @param  b  The byte array containing the content to use for this buffer.
1111   *
1112   * @throws  NullPointerException  If the provided array is {@code null}.
1113   *
1114   * @return  A reference to this buffer.
1115   */
1116  public ByteStringBuffer set(final byte[] b)
1117         throws NullPointerException
1118  {
1119    if (b == null)
1120    {
1121      final NullPointerException e =
1122           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
1123      Debug.debugCodingError(e);
1124      throw e;
1125    }
1126
1127    endPos = 0;
1128    return append(b, 0, b.length);
1129  }
1130
1131
1132
1133  /**
1134   * Sets the contents of this buffer to the specified portion of the provided
1135   * byte array.
1136   *
1137   * @param  b    The byte array containing the content to use for this buffer.
1138   * @param  off  The offset within the array at which to begin copying data.
1139   * @param  len  The number of bytes to copy.
1140   *
1141   * @return  A reference to this buffer.
1142   *
1143   * @throws  NullPointerException  If the provided array is {@code null}.
1144   *
1145   * @throws  IndexOutOfBoundsException  If the offset or length are negative,
1146   *                                     if the offset plus the length is beyond
1147   *                                     the end of the provided array.
1148   */
1149  public ByteStringBuffer set(final byte[] b, final int off, final int len)
1150         throws NullPointerException, IndexOutOfBoundsException
1151  {
1152    if (b == null)
1153    {
1154      final NullPointerException e =
1155           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
1156      Debug.debugCodingError(e);
1157      throw e;
1158    }
1159
1160    if ((off < 0) || (len < 0) || (off+len > b.length))
1161    {
1162      final String message;
1163      if (off < 0)
1164      {
1165        message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off);
1166      }
1167      else if (len < 0)
1168      {
1169        message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len);
1170      }
1171      else
1172      {
1173        message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len,
1174                                                                 b.length);
1175      }
1176
1177      final IndexOutOfBoundsException e =
1178           new IndexOutOfBoundsException(message);
1179      Debug.debugCodingError(e);
1180      throw e;
1181    }
1182
1183    endPos = 0;
1184    return append(b, off, len);
1185  }
1186
1187
1188
1189  /**
1190   * Sets the contents of this buffer to the contents of the provided byte
1191   * string.
1192   *
1193   * @param  b  The byte string that should be used as the content for this
1194   *            buffer.
1195   *
1196   * @throws  NullPointerException  If the provided byte string is {@code null}.
1197   *
1198   * @return  A reference to this buffer.
1199   */
1200  public ByteStringBuffer set(final ByteString b)
1201         throws NullPointerException
1202  {
1203    if (b == null)
1204    {
1205      final NullPointerException e =
1206           new NullPointerException(ERR_BS_BUFFER_BYTE_STRING_NULL.get());
1207      Debug.debugCodingError(e);
1208      throw e;
1209    }
1210
1211    endPos = 0;
1212    b.appendValueTo(this);
1213    return this;
1214  }
1215
1216
1217
1218  /**
1219   * Sets the contents of this buffer to the contents of the provided byte
1220   * string buffer.
1221   *
1222   * @param  buffer  The buffer whose contents should be used as the content for
1223   *                 this buffer.
1224   *
1225   * @throws  NullPointerException  If the provided buffer is {@code null}.
1226   *
1227   * @return  A reference to this buffer.
1228   */
1229  public ByteStringBuffer set(final ByteStringBuffer buffer)
1230         throws NullPointerException
1231  {
1232    if (buffer == null)
1233    {
1234      final NullPointerException e =
1235           new NullPointerException(ERR_BS_BUFFER_BUFFER_NULL.get());
1236      Debug.debugCodingError(e);
1237      throw e;
1238    }
1239
1240    endPos = 0;
1241    return append(buffer.array, 0, buffer.endPos);
1242  }
1243
1244
1245
1246  /**
1247   * Sets the contents of this buffer to include only the provided character.
1248   *
1249   * @param  c  The character use as the content for this buffer.
1250   *
1251   * @return  A reference to this buffer.
1252   */
1253  public ByteStringBuffer set(final char c)
1254  {
1255    endPos = 0;
1256    return append(c);
1257  }
1258
1259
1260
1261  /**
1262   * Sets the contents of this buffer to the contents of the provided character
1263   * array.
1264   *
1265   * @param  c  The character array containing the content to use for this
1266   *            buffer.
1267   *
1268   * @throws  NullPointerException  If the provided array is {@code null}.
1269   *
1270   * @return  A reference to this buffer.
1271   */
1272  public ByteStringBuffer set(final char[] c)
1273         throws NullPointerException
1274  {
1275    if (c == null)
1276    {
1277      final NullPointerException e =
1278           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
1279      Debug.debugCodingError(e);
1280      throw e;
1281    }
1282
1283    endPos = 0;
1284    return append(c, 0, c.length);
1285  }
1286
1287
1288
1289  /**
1290   * Sets the contents of this buffer to the specified portion of the provided
1291   * character array.
1292   *
1293   * @param  c    The character array containing the content to use for this
1294   *              buffer.
1295   * @param  off  The offset within the array at which to begin copying data.
1296   * @param  len  The number of characters to copy.
1297   *
1298   * @return  A reference to this buffer.
1299   *
1300   * @throws  NullPointerException  If the provided array is {@code null}.
1301   *
1302   * @throws  IndexOutOfBoundsException  If the offset or length are negative,
1303   *                                     if the offset plus the length is beyond
1304   *                                     the end of the provided array.
1305   */
1306  public ByteStringBuffer set(final char[] c, final int off, final int len)
1307         throws NullPointerException, IndexOutOfBoundsException
1308  {
1309    if (c == null)
1310    {
1311      final NullPointerException e =
1312           new NullPointerException(ERR_BS_BUFFER_ARRAY_NULL.get());
1313      Debug.debugCodingError(e);
1314      throw e;
1315    }
1316
1317    if ((off < 0) || (len < 0) || (off+len > c.length))
1318    {
1319      final String message;
1320      if (off < 0)
1321      {
1322        message = ERR_BS_BUFFER_OFFSET_NEGATIVE.get(off);
1323      }
1324      else if (len < 0)
1325      {
1326        message = ERR_BS_BUFFER_LENGTH_NEGATIVE.get(len);
1327      }
1328      else
1329      {
1330        message = ERR_BS_BUFFER_OFFSET_PLUS_LENGTH_TOO_LARGE.get(off, len,
1331                                                                 c.length);
1332      }
1333
1334      final IndexOutOfBoundsException e =
1335           new IndexOutOfBoundsException(message);
1336      Debug.debugCodingError(e);
1337      throw e;
1338    }
1339
1340    endPos = 0;
1341    return append(c, off, len);
1342  }
1343
1344
1345
1346  /**
1347   * Sets the contents of this buffer to the specified portion of the provided
1348   * character sequence.
1349   *
1350   * @param  s  The character sequence to use as the content for this buffer.
1351   *
1352   * @throws  NullPointerException  If the provided character sequence is
1353   *                                {@code null}.
1354   *
1355   * @return  A reference to this buffer.
1356   */
1357  public ByteStringBuffer set(final CharSequence s)
1358         throws NullPointerException
1359  {
1360    if (s == null)
1361    {
1362      final NullPointerException e =
1363           new NullPointerException(ERR_BS_BUFFER_CHAR_SEQUENCE_NULL.get());
1364      Debug.debugCodingError(e);
1365      throw e;
1366    }
1367
1368    endPos = 0;
1369    return append(s);
1370  }
1371
1372
1373
1374  /**
1375   * Sets the contents of this buffer to include only the provided integer
1376   * value.
1377   *
1378   * @param  i  The integer value to use as the content for this buffer.
1379   *
1380   * @return  A reference to this buffer.
1381   */
1382  public ByteStringBuffer set(final int i)
1383  {
1384    final int length = getBytes(i);
1385    return set(TEMP_NUMBER_BUFFER.get(), 0, length);
1386  }
1387
1388
1389
1390  /**
1391   * Sets the contents of this buffer to include only the provided long value.
1392   *
1393   * @param  l  The long value to use as the content for this buffer.
1394   *
1395   * @return  A reference to this buffer.
1396   */
1397  public ByteStringBuffer set(final long l)
1398  {
1399    final int length = getBytes(l);
1400    return set(TEMP_NUMBER_BUFFER.get(), 0, length);
1401  }
1402
1403
1404
1405  /**
1406   * Clears the contents of this buffer.
1407   *
1408   * @return  A reference to this buffer.
1409   */
1410  public ByteStringBuffer clear()
1411  {
1412    endPos = 0;
1413    return this;
1414  }
1415
1416
1417
1418  /**
1419   * Clears the contents of this buffer.
1420   *
1421   * @param  zero  Indicates whether to overwrite the content of the backing
1422   *               array with all zeros in order to wipe out any sensitive data
1423   *               it may contain.
1424   *
1425   * @return  A reference to this buffer.
1426   */
1427  public ByteStringBuffer clear(final boolean zero)
1428  {
1429    endPos = 0;
1430
1431    if (zero)
1432    {
1433      Arrays.fill(array, (byte) 0x00);
1434    }
1435
1436    return this;
1437  }
1438
1439
1440
1441  /**
1442   * Retrieves the current backing array for this buffer.  The data will begin
1443   * at position 0 and will contain {@link ByteStringBuffer#length} bytes.
1444   *
1445   * @return  The current backing array for this buffer.
1446   */
1447  public byte[] getBackingArray()
1448  {
1449    return array;
1450  }
1451
1452
1453
1454  /**
1455   * Indicates whether this buffer is currently empty.
1456   *
1457   * @return  {@code true} if this buffer is currently empty, or {@code false}
1458   *          if not.
1459   */
1460  public boolean isEmpty()
1461  {
1462    return (endPos == 0);
1463  }
1464
1465
1466
1467  /**
1468   * Retrieves the number of bytes contained in this buffer.
1469   *
1470   * @return  The number of bytes contained in this buffer.
1471   */
1472  public int length()
1473  {
1474    return endPos;
1475  }
1476
1477
1478
1479  /**
1480   * Sets the length of this buffer to the specified value.  If the new length
1481   * is greater than the current length, the value will be padded with zeroes.
1482   *
1483   * @param  length  The new length to use for the buffer.  It must be greater
1484   *                 than or equal to zero.
1485   *
1486   * @throws  IndexOutOfBoundsException  If the provided length is negative.
1487   */
1488  public void setLength(final int length)
1489         throws IndexOutOfBoundsException
1490  {
1491    if (length < 0)
1492    {
1493      final IndexOutOfBoundsException e = new IndexOutOfBoundsException(
1494           ERR_BS_BUFFER_LENGTH_NEGATIVE.get(length));
1495      Debug.debugCodingError(e);
1496      throw e;
1497    }
1498
1499    if (length > endPos)
1500    {
1501      ensureCapacity(length);
1502      Arrays.fill(array, endPos, length, (byte) 0x00);
1503      endPos = length;
1504    }
1505    else
1506    {
1507      endPos = length;
1508    }
1509  }
1510
1511
1512
1513  /**
1514   * Returns the current capacity for this buffer.
1515   *
1516   * @return  The current capacity for this buffer.
1517   */
1518  public int capacity()
1519  {
1520    return capacity;
1521  }
1522
1523
1524
1525  /**
1526   * Ensures that the total capacity of this buffer is at least equal to the
1527   * specified size.
1528   *
1529   * @param  minimumCapacity  The minimum capacity for this buffer.
1530   */
1531  public void ensureCapacity(final int minimumCapacity)
1532  {
1533    if (capacity < minimumCapacity)
1534    {
1535      final int newCapacity = Math.max(minimumCapacity, (2 * capacity) + 2);
1536      final byte[] newArray = new byte[newCapacity];
1537      System.arraycopy(array, 0, newArray, 0, capacity);
1538      array = newArray;
1539      capacity = newCapacity;
1540    }
1541  }
1542
1543
1544
1545  /**
1546   * Sets the capacity equal to the specified value.  If the provided capacity
1547   * is less than the current length, then the length will be reduced to the
1548   * new capacity.
1549   *
1550   * @param  capacity  The new capacity for this buffer.  It must be greater
1551   *                   than or equal to zero.
1552   *
1553   * @throws  IndexOutOfBoundsException  If the provided capacity is negative.
1554   */
1555  public void setCapacity(final int capacity)
1556         throws IndexOutOfBoundsException
1557  {
1558    if (capacity < 0)
1559    {
1560      final IndexOutOfBoundsException e = new IndexOutOfBoundsException(
1561           ERR_BS_BUFFER_CAPACITY_NEGATIVE.get(capacity));
1562      Debug.debugCodingError(e);
1563      throw e;
1564    }
1565
1566    if (this.capacity == capacity)
1567    {
1568      return;
1569    }
1570    else if (this.capacity < capacity)
1571    {
1572      final byte[] newArray = new byte[capacity];
1573      System.arraycopy(array, 0, newArray, 0, this.capacity);
1574      array = newArray;
1575      this.capacity = capacity;
1576    }
1577    else
1578    {
1579      final byte[] newArray = new byte[capacity];
1580      System.arraycopy(array, 0, newArray, 0, capacity);
1581      array = newArray;
1582      endPos = Math.min(endPos, capacity);
1583      this.capacity = capacity;
1584    }
1585  }
1586
1587
1588
1589  /**
1590   * Trims the backing array to the minimal size required for this buffer.
1591   *
1592   * @return  A reference to this buffer.
1593   */
1594  public ByteStringBuffer trimToSize()
1595  {
1596    if (endPos != capacity)
1597    {
1598      final byte[] newArray = new byte[endPos];
1599      System.arraycopy(array, 0, newArray, 0, endPos);
1600      array = newArray;
1601      capacity = endPos;
1602    }
1603
1604    return this;
1605  }
1606
1607
1608
1609  /**
1610   * Returns a new byte array with the content from this buffer.
1611   *
1612   * @return  A byte array containing the content from this buffer.
1613   */
1614  public byte[] toByteArray()
1615  {
1616    final byte[] newArray = new byte[endPos];
1617    System.arraycopy(array, 0, newArray, 0, endPos);
1618    return newArray;
1619  }
1620
1621
1622
1623  /**
1624   * Returns a new byte string with the content from this buffer.
1625   *
1626   * @return  A byte string with the content from this buffer.
1627   */
1628  public ByteString toByteString()
1629  {
1630    return new ASN1OctetString(toByteArray());
1631  }
1632
1633
1634
1635  /**
1636   * Creates an input stream that may be used to read content from this buffer.
1637   * This buffer should not be altered while the input stream is being used.
1638   *
1639   * @return  An input stream that may be used to read content from this buffer.
1640   */
1641  public InputStream asInputStream()
1642  {
1643    return new ByteArrayInputStream(array, 0, endPos);
1644  }
1645
1646
1647
1648  /**
1649   * Writes the contents of this byte string buffer to the provided output
1650   * stream.
1651   *
1652   * @param  outputStream  The output stream to which the data should be
1653   *                       written.
1654   *
1655   * @throws  IOException  If a problem occurs while writing to the provided
1656   *                       output stream.
1657   */
1658  public void write(final OutputStream outputStream)
1659         throws IOException
1660  {
1661    outputStream.write(array, 0, endPos);
1662  }
1663
1664
1665
1666  /**
1667   * Adds the bytes comprising the string representation of the provided long
1668   * value to the temporary number buffer.
1669   *
1670   * @param  l  The long value to be appended.
1671   *
1672   * @return  The number of bytes in the string representation of the value.
1673   */
1674  private static int getBytes(final long l)
1675  {
1676    // NOTE:  This method is probably not as efficient as it could be, but it is
1677    // more important to avoid the need for memory allocation.
1678    byte[] b = TEMP_NUMBER_BUFFER.get();
1679    if (b == null)
1680    {
1681      b = new byte[20];
1682      TEMP_NUMBER_BUFFER.set(b);
1683    }
1684
1685    if (l == Long.MIN_VALUE)
1686    {
1687      b[0]  = '-';
1688      b[1]  = '9';
1689      b[2]  = '2';
1690      b[3]  = '2';
1691      b[4]  = '3';
1692      b[5]  = '3';
1693      b[6]  = '7';
1694      b[7]  = '2';
1695      b[8]  = '0';
1696      b[9]  = '3';
1697      b[10] = '6';
1698      b[11] = '8';
1699      b[12] = '5';
1700      b[13] = '4';
1701      b[14] = '7';
1702      b[15] = '7';
1703      b[16] = '5';
1704      b[17] = '8';
1705      b[18] = '0';
1706      b[19] = '8';
1707      return 20;
1708    }
1709    else if (l == 0L)
1710    {
1711      b[0] = '0';
1712      return 1;
1713    }
1714
1715    int pos = 0;
1716    long v = l;
1717    if (l < 0)
1718    {
1719      b[0] = '-';
1720      pos = 1;
1721      v = Math.abs(l);
1722    }
1723
1724    long divisor;
1725    if (v <= 9L)
1726    {
1727      divisor = 1L;
1728    }
1729    else if (v <= 99L)
1730    {
1731      divisor = 10L;
1732    }
1733    else if (v <= 999L)
1734    {
1735      divisor = 100L;
1736    }
1737    else if (v <= 9999L)
1738    {
1739      divisor = 1000L;
1740    }
1741    else if (v <= 99_999L)
1742    {
1743      divisor = 10_000L;
1744    }
1745    else if (v <= 999_999L)
1746    {
1747      divisor = 100_000L;
1748    }
1749    else if (v <= 9_999_999L)
1750    {
1751      divisor = 1_000_000L;
1752    }
1753    else if (v <= 99_999_999L)
1754    {
1755      divisor = 10_000_000L;
1756    }
1757    else if (v <= 999_999_999L)
1758    {
1759      divisor = 100_000_000L;
1760    }
1761    else if (v <= 9_999_999_999L)
1762    {
1763      divisor = 1_000_000_000L;
1764    }
1765    else if (v <= 99_999_999_999L)
1766    {
1767      divisor = 10_000_000_000L;
1768    }
1769    else if (v <= 999_999_999_999L)
1770    {
1771      divisor = 100_000_000_000L;
1772    }
1773    else if (v <= 9_999_999_999_999L)
1774    {
1775      divisor = 1_000_000_000_000L;
1776    }
1777    else if (v <= 99_999_999_999_999L)
1778    {
1779      divisor = 10_000_000_000_000L;
1780    }
1781    else if (v <= 999_999_999_999_999L)
1782    {
1783      divisor = 100_000_000_000_000L;
1784    }
1785    else if (v <= 9_999_999_999_999_999L)
1786    {
1787      divisor = 1_000_000_000_000_000L;
1788    }
1789    else if (v <= 99_999_999_999_999_999L)
1790    {
1791      divisor = 10_000_000_000_000_000L;
1792    }
1793    else if (v <= 999_999_999_999_999_999L)
1794    {
1795      divisor = 100_000_000_000_000_000L;
1796    }
1797    else
1798    {
1799      divisor = 1_000_000_000_000_000_000L;
1800    }
1801
1802    while (true)
1803    {
1804      final long digit = v / divisor;
1805      switch ((int) digit)
1806      {
1807        case 0:
1808          b[pos++] = '0';
1809          break;
1810        case 1:
1811          b[pos++] = '1';
1812          break;
1813        case 2:
1814          b[pos++] = '2';
1815          break;
1816        case 3:
1817          b[pos++] = '3';
1818          break;
1819        case 4:
1820          b[pos++] = '4';
1821          break;
1822        case 5:
1823          b[pos++] = '5';
1824          break;
1825        case 6:
1826          b[pos++] = '6';
1827          break;
1828        case 7:
1829          b[pos++] = '7';
1830          break;
1831        case 8:
1832          b[pos++] = '8';
1833          break;
1834        case 9:
1835          b[pos++] = '9';
1836          break;
1837      }
1838
1839      if (divisor == 1L)
1840      {
1841        break;
1842      }
1843      else
1844      {
1845        v -= (divisor * digit);
1846        if (v == 0)
1847        {
1848          while (divisor > 1L)
1849          {
1850            b[pos++] = '0';
1851            divisor /= 10L;
1852          }
1853
1854          break;
1855        }
1856
1857        divisor /= 10L;
1858      }
1859    }
1860
1861    return pos;
1862  }
1863
1864
1865
1866  /**
1867   * Retrieves a hash code for this byte array.
1868   *
1869   * @return  A hash code for this byte array.
1870   */
1871  @Override()
1872  public int hashCode()
1873  {
1874    int hashCode = 0;
1875
1876    for (int i=0; i < endPos; i++)
1877    {
1878      hashCode += array[i];
1879    }
1880
1881    return hashCode;
1882  }
1883
1884
1885
1886  /**
1887   * Indicates whether the provided object is a byte string buffer with contents
1888   * that are identical to that of this buffer.
1889   *
1890   * @param  o  The object for which to make the determination.
1891   *
1892   * @return  {@code true} if the provided object is considered equal to this
1893   *          buffer, or {@code false} if not.
1894   */
1895  @Override()
1896  public boolean equals(final Object o)
1897  {
1898    if (o == null)
1899    {
1900      return false;
1901    }
1902
1903    if (o == this)
1904    {
1905      return true;
1906    }
1907
1908    if (! (o instanceof ByteStringBuffer))
1909    {
1910      return false;
1911    }
1912
1913    final ByteStringBuffer b = (ByteStringBuffer) o;
1914    if (endPos != b.endPos)
1915    {
1916      return false;
1917    }
1918
1919    for (int i=0; i < endPos; i++)
1920    {
1921      if (array[i] != b.array[i])
1922      {
1923        return false;
1924      }
1925    }
1926
1927    return true;
1928  }
1929
1930
1931
1932  /**
1933   * Creates a duplicate of this byte string buffer.  It will have identical
1934   * content but with a different backing array.  Changes to this byte string
1935   * buffer will not impact the duplicate, and vice-versa.
1936   *
1937   * @return  A duplicate of this byte string buffer.
1938   */
1939  public ByteStringBuffer duplicate()
1940  {
1941    final ByteStringBuffer newBuffer = new ByteStringBuffer(endPos);
1942    return newBuffer.append(this);
1943  }
1944
1945
1946
1947  /**
1948   * Retrieves a string representation of the contents for this buffer.
1949   *
1950   * @return  A string representation of the contents for this buffer.
1951   */
1952  @Override()
1953  public String toString()
1954  {
1955    return StaticUtils.toUTF8String(array, 0, endPos);
1956  }
1957}