001    /*
002     * Copyright (c) 2003, The Regents of the University of California, through
003     * Lawrence Berkeley National Laboratory (subject to receipt of any required
004     * approvals from the U.S. Dept. of Energy). All rights reserved.
005     */
006    package gov.lbl.dsd.sea.nio.util;
007    
008    import java.nio.ByteBuffer;
009    import java.nio.charset.Charset;
010    
011    /**
012    Highly efficient resizable list holding <code>byte</code> elements;
013    implemented with arrays; Convenient to assemble variable-sized byte messages
014    if message lengths are a priori unknown.
015    <p>
016    Manipulating primitive values other than bytes and ints is not directly
017    supported. However, this can be done along the following lines:
018    <pre>
019     
020        // append double at end
021        list = ...
022        double elemToAdd = 1234.0;
023        int s = list.size();
024        list.setSize(s + 8);
025        list.asByteBuffer().putDouble(s, elemToAdd);
026     
027        // or insertion at beginning can be done this way
028        list = ...
029        double elemToInsert = 1234.0;
030        list.replace(0, 0, 0, 8); // insert 8 bytes at beginning
031        list.asByteBuffer().putDouble(0, elemToInsert);
032     
033    </pre>
034     
035    @author whoschek@lbl.gov
036    @author $Author: hoschek3 $
037    @version $Revision: 1.34 $, $Date: 2004/06/17 18:34:39 $
038    */
039    public class ByteArrayList implements java.io.Serializable {
040    
041            /**
042             * The array into which the elements of the list are stored. The
043             * capacity of the list is the length of this array.
044             */
045            protected byte[] elements;
046    
047            /**
048             * The current number of elements contained in this list.
049             */
050            protected int size;
051                    
052            /**
053             * Constructs an empty list.
054             */
055            public ByteArrayList() {
056                    this(64);
057            }
058    
059            /**
060             * Constructs an empty list with the specified initial capacity.
061             * 
062             * @param initialCapacity
063             *            the number of elements the receiver can hold without
064             *            auto-expanding itself by allocating new internal memory.
065             */
066            public ByteArrayList(int initialCapacity) {
067                    this(new byte[initialCapacity]);
068                    size = 0;
069            }
070    
071            /**
072             * Constructs a list SHARING the specified elements. The initial size and
073             * capacity of the list is the length of the array.
074             * 
075             * <b>WARNING: </b> For efficiency reasons and to keep memory usage low,
076             * <b>the array is not copied </b>. So if subsequently you modify the
077             * specified array directly via the [] operator, be sure you know what
078             * you're doing.
079             * 
080             * @param elems
081             *            the array to be backed by the the constructed list
082             */
083            public ByteArrayList(byte[] elems) {
084                    elements = elems;
085                    size = elems.length;
086            }
087    
088            /**
089             * Constructs a list containing a copy of the remaining buffer elements. The
090             * initial size and capacity of the list is
091             * <code>elements.remaining()</code>.
092             * 
093             * @param elems
094             *            the elements initially to be added to the list
095             *  
096             */
097            public ByteArrayList(ByteBuffer elems) {
098                    this(elems.remaining());
099                    add(elems);
100            }
101    
102            /**
103             * Constructs a list containing the encoded representation of the given
104             * string.
105             * 
106             * @param str
107             *            the string to convert.
108             * @param charset
109             *            the requested charset to convert with (e.g.
110             *            Charset.forName("US-ASCII"), Charset.forName("UTF-8"))
111             */
112            public ByteArrayList(String str, Charset charset) {
113                    this(BufferUtil.toByteBuffer(str, charset));
114            }
115    
116    //      /**
117    //       * Constructs a list SHARING elements with the given buffer's contents. The
118    //       * initial size and capacity of the list is newSize and buffer.capacity(),
119    //       * respectively.
120    //       * 
121    //       * <b>WARNING: </b> For efficiency reasons and to keep memory usage low,
122    //       * <b>the buffer is not copied </b>. So if subsequently you modify the
123    //       * specified buffer directly, be sure you know what you're doing.
124    //       * 
125    //       * @param buffer
126    //       *            the buffer to use
127    //       * @param newSize
128    //       *            the desired list size
129    //       */
130    //      public ByteArrayList(ByteBuffer buffer, int newSize) {
131    //              if (! buffer.hasArray() || buffer.arrayOffset() != 0) {
132    //                      throw new IllegalArgumentException();
133    //              }
134    //              this.elements = buffer.array();
135    //              if (newSize < 0 || newSize > elements.length) throw new IllegalArgumentException();
136    //              this.size = newSize;
137    //      }
138    //
139            /**
140             * Appends the specified element to the end of this list.
141             * 
142             * @param elem
143             *            element to be appended to this list.
144             */
145            public void add(byte elem) {
146                    if (size == elements.length) ensureCapacity(size + 1);
147                    elements[size++] = elem;
148            }
149    
150            /**
151             * Appends the specified elements to the end of this list.
152             * 
153             * @param elems
154             *            elements to be appended.
155             */
156            public void add(byte[] elems) {
157                    insert(size, elems);
158            }
159            
160            /**
161             * Appends the specified elements to the end of this list.
162             * 
163             * @param elems
164             *            elements to be appended.
165             */
166            public void add(ByteArrayList elems) {
167                    insert(size, elems);
168            }
169            
170            /**
171             * Appends the remaining buffer elements to the end of this list.
172             * 
173             * @param elems
174             *            elements to be appended.
175             */
176            public void add(ByteBuffer elems) {
177                    insert(size, elems);
178            }
179            
180            /**
181             * Appends the encoded representation of the given string.
182             * 
183             * @param str
184             *            the string to convert.
185             * @param charset
186             *            the requested charset to convert with (e.g.
187             *            Charset.forName("US-ASCII"), Charset.forName("UTF-8"))
188             */
189            public void add(String str, Charset charset) {
190                    add(BufferUtil.toByteBuffer(str, charset));
191            }
192    
193            /**
194             * Returns the elements currently stored, including invalid elements between
195             * size and capacity, if any.
196             * <p>
197             * <b>WARNING: </b> For efficiency reasons and to keep memory usage low,
198             * <b>the array is not copied </b>. So if subsequently you modify the
199             * returned array directly via the [] operator, be sure you know what you're
200             * doing.
201             * 
202             * @return the elements currently stored.
203             */
204            public byte[] asArray() {
205                    return elements;
206            }
207    
208            /**
209             * Returns a byte buffer SHARING elements with the receiver; the buffer will
210             * have the default NIO byte order, which is ByteOrder.BIG_ENDIAN.
211             * <p>
212             * <b>WARNING: </b> For efficiency reasons and to keep memory usage low,
213             * <b>the array is not copied </b>. So if subsequently you modify the
214             * returned bytebuffer, be sure you know what you're doing.
215             */
216            public ByteBuffer asByteBuffer() {
217                    return ByteBuffer.wrap(elements, 0, size);
218            }
219    
220            /**
221             * Removes all elements from the receiver. The receiver will be empty after
222             * this call returns, but keep its current capacity.
223             */
224            public void clear() {
225                    size = 0;
226            }
227            
228            /**
229             * Returns a deep copy of the receiver.
230             * 
231             * @return a deep copy of the receiver.
232             */
233            public ByteArrayList copy() {
234                    return new ByteArrayList(toArray());
235            }
236    
237            /**
238             * Compares the specified Object with the receiver. Returns true if and only
239             * if the specified Object is also an ArrayList of the same type, both Lists
240             * have the same size, and all corresponding pairs of elements in the two
241             * Lists are identical. In other words, two Lists are defined to be equal if
242             * they contain the same elements in the same order.
243             * 
244             * @param otherObj
245             *            the Object to be compared for equality with the receiver.
246             * @return true if the specified Object is equal to the receiver.
247             */
248            public boolean equals(Object otherObj) { //delta
249                    // overridden for performance only.
250                    if (!(otherObj instanceof ByteArrayList))
251                                    return super.equals(otherObj);
252                    if (this == otherObj) return true;
253                    if (otherObj == null) return false;
254                    ByteArrayList other = (ByteArrayList) otherObj;
255                    if (size != other.size) return false;
256    
257                    byte[] theElements = asArray();
258                    byte[] otherElements = other.asArray();
259                    for (int i = size; --i >= 0;) {
260                            if (theElements[i] != otherElements[i]) return false;
261                    }
262                    return true;
263            }
264    
265            /**
266             * Ensures that the receiver can hold at least the specified number of
267             * elements without needing to allocate new internal memory. If necessary,
268             * allocates new internal memory and increases the capacity of the receiver.
269             * 
270             * @param minCapacity
271             *            the desired minimum capacity.
272             */
273            public void ensureCapacity(int minCapacity) {
274                    int oldCapacity = elements.length;
275                    if (minCapacity > oldCapacity) {
276                            int newCapacity = (oldCapacity * 3) / 2 + 1;
277                            if (newCapacity < minCapacity) {
278                                    newCapacity = minCapacity;
279                            }
280    
281                            byte[] newArray = new byte[newCapacity];
282                            System.arraycopy(elements, 0, newArray, 0, size);
283                            elements = newArray;
284                    }
285            }
286    
287            /**
288             * Returns the element at the specified position in the receiver.
289             * 
290             * @param index
291             *            index of element to return.
292             * @throws IndexOutOfBoundsException if index is out of range.
293             */
294            public final byte get(int index) {
295                    checkIndex(index);
296                    return elements[index];
297            }
298    
299            /**
300             * Returns the 4 byte integer at the specified position in the receiver;
301             * using the NIO default byte order, which is ByteOrder.BIG_ENDIAN.
302             * 
303             * @param index
304             *            index of first byte of the integer.
305             * @throws IndexOutOfBoundsException if index is out of range.
306             */
307            public int getInt(int index) {
308                    checkIndex(index);
309                    checkIndex(index + 4 - 1);
310                    return asByteBuffer().getInt(index);
311                    //return ByteConverter.readInt(elements, index);
312            }
313    
314            /**
315             * Returns a decoded string representation of the receiver's bytes in the
316             * given range.
317             * 
318             * @param from
319             *            the index of the first element (inclusive).
320             * @param to
321             *            the index of the last element (exclusive).
322             * @param charset
323             *            the requested charset to convert with (e.g.
324             *            Charset.forName("US-ASCII"), Charset.forName("UTF-8"))
325             * @throws IndexOutOfBoundsException
326             *             if indexes are out of range
327             */
328            public String getString(int from, int to, Charset charset) {
329                    checkFromTo(from, to);
330                    return BufferUtil.toString(this.elements, from, to-from, charset);
331            }
332    
333            /**
334             * Returns the index of the first occurrence of the specified element.
335             * Returns <code>-1</code> if the receiver does not contain this element.
336             * Searches between <code>from</code>, inclusive and <code>to</code>,
337             * exclusive. Tests for identity.
338             * 
339             * @param elem
340             *            element to search for.
341             * @param from
342             *            the leftmost search position, inclusive.
343             * @param to
344             *            the rightmost search position, exclusive.
345             * @return the index of the first occurrence of the element in the receiver;
346             *         returns <code>-1</code> if the element is not found.
347             * @throws IndexOutOfBoundsException if indexes are out of range
348             */
349            public int indexOf(byte elem, int from, int to) {
350                    checkFromTo(from, to);
351    
352                    byte[] theElements = elements;
353                    for (int i = from; i < to; i++) {
354                            if (elem == theElements[i]) { return i; } //found
355                    }
356                    return -1; //not found
357            }
358    
359    //      /**
360    //       * Returns the index of the first occurrence of the specified element.
361    //       * Returns <code>-1</code> if the receiver does not contain this element.
362    //       * 
363    //       * @param elem
364    //       *            the element to be searched for.
365    //       * @return the index of the first occurrence of the element in the receiver;
366    //       *         returns <code>-1</code> if the element is not found.
367    //       */
368    //      public int indexOf(byte elem) { //delta
369    //              return indexOf(elem, 0, size);
370    //      }
371    //
372            /**
373             * Inserts the specified element before the specified position into the
374             * receiver. Shifts the element currently at that position (if any) and any
375             * subsequent elements to the right.
376             * 
377             * @param index
378             *            index before which the specified element is to be inserted
379             * @param elem
380             *            element to insert.
381             */
382            public void insert(int index, byte elem) {
383                    replace(index, 0, elem, 1);
384            }
385            
386            /**
387             * Inserts the specified elements before the specified position into the
388             * receiver. Shifts the element currently at that position (if any) and any
389             * subsequent elements to the right.
390             * 
391             * @param index
392             *            index before which the specified elements are to be inserted
393             * @param elems
394             *            elements to insert.
395             */
396            public void insert(int index, byte[] elems) {
397                    replace(index, 0, elems);
398            }
399            
400            /**
401             * Inserts the specified elements before the specified position into the
402             * receiver. Shifts the element currently at that position (if any) and any
403             * subsequent elements to the right.
404             * 
405             * @param index
406             *            index before which the specified elements are to be inserted
407             * @param elems
408             *            elements to insert.
409             */
410            public void insert(int index, ByteArrayList elems) {
411                    replace(index, 0, elems);
412            }
413            
414            /**
415             * Inserts the remaining buffer elements before the specified position into
416             * the receiver. Shifts the element currently at that position (if any) and
417             * any subsequent elements to the right.
418             * 
419             * @param index
420             *            index before which the specified elements are to be inserted
421             * @param elems
422             *            elements to insert.
423             */
424            public void insert(int index, ByteBuffer elems) {
425                    replace(index, 0, elems);
426            }
427            
428    //      /**
429    //       * Returns the index of the last occurrence of the specified element.
430    //       * Returns <code>-1</code> if the receiver does not contain this element.
431    //       * 
432    //       * @param elem
433    //       *            the element to be searched for.
434    //       * @return the index of the last occurrence of the element in the receiver;
435    //       *         returns <code>-1</code> if the element is not found.
436    //       */
437    //      public int lastIndexOf(byte elem) {
438    //              return lastIndexOf(elem, 0, size);
439    //      }
440    
441            /**
442             * Returns the index of the last occurrence of the specified element.
443             * Returns <code>-1</code> if the receiver does not contain this element.
444             * Searches beginning at <code>to</code>, inclusive until
445             * <code>from</code>, exclusive. Tests for identity.
446             * 
447             * @param elem
448             *            element to search for.
449             * @param from
450             *            the leftmost search position, inclusive.
451             * @param to
452             *            the rightmost search position, exclusive.
453             * @return the index of the last occurrence of the element in the receiver;
454             *         returns <code>-1</code> if the element is not found.
455             * @throws IndexOutOfBoundsException if indexes are out of range.
456             */
457            public int lastIndexOf(byte elem, int from, int to) {
458                    checkFromTo(from, to);
459    
460                    for (int i = to-1; i >= from; i--) {
461                            if (elem == elements[i]) return i; //found
462                    }
463                    return -1; //not found
464            }
465    
466            /**
467             * Removes the element at the specified position from the receiver. Shifts
468             * any subsequent elements to the left.
469             * 
470             * @param index
471             *            the index of the element to removed.
472             * @throws IndexOutOfBoundsException if index is out of range
473             */
474            public void remove(int index) {
475                    remove(index, index+1);
476            }
477    
478            /**
479             * Removes the elements in the given range from the receiver. Shifts
480             * any subsequent elements to the left.
481             * 
482             * @param from
483             *            the index of the first element to removed (inclusive).
484             * @param to
485             *            the index of the last element to removed (exclusive).
486             * @throws IndexOutOfBoundsException if indexes are out of range
487             */
488            public void remove(int from, int to) {
489                    checkFromTo(from, to);
490                    if (from != to) {
491                            System.arraycopy(elements, to, elements, from, size - to);
492                            size -= to - from;
493                    }
494            }
495            
496            /**
497             * Removes from the receiver all elements that are contained in the
498             * specified list. Tests for identity.
499             * 
500             * @param other
501             *            the other list.
502             * @return <code>true</code> if the receiver changed as a result of the
503             *         call.
504             */
505            public boolean removeAll(ByteArrayList other) {
506                    if (other.size() == 0) return false; //nothing to do
507                    int limit = other.size() - 1;
508                    int j = 0;
509    
510                    for (int i = 0; i < size; i++) {
511                            if (other.indexOf(elements[i], 0, limit) < 0)
512                                    elements[j++] = elements[i];
513                    }
514    
515                    boolean modified = (j != size);
516                    size = j;
517                    return modified;
518            }
519    
520            /**
521             * The powerful work horse for all add/insert/replace operations.
522             * One powerful efficient method does it all :-)
523             */
524            private final void shrinkOrExpand(int from, int length, int replacementSize) {
525                    checkLength(from, length);
526                    int diff = replacementSize - length;
527                    if (diff < 0) {
528                            remove(from, from - diff);
529                    }
530                    else if (diff > 0) {
531                            ensureCapacity(this.size + diff);
532                            System.arraycopy(this.elements, from + length, this.elements, from + length + diff, size - (from + length));
533                            this.size += diff;
534                    }
535            }
536            
537            /**
538             * Replaces all elements[from..from+length-1] with the given replacement.
539             * The replacement can have any length.
540             * Examples:
541             * <p> 
542             * <code>[a,b,c,d,e].replace(1,3, [x,y]) --> [a,x,y,e]</code>
543             * <p>
544             * <code>[a,b].replace(1,0, [w,x,y,z]) --> [a,w,x,y,z,b]</code>
545             * 
546             * @param from the index of the first element to replace (inclusive)
547             * @param length the number of elements to replace
548             * @param replacement the elements to replace the replaced elements
549             */
550            public void replace(int from, int length, byte[] replacement) {
551                    shrinkOrExpand(from, length, replacement.length);
552                    System.arraycopy(replacement, 0, this.elements, from, replacement.length);
553            }
554            
555            /**
556             * Replaces all elements[from..from+length-1] with the given replacement.
557             * The replacement can have any length.
558             * Examples: 
559             * <p> 
560             * <code>[a,b,c,d,e].replace(1,3, [x,y]) --> [a,x,y,e]</code>
561             * <p>
562             * <code>[a,b].replace(1,0, [w,x,y,z]) --> [a,w,x,y,z,b]</code>
563             * 
564             * @param from the index of the first element to replace (inclusive)
565             * @param length the number of elements to replace
566             * @param replacement the elements to replace the replaced elements
567             */
568            public void replace(int from, int length, ByteArrayList replacement) {
569                    shrinkOrExpand(from, length, replacement.size);
570                    System.arraycopy(replacement.elements, 0, this.elements, from, replacement.size);
571            }
572            
573            /**
574             * Replaces all elements[from..from+length-1] with the given replacement.
575             * The replacement can have any length.
576             * Examples: 
577             * <p> 
578             * <code>[a,b,c,d,e].replace(1,3, [x,y]) --> [a,x,y,e]</code>
579             * <p>
580             * <code>[a,b].replace(1,0, [w,x,y,z]) --> [a,w,x,y,z,b]</code>
581             * 
582             * @param from the index of the first element to replace (inclusive)
583             * @param length the number of elements to replace
584             * @param replacement the elements to replace the replaced elements
585             */
586            public void replace(int from, int length, ByteBuffer replacement) {
587                    shrinkOrExpand(from, length, replacement.remaining());
588                    replacement.get(this.elements, from, replacement.remaining());
589            }
590            
591            /**
592             * Replaces all elements[from..from+length-1] with the given replacement.
593             * The replacement can have any length.
594             * Example: <code>[a,b,c,d,e].replace(1,3,x,4) --> [a,x,x,x,x,e]</code>
595             * 
596             * @param from the index of the first element to replace (inclusive)
597             * @param length the number of elements to replace
598             * @param replacement the elements to replace the replaced elements
599             * @param replacementSize the number of times <code>replacement</code> is to replace the replaced elements
600             */
601            public void replace(int from, int length, byte replacement, int replacementSize) {
602                    checkSize(replacementSize);
603                    shrinkOrExpand(from, length, replacementSize);
604                    java.util.Arrays.fill(this.elements, from, from + replacementSize, replacement);
605            }
606            
607            /**
608             * Retains (keeps) only the elements in the receiver that are contained in
609             * the specified other list. In other words, removes from the receiver all
610             * of its elements that are not contained in the specified other list.
611             * 
612             * @param other
613             *            the other list to test against.
614             * @return <code>true</code> if the receiver changed as a result of the
615             *         call.
616             */
617            public boolean retainAll(ByteArrayList other) {
618                    if (other.size() == 0) {
619                            if (size == 0) return false;
620                            size = 0;
621                            return true;
622                    }
623    
624                    int limit = other.size() - 1;
625                    int j = 0;
626                    for (int i = 0; i < size; i++) {
627                            if (other.indexOf(elements[i], 0, limit) >= 0)
628                                    elements[j++] = elements[i];
629                    }
630    
631                    boolean modified = (j != size);
632                    size = j;
633                    return modified;
634            }
635    
636            /**
637             * Reverses the elements of the receiver. Last becomes first, second last
638             * becomes second first, and so on.
639             */
640            public void reverse() {
641                    byte tmp;
642                    int limit = size / 2;
643                    int j = size - 1;
644    
645                    byte[] theElements = elements;
646                    for (int i = 0; i < limit;) { //swap
647                            tmp = theElements[i];
648                            theElements[i++] = theElements[j];
649                            theElements[j--] = tmp;
650                    }
651            }
652    
653            /**
654             * Replaces the element at the specified position in the receiver with the
655             * specified element.
656             * 
657             * @param index
658             *            index of element to replace.
659             * @param element
660             *            element to be stored at the specified position.
661             * @throws IndexOutOfBoundsException if index is out of range.
662             */
663            public final void set(int index, byte element) {
664                    checkIndex(index);
665                    elements[index] = element;
666            }
667    
668            /**
669             * Replaces the 4 bytes at the specified position in the receiver with the
670             * specified element;
671             * using the NIO default byte order, which is ByteOrder.BIG_ENDIAN.
672             * 
673             * @param index
674             *            index of first element to replace.
675             * @param elem
676             *            element value to set.
677             * @throws IndexOutOfBoundsException if index is out of range.
678             */
679            public void setInt(int index, int elem) {
680                    checkIndex(index);
681                    checkIndex(index + 4 - 1);
682                    asByteBuffer().putInt(index, elem);
683                    //ByteConverter.write(elem, elements, s);
684            }
685    
686    //      /**
687    //       * Sets the receiver's elements to be the specified array (not a copy of
688    //       * it).
689    //       * <p>
690    //       * The size and capacity of the list is the length of the array. <b>WARNING:
691    //       * </b> For efficiency reasons and to keep memory usage low, <b>the array is
692    //       * not copied </b>. So if subsequently you modify the specified array
693    //       * directly via the [] operator, be sure you know what you're doing.
694    //       * 
695    //       * @param elems
696    //       *            the new elements to be stored.
697    //       */
698    //      public void setElements(byte[] elems) {
699    //              this.elements = elems;
700    //              this.size = elems.length;
701    //      }
702    
703            /**
704             * Sets the size to the given new size; expanding the list capacity if
705             * necessary.
706             * 
707             * @param newSize
708             *            the new size.
709             * @throws IndexOutOfBoundsException
710             *             if index is out of range.
711             */
712            public void setSize(int newSize) {
713                    checkSize(newSize);
714                    ensureCapacity(newSize);
715                    size = newSize;
716            }
717    
718            /**
719             * Returns the number of elements contained in the receiver.
720             *
721             * @return  the number of elements contained in the receiver.
722             */
723            public int size() {
724                    return size;
725            }
726    
727            /**
728             * Sorts the receiver into ascending numerical order. 
729             */
730            public void sort() {
731                    java.util.Arrays.sort(elements, 0, size);
732            }
733            
734            /**
735             * Returns a new list of the copied part of the receiver between
736             * <code>from</code>, inclusive, and <code>to</code>, exclusive.
737             * 
738             * @param from
739             *            the index of the first element (inclusive).
740             * @param to
741             *            the index of the last element (exclusive).
742             * @return a new list
743             * @throws IndexOutOfBoundsException
744             *             if indexes are out of range
745             */
746            public ByteArrayList subList(int from, int to) {
747                    checkFromTo(from, to);
748                    byte[] part = new byte[to - from];
749                    System.arraycopy(elements, from, part, 0, to - from);
750                    return new ByteArrayList(part);
751            }
752    
753            /**
754             * Returns a copied array of bytes containing all the elements
755             * in the receiver; the returned array has length = this.size().
756             */
757            public byte[] toArray() {
758                    byte[] copy = new byte[size];
759                    System.arraycopy(elements, 0, copy, 0, size);
760                    return copy;
761            }
762    
763            /**
764             * Returns a <code>java.util.ArrayList</code> containing all the elements
765             * in the receiver.
766             */
767            public java.util.ArrayList toList() {
768                    java.util.ArrayList list = new java.util.ArrayList(size);
769                    for (int i = 0; i < size; i++)
770                            list.add(new Byte(elements[i]));
771                    return list;
772            }
773    
774            /**
775             * Returns a string representation of the receiver, containing the numeric
776             * String representation of each element.
777             */
778            public String toString() {
779                    //return toList().toString();
780                    StringBuffer buf = new StringBuffer(4*size);
781                    buf.append("[");
782                    for (int i = 0; i < size; i++) {
783                            buf.append(elements[i]);
784                            if (i < size-1) buf.append(", ");
785                    }
786                    buf.append("]");
787                    return buf.toString();
788            }
789    
790            /**
791             * Returns a decoded string representation of the receiver.
792             * 
793             * @param  charset the requested charset to convert with 
794             *                      (e.g. Charset.forName("US-ASCII"), Charset.forName("UTF-8"))
795             */
796            public String toString(Charset charset) {
797                    return getString(0, size, charset);
798            }
799    
800            /**
801             * Trims the capacity of the receiver to be the receiver's current size.
802             * Releases any superfluos internal memory. An application can use this
803             * operation to minimize the storage of the receiver.
804             */
805            public void trimToSize() {
806                    if (elements.length > size) {
807                            byte[] newArray = new byte[size];
808                            System.arraycopy(elements, 0, newArray, 0, size);
809                            elements = newArray;
810                    }
811            }
812    
813            /**
814             * Checks if the given index is in range.
815             */
816            protected final void checkIndex(int index) {
817                    if (index >= size || index < 0)
818                                    throw new IndexOutOfBoundsException("index: " + index
819                                                    + ", size: " + size);
820            }
821    
822            /**
823             * Checks if the given range is within the contained array's bounds.
824             */
825            protected final void checkLength(int from, int length) {
826                    if (from < 0 || length < 0 || from + length > size)
827                                    throw new IndexOutOfBoundsException("from: " + from + ", length: "
828                                                    + length + ", size=" + size);
829            }
830    
831            /**
832             * Checks if the given range is within the contained array's bounds.
833             */
834            protected final void checkFromTo(int from, int to) {
835                    if (from < 0 || from > to || to > size)
836                                    throw new IndexOutOfBoundsException("from: " + from + ", to: "
837                                                    + to + ", size=" + size);
838            }
839    
840            /**
841             * Checks if the given size is within bounds.
842             */
843            protected final void checkSize(int newSize) {
844                    if (newSize < 0)
845                            throw new IndexOutOfBoundsException("newSize: " + newSize
846                                            + ", Size: " + size);
847            }
848            
849    }