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