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