View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.common;
21  
22  import java.io.EOFException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.io.ObjectStreamClass;
28  import java.io.OutputStream;
29  import java.io.StreamCorruptedException;
30  import java.nio.BufferOverflowException;
31  import java.nio.BufferUnderflowException;
32  import java.nio.ByteBuffer;
33  import java.nio.ByteOrder;
34  import java.nio.CharBuffer;
35  import java.nio.DoubleBuffer;
36  import java.nio.FloatBuffer;
37  import java.nio.IntBuffer;
38  import java.nio.LongBuffer;
39  import java.nio.ShortBuffer;
40  import java.nio.charset.CharacterCodingException;
41  import java.nio.charset.CharsetDecoder;
42  import java.nio.charset.CharsetEncoder;
43  import java.nio.charset.CoderResult;
44  import java.util.EnumSet;
45  import java.util.Set;
46  
47  
48  /**
49   * A base implementation of {@link IoBuffer}.  This implementation
50   * assumes that {@link IoBuffer#buf()} always returns a correct NIO
51   * {@link ByteBuffer} instance.  Most implementations could
52   * extend this class and implement their own buffer management mechanism.
53   *
54   * @author The Apache MINA Project (dev@mina.apache.org)
55   * @version $Rev: 611312 $, $Date: 2008-01-11 14:59:43 -0700 (Fri, 11 Jan 2008) $
56   * @see IoBufferAllocator
57   */
58  public abstract class AbstractIoBuffer extends IoBuffer {
59      
60      private final IoBufferAllocator allocator;
61      private final boolean derived;
62      private boolean autoExpand;
63      private boolean autoShrink;
64      private boolean recapacityAllowed = true;
65      private int minimumCapacity;
66  
67      /**
68       * We don't have any access to Buffer.markValue(), so we need to track it down,
69       * which will cause small extra overhead.
70       */
71      private int mark = -1;
72  
73      /**
74       * Creates a new parent buffer.
75       */
76      protected AbstractIoBuffer(
77              IoBufferAllocator allocator, int initialCapacity) {
78          this.allocator = allocator;
79          this.recapacityAllowed = true;
80          this.derived = false;
81          this.minimumCapacity = initialCapacity;
82      }
83      
84      /**
85       * Creates a new derived buffer.
86       */
87      protected AbstractIoBuffer(AbstractIoBuffer parent) {
88          this.allocator = parent.allocator;
89          this.recapacityAllowed = false;
90          this.derived = true;
91          this.minimumCapacity = parent.minimumCapacity;
92      }
93  
94      @Override
95      public final boolean isDirect() {
96          return buf().isDirect();
97      }
98  
99      @Override
100     public final boolean isReadOnly() {
101         return buf().isReadOnly();
102     }
103 
104     /**
105      * Sets the underlying NIO buffer instance.
106      */
107     protected abstract void buf(ByteBuffer newBuf);
108 
109     @Override
110     public final int minimumCapacity() {
111         return minimumCapacity;
112     }
113     
114     @Override
115     public final IoBuffer minimumCapacity(int minimumCapacity) {
116         if (minimumCapacity < 0) {
117             throw new IllegalArgumentException(
118                     "minimumCapacity: " + minimumCapacity);
119         }
120         this.minimumCapacity = minimumCapacity;
121         return this;
122     }
123 
124     @Override
125     public final int capacity() {
126         return buf().capacity();
127     }
128 
129     @Override
130     public final IoBuffer capacity(int newCapacity) {
131         if (!recapacityAllowed) {
132             throw new IllegalStateException(
133                     "Derived buffers and their parent can't be expanded.");
134         }
135 
136         // Allocate a new buffer and transfer all settings to it.
137         if (newCapacity > capacity()) {
138             // Expand:
139             //// Save the state.
140             int pos = position();
141             int limit = limit();
142             ByteOrder bo = order();
143 
144             //// Reallocate.
145             ByteBuffer oldBuf = buf();
146             ByteBuffer newBuf = 
147                 allocator.allocateNioBuffer(newCapacity, isDirect());
148             oldBuf.clear();
149             newBuf.put(oldBuf);
150             buf(newBuf);
151 
152             //// Restore the state.
153             buf().limit(limit);
154             if (mark >= 0) {
155                 buf().position(mark);
156                 buf().mark();
157             }
158             buf().position(pos);
159             buf().order(bo);
160         }
161         
162         return this;
163     }
164 
165     @Override
166     public final boolean isAutoExpand() {
167         return autoExpand && recapacityAllowed;
168     }
169     
170     @Override
171     public final boolean isAutoShrink() {
172         return autoShrink && recapacityAllowed;
173     }
174     
175     @Override
176     public final boolean isDerived() {
177         return derived;
178     }
179 
180     @Override
181     public final IoBuffer setAutoExpand(boolean autoExpand) {
182         if (!recapacityAllowed) {
183             throw new IllegalStateException(
184                     "Derived buffers and their parent can't be expanded.");
185         }
186         this.autoExpand = autoExpand;
187         return this;
188     }
189 
190     @Override
191     public final IoBuffer setAutoShrink(boolean autoShrink) {
192         if (!recapacityAllowed) {
193             throw new IllegalStateException(
194                     "Derived buffers and their parent can't be shrinked.");
195         }
196         this.autoShrink = autoShrink;
197         return this;
198     }
199 
200     @Override
201     public final IoBuffer expand(int expectedRemaining) {
202         return expand(position(), expectedRemaining);
203     }
204 
205     @Override
206     public final IoBuffer expand(int pos, int expectedRemaining) {
207         if (!recapacityAllowed) {
208             throw new IllegalStateException(
209                     "Derived buffers and their parent can't be expanded.");
210         }
211 
212         int end = pos + expectedRemaining;
213         if (end > capacity()) {
214             // The buffer needs expansion.
215             capacity(end);
216         }
217 
218         if (end > limit()) {
219             // We call limit() directly to prevent StackOverflowError
220             buf().limit(end);
221         }
222         return this;
223     }
224     
225     @Override
226     public final IoBuffer shrink() {
227 
228         if (!recapacityAllowed) {
229             throw new IllegalStateException(
230                     "Derived buffers and their parent can't be expanded.");
231         }
232         
233         int position = position();
234         int capacity = capacity();
235         int limit = limit();
236         if (capacity == limit) {
237             return this;
238         }
239 
240         int newCapacity = capacity;
241         int minCapacity = Math.max(minimumCapacity, limit);
242         for (;;) {
243             if (newCapacity >>> 1 < minCapacity) {
244                 break;
245             }
246             newCapacity >>>= 1;
247         }
248         
249         newCapacity = Math.max(minCapacity, newCapacity);
250         
251         if (newCapacity == capacity) {
252             return this;
253         }
254 
255         // Shrink and compact:
256         //// Save the state.
257         ByteOrder bo = order();
258 
259         //// Reallocate.
260         ByteBuffer oldBuf = buf();
261         ByteBuffer newBuf = 
262             allocator.allocateNioBuffer(newCapacity, isDirect());
263         oldBuf.position(0);
264         oldBuf.limit(limit);
265         newBuf.put(oldBuf);
266         buf(newBuf);
267         
268         //// Restore the state.
269         buf().position(position);
270         buf().limit(limit);
271         buf().order(bo);
272         mark = -1;
273         
274         return this;
275     }
276 
277     @Override
278     public final int position() {
279         return buf().position();
280     }
281 
282     @Override
283     public final IoBuffer position(int newPosition) {
284         autoExpand(newPosition, 0);
285         buf().position(newPosition);
286         if (mark > newPosition) {
287             mark = -1;
288         }
289         return this;
290     }
291 
292     @Override
293     public final int limit() {
294         return buf().limit();
295     }
296 
297     @Override
298     public final IoBuffer limit(int newLimit) {
299         autoExpand(newLimit, 0);
300         buf().limit(newLimit);
301         if (mark > newLimit) {
302             mark = -1;
303         }
304         return this;
305     }
306 
307     @Override
308     public final IoBuffer mark() {
309         buf().mark();
310         mark = position();
311         return this;
312     }
313 
314     @Override
315     public final int markValue() {
316         return mark;
317     }
318 
319     @Override
320     public final IoBuffer reset() {
321         buf().reset();
322         return this;
323     }
324 
325     @Override
326     public final IoBuffer clear() {
327         buf().clear();
328         mark = -1;
329         return this;
330     }
331 
332     @Override
333     public final IoBuffer sweep() {
334         clear();
335         return fillAndReset(remaining());
336     }
337 
338     @Override
339     public final IoBuffer sweep(byte value) {
340         clear();
341         return fillAndReset(value, remaining());
342     }
343     
344     @Override
345     public final IoBuffer flip() {
346         buf().flip();
347         mark = -1;
348         return this;
349     }
350 
351     @Override
352     public final IoBuffer rewind() {
353         buf().rewind();
354         mark = -1;
355         return this;
356     }
357 
358     @Override
359     public final int remaining() {
360         return limit() - position();
361     }
362     
363     @Override
364     public final boolean hasRemaining() {
365         return limit() > position();
366     }
367 
368     @Override
369     public final byte get() {
370         return buf().get();
371     }
372 
373     @Override
374     public final short getUnsigned() {
375         return (short) (get() & 0xff);
376     }
377 
378     @Override
379     public final IoBuffer put(byte b) {
380         autoExpand(1);
381         buf().put(b);
382         return this;
383     }
384 
385     @Override
386     public final byte get(int index) {
387         return buf().get(index);
388     }
389 
390     @Override
391     public final short getUnsigned(int index) {
392         return (short) (get(index) & 0xff);
393     }
394 
395     @Override
396     public final IoBuffer put(int index, byte b) {
397         autoExpand(index, 1);
398         buf().put(index, b);
399         return this;
400     }
401 
402     @Override
403     public final IoBuffer get(byte[] dst, int offset, int length) {
404         buf().get(dst, offset, length);
405         return this;
406     }
407 
408     @Override
409     public final IoBuffer put(ByteBuffer src) {
410         autoExpand(src.remaining());
411         buf().put(src);
412         return this;
413     }
414 
415     @Override
416     public final IoBuffer put(byte[] src, int offset, int length) {
417         autoExpand(length);
418         buf().put(src, offset, length);
419         return this;
420     }
421 
422     @Override
423     public final IoBuffer compact() {
424         int remaining = remaining();
425         int capacity = capacity();
426         
427         if (capacity == 0) {
428             return this;
429         }
430 
431         if (isAutoShrink() && remaining <= capacity >>> 2 && capacity > minimumCapacity) {
432             int newCapacity = capacity;
433             int minCapacity = Math.max(minimumCapacity, remaining << 1);
434             for (;;) {
435                 if (newCapacity >>> 1 < minCapacity) {
436                     break;
437                 }
438                 newCapacity >>>= 1;
439             }
440             
441             newCapacity = Math.max(minCapacity, newCapacity);
442             
443             if (newCapacity == capacity) {
444                 return this;
445             }
446 
447             // Shrink and compact:
448             //// Save the state.
449             ByteOrder bo = order();
450 
451             //// Sanity check.
452             if (remaining > newCapacity) {
453                 throw new IllegalStateException(
454                         "The amount of the remaining bytes is greater than " +
455                         "the new capacity.");
456             }
457 
458             //// Reallocate.
459             ByteBuffer oldBuf = buf();
460             ByteBuffer newBuf = 
461                 allocator.allocateNioBuffer(newCapacity, isDirect());
462             newBuf.put(oldBuf);
463             buf(newBuf);
464             
465             //// Restore the state.
466             buf().order(bo);
467         } else {
468             buf().compact();
469         }
470         mark = -1;
471         return this;
472     }
473 
474     @Override
475     public final ByteOrder order() {
476         return buf().order();
477     }
478 
479     @Override
480     public final IoBuffer order(ByteOrder bo) {
481         buf().order(bo);
482         return this;
483     }
484 
485     @Override
486     public final char getChar() {
487         return buf().getChar();
488     }
489 
490     @Override
491     public final IoBuffer putChar(char value) {
492         autoExpand(2);
493         buf().putChar(value);
494         return this;
495     }
496 
497     @Override
498     public final char getChar(int index) {
499         return buf().getChar(index);
500     }
501 
502     @Override
503     public final IoBuffer putChar(int index, char value) {
504         autoExpand(index, 2);
505         buf().putChar(index, value);
506         return this;
507     }
508 
509     @Override
510     public final CharBuffer asCharBuffer() {
511         return buf().asCharBuffer();
512     }
513 
514     @Override
515     public final short getShort() {
516         return buf().getShort();
517     }
518 
519     @Override
520     public final IoBuffer putShort(short value) {
521         autoExpand(2);
522         buf().putShort(value);
523         return this;
524     }
525 
526     @Override
527     public final short getShort(int index) {
528         return buf().getShort(index);
529     }
530 
531     @Override
532     public final IoBuffer putShort(int index, short value) {
533         autoExpand(index, 2);
534         buf().putShort(index, value);
535         return this;
536     }
537 
538     @Override
539     public final ShortBuffer asShortBuffer() {
540         return buf().asShortBuffer();
541     }
542 
543     @Override
544     public final int getInt() {
545         return buf().getInt();
546     }
547 
548     @Override
549     public final IoBuffer putInt(int value) {
550         autoExpand(4);
551         buf().putInt(value);
552         return this;
553     }
554 
555     @Override
556     public final int getInt(int index) {
557         return buf().getInt(index);
558     }
559 
560     @Override
561     public final IoBuffer putInt(int index, int value) {
562         autoExpand(index, 4);
563         buf().putInt(index, value);
564         return this;
565     }
566 
567     @Override
568     public final IntBuffer asIntBuffer() {
569         return buf().asIntBuffer();
570     }
571 
572     @Override
573     public final long getLong() {
574         return buf().getLong();
575     }
576 
577     @Override
578     public final IoBuffer putLong(long value) {
579         autoExpand(8);
580         buf().putLong(value);
581         return this;
582     }
583 
584     @Override
585     public final long getLong(int index) {
586         return buf().getLong(index);
587     }
588 
589     @Override
590     public final IoBuffer putLong(int index, long value) {
591         autoExpand(index, 8);
592         buf().putLong(index, value);
593         return this;
594     }
595 
596     @Override
597     public final LongBuffer asLongBuffer() {
598         return buf().asLongBuffer();
599     }
600 
601     @Override
602     public final float getFloat() {
603         return buf().getFloat();
604     }
605 
606     @Override
607     public final IoBuffer putFloat(float value) {
608         autoExpand(4);
609         buf().putFloat(value);
610         return this;
611     }
612 
613     @Override
614     public final float getFloat(int index) {
615         return buf().getFloat(index);
616     }
617 
618     @Override
619     public final IoBuffer putFloat(int index, float value) {
620         autoExpand(index, 4);
621         buf().putFloat(index, value);
622         return this;
623     }
624 
625     @Override
626     public final FloatBuffer asFloatBuffer() {
627         return buf().asFloatBuffer();
628     }
629 
630     @Override
631     public final double getDouble() {
632         return buf().getDouble();
633     }
634 
635     @Override
636     public final IoBuffer putDouble(double value) {
637         autoExpand(8);
638         buf().putDouble(value);
639         return this;
640     }
641 
642     @Override
643     public final double getDouble(int index) {
644         return buf().getDouble(index);
645     }
646 
647     @Override
648     public final IoBuffer putDouble(int index, double value) {
649         autoExpand(index, 8);
650         buf().putDouble(index, value);
651         return this;
652     }
653 
654     @Override
655     public final DoubleBuffer asDoubleBuffer() {
656         return buf().asDoubleBuffer();
657     }
658 
659     @Override
660     public final IoBuffer asReadOnlyBuffer() {
661         recapacityAllowed = false;
662         return asReadOnlyBuffer0();
663     }
664 
665     /**
666      * Implement this method to return the unexpandable read only version of
667      * this buffer.
668      */
669     protected abstract IoBuffer asReadOnlyBuffer0();
670 
671     @Override
672     public final IoBuffer duplicate() {
673         recapacityAllowed = false;
674         return duplicate0();
675     }
676 
677     /**
678      * Implement this method to return the unexpandable duplicate of this
679      * buffer.
680      */
681     protected abstract IoBuffer duplicate0();
682 
683     @Override
684     public final IoBuffer slice() {
685         recapacityAllowed = false;
686         return slice0();
687     }
688     
689     @Override
690     public final IoBuffer getSlice(int index, int length) {
691         if (length < 0) {
692             throw new IllegalArgumentException("length: " + length);
693         }
694         int pos = position();
695         int limit = limit();
696         int endIndex = pos + length;
697 
698         if (capacity() < endIndex) {
699             throw new IndexOutOfBoundsException(
700                     "index + length (" + endIndex + ") is greater " +
701                     "than capacity (" + capacity() + ").");
702         }
703 
704         clear();
705         position(index);
706         limit(endIndex);
707 
708         IoBuffer slice = slice();
709         position(pos);
710         limit(limit);
711         return slice;
712     }
713 
714     @Override
715     public final IoBuffer getSlice(int length) {
716         if (length < 0) {
717             throw new IllegalArgumentException("length: " + length);
718         }
719         int pos = position();
720         int limit = limit();
721         int nextPos = pos + length;
722         if (limit < nextPos) {
723             throw new IndexOutOfBoundsException(
724                     "position + length (" + nextPos + ") is greater " +
725                     "than limit (" + limit + ").");
726         }
727         
728         limit(pos + length);
729         IoBuffer slice = slice();
730         position(nextPos);
731         limit(limit);
732         return slice;
733     }
734 
735     /**
736      * Implement this method to return the unexpandable slice of this
737      * buffer.
738      */
739     protected abstract IoBuffer slice0();
740     
741     @Override
742     public int hashCode() {
743         int h = 1;
744         int p = position();
745         for (int i = limit() - 1; i >= p; i--) {
746             h = 31 * h + get(i);
747         }
748         return h;
749     }
750     
751     @Override
752     public boolean equals(Object o) {
753         if (!(o instanceof IoBuffer)) {
754             return false;
755         }
756 
757         IoBuffer that = (IoBuffer) o;
758         if (this.remaining() != that.remaining()) {
759             return false;
760         }
761 
762         int p = this.position();
763         for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--) {
764             byte v1 = this.get(i);
765             byte v2 = that.get(j);
766             if (v1 != v2) {
767                 return false;
768             }
769         }
770         return true;
771     }
772 
773     public int compareTo(IoBuffer that) {
774         int n = this.position() + Math.min(this.remaining(), that.remaining());
775         for (int i = this.position(), j = that.position(); i < n; i++, j++) {
776             byte v1 = this.get(i);
777             byte v2 = that.get(j);
778             if (v1 == v2) {
779                 continue;
780             }
781             if (v1 < v2) {
782                 return -1;
783             }
784 
785             return +1;
786         }
787         return this.remaining() - that.remaining();
788     }
789 
790     @Override
791     public String toString() {
792         StringBuffer buf = new StringBuffer();
793         if (isDirect()) {
794             buf.append("DirectBuffer");
795         } else {
796             buf.append("HeapBuffer");
797         }
798         buf.append("[pos=");
799         buf.append(position());
800         buf.append(" lim=");
801         buf.append(limit());
802         buf.append(" cap=");
803         buf.append(capacity());
804         buf.append(": ");
805         buf.append(getHexDump(16));
806         buf.append(']');
807         return buf.toString();
808     }
809 
810     @Override
811     public IoBuffer get(byte[] dst) {
812         return get(dst, 0, dst.length);
813     }
814 
815     @Override
816     public IoBuffer put(IoBuffer src) {
817         return put(src.buf());
818     }
819 
820     @Override
821     public IoBuffer put(byte[] src) {
822         return put(src, 0, src.length);
823     }
824 
825     @Override
826     public int getUnsignedShort() {
827         return getShort() & 0xffff;
828     }
829 
830     @Override
831     public int getUnsignedShort(int index) {
832         return getShort(index) & 0xffff;
833     }
834 
835     @Override
836     public long getUnsignedInt() {
837         return getInt() & 0xffffffffL;
838     }
839 
840     @Override
841     public int getMediumInt() {
842         byte b1 = get();
843         byte b2 = get();
844         byte b3 = get();
845         if (ByteOrder.BIG_ENDIAN.equals(order())) {
846             return getMediumInt(b1, b2, b3);
847         } else {
848             return getMediumInt(b3, b2, b1);
849         }
850     }
851 
852     @Override
853     public int getUnsignedMediumInt() {
854         int b1 = getUnsigned();
855         int b2 = getUnsigned();
856         int b3 = getUnsigned();
857         if (ByteOrder.BIG_ENDIAN.equals(order())) {
858             return b1 << 16 | b2 << 8 | b3;
859         } else {
860             return b3 << 16 | b2 << 8 | b1;
861         }
862     }
863 
864     @Override
865     public int getMediumInt(int index) {
866         byte b1 = get(index);
867         byte b2 = get(index + 1);
868         byte b3 = get(index + 2);
869         if (ByteOrder.BIG_ENDIAN.equals(order())) {
870             return getMediumInt(b1, b2, b3);
871         } else {
872             return getMediumInt(b3, b2, b1);
873         }
874     }
875 
876     @Override
877     public int getUnsignedMediumInt(int index) {
878         int b1 = getUnsigned(index);
879         int b2 = getUnsigned(index + 1);
880         int b3 = getUnsigned(index + 2);
881         if (ByteOrder.BIG_ENDIAN.equals(order())) {
882             return b1 << 16 | b2 << 8 | b3;
883         } else {
884             return b3 << 16 | b2 << 8 | b1;
885         }
886     }
887 
888     private int getMediumInt(byte b1, byte b2, byte b3) {
889         int ret = b1 << 16 & 0xff0000 | b2 << 8 & 0xff00 | b3 & 0xff;
890         // Check to see if the medium int is negative (high bit in b1 set)
891         if ((b1 & 0x80) == 0x80) {
892             // Make the the whole int negative
893             ret |= 0xff000000;
894         }
895         return ret;
896     }
897 
898     @Override
899     public IoBuffer putMediumInt(int value) {
900         byte b1 = (byte) (value >> 16);
901         byte b2 = (byte) (value >> 8);
902         byte b3 = (byte) value;
903     
904         if (ByteOrder.BIG_ENDIAN.equals(order())) {
905             put(b1).put(b2).put(b3);
906         } else {
907             put(b3).put(b2).put(b1);
908         }
909     
910         return this;
911     }
912 
913     @Override
914     public IoBuffer putMediumInt(int index, int value) {
915         byte b1 = (byte) (value >> 16);
916         byte b2 = (byte) (value >> 8);
917         byte b3 = (byte) value;
918     
919         if (ByteOrder.BIG_ENDIAN.equals(order())) {
920             put(index, b1).put(index + 1, b2).put(index + 2, b3);
921         } else {
922             put(index, b3).put(index + 1, b2).put(index + 2, b1);
923         }
924     
925         return this;
926     }
927 
928     @Override
929     public long getUnsignedInt(int index) {
930         return getInt(index) & 0xffffffffL;
931     }
932 
933     @Override
934     public InputStream asInputStream() {
935         return new InputStream() {
936             @Override
937             public int available() {
938                 return AbstractIoBuffer.this.remaining();
939             }
940     
941             @Override
942             public synchronized void mark(int readlimit) {
943                 AbstractIoBuffer.this.mark();
944             }
945     
946             @Override
947             public boolean markSupported() {
948                 return true;
949             }
950     
951             @Override
952             public int read() {
953                 if (AbstractIoBuffer.this.hasRemaining()) {
954                     return AbstractIoBuffer.this.get() & 0xff;
955                 } else {
956                     return -1;
957                 }
958             }
959     
960             @Override
961             public int read(byte[] b, int off, int len) {
962                 int remaining = AbstractIoBuffer.this.remaining();
963                 if (remaining > 0) {
964                     int readBytes = Math.min(remaining, len);
965                     AbstractIoBuffer.this.get(b, off, readBytes);
966                     return readBytes;
967                 } else {
968                     return -1;
969                 }
970             }
971     
972             @Override
973             public synchronized void reset() {
974                 AbstractIoBuffer.this.reset();
975             }
976     
977             @Override
978             public long skip(long n) {
979                 int bytes;
980                 if (n > Integer.MAX_VALUE) {
981                     bytes = AbstractIoBuffer.this.remaining();
982                 } else {
983                     bytes = Math.min(AbstractIoBuffer.this.remaining(), (int) n);
984                 }
985                 AbstractIoBuffer.this.skip(bytes);
986                 return bytes;
987             }
988         };
989     }
990 
991     @Override
992     public OutputStream asOutputStream() {
993         return new OutputStream() {
994             @Override
995             public void write(byte[] b, int off, int len) {
996                 AbstractIoBuffer.this.put(b, off, len);
997             }
998     
999             @Override
1000             public void write(int b) {
1001                 AbstractIoBuffer.this.put((byte) b);
1002             }
1003         };
1004     }
1005 
1006     @Override
1007     public String getHexDump() {
1008         return this.getHexDump(Integer.MAX_VALUE);
1009     }
1010 
1011     @Override
1012     public String getHexDump(int lengthLimit) {
1013         return IoBufferHexDumper.getHexdump(this, lengthLimit);
1014     }
1015 
1016     @Override
1017     public String getString(CharsetDecoder decoder) throws CharacterCodingException {
1018         if (!hasRemaining()) {
1019             return "";
1020         }
1021     
1022         boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1023     
1024         int oldPos = position();
1025         int oldLimit = limit();
1026         int end = -1;
1027         int newPos;
1028     
1029         if (!utf16) {
1030             end = indexOf((byte) 0x00);
1031             if (end < 0) {
1032                 newPos = end = oldLimit;
1033             } else {
1034                 newPos = end + 1;
1035             }
1036         } else {
1037             int i = oldPos;
1038             for (;;) {
1039                 boolean wasZero = get(i) == 0;
1040                 i++;
1041     
1042                 if (i >= oldLimit) {
1043                     break;
1044                 }
1045     
1046                 if (get(i) != 0) {
1047                     i++;
1048                     if (i >= oldLimit) {
1049                         break;
1050                     } else {
1051                         continue;
1052                     }
1053                 }
1054     
1055                 if (wasZero) {
1056                     end = i - 1;
1057                     break;
1058                 }
1059             }
1060     
1061             if (end < 0) {
1062                 newPos = end = oldPos + (oldLimit - oldPos & 0xFFFFFFFE);
1063             } else {
1064                 if (end + 2 <= oldLimit) {
1065                     newPos = end + 2;
1066                 } else {
1067                     newPos = end;
1068                 }
1069             }
1070         }
1071     
1072         if (oldPos == end) {
1073             position(newPos);
1074             return "";
1075         }
1076     
1077         limit(end);
1078         decoder.reset();
1079     
1080         int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1081         CharBuffer out = CharBuffer.allocate(expectedLength);
1082         for (;;) {
1083             CoderResult cr;
1084             if (hasRemaining()) {
1085                 cr = decoder.decode(buf(), out, true);
1086             } else {
1087                 cr = decoder.flush(out);
1088             }
1089     
1090             if (cr.isUnderflow()) {
1091                 break;
1092             }
1093     
1094             if (cr.isOverflow()) {
1095                 CharBuffer o = CharBuffer.allocate(out.capacity()
1096                         + expectedLength);
1097                 out.flip();
1098                 o.put(out);
1099                 out = o;
1100                 continue;
1101             }
1102     
1103             if (cr.isError()) {
1104                 // Revert the buffer back to the previous state.
1105                 limit(oldLimit);
1106                 position(oldPos);
1107                 cr.throwException();
1108             }
1109         }
1110     
1111         limit(oldLimit);
1112         position(newPos);
1113         return out.flip().toString();
1114     }
1115 
1116     @Override
1117     public String getString(int fieldSize, CharsetDecoder decoder)
1118             throws CharacterCodingException {
1119                 checkFieldSize(fieldSize);
1120             
1121                 if (fieldSize == 0) {
1122                     return "";
1123                 }
1124             
1125                 if (!hasRemaining()) {
1126                     return "";
1127                 }
1128             
1129                 boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1130             
1131                 if (utf16 && (fieldSize & 1) != 0) {
1132                     throw new IllegalArgumentException("fieldSize is not even.");
1133                 }
1134             
1135                 int oldPos = position();
1136                 int oldLimit = limit();
1137                 int end = oldPos + fieldSize;
1138             
1139                 if (oldLimit < end) {
1140                     throw new BufferUnderflowException();
1141                 }
1142             
1143                 int i;
1144             
1145                 if (!utf16) {
1146                     for (i = oldPos; i < end; i++) {
1147                         if (get(i) == 0) {
1148                             break;
1149                         }
1150                     }
1151             
1152                     if (i == end) {
1153                         limit(end);
1154                     } else {
1155                         limit(i);
1156                     }
1157                 } else {
1158                     for (i = oldPos; i < end; i += 2) {
1159                         if (get(i) == 0 && get(i + 1) == 0) {
1160                             break;
1161                         }
1162                     }
1163             
1164                     if (i == end) {
1165                         limit(end);
1166                     } else {
1167                         limit(i);
1168                     }
1169                 }
1170             
1171                 if (!hasRemaining()) {
1172                     limit(oldLimit);
1173                     position(end);
1174                     return "";
1175                 }
1176                 decoder.reset();
1177             
1178                 int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1179                 CharBuffer out = CharBuffer.allocate(expectedLength);
1180                 for (;;) {
1181                     CoderResult cr;
1182                     if (hasRemaining()) {
1183                         cr = decoder.decode(buf(), out, true);
1184                     } else {
1185                         cr = decoder.flush(out);
1186                     }
1187             
1188                     if (cr.isUnderflow()) {
1189                         break;
1190                     }
1191             
1192                     if (cr.isOverflow()) {
1193                         CharBuffer o = CharBuffer.allocate(out.capacity()
1194                                 + expectedLength);
1195                         out.flip();
1196                         o.put(out);
1197                         out = o;
1198                         continue;
1199                     }
1200             
1201                     if (cr.isError()) {
1202                         // Revert the buffer back to the previous state.
1203                         limit(oldLimit);
1204                         position(oldPos);
1205                         cr.throwException();
1206                     }
1207                 }
1208             
1209                 limit(oldLimit);
1210                 position(end);
1211                 return out.flip().toString();
1212             }
1213 
1214     @Override
1215     public IoBuffer putString(CharSequence val, CharsetEncoder encoder)
1216             throws CharacterCodingException {
1217                 if (val.length() == 0) {
1218                     return this;
1219                 }
1220             
1221                 CharBuffer in = CharBuffer.wrap(val);
1222                 encoder.reset();
1223             
1224                 int expandedState = 0;
1225             
1226                 for (;;) {
1227                     CoderResult cr;
1228                     if (in.hasRemaining()) {
1229                         cr = encoder.encode(in, buf(), true);
1230                     } else {
1231                         cr = encoder.flush(buf());
1232                     }
1233             
1234                     if (cr.isUnderflow()) {
1235                         break;
1236                     }
1237                     if (cr.isOverflow()) {
1238                         if (isAutoExpand()) {
1239                             switch (expandedState) {
1240                             case 0:
1241                                 autoExpand((int) Math.ceil(in.remaining()
1242                                         * encoder.averageBytesPerChar()));
1243                                 expandedState++;
1244                                 break;
1245                             case 1:
1246                                 autoExpand((int) Math.ceil(in.remaining()
1247                                         * encoder.maxBytesPerChar()));
1248                                 expandedState++;
1249                                 break;
1250                             default:
1251                                 throw new RuntimeException("Expanded by "
1252                                         + (int) Math.ceil(in.remaining()
1253                                                 * encoder.maxBytesPerChar())
1254                                         + " but that wasn't enough for '" + val + "'");
1255                             }
1256                             continue;
1257                         }
1258                     } else {
1259                         expandedState = 0;
1260                     }
1261                     cr.throwException();
1262                 }
1263                 return this;
1264             }
1265 
1266     @Override
1267     public IoBuffer putString(CharSequence val, int fieldSize, CharsetEncoder encoder)
1268             throws CharacterCodingException {
1269                 checkFieldSize(fieldSize);
1270             
1271                 if (fieldSize == 0) {
1272                     return this;
1273                 }
1274             
1275                 autoExpand(fieldSize);
1276             
1277                 boolean utf16 = encoder.charset().name().startsWith("UTF-16");
1278             
1279                 if (utf16 && (fieldSize & 1) != 0) {
1280                     throw new IllegalArgumentException("fieldSize is not even.");
1281                 }
1282             
1283                 int oldLimit = limit();
1284                 int end = position() + fieldSize;
1285             
1286                 if (oldLimit < end) {
1287                     throw new BufferOverflowException();
1288                 }
1289             
1290                 if (val.length() == 0) {
1291                     if (!utf16) {
1292                         put((byte) 0x00);
1293                     } else {
1294                         put((byte) 0x00);
1295                         put((byte) 0x00);
1296                     }
1297                     position(end);
1298                     return this;
1299                 }
1300             
1301                 CharBuffer in = CharBuffer.wrap(val);
1302                 limit(end);
1303                 encoder.reset();
1304             
1305                 for (;;) {
1306                     CoderResult cr;
1307                     if (in.hasRemaining()) {
1308                         cr = encoder.encode(in, buf(), true);
1309                     } else {
1310                         cr = encoder.flush(buf());
1311                     }
1312             
1313                     if (cr.isUnderflow() || cr.isOverflow()) {
1314                         break;
1315                     }
1316                     cr.throwException();
1317                 }
1318             
1319                 limit(oldLimit);
1320             
1321                 if (position() < end) {
1322                     if (!utf16) {
1323                         put((byte) 0x00);
1324                     } else {
1325                         put((byte) 0x00);
1326                         put((byte) 0x00);
1327                     }
1328                 }
1329             
1330                 position(end);
1331                 return this;
1332             }
1333 
1334     @Override
1335     public String getPrefixedString(CharsetDecoder decoder)
1336             throws CharacterCodingException {
1337                 return getPrefixedString(2, decoder);
1338             }
1339 
1340   /**
1341    * Reads a string which has a length field before the actual
1342    * encoded string, using the specified <code>decoder</code> and returns it.
1343    *
1344    * @param prefixLength the length of the length field (1, 2, or 4)
1345    * @param decoder the decoder to use for decoding the string
1346    * @return the prefixed string
1347    * @throws CharacterCodingException when decoding fails
1348    * @throws BufferUnderflowException when there is not enough data available
1349    */
1350     @Override
1351     public String getPrefixedString(int prefixLength, CharsetDecoder decoder)
1352             throws CharacterCodingException {
1353                 if (!prefixedDataAvailable(prefixLength)) {
1354                     throw new BufferUnderflowException();
1355                 }
1356             
1357                 int fieldSize = 0;
1358             
1359                 switch (prefixLength) {
1360                 case 1:
1361                     fieldSize = getUnsigned();
1362                     break;
1363                 case 2:
1364                     fieldSize = getUnsignedShort();
1365                     break;
1366                 case 4:
1367                     fieldSize = getInt();
1368                     break;
1369                 }
1370             
1371                 if (fieldSize == 0) {
1372                     return "";
1373                 }
1374             
1375                 boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1376             
1377                 if (utf16 && (fieldSize & 1) != 0) {
1378                     throw new BufferDataException(
1379                             "fieldSize is not even for a UTF-16 string.");
1380                 }
1381             
1382                 int oldLimit = limit();
1383                 int end = position() + fieldSize;
1384             
1385                 if (oldLimit < end) {
1386                     throw new BufferUnderflowException();
1387                 }
1388             
1389                 limit(end);
1390                 decoder.reset();
1391             
1392                 int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1393                 CharBuffer out = CharBuffer.allocate(expectedLength);
1394                 for (;;) {
1395                     CoderResult cr;
1396                     if (hasRemaining()) {
1397                         cr = decoder.decode(buf(), out, true);
1398                     } else {
1399                         cr = decoder.flush(out);
1400                     }
1401             
1402                     if (cr.isUnderflow()) {
1403                         break;
1404                     }
1405             
1406                     if (cr.isOverflow()) {
1407                         CharBuffer o = CharBuffer.allocate(out.capacity()
1408                                 + expectedLength);
1409                         out.flip();
1410                         o.put(out);
1411                         out = o;
1412                         continue;
1413                     }
1414             
1415                     cr.throwException();
1416                 }
1417             
1418                 limit(oldLimit);
1419                 position(end);
1420                 return out.flip().toString();
1421             }
1422 
1423     @Override
1424     public IoBuffer putPrefixedString(CharSequence in, CharsetEncoder encoder)
1425             throws CharacterCodingException {
1426                 return putPrefixedString(in, 2, 0, encoder);
1427             }
1428 
1429     @Override
1430     public IoBuffer putPrefixedString(CharSequence in, int prefixLength, CharsetEncoder encoder)
1431             throws CharacterCodingException {
1432                 return putPrefixedString(in, prefixLength, 0, encoder);
1433             }
1434 
1435     @Override
1436     public IoBuffer putPrefixedString(CharSequence in, int prefixLength, int padding,
1437             CharsetEncoder encoder) throws CharacterCodingException {
1438                 return putPrefixedString(in, prefixLength, padding, (byte) 0, encoder);
1439             }
1440 
1441     @Override
1442     public IoBuffer putPrefixedString(CharSequence val, int prefixLength, int padding,
1443             byte padValue, CharsetEncoder encoder) throws CharacterCodingException {
1444                 int maxLength;
1445                 switch (prefixLength) {
1446                 case 1:
1447                     maxLength = 255;
1448                     break;
1449                 case 2:
1450                     maxLength = 65535;
1451                     break;
1452                 case 4:
1453                     maxLength = Integer.MAX_VALUE;
1454                     break;
1455                 default:
1456                     throw new IllegalArgumentException("prefixLength: " + prefixLength);
1457                 }
1458             
1459                 if (val.length() > maxLength) {
1460                     throw new IllegalArgumentException(
1461                             "The specified string is too long.");
1462                 }
1463                 if (val.length() == 0) {
1464                     switch (prefixLength) {
1465                     case 1:
1466                         put((byte) 0);
1467                         break;
1468                     case 2:
1469                         putShort((short) 0);
1470                         break;
1471                     case 4:
1472                         putInt(0);
1473                         break;
1474                     }
1475                     return this;
1476                 }
1477             
1478                 int padMask;
1479                 switch (padding) {
1480                 case 0:
1481                 case 1:
1482                     padMask = 0;
1483                     break;
1484                 case 2:
1485                     padMask = 1;
1486                     break;
1487                 case 4:
1488                     padMask = 3;
1489                     break;
1490                 default:
1491                     throw new IllegalArgumentException("padding: " + padding);
1492                 }
1493             
1494                 CharBuffer in = CharBuffer.wrap(val);
1495                 int expectedLength = (int) (in.remaining() * encoder
1496                         .averageBytesPerChar()) + 1;
1497             
1498                 skip(prefixLength); // make a room for the length field
1499                 int oldPos = position();
1500                 encoder.reset();
1501             
1502                 for (;;) {
1503                     CoderResult cr;
1504                     if (in.hasRemaining()) {
1505                         cr = encoder.encode(in, buf(), true);
1506                     } else {
1507                         cr = encoder.flush(buf());
1508                     }
1509             
1510                     if (position() - oldPos > maxLength) {
1511                         throw new IllegalArgumentException(
1512                                 "The specified string is too long.");
1513                     }
1514             
1515                     if (cr.isUnderflow()) {
1516                         break;
1517                     }
1518                     if (cr.isOverflow() && isAutoExpand()) {
1519                         autoExpand(expectedLength);
1520                         continue;
1521                     }
1522                     cr.throwException();
1523                 }
1524             
1525                 // Write the length field
1526                 fill(padValue, padding - (position() - oldPos & padMask));
1527                 int length = position() - oldPos;
1528                 switch (prefixLength) {
1529                 case 1:
1530                     put(oldPos - 1, (byte) length);
1531                     break;
1532                 case 2:
1533                     putShort(oldPos - 2, (short) length);
1534                     break;
1535                 case 4:
1536                     putInt(oldPos - 4, length);
1537                     break;
1538                 }
1539                 return this;
1540             }
1541 
1542     @Override
1543     public Object getObject() throws ClassNotFoundException {
1544         return getObject(Thread.currentThread().getContextClassLoader());
1545     }
1546 
1547     @Override
1548     public Object getObject(final ClassLoader classLoader) throws ClassNotFoundException {
1549         if (!prefixedDataAvailable(4)) {
1550             throw new BufferUnderflowException();
1551         }
1552     
1553         int length = getInt();
1554         if (length <= 4) {
1555             throw new BufferDataException(
1556                     "Object length should be greater than 4: " + length);
1557         }
1558     
1559         int oldLimit = limit();
1560         limit(position() + length);
1561         try {
1562             ObjectInputStream in = new ObjectInputStream(asInputStream()) {
1563                 @Override
1564                 protected ObjectStreamClass readClassDescriptor()
1565                         throws IOException, ClassNotFoundException {
1566                     int type = read();
1567                     if (type < 0) {
1568                         throw new EOFException();
1569                     }
1570                     switch (type) {
1571                     case 0: // Primitive types
1572                         return super.readClassDescriptor();
1573                     case 1: // Non-primitive types
1574                         String className = readUTF();
1575                         Class<?> clazz =
1576                             Class.forName(className, true, classLoader);
1577                         return ObjectStreamClass.lookup(clazz);
1578                     default:
1579                         throw new StreamCorruptedException(
1580                                 "Unexpected class descriptor type: " + type);
1581                     }
1582                 }
1583                 
1584                 @Override
1585                 protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
1586                     String name = desc.getName();
1587                     try {
1588                         return Class.forName(name, false, classLoader);
1589                     } catch (ClassNotFoundException ex) {
1590                         return super.resolveClass(desc);
1591                     }
1592                 }
1593             };
1594             return in.readObject();
1595         } catch (IOException e) {
1596             throw new BufferDataException(e);
1597         } finally {
1598             limit(oldLimit);
1599         }
1600     }
1601 
1602     @Override
1603     public IoBuffer putObject(Object o) {
1604         int oldPos = position();
1605         skip(4); // Make a room for the length field.
1606         try {
1607             ObjectOutputStream out = new ObjectOutputStream(asOutputStream()) {
1608                 @Override
1609                 protected void writeClassDescriptor(ObjectStreamClass desc)
1610                         throws IOException {
1611                     String className = desc.getName();
1612                     if (primitiveTypeNames.contains(className)) {
1613                         write(0);
1614                         super.writeClassDescriptor(desc);
1615                     } else {
1616                         write(1);
1617                         writeUTF(desc.getName());
1618                     }
1619                 }
1620             };
1621             out.writeObject(o);
1622             out.flush();
1623         } catch (IOException e) {
1624             throw new BufferDataException(e);
1625         }
1626     
1627         // Fill the length field
1628         int newPos = position();
1629         position(oldPos);
1630         putInt(newPos - oldPos - 4);
1631         position(newPos);
1632         return this;
1633     }
1634 
1635     @Override
1636     public boolean prefixedDataAvailable(int prefixLength) {
1637         return prefixedDataAvailable(prefixLength, Integer.MAX_VALUE);
1638     }
1639 
1640     @Override
1641     public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
1642         if (remaining() < prefixLength) {
1643             return false;
1644         }
1645     
1646         int dataLength;
1647         switch (prefixLength) {
1648         case 1:
1649             dataLength = getUnsigned(position());
1650             break;
1651         case 2:
1652             dataLength = getUnsignedShort(position());
1653             break;
1654         case 4:
1655             dataLength = getInt(position());
1656             break;
1657         default:
1658             throw new IllegalArgumentException("prefixLength: " + prefixLength);
1659         }
1660     
1661         if (dataLength < 0 || dataLength > maxDataLength) {
1662             throw new BufferDataException("dataLength: " + dataLength);
1663         }
1664     
1665         return remaining() - prefixLength >= dataLength;
1666     }
1667 
1668     @Override
1669     public int indexOf(byte b) {
1670         if (hasArray()) {
1671             int arrayOffset = arrayOffset();
1672             int beginPos = arrayOffset + position();
1673             int limit = arrayOffset + limit();
1674             byte[] array = array();
1675     
1676             for (int i = beginPos; i < limit; i++) {
1677                 if (array[i] == b) {
1678                     return i - arrayOffset;
1679                 }
1680             }
1681         } else {
1682             int beginPos = position();
1683             int limit = limit();
1684     
1685             for (int i = beginPos; i < limit; i++) {
1686                 if (get(i) == b) {
1687                     return i;
1688                 }
1689             }
1690         }
1691     
1692         return -1;
1693     }
1694 
1695     @Override
1696     public IoBuffer skip(int size) {
1697         autoExpand(size);
1698         return position(position() + size);
1699     }
1700 
1701     @Override
1702     public IoBuffer fill(byte value, int size) {
1703         autoExpand(size);
1704         int q = size >>> 3;
1705         int r = size & 7;
1706     
1707         if (q > 0) {
1708             int intValue = value | value << 8 | value << 16 | value << 24;
1709             long longValue = intValue;
1710             longValue <<= 32;
1711             longValue |= intValue;
1712     
1713             for (int i = q; i > 0; i--) {
1714                 putLong(longValue);
1715             }
1716         }
1717     
1718         q = r >>> 2;
1719         r = r & 3;
1720     
1721         if (q > 0) {
1722             int intValue = value | value << 8 | value << 16 | value << 24;
1723             putInt(intValue);
1724         }
1725     
1726         q = r >> 1;
1727         r = r & 1;
1728     
1729         if (q > 0) {
1730             short shortValue = (short) (value | value << 8);
1731             putShort(shortValue);
1732         }
1733     
1734         if (r > 0) {
1735             put(value);
1736         }
1737     
1738         return this;
1739     }
1740 
1741     @Override
1742     public IoBuffer fillAndReset(byte value, int size) {
1743         autoExpand(size);
1744         int pos = position();
1745         try {
1746             fill(value, size);
1747         } finally {
1748             position(pos);
1749         }
1750         return this;
1751     }
1752 
1753     @Override
1754     public IoBuffer fill(int size) {
1755         autoExpand(size);
1756         int q = size >>> 3;
1757         int r = size & 7;
1758     
1759         for (int i = q; i > 0; i--) {
1760             putLong(0L);
1761         }
1762     
1763         q = r >>> 2;
1764         r = r & 3;
1765     
1766         if (q > 0) {
1767             putInt(0);
1768         }
1769     
1770         q = r >> 1;
1771         r = r & 1;
1772     
1773         if (q > 0) {
1774             putShort((short) 0);
1775         }
1776     
1777         if (r > 0) {
1778             put((byte) 0);
1779         }
1780     
1781         return this;
1782     }
1783 
1784     @Override
1785     public IoBuffer fillAndReset(int size) {
1786         autoExpand(size);
1787         int pos = position();
1788         try {
1789             fill(size);
1790         } finally {
1791             position(pos);
1792         }
1793     
1794         return this;
1795     }
1796 
1797     private static final long BYTE_MASK = 0xFFL;
1798     private static final long SHORT_MASK = 0xFFFFL;
1799     private static final long INT_MASK = 0xFFFFFFFFL;
1800 
1801     @Override
1802     public <E extends Enum<E>> E getEnum(Class<E> enumClass) {
1803         return toEnum(enumClass, get());
1804     }
1805 
1806     @Override
1807     public <E extends Enum<E>> E getEnum(int index, Class<E> enumClass) {
1808         return toEnum(enumClass, get(index));
1809     }
1810 
1811     @Override
1812     public <E extends Enum<E>> E getEnumShort(Class<E> enumClass) {
1813         return toEnum(enumClass, getShort());
1814     }
1815 
1816     @Override
1817     public <E extends Enum<E>> E getEnumShort(int index, Class<E> enumClass) {
1818         return toEnum(enumClass, getShort(index));
1819     }
1820 
1821     @Override
1822     public <E extends Enum<E>> E getEnumInt(Class<E> enumClass) {
1823         return toEnum(enumClass, getInt());
1824     }
1825 
1826     @Override
1827     public <E extends Enum<E>> E getEnumInt(int index, Class<E> enumClass) {
1828         return toEnum(enumClass, getInt(index));
1829     }
1830 
1831     @Override
1832     public IoBuffer putEnum(Enum<?> e) {
1833         if (e.ordinal() > Byte.MAX_VALUE) {
1834             throw new IllegalArgumentException(enumConversionErrorMessage(e,
1835                     "byte"));
1836         }
1837         return put((byte) e.ordinal());
1838     }
1839 
1840     @Override
1841     public IoBuffer putEnum(int index, Enum<?> e) {
1842         if (e.ordinal() > Byte.MAX_VALUE) {
1843             throw new IllegalArgumentException(enumConversionErrorMessage(e,
1844                     "byte"));
1845         }
1846         return put(index, (byte) e.ordinal());
1847     }
1848 
1849     @Override
1850     public IoBuffer putEnumShort(Enum<?> e) {
1851         if (e.ordinal() > Short.MAX_VALUE) {
1852             throw new IllegalArgumentException(enumConversionErrorMessage(e,
1853                     "short"));
1854         }
1855         return putShort((short) e.ordinal());
1856     }
1857 
1858     @Override
1859     public IoBuffer putEnumShort(int index, Enum<?> e) {
1860         if (e.ordinal() > Short.MAX_VALUE) {
1861             throw new IllegalArgumentException(enumConversionErrorMessage(e,
1862                     "short"));
1863         }
1864         return putShort(index, (short) e.ordinal());
1865     }
1866 
1867     @Override
1868     public IoBuffer putEnumInt(Enum<?> e) {
1869         return putInt(e.ordinal());
1870     }
1871 
1872     @Override
1873     public IoBuffer putEnumInt(int index, Enum<?> e) {
1874         return putInt(index, e.ordinal());
1875     }
1876 
1877     private <E> E toEnum(Class<E> enumClass, int i) {
1878         E[] enumConstants = enumClass.getEnumConstants();
1879         if (i > enumConstants.length) {
1880             throw new IndexOutOfBoundsException(String.format(
1881                     "%d is too large of an ordinal to convert to the enum %s",
1882                     i, enumClass.getName()));
1883         }
1884         return enumConstants[i];
1885     }
1886 
1887     private String enumConversionErrorMessage(Enum<?> e, String type) {
1888         return String.format("%s.%s has an ordinal value too large for a %s", e
1889                 .getClass().getName(), e.name(), type);
1890     }
1891 
1892     @Override
1893     public <E extends Enum<E>> EnumSet<E> getEnumSet(Class<E> enumClass) {
1894         return toEnumSet(enumClass, get() & BYTE_MASK);
1895     }
1896 
1897     @Override
1898     public <E extends Enum<E>> EnumSet<E> getEnumSet(int index, Class<E> enumClass) {
1899         return toEnumSet(enumClass, get(index) & BYTE_MASK);
1900     }
1901 
1902     @Override
1903     public <E extends Enum<E>> EnumSet<E> getEnumSetShort(Class<E> enumClass) {
1904         return toEnumSet(enumClass, getShort() & SHORT_MASK);
1905     }
1906 
1907     @Override
1908     public <E extends Enum<E>> EnumSet<E> getEnumSetShort(int index, Class<E> enumClass) {
1909         return toEnumSet(enumClass, getShort(index) & SHORT_MASK);
1910     }
1911 
1912     @Override
1913     public <E extends Enum<E>> EnumSet<E> getEnumSetInt(Class<E> enumClass) {
1914         return toEnumSet(enumClass, getInt() & INT_MASK);
1915     }
1916 
1917     @Override
1918     public <E extends Enum<E>> EnumSet<E> getEnumSetInt(int index, Class<E> enumClass) {
1919         return toEnumSet(enumClass, getInt(index) & INT_MASK);
1920     }
1921 
1922     @Override
1923     public <E extends Enum<E>> EnumSet<E> getEnumSetLong(Class<E> enumClass) {
1924         return toEnumSet(enumClass, getLong());
1925     }
1926 
1927     @Override
1928     public <E extends Enum<E>> EnumSet<E> getEnumSetLong(int index, Class<E> enumClass) {
1929         return toEnumSet(enumClass, getLong(index));
1930     }
1931 
1932     private <E extends Enum<E>> EnumSet<E> toEnumSet(Class<E> clazz, long vector) {
1933         EnumSet<E> set = EnumSet.noneOf(clazz);
1934         long mask = 1;
1935         for (E e : clazz.getEnumConstants()) {
1936             if ((mask & vector) == mask) {
1937                 set.add(e);
1938             }
1939             mask <<= 1;
1940         }
1941         return set;
1942     }
1943 
1944     @Override
1945     public <E extends Enum<E>> IoBuffer putEnumSet(Set<E> set) {
1946         long vector = toLong(set);
1947         if ((vector & ~BYTE_MASK) != 0) {
1948             throw new IllegalArgumentException(
1949                     "The enum set is too large to fit in a byte: " + set);
1950         }
1951         return put((byte) vector);
1952     }
1953 
1954     @Override
1955     public <E extends Enum<E>> IoBuffer putEnumSet(int index, Set<E> set) {
1956         long vector = toLong(set);
1957         if ((vector & ~BYTE_MASK) != 0) {
1958             throw new IllegalArgumentException(
1959                     "The enum set is too large to fit in a byte: " + set);
1960         }
1961         return put(index, (byte) vector);
1962     }
1963 
1964     @Override
1965     public <E extends Enum<E>> IoBuffer putEnumSetShort(Set<E> set) {
1966         long vector = toLong(set);
1967         if ((vector & ~SHORT_MASK) != 0) {
1968             throw new IllegalArgumentException(
1969                     "The enum set is too large to fit in a short: " + set);
1970         }
1971         return putShort((short) vector);
1972     }
1973 
1974     @Override
1975     public <E extends Enum<E>> IoBuffer putEnumSetShort(int index, Set<E> set) {
1976         long vector = toLong(set);
1977         if ((vector & ~SHORT_MASK) != 0) {
1978             throw new IllegalArgumentException(
1979                     "The enum set is too large to fit in a short: " + set);
1980         }
1981         return putShort(index, (short) vector);
1982     }
1983 
1984     @Override
1985     public <E extends Enum<E>> IoBuffer putEnumSetInt(Set<E> set) {
1986         long vector = toLong(set);
1987         if ((vector & ~INT_MASK) != 0) {
1988             throw new IllegalArgumentException(
1989                     "The enum set is too large to fit in an int: " + set);
1990         }
1991         return putInt((int) vector);
1992     }
1993 
1994     @Override
1995     public <E extends Enum<E>> IoBuffer putEnumSetInt(int index, Set<E> set) {
1996         long vector = toLong(set);
1997         if ((vector & ~INT_MASK) != 0) {
1998             throw new IllegalArgumentException(
1999                     "The enum set is too large to fit in an int: " + set);
2000         }
2001         return putInt(index, (int) vector);
2002     }
2003 
2004     @Override
2005     public <E extends Enum<E>> IoBuffer putEnumSetLong(Set<E> set) {
2006         return putLong(toLong(set));
2007     }
2008 
2009     @Override
2010     public <E extends Enum<E>> IoBuffer putEnumSetLong(int index, Set<E> set) {
2011         return putLong(index, toLong(set));
2012     }
2013 
2014     private <E extends Enum<E>> long toLong(Set<E> set) {
2015         long vector = 0;
2016         for (E e : set) {
2017             if (e.ordinal() >= Long.SIZE) {
2018                 throw new IllegalArgumentException(
2019                         "The enum set is too large to fit in a bit vector: "
2020                                 + set);
2021             }
2022             vector |= 1L << e.ordinal();
2023         }
2024         return vector;
2025     }
2026 
2027     /**
2028      * This method forwards the call to {@link #expand(int)} only when
2029      * <tt>autoExpand</tt> property is <tt>true</tt>.
2030      */
2031     private IoBuffer autoExpand(int expectedRemaining) {
2032         if (isAutoExpand()) {
2033             expand(expectedRemaining);
2034         }
2035         return this;
2036     }
2037 
2038     /**
2039      * This method forwards the call to {@link #expand(int)} only when
2040      * <tt>autoExpand</tt> property is <tt>true</tt>.
2041      */
2042     private IoBuffer autoExpand(int pos, int expectedRemaining) {
2043         if (isAutoExpand()) {
2044             expand(pos, expectedRemaining);
2045         }
2046         return this;
2047     }
2048 
2049     private static void checkFieldSize(int fieldSize) {
2050         if (fieldSize < 0) {
2051             throw new IllegalArgumentException("fieldSize cannot be negative: "
2052                     + fieldSize);
2053         }
2054     }
2055 }