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 }