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  package org.apache.mina.codec;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.BufferOverflowException;
24  import java.nio.BufferUnderflowException;
25  import java.nio.ByteBuffer;
26  import java.nio.ByteOrder;
27  import java.nio.InvalidMarkException;
28  import java.nio.ReadOnlyBufferException;
29  
30  /**
31   * A proxy class used to manage ByteBuffers as if they were just a big ByteBuffer. We can add as many buffers as needed
32   * when accumulating data. From the user point of view, the methods are the very same as ByteBuffer provides.
33   *
34   * <p>IoBuffer instances are *not* thread safe.
35   *
36   * <p>The IoBuffer uses a singly linked list to handle the multiple Buffers. Thus sequential access is
37   * very efficient and random access is not. It fits well with the common usage patterns of IoBuffer.
38   *
39   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
40   */
41  public final class IoBuffer {
42      private static final int BYTE_MASK = 0xff;
43  
44      private static final long BYTE_MASK_L = 0xffL;
45  
46      /**
47       * @see ByteBuffer#allocate(int)
48       */
49      public static IoBuffer allocate(int capacity) {
50          return wrap(ByteBuffer.allocate(capacity));
51      }
52  
53      /**
54       * @see ByteBuffer#allocateDirect(int)
55       */
56      public static IoBuffer allocateDirect(int capacity) {
57          return wrap(ByteBuffer.allocateDirect(capacity));
58      }
59  
60      /**
61       * Build a new instance of {@link IoBuffer}
62       * 
63       * @return a new instance of {@link IoBuffer}
64       */
65      public static IoBuffer newInstance() {
66          return new IoBuffer();
67      }
68  
69      /**
70       * @see ByteBuffer#wrap(byte[])
71       */
72      public static IoBuffer wrap(byte[]... arrays) {
73          IoBuffer ioBuffer = new IoBuffer();
74          for (byte[] array : arrays) {
75              ioBuffer.add(ByteBuffer.wrap(array));
76          }
77          return ioBuffer;
78      }
79  
80      /**
81       * @see ByteBuffer#wrap(byte[], int, int)
82       */
83      public static IoBuffer wrap(byte[] array, int offset, int length) {
84          return wrap(ByteBuffer.wrap(array, offset, length));
85      }
86  
87      /**
88       * Wraps ByteBuffers into a new IoBuffer
89       * 
90       * @param buffers the ByteBuffers to wrap
91       * @return the new {@link IoBuffer}
92       */
93      public static IoBuffer wrap(ByteBuffer... buffers) {
94          IoBuffer ioBuffer = new IoBuffer();
95          for (ByteBuffer b : buffers) {
96              ioBuffer.add(b);
97          }
98          return ioBuffer;
99      }
100 
101     private ByteOrder bo = ByteOrder.BIG_ENDIAN;
102 
103     private int capacity = 0;
104 
105     private boolean direct = true;
106 
107     private BufferNode head, tail;
108 
109     /** The maximal position in the IoBuffer */
110     private Pointer limit = new Pointer();
111 
112     /** The current position in the buffer */
113     private Pointer mark = new Pointer();
114 
115     /** The marked position, for the next reset() */
116     private Pointer position = new Pointer();
117 
118     /** If the buffer is readonly */
119     private boolean readonly = false;
120 
121     private IoBuffer() {
122         limit(0);
123         position(0);
124         mark = null;
125     }
126 
127     /**
128      * Add one or more ByteBuffer to the current IoBuffer
129      * 
130      * @param buffers the ByteBuffers to add
131      * @return the current {@link IoBuffer}
132      */
133     public IoBuffer add(ByteBuffer... buffers) {
134         for (ByteBuffer buffer : buffers) {
135             enqueue(buffer.slice());
136         }
137         return this;
138     }
139 
140     /**
141      * @see ByteBuffer#array()
142      */
143     public byte[] array() {
144         if (capacity == 0) {
145             return new byte[0];
146         }
147         if (head.hasNext()) {
148             throw new UnsupportedOperationException();
149         }
150         return head.getBuffer().array();
151     }
152 
153     /**
154      * @see ByteBuffer#arrayOffset()
155      */
156     public int arrayOffset() {
157         if (capacity == 0) {
158             return 0;
159         }
160         if (head.hasNext()) {
161             throw new UnsupportedOperationException();
162         }
163         return head.getBuffer().arrayOffset();
164     }
165 
166     /**
167      * Provides an input stream which is actually reading the {@link IoBuffer}
168      * instance.
169      * <p>
170      * Further reads on the returned InputStream move the reading head of the {@link IoBuffer}
171      * instance used for it's creation
172      *
173      * @return an input stream
174      */
175     public InputStream asInputStream() {
176         return new InputStream() {
177 
178             @Override
179             public int read() throws IOException {
180                 return hasRemaining() ? get() & BYTE_MASK : -1;
181             }
182 
183             @Override
184             public int read(byte[] b, int off, int len) throws IOException {
185                 if (!hasRemaining()) {
186                     return -1;
187                 }
188 
189                 int toRead = Math.min(remaining(), len);
190                 get(b, off, toRead);
191                 return toRead;
192             }
193         };
194     }
195 
196     /**
197      * @see ByteBuffer#asReadOnlyBuffer()
198      */
199     public IoBuffer asReadOnlyBuffer() {
200         IoBuffer buffer = duplicate();
201         buffer.readonly = true;
202         return buffer;
203     }
204 
205     /**
206      * @see ByteBuffer#capacity()
207      */
208     public int capacity() {
209         return capacity;
210     }
211 
212     /**
213      * @see ByteBuffer#clear()
214      */
215     public IoBuffer clear() {
216         position = getPointerByPosition(0);
217         limit = getPointerByPosition(capacity);
218         mark = null;
219         return this;
220     }
221 
222     /**
223      * @see ByteBuffer#compact()
224      */
225     public IoBuffer compact() {
226         for (int i = 0; i < remaining(); i++) {
227             put(i, get(i + position.getPosition()));
228         }
229         position(limit() - position());
230         limit(capacity);
231         mark = null;
232         return this;
233     }
234 
235     /**
236      * Returns a copy of the current {@link IoBuffer}, with an independent copy of the position, limit and mark.
237      * 
238      * @return the copied {@link IoBuffer}
239      */
240     public IoBuffer duplicate() {
241         IoBuffer buffer = new IoBuffer();
242 
243         for (BufferNode node = head; node != null; node = node.getNext()) {
244             ByteBuffer byteBuffer = node.getBuffer().duplicate();
245             byteBuffer.rewind();
246             buffer.enqueue(byteBuffer);
247         }
248         buffer.position(position());
249         buffer.limit(limit());
250         buffer.mark = mark != null ? getPointerByPosition(mark.getPosition()) : null;
251 
252         buffer.readonly = readonly;
253         return buffer;
254     }
255 
256     private void enqueue(ByteBuffer buffer) {
257 
258         if (buffer.isReadOnly()) {
259             readonly = true;
260         }
261 
262         if (!buffer.isDirect()) {
263             direct = false;
264         }
265         if (buffer.remaining() > 0) {
266             BufferNode newnode = new BufferNode(buffer, capacity);
267             capacity += buffer.capacity();
268 
269             if (head == null) {
270                 head = newnode;
271                 position = getPointerByPosition(0);
272             } else {
273                 tail.setNext(newnode);
274             }
275             tail = newnode;
276 
277             limit = getPointerByPosition(capacity);
278         }
279     }
280 
281     /**
282      * {@inheritDoc}
283      */
284     @Override
285     public boolean equals(Object ob) {
286         if (this == ob) {
287             return true;
288         }
289         if (!(ob instanceof IoBuffer)) {
290             return false;
291         }
292         IoBuffer that = (IoBuffer) ob;
293         if (this.remaining() != that.remaining()) {
294             return false;
295         }
296         int p = this.position();
297         int q = that.position();
298         while (this.hasRemaining() && that.hasRemaining()) {
299             if (this.get() != that.get()) {
300                 this.position(p);
301                 that.position(q);
302                 return false;
303             }
304 
305         }
306         this.position(p);
307         that.position(q);
308         return true;
309     }
310 
311     /**
312      * Extends the current IoBuffer capacity.
313      * 
314      * @param size the number of bytes to extend the current IoBuffer 
315      * @return the current {@link IoBuffer}
316      */
317     public IoBuffer extend(int size) {
318         ByteBuffer extension = isDirect() ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size);
319         add(extension);
320         return this;
321     }
322 
323     /**
324      * @see ByteBuffer#flip()
325      */
326     public IoBuffer flip() {
327         limit = position;
328         position = getPointerByPosition(0);
329         return this;
330     }
331 
332     /**
333      * @see ByteBuffer#get()
334      */
335     public byte get() {
336         if (!hasRemaining()) {
337             throw new BufferUnderflowException();
338         }
339 
340         return get(position);
341     }
342 
343     /**
344      * @see ByteBuffer#get(byte[])
345      */
346     public IoBuffer get(byte[] dst) {
347         get(dst, 0, dst.length);
348         return this;
349     }
350 
351     /**
352      * @see ByteBuffer#get(byte[], int,int)
353      */
354     public IoBuffer get(byte[] dst, int offset, int length) {
355         if (remaining() < length) {
356             throw new BufferUnderflowException();
357         }
358         int remainsToCopy = length;
359         int currentOffset = offset;
360 
361         while (remainsToCopy > 0) {
362             position.updatePos();
363             position.getNode().getBuffer().position(position.getPositionInNode());
364             ByteBuffer currentBuffer = position.getNode().getBuffer();
365             int blocksize = Math.min(remainsToCopy, currentBuffer.remaining());
366             position.getNode().getBuffer().get(dst, currentOffset, blocksize);
367 
368             currentOffset += blocksize;
369             remainsToCopy -= blocksize;
370 
371             position.incrementPosition(blocksize);
372 
373             position.getNode().getBuffer().position(0);
374         }
375         return this;
376     }
377 
378     /**
379      * @see ByteBuffer#get(int)
380      */
381     public byte get(int index) {
382         if (index >= limit.getPosition()) {
383             throw new IndexOutOfBoundsException();
384         }
385         return get(getPointerByPosition(index));
386     }
387 
388     private byte get(Pointer pos) {
389         pos.updatePos();
390         byte b = pos.getNode().getBuffer().get(pos.getPositionInNode());
391         pos.incrPosition();
392         return b;
393     }
394 
395     /**
396      * @see ByteBuffer#getChar()
397      */
398     public char getChar() {
399         return getChar(position);
400     }
401 
402     /**
403      * @see ByteBuffer#getChar(int)
404      */
405     public char getChar(int index) {
406         return getChar(getPointerByPosition(index));
407     }
408 
409     private char getChar(Pointer position) {
410         return (char) getShort(position);
411     }
412 
413     /**
414     * @see ByteBuffer#getDouble()
415     */
416     public double getDouble() {
417         return Double.longBitsToDouble(getLong());
418     }
419 
420     /**
421      * @see ByteBuffer#getDouble(int)
422      */
423     public double getDouble(int index) {
424         return getDouble(getPointerByPosition(index));
425     }
426 
427     private double getDouble(Pointer pos) {
428         return Double.longBitsToDouble(getLong(pos));
429     }
430 
431     /**
432      * @see ByteBuffer#getFloat()
433      */
434     public float getFloat() {
435         return getFloat(position);
436     }
437 
438     /**
439      * @see ByteBuffer#getFloat(int)
440      */
441     public float getFloat(int index) {
442         return getFloat(getPointerByPosition(index));
443     }
444 
445     private float getFloat(Pointer pos) {
446         return Float.intBitsToFloat(getInt(pos));
447     }
448 
449     /**
450      * @see ByteBuffer#getInt()
451      */
452     public int getInt() {
453         return getInt(position);
454     }
455 
456     /**
457      * @see ByteBuffer#getInt(int)
458      */
459     public int getInt(int index) {
460         return getInt(getPointerByPosition(index));
461     }
462 
463     private int getInt(Pointer pos) {
464         if (pos.getPosition() > capacity - Integer.SIZE / Byte.SIZE) {
465             throw new BufferUnderflowException();
466         }
467 
468         int out = 0;
469         for (int i = 0; i < Integer.SIZE; i += Byte.SIZE) {
470             out |= (get(pos) & BYTE_MASK) << (bo == ByteOrder.BIG_ENDIAN ? (Integer.SIZE - Byte.SIZE) - i : i);
471         }
472         return out;
473     }
474 
475     /**
476      * @see ByteBuffer#getLong()
477      */
478     public long getLong() {
479         return getLong(position);
480     }
481 
482     /**
483      * @see ByteBuffer#getLong(int)
484      */
485     public long getLong(int index) {
486         return getLong(getPointerByPosition(index));
487     }
488 
489     private long getLong(Pointer pos) {
490         if (pos.getPosition() > capacity - Long.SIZE / Byte.SIZE) {
491             throw new BufferUnderflowException();
492         }
493 
494         long out = 0;
495         for (int i = 0; i < Long.SIZE; i += Byte.SIZE) {
496             out |= (get(pos) & BYTE_MASK_L) << (bo == ByteOrder.BIG_ENDIAN ? (Long.SIZE - Byte.SIZE) - i : i);
497         }
498         return out;
499     }
500 
501     private Pointer getPointerByPosition(int pos) {
502         return new Pointer(pos);
503     }
504 
505     /**
506      * @see ByteBuffer#getShort()
507      */
508     public short getShort() {
509         return getShort(position);
510     }
511 
512     /**
513      * @see ByteBuffer#getShort(int)
514      */
515     public long getShort(int index) {
516         return getShort(getPointerByPosition(index));
517     }
518 
519     private short getShort(Pointer pos) {
520         if (pos.getPosition() > capacity - Short.SIZE / Byte.SIZE) {
521             throw new BufferUnderflowException();
522         }
523         if (bo == ByteOrder.BIG_ENDIAN) {
524             return (short) ((get(pos) & BYTE_MASK) << Byte.SIZE | (get(pos) & BYTE_MASK));
525         } else {
526             return (short) ((get(pos) & BYTE_MASK) | (get(pos) & BYTE_MASK) << Byte.SIZE);
527         }
528     }
529 
530     /**
531      * {@inheritDoc}
532      */
533     @Override
534     public int hashCode() {
535         int hash = 0;
536         Pointer oldPos = position.duplicate();
537         while (hasRemaining()) {
538             hash *= 31; // NOSONAR, standard way of hashing
539             hash += get();
540         }
541         position = oldPos;
542         return hash;
543     }
544 
545     /**
546      * @see ByteBuffer#hasRemaining()
547      */
548     public boolean hasRemaining() {
549         return remaining() > 0;
550     }
551 
552     /**
553      * @see ByteBuffer#isDirect()
554      */
555     public boolean isDirect() {
556         return direct;
557     }
558 
559     /**
560      * @see ByteBuffer#isReadOnly()
561      */
562     public boolean isReadOnly() {
563         return readonly;
564     }
565 
566     /**
567      * @see ByteBuffer#limit()
568      */
569     public int limit() {
570         return limit.getPosition();
571     }
572 
573     /**
574      * @see ByteBuffer#limit(int)
575      */
576     public void limit(int newLimit) {
577         this.limit = getPointerByPosition(newLimit);
578     }
579 
580     /**
581      * @see ByteBuffer#mark()
582      */
583     public void mark() {
584         this.mark = position.duplicate();
585     }
586 
587     /**
588      * Returns the byte order used by this IoBuffer when converting bytes from/to other primitive
589      * types.
590      * <p>
591      * The default byte order of byte buffer is always {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN}
592      * 
593      * @return the byte order used by this IoBuffer when converting bytes from/to other primitive types.
594      * 
595      * @see ByteBuffer#order()
596      */
597     public ByteOrder order() {
598         return bo;
599     }
600 
601     /**
602      * Sets the byte order of this IoBuffer.
603      * 
604      * @param bo the byte order to set. If {@code null} then the order will be {@link ByteOrder#LITTLE_ENDIAN
605      *        LITTLE_ENDIAN}.
606      * @return this IoBuffer.
607      * @see ByteBuffer#order(ByteOrder)
608      */
609     public IoBuffer order(ByteOrder bo) {
610         this.bo = bo != null ? bo : ByteOrder.LITTLE_ENDIAN;
611 
612         return this;
613     }
614 
615     /**
616      * @see ByteBuffer#position()
617      */
618     public int position() {
619         return position.getPosition();
620     }
621 
622     /**
623      * @see ByteBuffer#position(int)
624      */
625     public void position(int newPosition) {
626         if (newPosition > limit() || newPosition < 0) {
627             throw new IllegalArgumentException();
628         }
629 
630         if (mark != null && mark.getPosition() > newPosition) {
631             mark = null;
632         }
633 
634         this.position.setPosition(newPosition);
635     }
636 
637     /**
638      * @see ByteBuffer#put(byte)
639      */
640     public IoBuffer put(byte b) {
641         if (readonly) {
642             throw new ReadOnlyBufferException();
643         }
644         if (position.getPosition() >= limit.getPosition()) {
645             throw new BufferUnderflowException();
646         }
647 
648         put(position, b);
649         return this;
650     }
651 
652     /**
653      * @see ByteBuffer#put(byte[])
654      */
655     public IoBuffer put(byte[] src) {
656         put(src, 0, src.length);
657         return this;
658     }
659 
660     /**
661      * @see ByteBuffer#put(ByteBuffer)
662      */
663     public IoBuffer put(ByteBuffer src) {
664 
665         if (remaining() < src.remaining()) {
666             throw new BufferOverflowException();
667         }
668         if (isReadOnly()) {
669             throw new ReadOnlyBufferException();
670         }
671 
672         while (src.hasRemaining()) {
673             put(src.get());
674         }
675 
676         return this;
677     }
678 
679     /**
680      * @see ByteBuffer#put(ByteBuffer)
681      */
682     public IoBuffer put(IoBuffer src) {
683         if (src == this) { // NOSONAR, checking the instance
684             throw new IllegalArgumentException();
685         }
686 
687         if (remaining() < src.remaining()) {
688             throw new BufferOverflowException();
689         }
690         if (isReadOnly()) {
691             throw new ReadOnlyBufferException();
692         }
693 
694         while (src.hasRemaining()) {
695             put(src.get());
696         }
697 
698         return this;
699     }
700 
701     /**
702      * @see ByteBuffer#put(byte[], int, int)
703      */
704     public IoBuffer put(byte[] src, int offset, int length) {
705         if (readonly) {
706             throw new ReadOnlyBufferException();
707         }
708         if (remaining() < length) {
709             throw new BufferUnderflowException();
710         }
711 
712         int remainsToCopy = length;
713         int currentOffset = offset;
714         position.getNode().getBuffer().position(position.getPositionInNode());
715         while (remainsToCopy > 0) {
716             position.updatePos();
717 
718             ByteBuffer currentBuffer = position.getNode().getBuffer();
719             int blocksize = Math.min(remainsToCopy, currentBuffer.remaining());
720             position.getNode().getBuffer().put(src, currentOffset, blocksize);
721 
722             currentOffset += blocksize;
723             remainsToCopy -= blocksize;
724 
725             position.incrementPosition(blocksize);
726         }
727         position.getNode().getBuffer().position(0);
728         return this;
729     }
730 
731     /**
732      * @see ByteBuffer#put(int, byte)
733      */
734     public IoBuffer put(int index, byte value) {
735         if (index >= limit.getPosition()) {
736             throw new IndexOutOfBoundsException();
737         }
738         Pointer p = getPointerByPosition(index);
739         put(p, value);
740         return this;
741     }
742 
743     private IoBuffer put(Pointer pos, byte b) {
744         pos.updatePos();
745         pos.getNode().getBuffer().put(pos.getPositionInNode(), b);
746         pos.incrPosition();
747         return this;
748     }
749 
750     /**
751      * @see ByteBuffer#putChar(char)
752      */
753     public IoBuffer putChar(char value) {
754         return putChar(position, value);
755     }
756 
757     /**
758      * @see ByteBuffer#putChar(int, char)
759      */
760     public IoBuffer putChar(int index, char value) {
761         return putChar(getPointerByPosition(index), value);
762     }
763 
764     private IoBuffer putChar(Pointer index, char value) {
765         return putShort(index, (short) value);
766     }
767 
768     /**
769      * @see ByteBuffer#putDouble(double)
770      */
771     public IoBuffer putDouble(double value) {
772         return putDouble(position, value);
773     }
774 
775     /**
776      * @see ByteBuffer#putDouble(int, double)
777      */
778     public IoBuffer putDouble(int index, double value) {
779         return putDouble(getPointerByPosition(index), value);
780     }
781 
782     private IoBuffer putDouble(Pointer pos, double value) {
783         return putLong(pos, Double.doubleToLongBits(value));
784     }
785 
786     /**
787      * @see ByteBuffer#putFloat(float)
788      */
789     public IoBuffer putFloat(float value) {
790         return putFloat(position, value);
791     }
792 
793     /**
794      * @see ByteBuffer#putFloat(int, float)
795      */
796     public IoBuffer putFloat(int index, float value) {
797         return putFloat(getPointerByPosition(index), value);
798     }
799 
800     private IoBuffer putFloat(Pointer pointer, float value) {
801         return putInt(pointer, Float.floatToIntBits(value));
802     }
803 
804     /**
805      * @see ByteBuffer#putInt(int)
806      */
807     public IoBuffer putInt(int value) {
808         return putInt(position, value);
809     }
810 
811     /**
812      * @see ByteBuffer#putInt(int, int)
813      */
814     public IoBuffer putInt(int index, int value) {
815         return putInt(getPointerByPosition(index), value);
816     }
817 
818     private IoBuffer putInt(Pointer pointer, int value) {
819         if (position.getPosition() > pointer.getPosition()
820                 || pointer.getPosition() > limit.getPosition() - Integer.SIZE / Byte.SIZE) {
821             throw new BufferUnderflowException();
822         }
823         for (int i = 0; i < Integer.SIZE; i += Byte.SIZE) {
824             put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? (Integer.SIZE - Byte.SIZE) - i : i)));
825         }
826         return this;
827     }
828 
829     /**
830      * @see ByteBuffer#putLong(int, long)
831      */
832     public IoBuffer putLong(int index, long value) {
833         return putLong(getPointerByPosition(index), value);
834     }
835 
836     /**
837      * @see ByteBuffer#putLong(long)
838      */
839     public IoBuffer putLong(long value) {
840         return putLong(position, value);
841     }
842 
843     private IoBuffer putLong(Pointer pointer, long value) {
844         if (position.getPosition() > pointer.getPosition()
845                 || pointer.getPosition() > limit.getPosition() - Long.SIZE / Byte.SIZE) {
846             throw new BufferUnderflowException();
847         }
848         for (int i = 0; i < Long.SIZE; i += Byte.SIZE) {
849             put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? (Long.SIZE - Byte.SIZE) - i : i)));
850         }
851 
852         return this;
853     }
854 
855     /**
856      * @see ByteBuffer#putShort(int, short)
857      */
858     public IoBuffer putShort(int index, short value) {
859         return putShort(getPointerByPosition(index), value);
860     }
861 
862     private IoBuffer putShort(Pointer pointer, short value) {
863         if (position.getPosition() > pointer.getPosition()
864                 || pointer.getPosition() > limit.getPosition() - Short.SIZE / Byte.SIZE) {
865             throw new BufferUnderflowException();
866         }
867         for (int i = 0; i < Short.SIZE; i += Byte.SIZE) {
868             put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? Byte.SIZE - i : i)));
869         }
870         return this;
871     }
872 
873     /**
874      * @see ByteBuffer#putShort(short)
875      */
876     public IoBuffer putShort(short value) {
877         return putShort(position, value);
878     }
879 
880     /**
881      * @see ByteBuffer#remaining()
882      */
883     public int remaining() {
884         return limit() - position();
885     }
886 
887     /**
888      * @see ByteBuffer#reset()
889      */
890     public IoBuffer reset() {
891         if (mark == null) {
892             throw new InvalidMarkException();
893         }
894         position = mark.duplicate();
895         return this;
896     }
897 
898     /**
899      * @see ByteBuffer#rewind()
900      */
901     public IoBuffer rewind() {
902         position(0);
903         mark = getPointerByPosition(-1);
904         return this;
905     }
906 
907     /**
908      * @see ByteBuffer#slice()
909      */
910     public IoBuffer slice() {
911         position.updatePos();
912         IoBuffer out = new IoBuffer();
913         out.order(order());
914 
915         position.getNode().getBuffer().position(position.getPositionInNode());
916         if (hasRemaining()) {
917             tail.getBuffer().limit(limit.getPositionInNode());
918             for (BufferNode node = position.getNode(); node != limit.getNode(); node = node.getNext()) {
919                 if (node != head) { //NOSONAR, check if instances are the same. 
920                     node.getBuffer().position(0);
921                 }
922                 out.add(node.getBuffer());
923             }
924             if (tail != head) { //NOSONAR, check if instances are the same. 
925                 tail.getBuffer().position(0);
926             }
927             out.add(tail.getBuffer().slice());
928             tail.getBuffer().limit(tail.getBuffer().capacity());
929         }
930         position.getNode().getBuffer().position(0);
931 
932         return out;
933     }
934 
935     @Override
936     public String toString() {
937         StringBuilder sb = new StringBuilder();
938         sb.append(getClass().getName());
939         sb.append("[pos=");
940         sb.append(position());
941         sb.append(" lim=");
942         sb.append(limit());
943         sb.append(" cap=");
944         sb.append(capacity());
945         sb.append("]");
946         return sb.toString();
947     }
948 
949     private static final class BufferNode {
950         private final ByteBuffer buffer;
951 
952         private BufferNode next;
953 
954         private final int offset;
955 
956         public BufferNode(ByteBuffer buffer, int offset) {
957             this.buffer = buffer;
958             this.offset = offset;
959         }
960 
961         public ByteBuffer getBuffer() {
962             return buffer;
963         }
964 
965         public BufferNode getNext() {
966             return next;
967         }
968 
969         public boolean hasNext() {
970             return next != null;
971         }
972 
973         public void setNext(BufferNode next) {
974             this.next = next;
975         }
976 
977         @Override
978         public String toString() {
979             return "BufferNode [offset=" + offset + ", buffer=" + buffer + "]";
980         }
981     }
982 
983     private final class Pointer {
984 
985         private BufferNode node;
986 
987         private int positionInBuffer;
988 
989         public Pointer(int position) {
990             this();
991 
992             setPosition(position);
993         }
994 
995         public Pointer() {
996         }
997 
998         public Pointer duplicate() {
999             return new Pointer(getPosition());
1000         }
1001 
1002         public BufferNode getNode() {
1003             return node;
1004         }
1005 
1006         public int getPosition() {
1007             return positionInBuffer + (node == null ? 0 : node.offset);
1008         }
1009 
1010         public int getPositionInNode() {
1011             updatePos();
1012             return positionInBuffer;
1013         }
1014 
1015         public void incrPosition() {
1016             incrementPosition(1);
1017         }
1018 
1019         public void setPosition(int newPosition) {
1020             if (node == null || newPosition < node.offset) {
1021                 node = head;
1022             }
1023             positionInBuffer = node == null ? 0 : newPosition - node.offset;
1024         }
1025 
1026         public void incrementPosition(int positionIncrement) {
1027             positionInBuffer += positionIncrement;
1028         }
1029 
1030         @Override
1031         public String toString() {
1032             StringBuilder sb = new StringBuilder();
1033             sb.append(getClass().getName());
1034             sb.append("[pos=");
1035             sb.append(getPosition());
1036             sb.append(", node=");
1037             sb.append(getNode());
1038             sb.append("]");
1039             return sb.toString();
1040         }
1041 
1042         public void updatePos() {
1043             while (node != null && positionInBuffer >= node.getBuffer().capacity() && node.hasNext()) {
1044                 positionInBuffer -= node.getBuffer().capacity();
1045                 node = node.getNext();
1046             }
1047         }
1048     }
1049 }