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 | } |