View Javadoc

1   /*
2    *   @(#) $Id: ByteBuffer.java 171076 2005-05-20 08:03:23Z trustin $
3    *
4    *   Copyright 2004 The Apache Software Foundation
5    *
6    *   Licensed under the Apache License, Version 2.0 (the "License");
7    *   you may not use this file except in compliance with the License.
8    *   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, software
13   *   distributed under the License is distributed on an "AS IS" BASIS,
14   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *   See the License for the specific language governing permissions and
16   *   limitations under the License.
17   *
18   */
19  package org.apache.mina.common;
20  
21  import java.nio.BufferOverflowException;
22  import java.nio.BufferUnderflowException;
23  import java.nio.ByteOrder;
24  import java.nio.CharBuffer;
25  import java.nio.DoubleBuffer;
26  import java.nio.FloatBuffer;
27  import java.nio.IntBuffer;
28  import java.nio.LongBuffer;
29  import java.nio.ShortBuffer;
30  import java.nio.charset.CharacterCodingException;
31  import java.nio.charset.CharsetDecoder;
32  import java.nio.charset.CharsetEncoder;
33  import java.nio.charset.CoderResult;
34  
35  import org.apache.mina.io.IoHandler;
36  import org.apache.mina.io.IoSession;
37  import org.apache.mina.protocol.ProtocolEncoderOutput;
38  import org.apache.mina.util.Stack;
39  
40  /***
41   * A pooled byte buffer used by MINA applications.
42   * <p>
43   * This is a replacement for {@link java.nio.ByteBuffer}. Please refer to
44   * {@link java.nio.ByteBuffer} and {@link java.nio.Buffer} documentation for
45   * usage.  MINA does not use NIO {@link java.nio.ByteBuffer} directly for two
46   * reasons:
47   * <ul>
48   *   <li>It doesn't provide useful getters and putters such as
49   *       <code>fill</code>, <code>get/putString</code>, and
50   *       <code>get/putAsciiInt()</code> enough.</li>
51   *   <li>It is hard to distinguish if the buffer is created from MINA buffer
52   *       pool or not.  MINA have to return used buffers back to pool.</li>
53   *   <li>It is difficult to write variable-length data due to its fixed
54   *       capacity</li>
55   * </ul>
56   * 
57   * <h2>Allocation</h2>
58   * <p>
59   * You can get a heap buffer from buffer pool:
60   * <pre>
61   * ByteBuffer buf = ByteBuffer.allocate(1024, false);
62   * </pre>
63   * you can also get a direct buffer from buffer pool:
64   * <pre>
65   * ByteBuffer buf = ByteBuffer.allocate(1024, true);
66   * </pre>
67   * or you can let MINA choose:
68   * <pre>
69   * ByteBuffer buf = ByteBuffer.allocate(1024);
70   * </pre>
71   * 
72   * <h2>Acquire/Release</h2>
73   * <p>
74   * <b>Please note that you never need to release the allocated buffer</b>
75   * because MINA will release it automatically when:
76   * <ul>
77   *   <li>You pass the buffer by calling {@link IoSession#write(ByteBuffer, Object)}.</li>
78   *   <li>You pass the buffer by calling {@link ProtocolEncoderOutput#write(ByteBuffer)}.</li>
79   * </ul>
80   * And, you don't need to release any {@link ByteBuffer} which is passed as a parameter
81   * of {@link IoHandler#dataRead(IoSession, ByteBuffer)} method.  They are released
82   * automatically when the method returns.
83   * <p>
84   * You have to release buffers manually by calling {@link #release()} when:
85   * <ul>
86   *   <li>You allocated a buffer, but didn't pass the buffer to any of two methods above.</li>
87   *   <li>You called {@link #acquire()} to prevent the buffer from being released.</li>
88   * </ul>
89   * 
90   * <h2>Wrapping existing NIO buffers and arrays</h2>
91   * <p>
92   * This class provides a few <tt>wrap(...)</tt> methods that wraps
93   * any NIO buffers and byte arrays.  But please be careful and use these
94   * methods at your own risk.  Using those methods can cause memory leakage
95   * if you keep wrapping new NIO buffers and arrays because even wrapped
96   * MINA buffers are going to be pooled when they are released by MINA.
97   * <p>
98   * To resolve this issue, please do not keeping wrapping NIO buffers if
99   * possible.  If you're working with any third party component that keeps
100  * creating NIO buffers, please call {@link #acquire()} once more and
101  * don't call {@link #release()} for it, then it will not be returned to
102  * the pool and GC'd eventually.  Here's the example:
103  * <pre>
104  * import org.apache.mina.common.*;
105  * import org.apache.mina.io.*;
106  *
107  * IoSession session = ...;
108  * for (;;) {
109  *     // readData() returns a newly allocate NIO buffer.
110  *     java.nio.ByteBuffer newBuffer = otherApplication.readData();
111  *     
112  *     // Wrap it.
113  *     ByteBuffer wrappedBuffer = ByteBuffer.wrap(newBuffer);
114  *     // Acquire once and don't call release to prevent MINA from pooling it. 
115  *     wrappedBuffer.acquire();
116  *     session.write(wrappedBuffer, marker);
117  * }
118  * </pre>
119  * 
120  * <h2>AutoExpand</h2>
121  * <p>
122  * Writing variable-length data using NIO <tt>ByteBuffers</tt> is not really
123  * easy, and it is because its size is fixed.  MINA <tt>ByteBuffer</tt>
124  * introduces <tt>autoExpand</tt> property.  If <tt>autoExpand</tt> property
125  * is true, you never get {@link BufferOverflowException} or
126  * {@link IndexOutOfBoundsException} (except when index is negative).
127  * It automatically expands its capacity and limit value.  For example:
128  * <pre>
129  * String greeting = messageBundle.getMessage( "hello" );
130  * ByteBuffer buf = ByteBuffer.allocate( 16 );
131  * // Turn on autoExpand (it is off by default)
132  * buf.setAutoExpand( true );
133  * buf.putString( greeting, utf8encoder );
134  * </pre>
135  * NIO <tt>ByteBuffer</tt> is reallocated by MINA <tt>ByteBuffer</tt> behind
136  * the scene if the encoded data is larger than 16 bytes.  Its capacity will
137  * increase by two times, and its limit will increase to the last position
138  * the string is written.
139  * 
140  * @author Trustin Lee (trustin@apache.org)
141  * @version $Rev: 171076 $, $Date: 2005-05-20 17:03:23 +0900 $
142  */
143 public abstract class ByteBuffer
144 {
145     private static final int MINIMUM_CAPACITY = 1;
146 
147     private static final Stack containerStack = new Stack();
148 
149     private static final Stack[] heapBufferStacks = new Stack[] {
150             new Stack(), new Stack(), new Stack(), new Stack(),
151             new Stack(), new Stack(), new Stack(), new Stack(),
152             new Stack(), new Stack(), new Stack(), new Stack(),
153             new Stack(), new Stack(), new Stack(), new Stack(),
154             new Stack(), new Stack(), new Stack(), new Stack(),
155             new Stack(), new Stack(), new Stack(), new Stack(),
156             new Stack(), new Stack(), new Stack(), new Stack(),
157             new Stack(), new Stack(), new Stack(), new Stack(), };
158     
159     private static final Stack[] directBufferStacks = new Stack[] {
160             new Stack(), new Stack(), new Stack(), new Stack(),
161             new Stack(), new Stack(), new Stack(), new Stack(),
162             new Stack(), new Stack(), new Stack(), new Stack(),
163             new Stack(), new Stack(), new Stack(), new Stack(),
164             new Stack(), new Stack(), new Stack(), new Stack(),
165             new Stack(), new Stack(), new Stack(), new Stack(),
166             new Stack(), new Stack(), new Stack(), new Stack(),
167             new Stack(), new Stack(), new Stack(), new Stack(), };
168     
169     /***
170      * Returns the direct or heap buffer which is capable of the specified
171      * size.  This method tries to allocate direct buffer first, and then
172      * tries heap buffer if direct buffer memory is exhausted.  Please use
173      * {@link #allocate(int, boolean)} to allocate buffers of specific type.
174      * 
175      * @param capacity the capacity of the buffer
176      */
177     public static ByteBuffer allocate( int capacity )
178     {
179         try
180         {
181             // first try to allocate direct buffer
182             return allocate( capacity, true );
183         }
184         catch( OutOfMemoryError e )
185         {
186             // if failed, try heap
187             return allocate( capacity, false );
188         }
189     }
190     
191     /***
192      * Returns the buffer which is capable of the specified size.
193      * 
194      * @param capacity the capacity of the buffer
195      * @param direct <tt>true</tt> to get a direct buffer,
196      *               <tt>false</tt> to get a heap buffer.
197      */
198     public static ByteBuffer allocate( int capacity, boolean direct )
199     {
200         java.nio.ByteBuffer nioBuffer = allocate0( capacity, direct );
201         DefaultByteBuffer buf = allocateContainer();
202         buf.init( nioBuffer );
203         return buf;
204     }
205 
206     private static DefaultByteBuffer allocateContainer()
207     {
208         DefaultByteBuffer buf;
209         synchronized( containerStack )
210         {
211             buf = ( DefaultByteBuffer ) containerStack.pop();
212         }
213         
214         if( buf == null )
215         {
216             buf = new DefaultByteBuffer();
217         }
218         return buf;
219     }
220     
221     private static java.nio.ByteBuffer allocate0( int capacity, boolean direct )
222     {
223         Stack[] bufferStacks = direct? directBufferStacks : heapBufferStacks;
224         int idx = getBufferStackIndex( bufferStacks, capacity );
225         Stack stack = bufferStacks[ idx ];
226 
227         java.nio.ByteBuffer buf;
228         synchronized( stack )
229         {
230             buf = ( java.nio.ByteBuffer ) stack.pop();
231         }
232 
233         if( buf == null )
234         {
235             buf = direct ? java.nio.ByteBuffer.allocateDirect( MINIMUM_CAPACITY << idx ) :
236                            java.nio.ByteBuffer.allocate( MINIMUM_CAPACITY << idx );
237         }
238         
239         buf.clear();
240         buf.order( ByteOrder.BIG_ENDIAN );
241         return buf;
242     }
243     
244     private static void release0( java.nio.ByteBuffer buf )
245     {
246         Stack[] bufferStacks = buf.isDirect()? directBufferStacks : heapBufferStacks;
247         Stack stack = bufferStacks[ getBufferStackIndex( bufferStacks, buf.capacity() ) ];
248         synchronized( stack )
249         {
250             // push back
251             stack.push( buf );
252         }
253     }
254     
255     /***
256      * Wraps the specified NIO {@link java.nio.ByteBuffer} into MINA buffer.
257      */
258     public static ByteBuffer wrap( java.nio.ByteBuffer nioBuffer )
259     {
260         DefaultByteBuffer buf = allocateContainer();
261         buf.init( nioBuffer );
262         return buf;
263     }
264     
265     /***
266      * Wraps the specified byte array into MINA heap buffer.
267      */
268     public static ByteBuffer wrap( byte[] byteArray )
269     {
270         return wrap( java.nio.ByteBuffer.wrap( byteArray ) );
271     }
272     
273     /***
274      * Wraps the specified byte array into MINA heap buffer.
275      * Please note that MINA buffers are going to be pooled, and
276      * therefore there can be waste of memory if you wrap
277      * your byte array specifying <tt>offset</tt> and <tt>length</tt>.
278      */
279     public static ByteBuffer wrap( byte[] byteArray, int offset, int length )
280     {
281         return wrap( java.nio.ByteBuffer.wrap( byteArray, offset, length ) );
282     }
283     
284     private static int getBufferStackIndex( Stack[] bufferStacks, int size )
285     {
286         int targetSize = MINIMUM_CAPACITY;
287         int stackIdx = 0;
288         while( size > targetSize )
289         {
290             targetSize <<= 1;
291             stackIdx ++ ;
292             if( stackIdx >= bufferStacks.length )
293             {
294                 throw new IllegalArgumentException(
295                         "Buffer size is too big: " + size );
296             }
297         }
298 
299         return stackIdx;
300     }
301 
302     protected ByteBuffer()
303     {
304     }
305 
306     /***
307      * Increases the internal reference count of this buffer to defer
308      * automatic release.  You have to invoke {@link #release()} as many
309      * as you invoked this method to release this buffer.
310      * 
311      * @throws IllegalStateException if you attempt to acquire already
312      *                               released buffer.
313      */
314     public abstract void acquire();
315 
316     /***
317      * Releases the specified buffer to buffer pool.
318      * 
319      * @throws IllegalStateException if you attempt to release already
320      *                               released buffer.
321      */
322     public abstract void release();
323 
324     /***
325      * Returns the underlying NIO buffer instance.
326      */
327     public abstract java.nio.ByteBuffer buf();
328     
329     public abstract boolean isDirect();
330     
331     public abstract int capacity();
332     
333     /***
334      * Returns <tt>true</tt> if and only if <tt>autoExpand</tt> is turned on.
335      */
336     public abstract boolean isAutoExpand();
337     
338     /***
339      * Turns on or off <tt>autoExpand</tt>.
340      */
341     public abstract ByteBuffer setAutoExpand( boolean autoExpand );
342     
343     public abstract int position();
344 
345     public abstract ByteBuffer position( int newPosition );
346 
347     public abstract int limit();
348 
349     public abstract ByteBuffer limit( int newLimit );
350 
351     public abstract ByteBuffer mark();
352 
353     public abstract ByteBuffer reset();
354 
355     public abstract ByteBuffer clear();
356 
357     public abstract ByteBuffer flip();
358 
359     public abstract ByteBuffer rewind();
360 
361     public abstract int remaining();
362 
363     public abstract boolean hasRemaining();
364 
365     public abstract byte get();
366 
367     public abstract short getUnsigned();
368 
369     public abstract ByteBuffer put( byte b );
370 
371     public abstract byte get( int index );
372 
373     public abstract short getUnsigned( int index );
374 
375     public abstract ByteBuffer put( int index, byte b );
376 
377     public abstract ByteBuffer get( byte[] dst, int offset, int length );
378 
379     public abstract ByteBuffer get( byte[] dst );
380 
381     public abstract ByteBuffer put( java.nio.ByteBuffer src );
382 
383     public abstract ByteBuffer put( ByteBuffer src );
384 
385     public abstract ByteBuffer put( byte[] src, int offset, int length );
386 
387     public abstract ByteBuffer put( byte[] src );
388 
389     public abstract ByteBuffer compact();
390 
391     public abstract String toString();
392 
393     public abstract int hashCode();
394 
395     public abstract boolean equals( Object ob );
396 
397     public abstract int compareTo( ByteBuffer that );
398 
399     public abstract ByteOrder order();
400 
401     public abstract ByteBuffer order( ByteOrder bo );
402 
403     public abstract char getChar();
404 
405     public abstract ByteBuffer putChar( char value );
406 
407     public abstract char getChar( int index );
408 
409     public abstract ByteBuffer putChar( int index, char value );
410 
411     public abstract CharBuffer asCharBuffer();
412 
413     public abstract short getShort();
414 
415     public abstract int getUnsignedShort();
416 
417     public abstract ByteBuffer putShort( short value );
418 
419     public abstract short getShort( int index );
420 
421     public abstract int getUnsignedShort( int index );
422 
423     public abstract ByteBuffer putShort( int index, short value );
424 
425     public abstract ShortBuffer asShortBuffer();
426 
427     public abstract int getInt();
428 
429     public abstract long getUnsignedInt();
430 
431     public abstract ByteBuffer putInt( int value );
432 
433     public abstract int getInt( int index );
434 
435     public abstract long getUnsignedInt( int index );
436 
437     public abstract ByteBuffer putInt( int index, int value );
438 
439     public abstract IntBuffer asIntBuffer();
440 
441     public abstract long getLong();
442 
443     public abstract ByteBuffer putLong( long value );
444 
445     public abstract long getLong( int index );
446 
447     public abstract ByteBuffer putLong( int index, long value );
448 
449     public abstract LongBuffer asLongBuffer();
450 
451     public abstract float getFloat();
452 
453     public abstract ByteBuffer putFloat( float value );
454 
455     public abstract float getFloat( int index );
456 
457     public abstract ByteBuffer putFloat( int index, float value );
458 
459     public abstract FloatBuffer asFloatBuffer();
460 
461     public abstract double getDouble();
462 
463     public abstract ByteBuffer putDouble( double value );
464 
465     public abstract double getDouble( int index );
466 
467     public abstract ByteBuffer putDouble( int index, double value );
468 
469     public abstract DoubleBuffer asDoubleBuffer();
470 
471     /***
472      * Returns hexdump of this buffer.
473      */
474     public abstract String getHexDump();
475 
476     ////////////////////////////////
477     // String getters and putters //
478     ////////////////////////////////
479 
480     /***
481      * Reads a <code>NUL</code>-terminated string from this buffer using the
482      * specified <code>decoder</code> and returns it.  This method reads
483      * until the limit of this buffer if no <tt>NUL</tt> is found.
484      */
485     public abstract String getString( CharsetDecoder decoder ) throws CharacterCodingException;
486 
487     /***
488      * Reads a <code>NUL</code>-terminated string from this buffer using the
489      * specified <code>decoder</code> and returns it.
490      * 
491      * @param fieldSize the maximum number of bytes to read
492      */
493     public abstract String getString( int fieldSize, CharsetDecoder decoder ) throws CharacterCodingException;
494     
495     /***
496      * Writes the content of <code>in</code> into this buffer using the
497      * specified <code>encoder</code>.  This method doesn't terminate
498      * string with <tt>NUL</tt>.  You have to do it by yourself.
499      * 
500      * @throws BufferOverflowException if the specified string doesn't fit
501      */
502     public abstract ByteBuffer putString( CharSequence in, CharsetEncoder encoder ) throws CharacterCodingException;
503 
504     /***
505      * Writes the content of <code>in</code> into this buffer as a 
506      * <code>NUL</code>-terminated string using the specified
507      * <code>encoder</code>.
508      * <p>
509      * If the charset name of the encoder is UTF-16, you cannot specify
510      * odd <code>fieldSize</code>, and this method will append two
511      * <code>NUL</code>s as a terminator.
512      * <p>
513      * Please note that this method doesn't terminate with <code>NUL</code>
514      * if the input string is longer than <tt>fieldSize</tt>.
515      * 
516      * @param fieldSize the maximum number of bytes to write
517      */
518     public abstract ByteBuffer putString(
519             CharSequence in, int fieldSize, CharsetEncoder encoder ) throws CharacterCodingException;
520 
521     //////////////////////////
522     // Skip or fill methods //
523     //////////////////////////
524 
525     /***
526      * Forwards the position of this buffer as the specified <code>size</code>
527      * bytes.
528      */
529     public abstract ByteBuffer skip( int size );
530 
531     /***
532      * Fills this buffer with the specified value.
533      * This method moves buffer position forward.
534      */
535     public abstract ByteBuffer fill( byte value, int size );
536 
537     /***
538      * Fills this buffer with the specified value.
539      * This method does not change buffer position.
540      */
541     public abstract ByteBuffer fillAndReset( byte value, int size );
542 
543     /***
544      * Fills this buffer with <code>NUL (0x00)</code>.
545      * This method moves buffer position forward.
546      */
547     public abstract ByteBuffer fill( int size );
548 
549     /***
550      * Fills this buffer with <code>NUL (0x00)</code>.
551      * This method does not change buffer position.
552      */
553     public abstract ByteBuffer fillAndReset( int size );
554     
555     private static class DefaultByteBuffer extends ByteBuffer
556     {
557         private java.nio.ByteBuffer buf;
558         private int refCount = 1;
559         private boolean autoExpand;
560 
561         protected DefaultByteBuffer()
562         {
563         }
564 
565         private synchronized void init( java.nio.ByteBuffer buf )
566         {
567             this.buf = buf;
568             autoExpand = false;
569             refCount = 1;
570         }
571         
572         public synchronized void acquire()
573         {
574             if( refCount <= 0 )
575             {
576                 throw new IllegalStateException( "Already released buffer." );
577             }
578 
579             refCount ++;
580         }
581 
582         public void release()
583         {
584             synchronized( this )
585             {
586                 if( refCount <= 0 )
587                 {
588                     refCount = 0;
589                     throw new IllegalStateException(
590                             "Already released buffer.  You released the buffer too many times." );
591                 }
592 
593                 refCount --;
594                 if( refCount > 0)
595                 {
596                     return;
597                 }
598             }
599 
600             release0( buf );
601             synchronized( containerStack )
602             {
603                 containerStack.push( this );
604             }
605         }
606 
607         public java.nio.ByteBuffer buf()
608         {
609             return buf;
610         }
611         
612         public boolean isDirect()
613         {
614             return buf.isDirect();
615         }
616         
617         public boolean isReadOnly()
618         {
619             return buf.isReadOnly();
620         }
621         
622         public boolean isAutoExpand()
623         {
624             return autoExpand;
625         }
626         
627         public ByteBuffer setAutoExpand( boolean autoExpand )
628         {
629             this.autoExpand = autoExpand;
630             return this;
631         }
632 
633         public int capacity()
634         {
635             return buf.capacity();
636         }
637         
638         public int position()
639         {
640             return buf.position();
641         }
642 
643         public ByteBuffer position( int newPosition )
644         {
645             autoExpand( newPosition, 0 );
646             buf.position( newPosition );
647             return this;
648         }
649 
650         public int limit()
651         {
652             return buf.limit();
653         }
654 
655         public ByteBuffer limit( int newLimit )
656         {
657             autoExpand( newLimit, 0 );
658             buf.limit( newLimit );
659             return this;
660         }
661 
662         public ByteBuffer mark()
663         {
664             buf.mark();
665             return this;
666         }
667 
668         public ByteBuffer reset()
669         {
670             buf.reset();
671             return this;
672         }
673 
674         public ByteBuffer clear()
675         {
676             buf.clear();
677             return this;
678         }
679 
680         public ByteBuffer flip()
681         {
682             buf.flip();
683             return this;
684         }
685 
686         public ByteBuffer rewind()
687         {
688             buf.rewind();
689             return this;
690         }
691 
692         public int remaining()
693         {
694             return buf.remaining();
695         }
696 
697         public boolean hasRemaining()
698         {
699             return buf.hasRemaining();
700         }
701 
702         public byte get()
703         {
704             return buf.get();
705         }
706 
707         public short getUnsigned()
708         {
709             return ( short ) ( get() & 0xff );
710         }
711 
712         public ByteBuffer put( byte b )
713         {
714             autoExpand( 1 );
715             buf.put( b );
716             return this;
717         }
718 
719         public byte get( int index )
720         {
721             return buf.get( index );
722         }
723 
724         public short getUnsigned( int index )
725         {
726             return ( short ) ( get( index ) & 0xff );
727         }
728 
729         public ByteBuffer put( int index, byte b )
730         {
731             autoExpand( index, 1 );
732             buf.put( index, b );
733             return this;
734         }
735 
736         public ByteBuffer get( byte[] dst, int offset, int length )
737         {
738             buf.get( dst, offset, length );
739             return this;
740         }
741 
742         public ByteBuffer get( byte[] dst )
743         {
744             buf.get( dst );
745             return this;
746         }
747 
748         public ByteBuffer put( java.nio.ByteBuffer src )
749         {
750             autoExpand( src.remaining() );
751             buf.put( src );
752             return this;
753         }
754 
755         public ByteBuffer put( ByteBuffer src )
756         {
757             autoExpand( src.remaining() );
758             buf.put( src.buf() );
759             return this;
760         }
761 
762         public ByteBuffer put( byte[] src, int offset, int length )
763         {
764             autoExpand( length );
765             buf.put( src, offset, length );
766             return this;
767         }
768 
769         public ByteBuffer put( byte[] src )
770         {
771             autoExpand( src.length );
772             buf.put( src );
773             return this;
774         }
775 
776         public ByteBuffer compact()
777         {
778             buf.compact();
779             return this;
780         }
781 
782         public String toString()
783         {
784             return buf.toString();
785         }
786 
787         public int hashCode()
788         {
789             return buf.hashCode();
790         }
791 
792         public boolean equals( Object ob )
793         {
794             if( !( ob instanceof ByteBuffer ) )
795                 return false;
796 
797             ByteBuffer that = ( ByteBuffer ) ob;
798             return this.buf.equals( that.buf() );
799         }
800 
801         public int compareTo( ByteBuffer that )
802         {
803             return this.buf.compareTo( that.buf() );
804         }
805 
806         public ByteOrder order()
807         {
808             return buf.order();
809         }
810 
811         public ByteBuffer order( ByteOrder bo )
812         {
813             buf.order( bo );
814             return this;
815         }
816 
817         public char getChar()
818         {
819             return buf.getChar();
820         }
821 
822         public ByteBuffer putChar( char value )
823         {
824             autoExpand( 2 );
825             buf.putChar( value );
826             return this;
827         }
828 
829         public char getChar( int index )
830         {
831             return buf.getChar( index );
832         }
833 
834         public ByteBuffer putChar( int index, char value )
835         {
836             autoExpand( index, 2 );
837             buf.putChar( index, value );
838             return this;
839         }
840 
841         public CharBuffer asCharBuffer()
842         {
843             return buf.asCharBuffer();
844         }
845 
846         public short getShort()
847         {
848             return buf.getShort();
849         }
850 
851         public int getUnsignedShort()
852         {
853             return getShort() & 0xffff;
854         }
855 
856         public ByteBuffer putShort( short value )
857         {
858             autoExpand( 2 );
859             buf.putShort( value );
860             return this;
861         }
862 
863         public short getShort( int index )
864         {
865             return buf.getShort( index );
866         }
867 
868         public int getUnsignedShort( int index )
869         {
870             return getShort( index ) & 0xffff;
871         }
872 
873         public ByteBuffer putShort( int index, short value )
874         {
875             autoExpand( index, 2 );
876             buf.putShort( index, value );
877             return this;
878         }
879 
880         public ShortBuffer asShortBuffer()
881         {
882             return buf.asShortBuffer();
883         }
884 
885         public int getInt()
886         {
887             return buf.getInt();
888         }
889 
890         public long getUnsignedInt()
891         {
892             return getInt() & 0xffffffffL;
893         }
894 
895         public ByteBuffer putInt( int value )
896         {
897             autoExpand( 4 );
898             buf.putInt( value );
899             return this;
900         }
901 
902         public int getInt( int index )
903         {
904             return buf.getInt( index );
905         }
906 
907         public long getUnsignedInt( int index )
908         {
909             return getInt( index ) & 0xffffffffL;
910         }
911 
912         public ByteBuffer putInt( int index, int value )
913         {
914             autoExpand( index, 4 );
915             buf.putInt( index, value );
916             return this;
917         }
918 
919         public IntBuffer asIntBuffer()
920         {
921             return buf.asIntBuffer();
922         }
923 
924         public long getLong()
925         {
926             return buf.getLong();
927         }
928 
929         public ByteBuffer putLong( long value )
930         {
931             autoExpand( 8 );
932             buf.putLong( value );
933             return this;
934         }
935 
936         public long getLong( int index )
937         {
938             return buf.getLong( index );
939         }
940 
941         public ByteBuffer putLong( int index, long value )
942         {
943             autoExpand( index, 8 );
944             buf.putLong( index, value );
945             return this;
946         }
947 
948         public LongBuffer asLongBuffer()
949         {
950             return buf.asLongBuffer();
951         }
952 
953         public float getFloat()
954         {
955             return buf.getFloat();
956         }
957 
958         public ByteBuffer putFloat( float value )
959         {
960             autoExpand( 4 );
961             buf.putFloat( value );
962             return this;
963         }
964 
965         public float getFloat( int index )
966         {
967             return buf.getFloat( index );
968         }
969 
970         public ByteBuffer putFloat( int index, float value )
971         {
972             autoExpand( index, 4 );
973             buf.putFloat( index, value );
974             return this;
975         }
976 
977         public FloatBuffer asFloatBuffer()
978         {
979             return buf.asFloatBuffer();
980         }
981 
982         public double getDouble()
983         {
984             return buf.getDouble();
985         }
986 
987         public ByteBuffer putDouble( double value )
988         {
989             autoExpand( 8 );
990             buf.putDouble( value );
991             return this;
992         }
993 
994         public double getDouble( int index )
995         {
996             return buf.getDouble( index );
997         }
998 
999         public ByteBuffer putDouble( int index, double value )
1000         {
1001             autoExpand( index, 8 );
1002             buf.putDouble( index, value );
1003             return this;
1004         }
1005 
1006         public DoubleBuffer asDoubleBuffer()
1007         {
1008             return buf.asDoubleBuffer();
1009         }
1010 
1011         public String getHexDump()
1012         {
1013             return ByteBufferHexDumper.getHexdump( this );
1014         }
1015 
1016         public String getString( CharsetDecoder decoder ) throws CharacterCodingException
1017         {
1018             boolean utf16 = decoder.charset().name().startsWith( "UTF-16" );
1019 
1020             int oldPos = buf.position();
1021             int oldLimit = buf.limit();
1022             int end;
1023 
1024             if( !utf16 )
1025             {
1026                 while( buf.hasRemaining() )
1027                 {
1028                     if( buf.get() == 0 )
1029                     {
1030                         break;
1031                     }
1032                 }
1033 
1034                 end = buf.position();
1035                 if( end == oldLimit )
1036                 {
1037                     buf.limit( end );
1038                 }
1039                 else
1040                 {
1041                     buf.limit( end - 1 );
1042                 }
1043             }
1044             else
1045             {
1046                 while( buf.remaining() >= 2 )
1047                 {
1048                     if( ( buf.get() == 0 ) && ( buf.get() == 0 ) )
1049                     {
1050                         break;
1051                     }
1052                 }
1053 
1054                 end = buf.position();
1055                 if( end == oldLimit || end == oldLimit - 1 )
1056                 {
1057                     buf.limit( end );
1058                 }
1059                 else
1060                 {
1061                     buf.limit( end - 2 );
1062                 }
1063             }
1064 
1065             buf.position( oldPos );
1066             decoder.reset();
1067 
1068             int expectedLength = (int) ( buf.remaining() * decoder.averageCharsPerByte() );
1069             CharBuffer out = CharBuffer.allocate( expectedLength );
1070             for( ;; )
1071             {
1072                 CoderResult cr;
1073                 if ( buf.hasRemaining() )
1074                 {
1075                     cr = decoder.decode( buf, out, true );
1076                 }
1077                 else
1078                 {
1079                     cr = decoder.flush( out );
1080                 }
1081                 
1082                 if ( cr.isUnderflow() )
1083                 {
1084                     break;
1085                 }
1086                 
1087                 if ( cr.isOverflow() )
1088                 {
1089                     CharBuffer o = CharBuffer.allocate( out.capacity() + expectedLength );
1090                     out.flip();
1091                     o.put(out);
1092                     out = o;
1093                     continue;
1094                 }
1095 
1096                 cr.throwException();
1097             }
1098             
1099             buf.limit( oldLimit );
1100             buf.position( end );
1101             return out.flip().toString();
1102         }
1103         
1104         public String getString( int fieldSize, CharsetDecoder decoder ) throws CharacterCodingException
1105         {
1106             checkFieldSize( fieldSize );
1107 
1108             if( fieldSize == 0 )
1109             {
1110                 return "";
1111             }
1112 
1113             boolean utf16 = decoder.charset().name().startsWith( "UTF-16" );
1114 
1115             if( utf16 && ( ( fieldSize & 1 ) != 0 ) )
1116             {
1117                 throw new IllegalArgumentException( "fieldSize is not even." );
1118             }
1119 
1120             int i;
1121             int oldPos = buf.position();
1122             int oldLimit = buf.limit();
1123             int end = buf.position() + fieldSize;
1124 
1125             if( oldLimit < end )
1126             {
1127                 throw new BufferUnderflowException();
1128             }
1129 
1130             if( !utf16 )
1131             {
1132                 for( i = 0; i < fieldSize; i ++ )
1133                 {
1134                     if( buf.get() == 0 )
1135                     {
1136                         break;
1137                     }
1138                 }
1139 
1140                 if( i == fieldSize )
1141                 {
1142                     buf.limit( end );
1143                 }
1144                 else
1145                 {
1146                     buf.limit( buf.position() - 1 );
1147                 }
1148             }
1149             else
1150             {
1151                 for( i = 0; i < fieldSize; i += 2 )
1152                 {
1153                     if( ( buf.get() == 0 ) && ( buf.get() == 0 ) )
1154                     {
1155                         break;
1156                     }
1157                 }
1158 
1159                 if( i == fieldSize )
1160                 {
1161                     buf.limit( end );
1162                 }
1163                 else
1164                 {
1165                     buf.limit( buf.position() - 2 );
1166                 }
1167             }
1168 
1169             buf.position( oldPos );
1170             decoder.reset();
1171 
1172             int expectedLength = (int) ( buf.remaining() * decoder.averageCharsPerByte() );
1173             CharBuffer out = CharBuffer.allocate( expectedLength );
1174             for( ;; )
1175             {
1176                 CoderResult cr;
1177                 if ( buf.hasRemaining() )
1178                 {
1179                     cr = decoder.decode( buf, out, true );
1180                 }
1181                 else
1182                 {
1183                     cr = decoder.flush( out );
1184                 }
1185                 
1186                 if ( cr.isUnderflow() )
1187                 {
1188                     break;
1189                 }
1190                 
1191                 if ( cr.isOverflow() )
1192                 {
1193                     CharBuffer o = CharBuffer.allocate( out.capacity() + expectedLength );
1194                     out.flip();
1195                     o.put(out);
1196                     out = o;
1197                     continue;
1198                 }
1199 
1200                 cr.throwException();
1201             }
1202             
1203             buf.limit( oldLimit );
1204             buf.position( end );
1205             return out.flip().toString();
1206         }
1207 
1208         public ByteBuffer putString(
1209                 CharSequence val, int fieldSize, CharsetEncoder encoder ) throws CharacterCodingException
1210         {
1211             checkFieldSize( fieldSize );
1212 
1213             if( fieldSize == 0 )
1214                 return this;
1215             
1216             autoExpand( fieldSize );
1217             
1218             CharBuffer in = CharBuffer.wrap( val ); 
1219             boolean utf16 = encoder.charset().name().startsWith( "UTF-16" );
1220 
1221             if( utf16 && ( ( fieldSize & 1 ) != 0 ) )
1222             {
1223                 throw new IllegalArgumentException( "fieldSize is not even." );
1224             }
1225 
1226             int oldLimit = buf.limit();
1227             int end = buf.position() + fieldSize;
1228 
1229             if( oldLimit < end )
1230             {
1231                 throw new BufferOverflowException();
1232             }
1233 
1234             buf.limit( end );
1235             encoder.reset();
1236 
1237             for (;;) {
1238                 CoderResult cr;
1239                 if( in.hasRemaining() )
1240                 {
1241                     cr = encoder.encode( in, buf(), true );
1242                 }
1243                 else
1244                 {
1245                     cr = encoder.flush( buf() );
1246                 }
1247                 
1248                 if( cr.isUnderflow() || cr.isOverflow() )
1249                 {
1250                     break;
1251                 }
1252                 cr.throwException();
1253             }
1254 
1255             buf.limit( oldLimit );
1256 
1257             if( buf.position() < end )
1258             {
1259                 if( !utf16 )
1260                 {
1261                     buf.put( ( byte ) 0x00 );
1262                 }
1263                 else
1264                 {
1265                     buf.put( ( byte ) 0x00 );
1266                     buf.put( ( byte ) 0x00 );
1267                 }
1268             }
1269 
1270             buf.position( end );
1271             return this;
1272         }
1273 
1274         public ByteBuffer putString(
1275                 CharSequence val, CharsetEncoder encoder ) throws CharacterCodingException
1276         {
1277             CharBuffer in = CharBuffer.wrap( val ); 
1278             int expectedLength = (int) (in.remaining() * encoder.averageBytesPerChar());
1279 
1280             encoder.reset();
1281 
1282             for (;;) {
1283                 CoderResult cr;
1284                 if( in.hasRemaining() )
1285                 {
1286                     cr = encoder.encode( in, buf(), true );
1287                 }
1288                 else
1289                 {
1290                     cr = encoder.flush( buf() );
1291                 }
1292                 
1293                 if( cr.isUnderflow() )
1294                 {
1295                     break;
1296                 }
1297                 if( cr.isOverflow() && autoExpand )
1298                 {
1299                     autoExpand( expectedLength );
1300                     continue;
1301                 }
1302                 cr.throwException();
1303             }
1304             return this;
1305         }
1306         
1307         public ByteBuffer skip( int size )
1308         {
1309             autoExpand( size );
1310             return position( position() + size );
1311         }
1312 
1313         public ByteBuffer fill( byte value, int size )
1314         {
1315             autoExpand( size );
1316             int q = size >>> 3;
1317             int r = size & 7;
1318 
1319             if( q > 0 )
1320             {
1321                 int intValue = value | ( value << 8 ) | ( value << 16 )
1322                                | ( value << 24 );
1323                 long longValue = intValue;
1324                 longValue <<= 32;
1325                 longValue |= intValue;
1326 
1327                 for( int i = q; i > 0; i -- )
1328                 {
1329                     buf.putLong( longValue );
1330                 }
1331             }
1332 
1333             q = r >>> 2;
1334             r = r & 3;
1335 
1336             if( q > 0 )
1337             {
1338                 int intValue = value | ( value << 8 ) | ( value << 16 )
1339                                | ( value << 24 );
1340                 buf.putInt( intValue );
1341             }
1342 
1343             q = r >> 1;
1344             r = r & 1;
1345 
1346             if( q > 0 )
1347             {
1348                 short shortValue = ( short ) ( value | ( value << 8 ) );
1349                 buf.putShort( shortValue );
1350             }
1351 
1352             if( r > 0 )
1353             {
1354                 buf.put( value );
1355             }
1356 
1357             return this;
1358         }
1359 
1360         public ByteBuffer fillAndReset( byte value, int size )
1361         {
1362             autoExpand( size );
1363             int pos = buf.position();
1364             try
1365             {
1366                 fill( value, size );
1367             }
1368             finally
1369             {
1370                 buf.position( pos );
1371             }
1372             return this;
1373         }
1374 
1375         public ByteBuffer fill( int size )
1376         {
1377             autoExpand( size );
1378             int q = size >>> 3;
1379             int r = size & 7;
1380 
1381             for( int i = q; i > 0; i -- )
1382             {
1383                 buf.putLong( 0L );
1384             }
1385 
1386             q = r >>> 2;
1387             r = r & 3;
1388 
1389             if( q > 0 )
1390             {
1391                 buf.putInt( 0 );
1392             }
1393 
1394             q = r >> 1;
1395             r = r & 1;
1396 
1397             if( q > 0 )
1398             {
1399                 buf.putShort( ( short ) 0 );
1400             }
1401 
1402             if( r > 0 )
1403             {
1404                 buf.put( ( byte ) 0 );
1405             }
1406 
1407             return this;
1408         }
1409 
1410         public ByteBuffer fillAndReset( int size )
1411         {
1412             autoExpand( size );
1413             int pos = buf.position();
1414             try
1415             {
1416                 fill( size );
1417             }
1418             finally
1419             {
1420                 buf.position( pos );
1421             }
1422 
1423             return this;
1424         }
1425 
1426         private void autoExpand( int delta )
1427         {
1428             if( autoExpand )
1429             {
1430                 int pos = buf.position();
1431                 int limit = buf.limit();
1432                 int end = pos + delta;
1433                 if( end > limit ) {
1434                     ensureCapacity( end );
1435                     buf.limit( end );
1436                 }
1437             }
1438         }
1439         
1440         private void autoExpand( int pos, int delta )
1441         {
1442             if( autoExpand )
1443             {
1444                 int limit = buf.limit();
1445                 int end = pos + delta;
1446                 if( end > limit ) {
1447                     ensureCapacity( end ); // expand by 50%
1448                     buf.limit( end );
1449                 }
1450             }
1451         }
1452         
1453         private void ensureCapacity( int requestedCapacity )
1454         {
1455             if( requestedCapacity <= buf.capacity() )
1456             {
1457                 return;
1458             }
1459             
1460             int newCapacity = MINIMUM_CAPACITY;
1461             while( newCapacity < requestedCapacity )
1462             {
1463                 newCapacity <<= 1;
1464             }
1465             
1466             java.nio.ByteBuffer oldBuf = this.buf;
1467             java.nio.ByteBuffer newBuf = isDirect() ? java.nio.ByteBuffer.allocateDirect( newCapacity ) :
1468                                                       java.nio.ByteBuffer.allocate( newCapacity );
1469             
1470             int pos = oldBuf.position();
1471             int limit = oldBuf.limit();
1472             oldBuf.clear();
1473             newBuf.put( oldBuf );
1474             newBuf.position( 0 );
1475             newBuf.limit( limit );
1476             newBuf.position( pos );
1477             this.buf = newBuf;
1478             release0( oldBuf );
1479         }
1480         
1481         private static void checkFieldSize( int fieldSize )
1482         {
1483             if( fieldSize < 0 )
1484             {
1485                 throw new IllegalArgumentException(
1486                         "fieldSize cannot be negative: " + fieldSize );
1487             }
1488         }
1489         
1490     }
1491 }