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