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.Serializable;
30  import java.io.StreamCorruptedException;
31  import java.nio.BufferOverflowException;
32  import java.nio.BufferUnderflowException;
33  import java.nio.ByteBuffer;
34  import java.nio.ByteOrder;
35  import java.nio.CharBuffer;
36  import java.nio.DoubleBuffer;
37  import java.nio.FloatBuffer;
38  import java.nio.IntBuffer;
39  import java.nio.LongBuffer;
40  import java.nio.ShortBuffer;
41  import java.nio.charset.CharacterCodingException;
42  import java.nio.charset.CharsetDecoder;
43  import java.nio.charset.CharsetEncoder;
44  import java.nio.charset.CoderResult;
45  import java.util.EnumSet;
46  import java.util.Set;
47  
48  /**
49   * A base implementation of {@link IoBuffer}.  This implementation
50   * assumes that {@link IoBuffer#buf()} always returns a correct NIO
51   * {@link ByteBuffer} instance.  Most implementations could
52   * extend this class and implement their own buffer management mechanism.
53   *
54   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
55   * @see IoBufferAllocator
56   */
57  public abstract class AbstractIoBuffer extends IoBuffer {
58      /** Tells if a buffer has been created from an existing buffer */
59      private final boolean derived;
60  
61      /** A flag set to true if the buffer can extend automatically */
62      private boolean autoExpand;
63  
64      /** A flag set to true if the buffer can shrink automatically */
65      private boolean autoShrink;
66  
67      /** Tells if a buffer can be expanded */
68      private boolean recapacityAllowed = true;
69  
70      /** The minimum number of bytes the IoBuffer can hold */
71      private int minimumCapacity;
72  
73      /** A mask for a byte */
74      private static final long BYTE_MASK = 0xFFL;
75  
76      /** A mask for a short */
77      private static final long SHORT_MASK = 0xFFFFL;
78  
79      /** A mask for an int */
80      private static final long INT_MASK = 0xFFFFFFFFL;
81  
82      /**
83       * We don't have any access to Buffer.markValue(), so we need to track it down,
84       * which will cause small extra overhead.
85       */
86      private int mark = -1;
87  
88      /**
89       * Creates a new parent buffer.
90       * 
91       * @param allocator The allocator to use to create new buffers
92       * @param initialCapacity The initial buffer capacity when created
93       */
94      protected AbstractIoBuffer(IoBufferAllocator allocator, int initialCapacity) {
95          setAllocator(allocator);
96          this.recapacityAllowed = true;
97          this.derived = false;
98          this.minimumCapacity = initialCapacity;
99      }
100 
101     /**
102      * Creates a new derived buffer. A derived buffer uses an existing
103      * buffer properties - the allocator and capacity -.
104      * 
105      * @param parent The buffer we get the properties from
106      */
107     protected AbstractIoBuffer(AbstractIoBuffer parent) {
108         setAllocator(parent.getAllocator());
109         this.recapacityAllowed = false;
110         this.derived = true;
111         this.minimumCapacity = parent.minimumCapacity;
112     }
113 
114     /**
115      * {@inheritDoc}
116      */
117     @Override
118     public final boolean isDirect() {
119         return buf().isDirect();
120     }
121 
122     /**
123      * {@inheritDoc}
124      */
125     @Override
126     public final boolean isReadOnly() {
127         return buf().isReadOnly();
128     }
129 
130     /**
131      * Sets the underlying NIO buffer instance.
132      * 
133      * @param newBuf The buffer to store within this IoBuffer
134      */
135     protected abstract void buf(ByteBuffer newBuf);
136 
137     /**
138      * {@inheritDoc}
139      */
140     @Override
141     public final int minimumCapacity() {
142         return minimumCapacity;
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     @Override
149     public final IoBuffer minimumCapacity(int minimumCapacity) {
150         if (minimumCapacity < 0) {
151             throw new IllegalArgumentException("minimumCapacity: " + minimumCapacity);
152         }
153         this.minimumCapacity = minimumCapacity;
154         return this;
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
161     public final int capacity() {
162         return buf().capacity();
163     }
164 
165     /**
166      * {@inheritDoc}
167      */
168     @Override
169     public final IoBuffer capacity(int newCapacity) {
170         if (!recapacityAllowed) {
171             throw new IllegalStateException("Derived buffers and their parent can't be expanded.");
172         }
173 
174         // Allocate a new buffer and transfer all settings to it.
175         if (newCapacity > capacity()) {
176             // Expand:
177             //// Save the state.
178             int pos = position();
179             int limit = limit();
180             ByteOrder bo = order();
181 
182             //// Reallocate.
183             ByteBuffer oldBuf = buf();
184             ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect());
185             oldBuf.clear();
186             newBuf.put(oldBuf);
187             buf(newBuf);
188 
189             //// Restore the state.
190             buf().limit(limit);
191             if (mark >= 0) {
192                 buf().position(mark);
193                 buf().mark();
194             }
195             buf().position(pos);
196             buf().order(bo);
197         }
198 
199         return this;
200     }
201 
202     /**
203      * {@inheritDoc}
204      */
205     @Override
206     public final boolean isAutoExpand() {
207         return autoExpand && recapacityAllowed;
208     }
209 
210     /**
211      * {@inheritDoc}
212      */
213     @Override
214     public final boolean isAutoShrink() {
215         return autoShrink && recapacityAllowed;
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
222     public final boolean isDerived() {
223         return derived;
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
230     public final IoBuffer setAutoExpand(boolean autoExpand) {
231         if (!recapacityAllowed) {
232             throw new IllegalStateException("Derived buffers and their parent can't be expanded.");
233         }
234         this.autoExpand = autoExpand;
235         return this;
236     }
237 
238     /**
239      * {@inheritDoc}
240      */
241     @Override
242     public final IoBuffer setAutoShrink(boolean autoShrink) {
243         if (!recapacityAllowed) {
244             throw new IllegalStateException("Derived buffers and their parent can't be shrinked.");
245         }
246         this.autoShrink = autoShrink;
247         return this;
248     }
249 
250     /**
251      * {@inheritDoc}
252      */
253     @Override
254     public final IoBuffer expand(int expectedRemaining) {
255         return expand(position(), expectedRemaining, false);
256     }
257 
258     private IoBuffer expand(int expectedRemaining, boolean autoExpand) {
259         return expand(position(), expectedRemaining, autoExpand);
260     }
261 
262     /**
263      * {@inheritDoc}
264      */
265     @Override
266     public final IoBuffer expand(int pos, int expectedRemaining) {
267         return expand(pos, expectedRemaining, false);
268     }
269 
270     private IoBuffer expand(int pos, int expectedRemaining, boolean autoExpand) {
271         if (!recapacityAllowed) {
272             throw new IllegalStateException("Derived buffers and their parent can't be expanded.");
273         }
274 
275         int end = pos + expectedRemaining;
276         int newCapacity;
277         if (autoExpand) {
278             newCapacity = IoBuffer.normalizeCapacity(end);
279         } else {
280             newCapacity = end;
281         }
282         if (newCapacity > capacity()) {
283             // The buffer needs expansion.
284             capacity(newCapacity);
285         }
286 
287         if (end > limit()) {
288             // We call limit() directly to prevent StackOverflowError
289             buf().limit(end);
290         }
291         return this;
292     }
293 
294     /**
295      * {@inheritDoc}
296      */
297     @Override
298     public final IoBuffer shrink() {
299 
300         if (!recapacityAllowed) {
301             throw new IllegalStateException("Derived buffers and their parent can't be expanded.");
302         }
303 
304         int position = position();
305         int capacity = capacity();
306         int limit = limit();
307 
308         if (capacity == limit) {
309             return this;
310         }
311 
312         int newCapacity = capacity;
313         int minCapacity = Math.max(minimumCapacity, limit);
314 
315         for (;;) {
316             if (newCapacity >>> 1 < minCapacity) {
317                 break;
318             }
319 
320             newCapacity >>>= 1;
321 
322             if (minCapacity == 0) {
323                 break;
324             }
325         }
326 
327         newCapacity = Math.max(minCapacity, newCapacity);
328 
329         if (newCapacity == capacity) {
330             return this;
331         }
332 
333         // Shrink and compact:
334         //// Save the state.
335         ByteOrder bo = order();
336 
337         //// Reallocate.
338         ByteBuffer oldBuf = buf();
339         ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect());
340         oldBuf.position(0);
341         oldBuf.limit(limit);
342         newBuf.put(oldBuf);
343         buf(newBuf);
344 
345         //// Restore the state.
346         buf().position(position);
347         buf().limit(limit);
348         buf().order(bo);
349         mark = -1;
350 
351         return this;
352     }
353 
354     /**
355      * {@inheritDoc}
356      */
357     @Override
358     public final int position() {
359         return buf().position();
360     }
361 
362     /**
363      * {@inheritDoc}
364      */
365     @Override
366     public final IoBuffer position(int newPosition) {
367         autoExpand(newPosition, 0);
368         buf().position(newPosition);
369         if (mark > newPosition) {
370             mark = -1;
371         }
372         return this;
373     }
374 
375     /**
376      * {@inheritDoc}
377      */
378     @Override
379     public final int limit() {
380         return buf().limit();
381     }
382 
383     /**
384      * {@inheritDoc}
385      */
386     @Override
387     public final IoBuffer limit(int newLimit) {
388         autoExpand(newLimit, 0);
389         buf().limit(newLimit);
390         if (mark > newLimit) {
391             mark = -1;
392         }
393         return this;
394     }
395 
396     /**
397      * {@inheritDoc}
398      */
399     @Override
400     public final IoBuffer mark() {
401         ByteBuffer byteBuffer = buf();
402         byteBuffer.mark();
403         mark = byteBuffer.position();
404 
405         return this;
406     }
407 
408     /**
409      * {@inheritDoc}
410      */
411     @Override
412     public final int markValue() {
413         return mark;
414     }
415 
416     /**
417      * {@inheritDoc}
418      */
419     @Override
420     public final IoBuffer reset() {
421         buf().reset();
422         return this;
423     }
424 
425     /**
426      * {@inheritDoc}
427      */
428     @Override
429     public final IoBuffer clear() {
430         buf().clear();
431         mark = -1;
432         return this;
433     }
434 
435     /**
436      * {@inheritDoc}
437      */
438     @Override
439     public final IoBuffer sweep() {
440         clear();
441         return fillAndReset(remaining());
442     }
443 
444     /**
445      * {@inheritDoc}
446      */
447     @Override
448     public final IoBuffer sweep(byte value) {
449         clear();
450         return fillAndReset(value, remaining());
451     }
452 
453     /**
454      * {@inheritDoc}
455      */
456     @Override
457     public final IoBuffer flip() {
458         buf().flip();
459         mark = -1;
460         return this;
461     }
462 
463     /**
464      * {@inheritDoc}
465      */
466     @Override
467     public final IoBuffer rewind() {
468         buf().rewind();
469         mark = -1;
470         return this;
471     }
472 
473     /**
474      * {@inheritDoc}
475      */
476     @Override
477     public final int remaining() {
478         ByteBuffer byteBuffer = buf();
479 
480         return byteBuffer.limit() - byteBuffer.position();
481     }
482 
483     /**
484      * {@inheritDoc}
485      */
486     @Override
487     public final boolean hasRemaining() {
488         ByteBuffer byteBuffer = buf();
489 
490         return byteBuffer.limit() > byteBuffer.position();
491     }
492 
493     /**
494      * {@inheritDoc}
495      */
496     @Override
497     public final byte get() {
498         return buf().get();
499     }
500 
501     /**
502      * {@inheritDoc}
503      */
504     @Override
505     public final short getUnsigned() {
506         return (short) (get() & 0xff);
507     }
508 
509     /**
510      * {@inheritDoc}
511      */
512     @Override
513     public final IoBuffer put(byte b) {
514         autoExpand(1);
515         buf().put(b);
516         return this;
517     }
518 
519     /**
520      * {@inheritDoc}
521      */
522     public IoBuffer putUnsigned(byte value) {
523         autoExpand(1);
524         buf().put((byte) (value & 0xff));
525         return this;
526     }
527 
528     /**
529      * {@inheritDoc}
530      */
531     public IoBuffer putUnsigned(int index, byte value) {
532         autoExpand(index, 1);
533         buf().put(index, (byte) (value & 0xff));
534         return this;
535     }
536 
537     /**
538      * {@inheritDoc}
539      */
540     public IoBuffer putUnsigned(short value) {
541         autoExpand(1);
542         buf().put((byte) (value & 0x00ff));
543         return this;
544     }
545 
546     /**
547      * {@inheritDoc}
548      */
549     public IoBuffer putUnsigned(int index, short value) {
550         autoExpand(index, 1);
551         buf().put(index, (byte) (value & 0x00ff));
552         return this;
553     }
554 
555     /**
556      * {@inheritDoc}
557      */
558     public IoBuffer putUnsigned(int value) {
559         autoExpand(1);
560         buf().put((byte) (value & 0x000000ff));
561         return this;
562     }
563 
564     /**
565      * {@inheritDoc}
566      */
567     public IoBuffer putUnsigned(int index, int value) {
568         autoExpand(index, 1);
569         buf().put(index, (byte) (value & 0x000000ff));
570         return this;
571     }
572 
573     /**
574      * {@inheritDoc}
575      */
576     public IoBuffer putUnsigned(long value) {
577         autoExpand(1);
578         buf().put((byte) (value & 0x00000000000000ffL));
579         return this;
580     }
581 
582     /**
583      * {@inheritDoc}
584      */
585     public IoBuffer putUnsigned(int index, long value) {
586         autoExpand(index, 1);
587         buf().put(index, (byte) (value & 0x00000000000000ffL));
588         return this;
589     }
590 
591     /**
592      * {@inheritDoc}
593      */
594     @Override
595     public final byte get(int index) {
596         return buf().get(index);
597     }
598 
599     /**
600      * {@inheritDoc}
601      */
602     @Override
603     public final short getUnsigned(int index) {
604         return (short) (get(index) & 0xff);
605     }
606 
607     /**
608      * {@inheritDoc}
609      */
610     @Override
611     public final IoBuffer put(int index, byte b) {
612         autoExpand(index, 1);
613         buf().put(index, b);
614         return this;
615     }
616 
617     /**
618      * {@inheritDoc}
619      */
620     @Override
621     public final IoBuffer get(byte[] dst, int offset, int length) {
622         buf().get(dst, offset, length);
623         return this;
624     }
625 
626     /**
627      * {@inheritDoc}
628      */
629     @Override
630     public final IoBuffer put(ByteBuffer src) {
631         autoExpand(src.remaining());
632         buf().put(src);
633         return this;
634     }
635 
636     /**
637      * {@inheritDoc}
638      */
639     @Override
640     public final IoBuffer put(byte[] src, int offset, int length) {
641         autoExpand(length);
642         buf().put(src, offset, length);
643         return this;
644     }
645 
646     /**
647      * {@inheritDoc}
648      */
649     @Override
650     public final IoBuffer compact() {
651         int remaining = remaining();
652         int capacity = capacity();
653 
654         if (capacity == 0) {
655             return this;
656         }
657 
658         if (isAutoShrink() && remaining <= capacity >>> 2 && capacity > minimumCapacity) {
659             int newCapacity = capacity;
660             int minCapacity = Math.max(minimumCapacity, remaining << 1);
661             for (;;) {
662                 if (newCapacity >>> 1 < minCapacity) {
663                     break;
664                 }
665                 newCapacity >>>= 1;
666             }
667 
668             newCapacity = Math.max(minCapacity, newCapacity);
669 
670             if (newCapacity == capacity) {
671                 return this;
672             }
673 
674             // Shrink and compact:
675             //// Save the state.
676             ByteOrder bo = order();
677 
678             //// Sanity check.
679             if (remaining > newCapacity) {
680                 throw new IllegalStateException("The amount of the remaining bytes is greater than "
681                         + "the new capacity.");
682             }
683 
684             //// Reallocate.
685             ByteBuffer oldBuf = buf();
686             ByteBuffer newBuf = getAllocator().allocateNioBuffer(newCapacity, isDirect());
687             newBuf.put(oldBuf);
688             buf(newBuf);
689 
690             //// Restore the state.
691             buf().order(bo);
692         } else {
693             buf().compact();
694         }
695         mark = -1;
696         return this;
697     }
698 
699     /**
700      * {@inheritDoc}
701      */
702     @Override
703     public final ByteOrder order() {
704         return buf().order();
705     }
706 
707     /**
708      * {@inheritDoc}
709      */
710     @Override
711     public final IoBuffer order(ByteOrder bo) {
712         buf().order(bo);
713         return this;
714     }
715 
716     /**
717      * {@inheritDoc}
718      */
719     @Override
720     public final char getChar() {
721         return buf().getChar();
722     }
723 
724     /**
725      * {@inheritDoc}
726      */
727     @Override
728     public final IoBuffer putChar(char value) {
729         autoExpand(2);
730         buf().putChar(value);
731         return this;
732     }
733 
734     /**
735      * {@inheritDoc}
736      */
737     @Override
738     public final char getChar(int index) {
739         return buf().getChar(index);
740     }
741 
742     /**
743      * {@inheritDoc}
744      */
745     @Override
746     public final IoBuffer putChar(int index, char value) {
747         autoExpand(index, 2);
748         buf().putChar(index, value);
749         return this;
750     }
751 
752     /**
753      * {@inheritDoc}
754      */
755     @Override
756     public final CharBuffer asCharBuffer() {
757         return buf().asCharBuffer();
758     }
759 
760     /**
761      * {@inheritDoc}
762      */
763     @Override
764     public final short getShort() {
765         return buf().getShort();
766     }
767 
768     /**
769      * {@inheritDoc}
770      */
771     @Override
772     public final IoBuffer putShort(short value) {
773         autoExpand(2);
774         buf().putShort(value);
775         return this;
776     }
777 
778     /**
779      * {@inheritDoc}
780      */
781     @Override
782     public final short getShort(int index) {
783         return buf().getShort(index);
784     }
785 
786     /**
787      * {@inheritDoc}
788      */
789     @Override
790     public final IoBuffer putShort(int index, short value) {
791         autoExpand(index, 2);
792         buf().putShort(index, value);
793         return this;
794     }
795 
796     /**
797      * {@inheritDoc}
798      */
799     @Override
800     public final ShortBuffer asShortBuffer() {
801         return buf().asShortBuffer();
802     }
803 
804     /**
805      * {@inheritDoc}
806      */
807     @Override
808     public final int getInt() {
809         return buf().getInt();
810     }
811 
812     /**
813      * {@inheritDoc}
814      */
815     @Override
816     public final IoBuffer putInt(int value) {
817         autoExpand(4);
818         buf().putInt(value);
819         return this;
820     }
821 
822     /**
823      * {@inheritDoc}
824      */
825     @Override
826     public final IoBuffer putUnsignedInt(byte value) {
827         autoExpand(4);
828         buf().putInt((value & 0x00ff));
829         return this;
830     }
831 
832     /**
833      * {@inheritDoc}
834      */
835     @Override
836     public final IoBuffer putUnsignedInt(int index, byte value) {
837         autoExpand(index, 4);
838         buf().putInt(index, (value & 0x00ff));
839         return this;
840     }
841 
842     /**
843      * {@inheritDoc}
844      */
845     @Override
846     public final IoBuffer putUnsignedInt(short value) {
847         autoExpand(4);
848         buf().putInt((value & 0x0000ffff));
849         return this;
850     }
851 
852     /**
853      * {@inheritDoc}
854      */
855     @Override
856     public final IoBuffer putUnsignedInt(int index, short value) {
857         autoExpand(index, 4);
858         buf().putInt(index, (value & 0x0000ffff));
859         return this;
860     }
861 
862     /**
863      * {@inheritDoc}
864      */
865     @Override
866     public final IoBuffer putUnsignedInt(int value) {
867         autoExpand(4);
868         buf().putInt(value);
869         return this;
870     }
871 
872     /**
873      * {@inheritDoc}
874      */
875     @Override
876     public final IoBuffer putUnsignedInt(int index, int value) {
877         autoExpand(index, 4);
878         buf().putInt(index, value);
879         return this;
880     }
881 
882     /**
883      * {@inheritDoc}
884      */
885     @Override
886     public final IoBuffer putUnsignedInt(long value) {
887         autoExpand(4);
888         buf().putInt((int) (value & 0x00000000ffffffff));
889         return this;
890     }
891 
892     /**
893      * {@inheritDoc}
894      */
895     @Override
896     public final IoBuffer putUnsignedInt(int index, long value) {
897         autoExpand(index, 4);
898         buf().putInt(index, (int) (value & 0x00000000ffffffffL));
899         return this;
900     }
901 
902     /**
903      * {@inheritDoc}
904      */
905     @Override
906     public final IoBuffer putUnsignedShort(byte value) {
907         autoExpand(2);
908         buf().putShort((short) (value & 0x00ff));
909         return this;
910     }
911 
912     /**
913      * {@inheritDoc}
914      */
915     @Override
916     public final IoBuffer putUnsignedShort(int index, byte value) {
917         autoExpand(index, 2);
918         buf().putShort(index, (short) (value & 0x00ff));
919         return this;
920     }
921 
922     /**
923      * {@inheritDoc}
924      */
925     @Override
926     public final IoBuffer putUnsignedShort(short value) {
927         autoExpand(2);
928         buf().putShort(value);
929         return this;
930     }
931 
932     /**
933      * {@inheritDoc}
934      */
935     @Override
936     public final IoBuffer putUnsignedShort(int index, short value) {
937         autoExpand(index, 2);
938         buf().putShort(index, value);
939         return this;
940     }
941 
942     /**
943      * {@inheritDoc}
944      */
945     @Override
946     public final IoBuffer putUnsignedShort(int value) {
947         autoExpand(2);
948         buf().putShort((short) value);
949         return this;
950     }
951 
952     /**
953      * {@inheritDoc}
954      */
955     @Override
956     public final IoBuffer putUnsignedShort(int index, int value) {
957         autoExpand(index, 2);
958         buf().putShort(index, (short) value);
959         return this;
960     }
961 
962     /**
963      * {@inheritDoc}
964      */
965     @Override
966     public final IoBuffer putUnsignedShort(long value) {
967         autoExpand(2);
968         buf().putShort((short) (value));
969         return this;
970     }
971 
972     /**
973      * {@inheritDoc}
974      */
975     @Override
976     public final IoBuffer putUnsignedShort(int index, long value) {
977         autoExpand(index, 2);
978         buf().putShort(index, (short) (value));
979         return this;
980     }
981 
982     /**
983      * {@inheritDoc}
984      */
985     @Override
986     public final int getInt(int index) {
987         return buf().getInt(index);
988     }
989 
990     /**
991      * {@inheritDoc}
992      */
993     @Override
994     public final IoBuffer putInt(int index, int value) {
995         autoExpand(index, 4);
996         buf().putInt(index, value);
997         return this;
998     }
999 
1000     /**
1001      * {@inheritDoc}
1002      */
1003     @Override
1004     public final IntBuffer asIntBuffer() {
1005         return buf().asIntBuffer();
1006     }
1007 
1008     /**
1009      * {@inheritDoc}
1010      */
1011     @Override
1012     public final long getLong() {
1013         return buf().getLong();
1014     }
1015 
1016     /**
1017      * {@inheritDoc}
1018      */
1019     @Override
1020     public final IoBuffer putLong(long value) {
1021         autoExpand(8);
1022         buf().putLong(value);
1023         return this;
1024     }
1025 
1026     /**
1027      * {@inheritDoc}
1028      */
1029     @Override
1030     public final long getLong(int index) {
1031         return buf().getLong(index);
1032     }
1033 
1034     /**
1035      * {@inheritDoc}
1036      */
1037     @Override
1038     public final IoBuffer putLong(int index, long value) {
1039         autoExpand(index, 8);
1040         buf().putLong(index, value);
1041         return this;
1042     }
1043 
1044     /**
1045      * {@inheritDoc}
1046      */
1047     @Override
1048     public final LongBuffer asLongBuffer() {
1049         return buf().asLongBuffer();
1050     }
1051 
1052     /**
1053      * {@inheritDoc}
1054      */
1055     @Override
1056     public final float getFloat() {
1057         return buf().getFloat();
1058     }
1059 
1060     /**
1061      * {@inheritDoc}
1062      */
1063     @Override
1064     public final IoBuffer putFloat(float value) {
1065         autoExpand(4);
1066         buf().putFloat(value);
1067         return this;
1068     }
1069 
1070     /**
1071      * {@inheritDoc}
1072      */
1073     @Override
1074     public final float getFloat(int index) {
1075         return buf().getFloat(index);
1076     }
1077 
1078     /**
1079      * {@inheritDoc}
1080      */
1081     @Override
1082     public final IoBuffer putFloat(int index, float value) {
1083         autoExpand(index, 4);
1084         buf().putFloat(index, value);
1085         return this;
1086     }
1087 
1088     /**
1089      * {@inheritDoc}
1090      */
1091     @Override
1092     public final FloatBuffer asFloatBuffer() {
1093         return buf().asFloatBuffer();
1094     }
1095 
1096     /**
1097      * {@inheritDoc}
1098      */
1099     @Override
1100     public final double getDouble() {
1101         return buf().getDouble();
1102     }
1103 
1104     /**
1105      * {@inheritDoc}
1106      */
1107     @Override
1108     public final IoBuffer putDouble(double value) {
1109         autoExpand(8);
1110         buf().putDouble(value);
1111         return this;
1112     }
1113 
1114     /**
1115      * {@inheritDoc}
1116      */
1117     @Override
1118     public final double getDouble(int index) {
1119         return buf().getDouble(index);
1120     }
1121 
1122     /**
1123      * {@inheritDoc}
1124      */
1125     @Override
1126     public final IoBuffer putDouble(int index, double value) {
1127         autoExpand(index, 8);
1128         buf().putDouble(index, value);
1129         return this;
1130     }
1131 
1132     /**
1133      * {@inheritDoc}
1134      */
1135     @Override
1136     public final DoubleBuffer asDoubleBuffer() {
1137         return buf().asDoubleBuffer();
1138     }
1139 
1140     /**
1141      * {@inheritDoc}
1142      */
1143     @Override
1144     public final IoBuffer asReadOnlyBuffer() {
1145         recapacityAllowed = false;
1146         return asReadOnlyBuffer0();
1147     }
1148 
1149     /**
1150      * Implement this method to return the unexpandable read only version of
1151      * this buffer.
1152      */
1153     protected abstract IoBuffer asReadOnlyBuffer0();
1154 
1155     /**
1156      * {@inheritDoc}
1157      */
1158     @Override
1159     public final IoBuffer duplicate() {
1160         recapacityAllowed = false;
1161         return duplicate0();
1162     }
1163 
1164     /**
1165      * Implement this method to return the unexpandable duplicate of this
1166      * buffer.
1167      */
1168     protected abstract IoBuffer duplicate0();
1169 
1170     /**
1171      * {@inheritDoc}
1172      */
1173     @Override
1174     public final IoBuffer slice() {
1175         recapacityAllowed = false;
1176         return slice0();
1177     }
1178 
1179     /**
1180      * {@inheritDoc}
1181      */
1182     @Override
1183     public final IoBuffer getSlice(int index, int length) {
1184         if (length < 0) {
1185             throw new IllegalArgumentException("length: " + length);
1186         }
1187 
1188         int pos = position();
1189         int limit = limit();
1190 
1191         if (index > limit) {
1192             throw new IllegalArgumentException("index: " + index);
1193         }
1194 
1195         int endIndex = index + length;
1196 
1197         if (endIndex > limit) {
1198             throw new IndexOutOfBoundsException("index + length (" + endIndex + ") is greater " + "than limit ("
1199                     + limit + ").");
1200         }
1201 
1202         clear();
1203         limit(endIndex);
1204         position(index);
1205 
1206         IoBuffer slice = slice();
1207         limit(limit);
1208         position(pos);
1209 
1210         return slice;
1211     }
1212 
1213     /**
1214      * {@inheritDoc}
1215      */
1216     @Override
1217     public final IoBuffer getSlice(int length) {
1218         if (length < 0) {
1219             throw new IllegalArgumentException("length: " + length);
1220         }
1221         int pos = position();
1222         int limit = limit();
1223         int nextPos = pos + length;
1224         if (limit < nextPos) {
1225             throw new IndexOutOfBoundsException("position + length (" + nextPos + ") is greater " + "than limit ("
1226                     + limit + ").");
1227         }
1228 
1229         limit(pos + length);
1230         IoBuffer slice = slice();
1231         position(nextPos);
1232         limit(limit);
1233         return slice;
1234     }
1235 
1236     /**
1237      * Implement this method to return the unexpandable slice of this
1238      * buffer.
1239      */
1240     protected abstract IoBuffer slice0();
1241 
1242     /**
1243      * {@inheritDoc}
1244      */
1245     @Override
1246     public int hashCode() {
1247         int h = 1;
1248         int p = position();
1249         for (int i = limit() - 1; i >= p; i--) {
1250             h = 31 * h + get(i);
1251         }
1252         return h;
1253     }
1254 
1255     /**
1256      * {@inheritDoc}
1257      */
1258     @Override
1259     public boolean equals(Object o) {
1260         if (!(o instanceof IoBuffer)) {
1261             return false;
1262         }
1263 
1264         IoBuffer that = (IoBuffer) o;
1265         if (this.remaining() != that.remaining()) {
1266             return false;
1267         }
1268 
1269         int p = this.position();
1270         for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--) {
1271             byte v1 = this.get(i);
1272             byte v2 = that.get(j);
1273             if (v1 != v2) {
1274                 return false;
1275             }
1276         }
1277         return true;
1278     }
1279 
1280     /**
1281      * {@inheritDoc}
1282      */
1283     public int compareTo(IoBuffer that) {
1284         int n = this.position() + Math.min(this.remaining(), that.remaining());
1285         for (int i = this.position(), j = that.position(); i < n; i++, j++) {
1286             byte v1 = this.get(i);
1287             byte v2 = that.get(j);
1288             if (v1 == v2) {
1289                 continue;
1290             }
1291             if (v1 < v2) {
1292                 return -1;
1293             }
1294 
1295             return +1;
1296         }
1297         return this.remaining() - that.remaining();
1298     }
1299 
1300     /**
1301      * {@inheritDoc}
1302      */
1303     @Override
1304     public String toString() {
1305         StringBuilder buf = new StringBuilder();
1306         if (isDirect()) {
1307             buf.append("DirectBuffer");
1308         } else {
1309             buf.append("HeapBuffer");
1310         }
1311         buf.append("[pos=");
1312         buf.append(position());
1313         buf.append(" lim=");
1314         buf.append(limit());
1315         buf.append(" cap=");
1316         buf.append(capacity());
1317         buf.append(": ");
1318         buf.append(getHexDump(16));
1319         buf.append(']');
1320         return buf.toString();
1321     }
1322 
1323     /**
1324      * {@inheritDoc}
1325      */
1326     @Override
1327     public IoBuffer get(byte[] dst) {
1328         return get(dst, 0, dst.length);
1329     }
1330 
1331     /**
1332      * {@inheritDoc}
1333      */
1334     @Override
1335     public IoBuffer put(IoBuffer src) {
1336         return put(src.buf());
1337     }
1338 
1339     /**
1340      * {@inheritDoc}
1341      */
1342     @Override
1343     public IoBuffer put(byte[] src) {
1344         return put(src, 0, src.length);
1345     }
1346 
1347     /**
1348      * {@inheritDoc}
1349      */
1350     @Override
1351     public int getUnsignedShort() {
1352         return getShort() & 0xffff;
1353     }
1354 
1355     /**
1356      * {@inheritDoc}
1357      */
1358     @Override
1359     public int getUnsignedShort(int index) {
1360         return getShort(index) & 0xffff;
1361     }
1362 
1363     /**
1364      * {@inheritDoc}
1365      */
1366     @Override
1367     public long getUnsignedInt() {
1368         return getInt() & 0xffffffffL;
1369     }
1370 
1371     /**
1372      * {@inheritDoc}
1373      */
1374     @Override
1375     public int getMediumInt() {
1376         byte b1 = get();
1377         byte b2 = get();
1378         byte b3 = get();
1379         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1380             return getMediumInt(b1, b2, b3);
1381         }
1382 
1383         return getMediumInt(b3, b2, b1);
1384     }
1385 
1386     /**
1387      * {@inheritDoc}
1388      */
1389     @Override
1390     public int getUnsignedMediumInt() {
1391         int b1 = getUnsigned();
1392         int b2 = getUnsigned();
1393         int b3 = getUnsigned();
1394         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1395             return b1 << 16 | b2 << 8 | b3;
1396         }
1397 
1398         return b3 << 16 | b2 << 8 | b1;
1399     }
1400 
1401     /**
1402      * {@inheritDoc}
1403      */
1404     @Override
1405     public int getMediumInt(int index) {
1406         byte b1 = get(index);
1407         byte b2 = get(index + 1);
1408         byte b3 = get(index + 2);
1409         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1410             return getMediumInt(b1, b2, b3);
1411         }
1412 
1413         return getMediumInt(b3, b2, b1);
1414     }
1415 
1416     /**
1417      * {@inheritDoc}
1418      */
1419     @Override
1420     public int getUnsignedMediumInt(int index) {
1421         int b1 = getUnsigned(index);
1422         int b2 = getUnsigned(index + 1);
1423         int b3 = getUnsigned(index + 2);
1424         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1425             return b1 << 16 | b2 << 8 | b3;
1426         }
1427 
1428         return b3 << 16 | b2 << 8 | b1;
1429     }
1430 
1431     /**
1432      * {@inheritDoc}
1433      */
1434     private int getMediumInt(byte b1, byte b2, byte b3) {
1435         int ret = b1 << 16 & 0xff0000 | b2 << 8 & 0xff00 | b3 & 0xff;
1436         // Check to see if the medium int is negative (high bit in b1 set)
1437         if ((b1 & 0x80) == 0x80) {
1438             // Make the the whole int negative
1439             ret |= 0xff000000;
1440         }
1441         return ret;
1442     }
1443 
1444     /**
1445      * {@inheritDoc}
1446      */
1447     @Override
1448     public IoBuffer putMediumInt(int value) {
1449         byte b1 = (byte) (value >> 16);
1450         byte b2 = (byte) (value >> 8);
1451         byte b3 = (byte) value;
1452 
1453         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1454             put(b1).put(b2).put(b3);
1455         } else {
1456             put(b3).put(b2).put(b1);
1457         }
1458 
1459         return this;
1460     }
1461 
1462     /**
1463      * {@inheritDoc}
1464      */
1465     @Override
1466     public IoBuffer putMediumInt(int index, int value) {
1467         byte b1 = (byte) (value >> 16);
1468         byte b2 = (byte) (value >> 8);
1469         byte b3 = (byte) value;
1470 
1471         if (ByteOrder.BIG_ENDIAN.equals(order())) {
1472             put(index, b1).put(index + 1, b2).put(index + 2, b3);
1473         } else {
1474             put(index, b3).put(index + 1, b2).put(index + 2, b1);
1475         }
1476 
1477         return this;
1478     }
1479 
1480     /**
1481      * {@inheritDoc}
1482      */
1483     @Override
1484     public long getUnsignedInt(int index) {
1485         return getInt(index) & 0xffffffffL;
1486     }
1487 
1488     /**
1489      * {@inheritDoc}
1490      */
1491     @Override
1492     public InputStream asInputStream() {
1493         return new InputStream() {
1494             @Override
1495             public int available() {
1496                 return AbstractIoBuffer.this.remaining();
1497             }
1498 
1499             @Override
1500             public synchronized void mark(int readlimit) {
1501                 AbstractIoBuffer.this.mark();
1502             }
1503 
1504             @Override
1505             public boolean markSupported() {
1506                 return true;
1507             }
1508 
1509             @Override
1510             public int read() {
1511                 if (AbstractIoBuffer.this.hasRemaining()) {
1512                     return AbstractIoBuffer.this.get() & 0xff;
1513                 }
1514 
1515                 return -1;
1516             }
1517 
1518             @Override
1519             public int read(byte[] b, int off, int len) {
1520                 int remaining = AbstractIoBuffer.this.remaining();
1521                 if (remaining > 0) {
1522                     int readBytes = Math.min(remaining, len);
1523                     AbstractIoBuffer.this.get(b, off, readBytes);
1524                     return readBytes;
1525                 }
1526 
1527                 return -1;
1528             }
1529 
1530             @Override
1531             public synchronized void reset() {
1532                 AbstractIoBuffer.this.reset();
1533             }
1534 
1535             @Override
1536             public long skip(long n) {
1537                 int bytes;
1538                 if (n > Integer.MAX_VALUE) {
1539                     bytes = AbstractIoBuffer.this.remaining();
1540                 } else {
1541                     bytes = Math.min(AbstractIoBuffer.this.remaining(), (int) n);
1542                 }
1543                 AbstractIoBuffer.this.skip(bytes);
1544                 return bytes;
1545             }
1546         };
1547     }
1548 
1549     /**
1550      * {@inheritDoc}
1551      */
1552     @Override
1553     public OutputStream asOutputStream() {
1554         return new OutputStream() {
1555             @Override
1556             public void write(byte[] b, int off, int len) {
1557                 AbstractIoBuffer.this.put(b, off, len);
1558             }
1559 
1560             @Override
1561             public void write(int b) {
1562                 AbstractIoBuffer.this.put((byte) b);
1563             }
1564         };
1565     }
1566 
1567     /**
1568      * {@inheritDoc}
1569      */
1570     @Override
1571     public String getHexDump() {
1572         return this.getHexDump(Integer.MAX_VALUE);
1573     }
1574 
1575     /**
1576      * {@inheritDoc}
1577      */
1578     @Override
1579     public String getHexDump(int lengthLimit) {
1580         return IoBufferHexDumper.getHexdump(this, lengthLimit);
1581     }
1582 
1583     /**
1584      * {@inheritDoc}
1585      */
1586     @Override
1587     public String getString(CharsetDecoder decoder) throws CharacterCodingException {
1588         if (!hasRemaining()) {
1589             return "";
1590         }
1591 
1592         boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1593 
1594         int oldPos = position();
1595         int oldLimit = limit();
1596         int end = -1;
1597         int newPos;
1598 
1599         if (!utf16) {
1600             end = indexOf((byte) 0x00);
1601             if (end < 0) {
1602                 newPos = end = oldLimit;
1603             } else {
1604                 newPos = end + 1;
1605             }
1606         } else {
1607             int i = oldPos;
1608             for (;;) {
1609                 boolean wasZero = get(i) == 0;
1610                 i++;
1611 
1612                 if (i >= oldLimit) {
1613                     break;
1614                 }
1615 
1616                 if (get(i) != 0) {
1617                     i++;
1618                     if (i >= oldLimit) {
1619                         break;
1620                     }
1621 
1622                     continue;
1623                 }
1624 
1625                 if (wasZero) {
1626                     end = i - 1;
1627                     break;
1628                 }
1629             }
1630 
1631             if (end < 0) {
1632                 newPos = end = oldPos + (oldLimit - oldPos & 0xFFFFFFFE);
1633             } else {
1634                 if (end + 2 <= oldLimit) {
1635                     newPos = end + 2;
1636                 } else {
1637                     newPos = end;
1638                 }
1639             }
1640         }
1641 
1642         if (oldPos == end) {
1643             position(newPos);
1644             return "";
1645         }
1646 
1647         limit(end);
1648         decoder.reset();
1649 
1650         int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1651         CharBuffer out = CharBuffer.allocate(expectedLength);
1652         for (;;) {
1653             CoderResult cr;
1654             if (hasRemaining()) {
1655                 cr = decoder.decode(buf(), out, true);
1656             } else {
1657                 cr = decoder.flush(out);
1658             }
1659 
1660             if (cr.isUnderflow()) {
1661                 break;
1662             }
1663 
1664             if (cr.isOverflow()) {
1665                 CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength);
1666                 out.flip();
1667                 o.put(out);
1668                 out = o;
1669                 continue;
1670             }
1671 
1672             if (cr.isError()) {
1673                 // Revert the buffer back to the previous state.
1674                 limit(oldLimit);
1675                 position(oldPos);
1676                 cr.throwException();
1677             }
1678         }
1679 
1680         limit(oldLimit);
1681         position(newPos);
1682         return out.flip().toString();
1683     }
1684 
1685     /**
1686      * {@inheritDoc}
1687      */
1688     @Override
1689     public String getString(int fieldSize, CharsetDecoder decoder) throws CharacterCodingException {
1690         checkFieldSize(fieldSize);
1691 
1692         if (fieldSize == 0) {
1693             return "";
1694         }
1695 
1696         if (!hasRemaining()) {
1697             return "";
1698         }
1699 
1700         boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1701 
1702         if (utf16 && (fieldSize & 1) != 0) {
1703             throw new IllegalArgumentException("fieldSize is not even.");
1704         }
1705 
1706         int oldPos = position();
1707         int oldLimit = limit();
1708         int end = oldPos + fieldSize;
1709 
1710         if (oldLimit < end) {
1711             throw new BufferUnderflowException();
1712         }
1713 
1714         int i;
1715 
1716         if (!utf16) {
1717             for (i = oldPos; i < end; i++) {
1718                 if (get(i) == 0) {
1719                     break;
1720                 }
1721             }
1722 
1723             if (i == end) {
1724                 limit(end);
1725             } else {
1726                 limit(i);
1727             }
1728         } else {
1729             for (i = oldPos; i < end; i += 2) {
1730                 if (get(i) == 0 && get(i + 1) == 0) {
1731                     break;
1732                 }
1733             }
1734 
1735             if (i == end) {
1736                 limit(end);
1737             } else {
1738                 limit(i);
1739             }
1740         }
1741 
1742         if (!hasRemaining()) {
1743             limit(oldLimit);
1744             position(end);
1745             return "";
1746         }
1747         decoder.reset();
1748 
1749         int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1750         CharBuffer out = CharBuffer.allocate(expectedLength);
1751         for (;;) {
1752             CoderResult cr;
1753             if (hasRemaining()) {
1754                 cr = decoder.decode(buf(), out, true);
1755             } else {
1756                 cr = decoder.flush(out);
1757             }
1758 
1759             if (cr.isUnderflow()) {
1760                 break;
1761             }
1762 
1763             if (cr.isOverflow()) {
1764                 CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength);
1765                 out.flip();
1766                 o.put(out);
1767                 out = o;
1768                 continue;
1769             }
1770 
1771             if (cr.isError()) {
1772                 // Revert the buffer back to the previous state.
1773                 limit(oldLimit);
1774                 position(oldPos);
1775                 cr.throwException();
1776             }
1777         }
1778 
1779         limit(oldLimit);
1780         position(end);
1781         return out.flip().toString();
1782     }
1783 
1784     /**
1785      * {@inheritDoc}
1786      */
1787     @Override
1788     public IoBuffer putString(CharSequence val, CharsetEncoder encoder) throws CharacterCodingException {
1789         if (val.length() == 0) {
1790             return this;
1791         }
1792 
1793         CharBuffer in = CharBuffer.wrap(val);
1794         encoder.reset();
1795 
1796         int expandedState = 0;
1797 
1798         for (;;) {
1799             CoderResult cr;
1800             if (in.hasRemaining()) {
1801                 cr = encoder.encode(in, buf(), true);
1802             } else {
1803                 cr = encoder.flush(buf());
1804             }
1805 
1806             if (cr.isUnderflow()) {
1807                 break;
1808             }
1809             if (cr.isOverflow()) {
1810                 if (isAutoExpand()) {
1811                     switch (expandedState) {
1812                     case 0:
1813                         autoExpand((int) Math.ceil(in.remaining() * encoder.averageBytesPerChar()));
1814                         expandedState++;
1815                         break;
1816                     case 1:
1817                         autoExpand((int) Math.ceil(in.remaining() * encoder.maxBytesPerChar()));
1818                         expandedState++;
1819                         break;
1820                     default:
1821                         throw new RuntimeException("Expanded by "
1822                                 + (int) Math.ceil(in.remaining() * encoder.maxBytesPerChar())
1823                                 + " but that wasn't enough for '" + val + "'");
1824                     }
1825                     continue;
1826                 }
1827             } else {
1828                 expandedState = 0;
1829             }
1830             cr.throwException();
1831         }
1832         return this;
1833     }
1834 
1835     /**
1836      * {@inheritDoc}
1837      */
1838     @Override
1839     public IoBuffer putString(CharSequence val, int fieldSize, CharsetEncoder encoder) throws CharacterCodingException {
1840         checkFieldSize(fieldSize);
1841 
1842         if (fieldSize == 0) {
1843             return this;
1844         }
1845 
1846         autoExpand(fieldSize);
1847 
1848         boolean utf16 = encoder.charset().name().startsWith("UTF-16");
1849 
1850         if (utf16 && (fieldSize & 1) != 0) {
1851             throw new IllegalArgumentException("fieldSize is not even.");
1852         }
1853 
1854         int oldLimit = limit();
1855         int end = position() + fieldSize;
1856 
1857         if (oldLimit < end) {
1858             throw new BufferOverflowException();
1859         }
1860 
1861         if (val.length() == 0) {
1862             if (!utf16) {
1863                 put((byte) 0x00);
1864             } else {
1865                 put((byte) 0x00);
1866                 put((byte) 0x00);
1867             }
1868             position(end);
1869             return this;
1870         }
1871 
1872         CharBuffer in = CharBuffer.wrap(val);
1873         limit(end);
1874         encoder.reset();
1875 
1876         for (;;) {
1877             CoderResult cr;
1878             if (in.hasRemaining()) {
1879                 cr = encoder.encode(in, buf(), true);
1880             } else {
1881                 cr = encoder.flush(buf());
1882             }
1883 
1884             if (cr.isUnderflow() || cr.isOverflow()) {
1885                 break;
1886             }
1887             cr.throwException();
1888         }
1889 
1890         limit(oldLimit);
1891 
1892         if (position() < end) {
1893             if (!utf16) {
1894                 put((byte) 0x00);
1895             } else {
1896                 put((byte) 0x00);
1897                 put((byte) 0x00);
1898             }
1899         }
1900 
1901         position(end);
1902         return this;
1903     }
1904 
1905     /**
1906      * {@inheritDoc}
1907      */
1908     @Override
1909     public String getPrefixedString(CharsetDecoder decoder) throws CharacterCodingException {
1910         return getPrefixedString(2, decoder);
1911     }
1912 
1913     /**
1914      * Reads a string which has a length field before the actual
1915      * encoded string, using the specified <code>decoder</code> and returns it.
1916      *
1917      * @param prefixLength the length of the length field (1, 2, or 4)
1918      * @param decoder the decoder to use for decoding the string
1919      * @return the prefixed string
1920      * @throws CharacterCodingException when decoding fails
1921      * @throws BufferUnderflowException when there is not enough data available
1922      */
1923     @Override
1924     public String getPrefixedString(int prefixLength, CharsetDecoder decoder) throws CharacterCodingException {
1925         if (!prefixedDataAvailable(prefixLength)) {
1926             throw new BufferUnderflowException();
1927         }
1928 
1929         int fieldSize = 0;
1930 
1931         switch (prefixLength) {
1932         case 1:
1933             fieldSize = getUnsigned();
1934             break;
1935         case 2:
1936             fieldSize = getUnsignedShort();
1937             break;
1938         case 4:
1939             fieldSize = getInt();
1940             break;
1941         }
1942 
1943         if (fieldSize == 0) {
1944             return "";
1945         }
1946 
1947         boolean utf16 = decoder.charset().name().startsWith("UTF-16");
1948 
1949         if (utf16 && (fieldSize & 1) != 0) {
1950             throw new BufferDataException("fieldSize is not even for a UTF-16 string.");
1951         }
1952 
1953         int oldLimit = limit();
1954         int end = position() + fieldSize;
1955 
1956         if (oldLimit < end) {
1957             throw new BufferUnderflowException();
1958         }
1959 
1960         limit(end);
1961         decoder.reset();
1962 
1963         int expectedLength = (int) (remaining() * decoder.averageCharsPerByte()) + 1;
1964         CharBuffer out = CharBuffer.allocate(expectedLength);
1965         for (;;) {
1966             CoderResult cr;
1967             if (hasRemaining()) {
1968                 cr = decoder.decode(buf(), out, true);
1969             } else {
1970                 cr = decoder.flush(out);
1971             }
1972 
1973             if (cr.isUnderflow()) {
1974                 break;
1975             }
1976 
1977             if (cr.isOverflow()) {
1978                 CharBuffer o = CharBuffer.allocate(out.capacity() + expectedLength);
1979                 out.flip();
1980                 o.put(out);
1981                 out = o;
1982                 continue;
1983             }
1984 
1985             cr.throwException();
1986         }
1987 
1988         limit(oldLimit);
1989         position(end);
1990         return out.flip().toString();
1991     }
1992 
1993     /**
1994      * {@inheritDoc}
1995      */
1996     @Override
1997     public IoBuffer putPrefixedString(CharSequence in, CharsetEncoder encoder) throws CharacterCodingException {
1998         return putPrefixedString(in, 2, 0, encoder);
1999     }
2000 
2001     /**
2002      * {@inheritDoc}
2003      */
2004     @Override
2005     public IoBuffer putPrefixedString(CharSequence in, int prefixLength, CharsetEncoder encoder)
2006             throws CharacterCodingException {
2007         return putPrefixedString(in, prefixLength, 0, encoder);
2008     }
2009 
2010     /**
2011      * {@inheritDoc}
2012      */
2013     @Override
2014     public IoBuffer putPrefixedString(CharSequence in, int prefixLength, int padding, CharsetEncoder encoder)
2015             throws CharacterCodingException {
2016         return putPrefixedString(in, prefixLength, padding, (byte) 0, encoder);
2017     }
2018 
2019     /**
2020      * {@inheritDoc}
2021      */
2022     @Override
2023     public IoBuffer putPrefixedString(CharSequence val, int prefixLength, int padding, byte padValue,
2024             CharsetEncoder encoder) throws CharacterCodingException {
2025         int maxLength;
2026         switch (prefixLength) {
2027         case 1:
2028             maxLength = 255;
2029             break;
2030         case 2:
2031             maxLength = 65535;
2032             break;
2033         case 4:
2034             maxLength = Integer.MAX_VALUE;
2035             break;
2036         default:
2037             throw new IllegalArgumentException("prefixLength: " + prefixLength);
2038         }
2039 
2040         if (val.length() > maxLength) {
2041             throw new IllegalArgumentException("The specified string is too long.");
2042         }
2043         if (val.length() == 0) {
2044             switch (prefixLength) {
2045             case 1:
2046                 put((byte) 0);
2047                 break;
2048             case 2:
2049                 putShort((short) 0);
2050                 break;
2051             case 4:
2052                 putInt(0);
2053                 break;
2054             }
2055             return this;
2056         }
2057 
2058         int padMask;
2059         switch (padding) {
2060         case 0:
2061         case 1:
2062             padMask = 0;
2063             break;
2064         case 2:
2065             padMask = 1;
2066             break;
2067         case 4:
2068             padMask = 3;
2069             break;
2070         default:
2071             throw new IllegalArgumentException("padding: " + padding);
2072         }
2073 
2074         CharBuffer in = CharBuffer.wrap(val);
2075         skip(prefixLength); // make a room for the length field
2076         int oldPos = position();
2077         encoder.reset();
2078 
2079         int expandedState = 0;
2080 
2081         for (;;) {
2082             CoderResult cr;
2083             if (in.hasRemaining()) {
2084                 cr = encoder.encode(in, buf(), true);
2085             } else {
2086                 cr = encoder.flush(buf());
2087             }
2088 
2089             if (position() - oldPos > maxLength) {
2090                 throw new IllegalArgumentException("The specified string is too long.");
2091             }
2092 
2093             if (cr.isUnderflow()) {
2094                 break;
2095             }
2096             if (cr.isOverflow()) {
2097                 if (isAutoExpand()) {
2098                     switch (expandedState) {
2099                     case 0:
2100                         autoExpand((int) Math.ceil(in.remaining() * encoder.averageBytesPerChar()));
2101                         expandedState++;
2102                         break;
2103                     case 1:
2104                         autoExpand((int) Math.ceil(in.remaining() * encoder.maxBytesPerChar()));
2105                         expandedState++;
2106                         break;
2107                     default:
2108                         throw new RuntimeException("Expanded by "
2109                                 + (int) Math.ceil(in.remaining() * encoder.maxBytesPerChar())
2110                                 + " but that wasn't enough for '" + val + "'");
2111                     }
2112                     continue;
2113                 }
2114             } else {
2115                 expandedState = 0;
2116             }
2117             cr.throwException();
2118         }
2119 
2120         // Write the length field
2121         fill(padValue, padding - (position() - oldPos & padMask));
2122         int length = position() - oldPos;
2123         switch (prefixLength) {
2124         case 1:
2125             put(oldPos - 1, (byte) length);
2126             break;
2127         case 2:
2128             putShort(oldPos - 2, (short) length);
2129             break;
2130         case 4:
2131             putInt(oldPos - 4, length);
2132             break;
2133         }
2134         return this;
2135     }
2136 
2137     /**
2138      * {@inheritDoc}
2139      */
2140     @Override
2141     public Object getObject() throws ClassNotFoundException {
2142         return getObject(Thread.currentThread().getContextClassLoader());
2143     }
2144 
2145     /**
2146      * {@inheritDoc}
2147      */
2148     @Override
2149     public Object getObject(final ClassLoader classLoader) throws ClassNotFoundException {
2150         if (!prefixedDataAvailable(4)) {
2151             throw new BufferUnderflowException();
2152         }
2153 
2154         int length = getInt();
2155         if (length <= 4) {
2156             throw new BufferDataException("Object length should be greater than 4: " + length);
2157         }
2158 
2159         int oldLimit = limit();
2160         limit(position() + length);
2161         try {
2162             ObjectInputStream in = new ObjectInputStream(asInputStream()) {
2163                 @Override
2164                 protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
2165                     int type = read();
2166                     if (type < 0) {
2167                         throw new EOFException();
2168                     }
2169                     switch (type) {
2170                     case 0: // NON-Serializable class or Primitive types
2171                         return super.readClassDescriptor();
2172                     case 1: // Serializable class
2173                         String className = readUTF();
2174                         Class<?> clazz = Class.forName(className, true, classLoader);
2175                         return ObjectStreamClass.lookup(clazz);
2176                     default:
2177                         throw new StreamCorruptedException("Unexpected class descriptor type: " + type);
2178                     }
2179                 }
2180 
2181                 @Override
2182                 protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
2183                     Class<?> clazz = desc.forClass();
2184                     
2185                     if (clazz == null) {
2186                         String name = desc.getName();
2187                         try {
2188                             return Class.forName(name, false, classLoader);
2189                         } catch (ClassNotFoundException ex) {
2190                             return super.resolveClass(desc);
2191                         }
2192                     } else {
2193                         return clazz;
2194                     }
2195                 }
2196             };
2197             return in.readObject();
2198         } catch (IOException e) {
2199             throw new BufferDataException(e);
2200         } finally {
2201             limit(oldLimit);
2202         }
2203     }
2204 
2205     /**
2206      * {@inheritDoc}
2207      */
2208     @Override
2209     public IoBuffer putObject(Object o) {
2210         int oldPos = position();
2211         skip(4); // Make a room for the length field.
2212         try {
2213             ObjectOutputStream out = new ObjectOutputStream(asOutputStream()) {
2214                 @Override
2215                 protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
2216                     Class<?> clazz = desc.forClass();
2217                     
2218                     if (clazz.isArray() || clazz.isPrimitive() || !Serializable.class.isAssignableFrom(clazz)) {
2219                         write(0);
2220                         super.writeClassDescriptor(desc); 
2221                     } else {
2222                         // Serializable class
2223                         write(1);
2224                         writeUTF(desc.getName());                            
2225                     }
2226                 }
2227             };
2228             out.writeObject(o);
2229             out.flush();
2230         } catch (IOException e) {
2231             throw new BufferDataException(e);
2232         }
2233 
2234         // Fill the length field
2235         int newPos = position();
2236         position(oldPos);
2237         putInt(newPos - oldPos - 4);
2238         position(newPos);
2239         return this;
2240     }
2241 
2242     /**
2243      * {@inheritDoc}
2244      */
2245     @Override
2246     public boolean prefixedDataAvailable(int prefixLength) {
2247         return prefixedDataAvailable(prefixLength, Integer.MAX_VALUE);
2248     }
2249 
2250     /**
2251      * {@inheritDoc}
2252      */
2253     @Override
2254     public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
2255         if (remaining() < prefixLength) {
2256             return false;
2257         }
2258 
2259         int dataLength;
2260         switch (prefixLength) {
2261         case 1:
2262             dataLength = getUnsigned(position());
2263             break;
2264         case 2:
2265             dataLength = getUnsignedShort(position());
2266             break;
2267         case 4:
2268             dataLength = getInt(position());
2269             break;
2270         default:
2271             throw new IllegalArgumentException("prefixLength: " + prefixLength);
2272         }
2273 
2274         if (dataLength < 0 || dataLength > maxDataLength) {
2275             throw new BufferDataException("dataLength: " + dataLength);
2276         }
2277 
2278         return remaining() - prefixLength >= dataLength;
2279     }
2280 
2281     /**
2282      * {@inheritDoc}
2283      */
2284     @Override
2285     public int indexOf(byte b) {
2286         if (hasArray()) {
2287             int arrayOffset = arrayOffset();
2288             int beginPos = arrayOffset + position();
2289             int limit = arrayOffset + limit();
2290             byte[] array = array();
2291 
2292             for (int i = beginPos; i < limit; i++) {
2293                 if (array[i] == b) {
2294                     return i - arrayOffset;
2295                 }
2296             }
2297         } else {
2298             int beginPos = position();
2299             int limit = limit();
2300 
2301             for (int i = beginPos; i < limit; i++) {
2302                 if (get(i) == b) {
2303                     return i;
2304                 }
2305             }
2306         }
2307 
2308         return -1;
2309     }
2310 
2311     /**
2312      * {@inheritDoc}
2313      */
2314     @Override
2315     public IoBuffer skip(int size) {
2316         autoExpand(size);
2317         return position(position() + size);
2318     }
2319 
2320     /**
2321      * {@inheritDoc}
2322      */
2323     @Override
2324     public IoBuffer fill(byte value, int size) {
2325         autoExpand(size);
2326         int q = size >>> 3;
2327             int r = size & 7;
2328 
2329             if (q > 0) {
2330                 int intValue = value | value << 8 | value << 16 | value << 24;
2331                 long longValue = intValue;
2332                 longValue <<= 32;
2333                 longValue |= intValue;
2334 
2335                 for (int i = q; i > 0; i--) {
2336                     putLong(longValue);
2337                 }
2338             }
2339 
2340             q = r >>> 2;
2341                 r = r & 3;
2342 
2343                 if (q > 0) {
2344                     int intValue = value | value << 8 | value << 16 | value << 24;
2345                     putInt(intValue);
2346                 }
2347 
2348                 q = r >> 1;
2349                     r = r & 1;
2350 
2351                     if (q > 0) {
2352                         short shortValue = (short) (value | value << 8);
2353                         putShort(shortValue);
2354                     }
2355 
2356                     if (r > 0) {
2357                         put(value);
2358                     }
2359 
2360                     return this;
2361     }
2362 
2363     /**
2364      * {@inheritDoc}
2365      */
2366     @Override
2367     public IoBuffer fillAndReset(byte value, int size) {
2368         autoExpand(size);
2369         int pos = position();
2370         try {
2371             fill(value, size);
2372         } finally {
2373             position(pos);
2374         }
2375         return this;
2376     }
2377 
2378     /**
2379      * {@inheritDoc}
2380      */
2381     @Override
2382     public IoBuffer fill(int size) {
2383         autoExpand(size);
2384         int q = size >>> 3;
2385                     int r = size & 7;
2386 
2387                     for (int i = q; i > 0; i--) {
2388                         putLong(0L);
2389                     }
2390 
2391                     q = r >>> 2;
2392                 r = r & 3;
2393 
2394                 if (q > 0) {
2395                     putInt(0);
2396                 }
2397 
2398                 q = r >> 1;
2399             r = r & 1;
2400 
2401             if (q > 0) {
2402                 putShort((short) 0);
2403             }
2404 
2405             if (r > 0) {
2406                 put((byte) 0);
2407             }
2408 
2409             return this;
2410     }
2411 
2412     /**
2413      * {@inheritDoc}
2414      */
2415     @Override
2416     public IoBuffer fillAndReset(int size) {
2417         autoExpand(size);
2418         int pos = position();
2419         try {
2420             fill(size);
2421         } finally {
2422             position(pos);
2423         }
2424 
2425         return this;
2426     }
2427 
2428     /**
2429      * {@inheritDoc}
2430      */
2431     @Override
2432     public <E extends Enum<E>> E getEnum(Class<E> enumClass) {
2433         return toEnum(enumClass, getUnsigned());
2434     }
2435 
2436     /**
2437      * {@inheritDoc}
2438      */
2439     @Override
2440     public <E extends Enum<E>> E getEnum(int index, Class<E> enumClass) {
2441         return toEnum(enumClass, getUnsigned(index));
2442     }
2443 
2444     /**
2445      * {@inheritDoc}
2446      */
2447     @Override
2448     public <E extends Enum<E>> E getEnumShort(Class<E> enumClass) {
2449         return toEnum(enumClass, getUnsignedShort());
2450     }
2451 
2452     /**
2453      * {@inheritDoc}
2454      */
2455     @Override
2456     public <E extends Enum<E>> E getEnumShort(int index, Class<E> enumClass) {
2457         return toEnum(enumClass, getUnsignedShort(index));
2458     }
2459 
2460     /**
2461      * {@inheritDoc}
2462      */
2463     @Override
2464     public <E extends Enum<E>> E getEnumInt(Class<E> enumClass) {
2465         return toEnum(enumClass, getInt());
2466     }
2467 
2468     /**
2469      * {@inheritDoc}
2470      */
2471     public <E extends Enum<E>> E getEnumInt(int index, Class<E> enumClass) {
2472         return toEnum(enumClass, getInt(index));
2473     }
2474 
2475     /**
2476      * {@inheritDoc}
2477      */
2478     @Override
2479     public IoBuffer putEnum(Enum<?> e) {
2480         if (e.ordinal() > BYTE_MASK) {
2481             throw new IllegalArgumentException(enumConversionErrorMessage(e, "byte"));
2482         }
2483         return put((byte) e.ordinal());
2484     }
2485 
2486     /**
2487      * {@inheritDoc}
2488      */
2489     @Override
2490     public IoBuffer putEnum(int index, Enum<?> e) {
2491         if (e.ordinal() > BYTE_MASK) {
2492             throw new IllegalArgumentException(enumConversionErrorMessage(e, "byte"));
2493         }
2494         return put(index, (byte) e.ordinal());
2495     }
2496 
2497     /**
2498      * {@inheritDoc}
2499      */
2500     @Override
2501     public IoBuffer putEnumShort(Enum<?> e) {
2502         if (e.ordinal() > SHORT_MASK) {
2503             throw new IllegalArgumentException(enumConversionErrorMessage(e, "short"));
2504         }
2505         return putShort((short) e.ordinal());
2506     }
2507 
2508     /**
2509      * {@inheritDoc}
2510      */
2511     @Override
2512     public IoBuffer putEnumShort(int index, Enum<?> e) {
2513         if (e.ordinal() > SHORT_MASK) {
2514             throw new IllegalArgumentException(enumConversionErrorMessage(e, "short"));
2515         }
2516         return putShort(index, (short) e.ordinal());
2517     }
2518 
2519     /**
2520      * {@inheritDoc}
2521      */
2522     @Override
2523     public IoBuffer putEnumInt(Enum<?> e) {
2524         return putInt(e.ordinal());
2525     }
2526 
2527     /**
2528      * {@inheritDoc}
2529      */
2530     @Override
2531     public IoBuffer putEnumInt(int index, Enum<?> e) {
2532         return putInt(index, e.ordinal());
2533     }
2534 
2535     private <E> E toEnum(Class<E> enumClass, int i) {
2536         E[] enumConstants = enumClass.getEnumConstants();
2537         if (i > enumConstants.length) {
2538             throw new IndexOutOfBoundsException(String.format(
2539                     "%d is too large of an ordinal to convert to the enum %s", i, enumClass.getName()));
2540         }
2541         return enumConstants[i];
2542     }
2543 
2544     private String enumConversionErrorMessage(Enum<?> e, String type) {
2545         return String.format("%s.%s has an ordinal value too large for a %s", e.getClass().getName(), e.name(), type);
2546     }
2547 
2548     /**
2549      * {@inheritDoc}
2550      */
2551     @Override
2552     public <E extends Enum<E>> EnumSet<E> getEnumSet(Class<E> enumClass) {
2553         return toEnumSet(enumClass, get() & BYTE_MASK);
2554     }
2555 
2556     /**
2557      * {@inheritDoc}
2558      */
2559     @Override
2560     public <E extends Enum<E>> EnumSet<E> getEnumSet(int index, Class<E> enumClass) {
2561         return toEnumSet(enumClass, get(index) & BYTE_MASK);
2562     }
2563 
2564     /**
2565      * {@inheritDoc}
2566      */
2567     @Override
2568     public <E extends Enum<E>> EnumSet<E> getEnumSetShort(Class<E> enumClass) {
2569         return toEnumSet(enumClass, getShort() & SHORT_MASK);
2570     }
2571 
2572     /**
2573      * {@inheritDoc}
2574      */
2575     @Override
2576     public <E extends Enum<E>> EnumSet<E> getEnumSetShort(int index, Class<E> enumClass) {
2577         return toEnumSet(enumClass, getShort(index) & SHORT_MASK);
2578     }
2579 
2580     /**
2581      * {@inheritDoc}
2582      */
2583     @Override
2584     public <E extends Enum<E>> EnumSet<E> getEnumSetInt(Class<E> enumClass) {
2585         return toEnumSet(enumClass, getInt() & INT_MASK);
2586     }
2587 
2588     /**
2589      * {@inheritDoc}
2590      */
2591     @Override
2592     public <E extends Enum<E>> EnumSet<E> getEnumSetInt(int index, Class<E> enumClass) {
2593         return toEnumSet(enumClass, getInt(index) & INT_MASK);
2594     }
2595 
2596     /**
2597      * {@inheritDoc}
2598      */
2599     @Override
2600     public <E extends Enum<E>> EnumSet<E> getEnumSetLong(Class<E> enumClass) {
2601         return toEnumSet(enumClass, getLong());
2602     }
2603 
2604     /**
2605      * {@inheritDoc}
2606      */
2607     @Override
2608     public <E extends Enum<E>> EnumSet<E> getEnumSetLong(int index, Class<E> enumClass) {
2609         return toEnumSet(enumClass, getLong(index));
2610     }
2611 
2612     private <E extends Enum<E>> EnumSet<E> toEnumSet(Class<E> clazz, long vector) {
2613         EnumSet<E> set = EnumSet.noneOf(clazz);
2614         long mask = 1;
2615         for (E e : clazz.getEnumConstants()) {
2616             if ((mask & vector) == mask) {
2617                 set.add(e);
2618             }
2619             mask <<= 1;
2620         }
2621         return set;
2622     }
2623 
2624     /**
2625      * {@inheritDoc}
2626      */
2627     @Override
2628     public <E extends Enum<E>> IoBuffer putEnumSet(Set<E> set) {
2629         long vector = toLong(set);
2630         if ((vector & ~BYTE_MASK) != 0) {
2631             throw new IllegalArgumentException("The enum set is too large to fit in a byte: " + set);
2632         }
2633         return put((byte) vector);
2634     }
2635 
2636     /**
2637      * {@inheritDoc}
2638      */
2639     @Override
2640     public <E extends Enum<E>> IoBuffer putEnumSet(int index, Set<E> set) {
2641         long vector = toLong(set);
2642         if ((vector & ~BYTE_MASK) != 0) {
2643             throw new IllegalArgumentException("The enum set is too large to fit in a byte: " + set);
2644         }
2645         return put(index, (byte) vector);
2646     }
2647 
2648     /**
2649      * {@inheritDoc}
2650      */
2651     @Override
2652     public <E extends Enum<E>> IoBuffer putEnumSetShort(Set<E> set) {
2653         long vector = toLong(set);
2654         if ((vector & ~SHORT_MASK) != 0) {
2655             throw new IllegalArgumentException("The enum set is too large to fit in a short: " + set);
2656         }
2657         return putShort((short) vector);
2658     }
2659 
2660     /**
2661      * {@inheritDoc}
2662      */
2663     @Override
2664     public <E extends Enum<E>> IoBuffer putEnumSetShort(int index, Set<E> set) {
2665         long vector = toLong(set);
2666         if ((vector & ~SHORT_MASK) != 0) {
2667             throw new IllegalArgumentException("The enum set is too large to fit in a short: " + set);
2668         }
2669         return putShort(index, (short) vector);
2670     }
2671 
2672     /**
2673      * {@inheritDoc}
2674      */
2675     @Override
2676     public <E extends Enum<E>> IoBuffer putEnumSetInt(Set<E> set) {
2677         long vector = toLong(set);
2678         if ((vector & ~INT_MASK) != 0) {
2679             throw new IllegalArgumentException("The enum set is too large to fit in an int: " + set);
2680         }
2681         return putInt((int) vector);
2682     }
2683 
2684     /**
2685      * {@inheritDoc}
2686      */
2687     @Override
2688     public <E extends Enum<E>> IoBuffer putEnumSetInt(int index, Set<E> set) {
2689         long vector = toLong(set);
2690         if ((vector & ~INT_MASK) != 0) {
2691             throw new IllegalArgumentException("The enum set is too large to fit in an int: " + set);
2692         }
2693         return putInt(index, (int) vector);
2694     }
2695 
2696     /**
2697      * {@inheritDoc}
2698      */
2699     @Override
2700     public <E extends Enum<E>> IoBuffer putEnumSetLong(Set<E> set) {
2701         return putLong(toLong(set));
2702     }
2703 
2704     /**
2705      * {@inheritDoc}
2706      */
2707     @Override
2708     public <E extends Enum<E>> IoBuffer putEnumSetLong(int index, Set<E> set) {
2709         return putLong(index, toLong(set));
2710     }
2711 
2712     private <E extends Enum<E>> long toLong(Set<E> set) {
2713         long vector = 0;
2714         for (E e : set) {
2715             if (e.ordinal() >= Long.SIZE) {
2716                 throw new IllegalArgumentException("The enum set is too large to fit in a bit vector: " + set);
2717             }
2718             vector |= 1L << e.ordinal();
2719         }
2720         return vector;
2721     }
2722 
2723     /**
2724      * This method forwards the call to {@link #expand(int)} only when
2725      * <tt>autoExpand</tt> property is <tt>true</tt>.
2726      */
2727     private IoBuffer autoExpand(int expectedRemaining) {
2728         if (isAutoExpand()) {
2729             expand(expectedRemaining, true);
2730         }
2731         return this;
2732     }
2733 
2734     /**
2735      * This method forwards the call to {@link #expand(int)} only when
2736      * <tt>autoExpand</tt> property is <tt>true</tt>.
2737      */
2738     private IoBuffer autoExpand(int pos, int expectedRemaining) {
2739         if (isAutoExpand()) {
2740             expand(pos, expectedRemaining, true);
2741         }
2742         return this;
2743     }
2744 
2745     private static void checkFieldSize(int fieldSize) {
2746         if (fieldSize < 0) {
2747             throw new IllegalArgumentException("fieldSize cannot be negative: " + fieldSize);
2748         }
2749     }
2750 }