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