| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.mina.codec; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.nio.BufferOverflowException; |
| import java.nio.BufferUnderflowException; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.nio.InvalidMarkException; |
| import java.nio.ReadOnlyBufferException; |
| |
| /** |
| * A proxy class used to manage ByteBuffers as if they were just a big |
| * ByteBuffer. We can add as many buffers as needed when accumulating data. From |
| * the user point of view, the methods are the very same as ByteBuffer provides. |
| * |
| * <p> |
| * IoBuffer instances are *not* thread safe. |
| * |
| * <p> |
| * The IoBuffer uses a singly linked list to handle the multiple Buffers. Thus |
| * sequential access is very efficient and random access is not. It fits well |
| * with the common usage patterns of IoBuffer. |
| * |
| * @author <a href="http://mina.apache.org">Apache MINA Project</a> |
| */ |
| public final class IoBuffer { |
| private static final int BYTE_MASK = 0xff; |
| |
| private static final long BYTE_MASK_L = 0xffL; |
| |
| /** |
| * @see ByteBuffer#allocate(int) |
| */ |
| public static IoBuffer allocate(int capacity) { |
| return wrap(ByteBuffer.allocate(capacity)); |
| } |
| |
| /** |
| * @see ByteBuffer#allocateDirect(int) |
| */ |
| public static IoBuffer allocateDirect(int capacity) { |
| return wrap(ByteBuffer.allocateDirect(capacity)); |
| } |
| |
| /** |
| * Build a new instance of {@link IoBuffer} |
| * |
| * @return a new instance of {@link IoBuffer} |
| */ |
| public static IoBuffer newInstance() { |
| return new IoBuffer(); |
| } |
| |
| /** |
| * @see ByteBuffer#wrap(byte[]) |
| */ |
| public static IoBuffer wrap(byte[]... arrays) { |
| IoBuffer ioBuffer = new IoBuffer(); |
| for (byte[] array : arrays) { |
| ioBuffer.add(ByteBuffer.wrap(array)); |
| } |
| return ioBuffer; |
| } |
| |
| /** |
| * @see ByteBuffer#wrap(byte[], int, int) |
| */ |
| public static IoBuffer wrap(byte[] array, int offset, int length) { |
| return wrap(ByteBuffer.wrap(array, offset, length)); |
| } |
| |
| /** |
| * Wraps ByteBuffers into a new IoBuffer |
| * |
| * @param buffers |
| * the ByteBuffers to wrap |
| * @return the new {@link IoBuffer} |
| */ |
| public static IoBuffer wrap(ByteBuffer... buffers) { |
| IoBuffer ioBuffer = new IoBuffer(); |
| for (ByteBuffer b : buffers) { |
| ioBuffer.add(b); |
| } |
| return ioBuffer; |
| } |
| |
| private ByteOrder bo = ByteOrder.BIG_ENDIAN; |
| |
| private int capacity = 0; |
| |
| private boolean direct = true; |
| |
| private BufferNode head, tail; |
| |
| /** The maximal position in the IoBuffer */ |
| private Pointer limit = new Pointer(); |
| |
| /** The current position in the buffer */ |
| private Pointer mark = new Pointer(); |
| |
| /** The marked position, for the next reset() */ |
| private Pointer position = new Pointer(); |
| |
| /** If the buffer is readonly */ |
| private boolean readonly = false; |
| |
| private IoBuffer() { |
| limit(0); |
| position(0); |
| mark = null; |
| } |
| |
| /** |
| * Add one or more ByteBuffer to the current IoBuffer |
| * |
| * @param buffers |
| * the ByteBuffers to add |
| * @return the current {@link IoBuffer} |
| */ |
| public IoBuffer add(ByteBuffer... buffers) { |
| for (ByteBuffer buffer : buffers) { |
| enqueue(buffer.slice()); |
| } |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#array() |
| */ |
| public byte[] array() { |
| if (capacity == 0) { |
| return new byte[0]; |
| } |
| if (head.hasNext()) { |
| throw new UnsupportedOperationException(); |
| } |
| return head.getBuffer().array(); |
| } |
| |
| /** |
| * @see ByteBuffer#arrayOffset() |
| */ |
| public int arrayOffset() { |
| if (capacity == 0) { |
| return 0; |
| } |
| if (head.hasNext()) { |
| throw new UnsupportedOperationException(); |
| } |
| return head.getBuffer().arrayOffset(); |
| } |
| |
| /** |
| * Provides an input stream which is actually reading the {@link IoBuffer} |
| * instance. |
| * <p> |
| * Further reads on the returned InputStream move the reading head of the |
| * {@link IoBuffer} instance used for its creation |
| * |
| * @return an input stream |
| */ |
| public InputStream asInputStream() { |
| return new InputStream() { |
| |
| @Override |
| public int read() throws IOException { |
| return hasRemaining() ? get() & BYTE_MASK : -1; |
| } |
| |
| @Override |
| public int read(byte[] b, int off, int len) throws IOException { |
| if (!hasRemaining()) { |
| return -1; |
| } |
| |
| int toRead = Math.min(remaining(), len); |
| get(b, off, toRead); |
| return toRead; |
| } |
| }; |
| } |
| |
| /** |
| * @see ByteBuffer#asReadOnlyBuffer() |
| */ |
| public IoBuffer asReadOnlyBuffer() { |
| IoBuffer buffer = duplicate(); |
| buffer.readonly = true; |
| return buffer; |
| } |
| |
| /** |
| * @see ByteBuffer#capacity() |
| */ |
| public int capacity() { |
| return capacity; |
| } |
| |
| /** |
| * @see ByteBuffer#clear() |
| */ |
| public IoBuffer clear() { |
| position = getPointerByPosition(0); |
| limit = getPointerByPosition(capacity); |
| mark = null; |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#compact() |
| */ |
| public IoBuffer compact() { |
| for (int i = 0; i < remaining(); i++) { |
| put(i, get(i + position.getPosition())); |
| } |
| position(limit() - position()); |
| limit(capacity); |
| mark = null; |
| return this; |
| } |
| |
| /** |
| * Returns a copy of the current {@link IoBuffer}, with an independent copy |
| * of the position, limit and mark. |
| * |
| * @return the copied {@link IoBuffer} |
| */ |
| public IoBuffer duplicate() { |
| IoBuffer buffer = new IoBuffer(); |
| |
| for (BufferNode node = head; node != null; node = node.getNext()) { |
| ByteBuffer byteBuffer = node.getBuffer().duplicate(); |
| byteBuffer.rewind(); |
| buffer.enqueue(byteBuffer); |
| } |
| buffer.position(position()); |
| buffer.limit(limit()); |
| buffer.mark = mark != null ? getPointerByPosition(mark.getPosition()) : null; |
| |
| buffer.readonly = readonly; |
| return buffer; |
| } |
| |
| private void enqueue(ByteBuffer buffer) { |
| |
| if (buffer.isReadOnly()) { |
| readonly = true; |
| } |
| |
| if (!buffer.isDirect()) { |
| direct = false; |
| } |
| if (buffer.remaining() > 0) { |
| BufferNode newnode = new BufferNode(buffer, capacity); |
| capacity += buffer.capacity(); |
| |
| if (head == null) { |
| head = newnode; |
| position = getPointerByPosition(0); |
| } else { |
| tail.setNext(newnode); |
| } |
| tail = newnode; |
| |
| limit = getPointerByPosition(capacity); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean equals(Object ob) { |
| if (this == ob) { |
| return true; |
| } |
| if (!(ob instanceof IoBuffer)) { |
| return false; |
| } |
| IoBuffer that = (IoBuffer) ob; |
| if (this.remaining() != that.remaining()) { |
| return false; |
| } |
| int p = this.position(); |
| int q = that.position(); |
| while (this.hasRemaining() && that.hasRemaining()) { |
| if (this.get() != that.get()) { |
| this.position(p); |
| that.position(q); |
| return false; |
| } |
| |
| } |
| this.position(p); |
| that.position(q); |
| return true; |
| } |
| |
| /** |
| * Extends the current IoBuffer capacity. |
| * |
| * @param size |
| * the number of bytes to extend the current IoBuffer |
| * @return the current {@link IoBuffer} |
| */ |
| public IoBuffer extend(int size) { |
| ByteBuffer extension = isDirect() ? ByteBuffer.allocateDirect(size) : ByteBuffer.allocate(size); |
| add(extension); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#flip() |
| */ |
| public IoBuffer flip() { |
| limit = position; |
| position = getPointerByPosition(0); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#get() |
| */ |
| public byte get() { |
| if (!hasRemaining()) { |
| throw new BufferUnderflowException(); |
| } |
| |
| return get(position); |
| } |
| |
| /** |
| * @see ByteBuffer#get(byte[]) |
| */ |
| public IoBuffer get(byte[] dst) { |
| get(dst, 0, dst.length); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#get(byte[], int,int) |
| */ |
| public IoBuffer get(byte[] dst, int offset, int length) { |
| if (remaining() < length) { |
| throw new BufferUnderflowException(); |
| } |
| int remainsToCopy = length; |
| int currentOffset = offset; |
| |
| while (remainsToCopy > 0) { |
| position.updatePos(); |
| position.getNode().getBuffer().position(position.getPositionInNode()); |
| ByteBuffer currentBuffer = position.getNode().getBuffer(); |
| int blocksize = Math.min(remainsToCopy, currentBuffer.remaining()); |
| position.getNode().getBuffer().get(dst, currentOffset, blocksize); |
| |
| currentOffset += blocksize; |
| remainsToCopy -= blocksize; |
| |
| position.incrementPosition(blocksize); |
| |
| position.getNode().getBuffer().position(0); |
| } |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#get(int) |
| */ |
| public byte get(int index) { |
| if (index >= limit.getPosition()) { |
| throw new IndexOutOfBoundsException(); |
| } |
| return get(getPointerByPosition(index)); |
| } |
| |
| private byte get(Pointer pos) { |
| pos.updatePos(); |
| byte b = pos.getNode().getBuffer().get(pos.getPositionInNode()); |
| pos.incrPosition(); |
| return b; |
| } |
| |
| /** |
| * @see ByteBuffer#getChar() |
| */ |
| public char getChar() { |
| return getChar(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getChar(int) |
| */ |
| public char getChar(int index) { |
| return getChar(getPointerByPosition(index)); |
| } |
| |
| private char getChar(Pointer position) { |
| return (char) getShort(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getDouble() |
| */ |
| public double getDouble() { |
| return Double.longBitsToDouble(getLong()); |
| } |
| |
| /** |
| * @see ByteBuffer#getDouble(int) |
| */ |
| public double getDouble(int index) { |
| return getDouble(getPointerByPosition(index)); |
| } |
| |
| private double getDouble(Pointer pos) { |
| return Double.longBitsToDouble(getLong(pos)); |
| } |
| |
| /** |
| * @see ByteBuffer#getFloat() |
| */ |
| public float getFloat() { |
| return getFloat(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getFloat(int) |
| */ |
| public float getFloat(int index) { |
| return getFloat(getPointerByPosition(index)); |
| } |
| |
| private float getFloat(Pointer pos) { |
| return Float.intBitsToFloat(getInt(pos)); |
| } |
| |
| /** |
| * @see ByteBuffer#getInt() |
| */ |
| public int getInt() { |
| return getInt(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getInt(int) |
| */ |
| public int getInt(int index) { |
| return getInt(getPointerByPosition(index)); |
| } |
| |
| private int getInt(Pointer pos) { |
| if (pos.getPosition() > capacity - Integer.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| |
| int out = 0; |
| for (int i = 0; i < Integer.SIZE; i += Byte.SIZE) { |
| out |= (get(pos) & BYTE_MASK) << (bo == ByteOrder.BIG_ENDIAN ? (Integer.SIZE - Byte.SIZE) - i : i); |
| } |
| return out; |
| } |
| |
| /** |
| * @see ByteBuffer#getLong() |
| */ |
| public long getLong() { |
| return getLong(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getLong(int) |
| */ |
| public long getLong(int index) { |
| return getLong(getPointerByPosition(index)); |
| } |
| |
| private long getLong(Pointer pos) { |
| if (pos.getPosition() > capacity - Long.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| |
| long out = 0; |
| for (int i = 0; i < Long.SIZE; i += Byte.SIZE) { |
| out |= (get(pos) & BYTE_MASK_L) << (bo == ByteOrder.BIG_ENDIAN ? (Long.SIZE - Byte.SIZE) - i : i); |
| } |
| return out; |
| } |
| |
| private Pointer getPointerByPosition(int pos) { |
| return new Pointer(pos); |
| } |
| |
| /** |
| * @see ByteBuffer#getShort() |
| */ |
| public short getShort() { |
| return getShort(position); |
| } |
| |
| /** |
| * @see ByteBuffer#getShort(int) |
| */ |
| public long getShort(int index) { |
| return getShort(getPointerByPosition(index)); |
| } |
| |
| private short getShort(Pointer pos) { |
| if (pos.getPosition() > capacity - Short.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| if (bo == ByteOrder.BIG_ENDIAN) { |
| return (short) ((get(pos) & BYTE_MASK) << Byte.SIZE | (get(pos) & BYTE_MASK)); |
| } else { |
| return (short) ((get(pos) & BYTE_MASK) | (get(pos) & BYTE_MASK) << Byte.SIZE); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public int hashCode() { |
| int hash = 0; |
| Pointer oldPos = position.duplicate(); |
| while (hasRemaining()) { |
| hash *= 31; // NOSONAR, standard way of hashing |
| hash += get(); |
| } |
| position = oldPos; |
| return hash; |
| } |
| |
| /** |
| * @see ByteBuffer#hasRemaining() |
| */ |
| public boolean hasRemaining() { |
| return remaining() > 0; |
| } |
| |
| /** |
| * @see ByteBuffer#isDirect() |
| */ |
| public boolean isDirect() { |
| return direct; |
| } |
| |
| /** |
| * @see ByteBuffer#isReadOnly() |
| */ |
| public boolean isReadOnly() { |
| return readonly; |
| } |
| |
| /** |
| * @see ByteBuffer#limit() |
| */ |
| public int limit() { |
| return limit.getPosition(); |
| } |
| |
| /** |
| * @see ByteBuffer#limit(int) |
| */ |
| public void limit(int newLimit) { |
| this.limit = getPointerByPosition(newLimit); |
| } |
| |
| /** |
| * @see ByteBuffer#mark() |
| */ |
| public void mark() { |
| this.mark = position.duplicate(); |
| } |
| |
| /** |
| * Returns the byte order used by this IoBuffer when converting bytes |
| * from/to other primitive types. |
| * <p> |
| * The default byte order of byte buffer is always |
| * {@link ByteOrder#BIG_ENDIAN BIG_ENDIAN} |
| * |
| * @return the byte order used by this IoBuffer when converting bytes |
| * from/to other primitive types. |
| * |
| * @see ByteBuffer#order() |
| */ |
| public ByteOrder order() { |
| return bo; |
| } |
| |
| /** |
| * Sets the byte order of this IoBuffer. |
| * |
| * @param bo |
| * the byte order to set. If {@code null} then the order will be |
| * {@link ByteOrder#LITTLE_ENDIAN LITTLE_ENDIAN}. |
| * @return this IoBuffer. |
| * @see ByteBuffer#order(ByteOrder) |
| */ |
| public IoBuffer order(ByteOrder bo) { |
| this.bo = bo != null ? bo : ByteOrder.LITTLE_ENDIAN; |
| |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#position() |
| */ |
| public int position() { |
| return position.getPosition(); |
| } |
| |
| /** |
| * @see ByteBuffer#position(int) |
| */ |
| public void position(int newPosition) { |
| if (newPosition > limit() || newPosition < 0) { |
| throw new IllegalArgumentException(); |
| } |
| |
| if (mark != null && mark.getPosition() > newPosition) { |
| mark = null; |
| } |
| |
| this.position.setPosition(newPosition); |
| } |
| |
| /** |
| * @see ByteBuffer#put(byte) |
| */ |
| public IoBuffer put(byte b) { |
| if (readonly) { |
| throw new ReadOnlyBufferException(); |
| } |
| if (position.getPosition() >= limit.getPosition()) { |
| throw new BufferUnderflowException(); |
| } |
| |
| put(position, b); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#put(byte[]) |
| */ |
| public IoBuffer put(byte[] src) { |
| put(src, 0, src.length); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#put(ByteBuffer) |
| */ |
| public IoBuffer put(ByteBuffer src) { |
| |
| if (remaining() < src.remaining()) { |
| throw new BufferOverflowException(); |
| } |
| if (isReadOnly()) { |
| throw new ReadOnlyBufferException(); |
| } |
| |
| while (src.hasRemaining()) { |
| put(src.get()); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#put(ByteBuffer) |
| */ |
| public IoBuffer put(IoBuffer src) { |
| if (src == this) { // NOSONAR, checking the instance |
| throw new IllegalArgumentException(); |
| } |
| |
| if (remaining() < src.remaining()) { |
| throw new BufferOverflowException(); |
| } |
| if (isReadOnly()) { |
| throw new ReadOnlyBufferException(); |
| } |
| |
| while (src.hasRemaining()) { |
| put(src.get()); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#put(byte[], int, int) |
| */ |
| public IoBuffer put(byte[] src, int offset, int length) { |
| if (readonly) { |
| throw new ReadOnlyBufferException(); |
| } |
| if (remaining() < length) { |
| throw new BufferUnderflowException(); |
| } |
| |
| int remainsToCopy = length; |
| int currentOffset = offset; |
| position.getNode().getBuffer().position(position.getPositionInNode()); |
| while (remainsToCopy > 0) { |
| position.updatePos(); |
| |
| ByteBuffer currentBuffer = position.getNode().getBuffer(); |
| int blocksize = Math.min(remainsToCopy, currentBuffer.remaining()); |
| position.getNode().getBuffer().put(src, currentOffset, blocksize); |
| |
| currentOffset += blocksize; |
| remainsToCopy -= blocksize; |
| |
| position.incrementPosition(blocksize); |
| } |
| position.getNode().getBuffer().position(0); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#put(int, byte) |
| */ |
| public IoBuffer put(int index, byte value) { |
| if (index >= limit.getPosition()) { |
| throw new IndexOutOfBoundsException(); |
| } |
| Pointer p = getPointerByPosition(index); |
| put(p, value); |
| return this; |
| } |
| |
| private IoBuffer put(Pointer pos, byte b) { |
| pos.updatePos(); |
| pos.getNode().getBuffer().put(pos.getPositionInNode(), b); |
| pos.incrPosition(); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#putChar(char) |
| */ |
| public IoBuffer putChar(char value) { |
| return putChar(position, value); |
| } |
| |
| /** |
| * @see ByteBuffer#putChar(int, char) |
| */ |
| public IoBuffer putChar(int index, char value) { |
| return putChar(getPointerByPosition(index), value); |
| } |
| |
| private IoBuffer putChar(Pointer index, char value) { |
| return putShort(index, (short) value); |
| } |
| |
| /** |
| * @see ByteBuffer#putDouble(double) |
| */ |
| public IoBuffer putDouble(double value) { |
| return putDouble(position, value); |
| } |
| |
| /** |
| * @see ByteBuffer#putDouble(int, double) |
| */ |
| public IoBuffer putDouble(int index, double value) { |
| return putDouble(getPointerByPosition(index), value); |
| } |
| |
| private IoBuffer putDouble(Pointer pos, double value) { |
| return putLong(pos, Double.doubleToLongBits(value)); |
| } |
| |
| /** |
| * @see ByteBuffer#putFloat(float) |
| */ |
| public IoBuffer putFloat(float value) { |
| return putFloat(position, value); |
| } |
| |
| /** |
| * @see ByteBuffer#putFloat(int, float) |
| */ |
| public IoBuffer putFloat(int index, float value) { |
| return putFloat(getPointerByPosition(index), value); |
| } |
| |
| private IoBuffer putFloat(Pointer pointer, float value) { |
| return putInt(pointer, Float.floatToIntBits(value)); |
| } |
| |
| /** |
| * @see ByteBuffer#putInt(int) |
| */ |
| public IoBuffer putInt(int value) { |
| return putInt(position, value); |
| } |
| |
| /** |
| * @see ByteBuffer#putInt(int, int) |
| */ |
| public IoBuffer putInt(int index, int value) { |
| return putInt(getPointerByPosition(index), value); |
| } |
| |
| private IoBuffer putInt(Pointer pointer, int value) { |
| if (position.getPosition() > pointer.getPosition() |
| || pointer.getPosition() > limit.getPosition() - Integer.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| for (int i = 0; i < Integer.SIZE; i += Byte.SIZE) { |
| put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? (Integer.SIZE - Byte.SIZE) - i : i))); |
| } |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#putLong(int, long) |
| */ |
| public IoBuffer putLong(int index, long value) { |
| return putLong(getPointerByPosition(index), value); |
| } |
| |
| /** |
| * @see ByteBuffer#putLong(long) |
| */ |
| public IoBuffer putLong(long value) { |
| return putLong(position, value); |
| } |
| |
| private IoBuffer putLong(Pointer pointer, long value) { |
| if (position.getPosition() > pointer.getPosition() |
| || pointer.getPosition() > limit.getPosition() - Long.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| for (int i = 0; i < Long.SIZE; i += Byte.SIZE) { |
| put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? (Long.SIZE - Byte.SIZE) - i : i))); |
| } |
| |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#putShort(int, short) |
| */ |
| public IoBuffer putShort(int index, short value) { |
| return putShort(getPointerByPosition(index), value); |
| } |
| |
| private IoBuffer putShort(Pointer pointer, short value) { |
| if (position.getPosition() > pointer.getPosition() |
| || pointer.getPosition() > limit.getPosition() - Short.SIZE / Byte.SIZE) { |
| throw new BufferUnderflowException(); |
| } |
| for (int i = 0; i < Short.SIZE; i += Byte.SIZE) { |
| put(pointer, (byte) (value >> (bo == ByteOrder.BIG_ENDIAN ? Byte.SIZE - i : i))); |
| } |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#putShort(short) |
| */ |
| public IoBuffer putShort(short value) { |
| return putShort(position, value); |
| } |
| |
| /** |
| * @see ByteBuffer#remaining() |
| */ |
| public int remaining() { |
| return limit() - position(); |
| } |
| |
| /** |
| * @see ByteBuffer#reset() |
| */ |
| public IoBuffer reset() { |
| if (mark == null) { |
| throw new InvalidMarkException(); |
| } |
| position = mark.duplicate(); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#rewind() |
| */ |
| public IoBuffer rewind() { |
| position(0); |
| mark = getPointerByPosition(-1); |
| return this; |
| } |
| |
| /** |
| * @see ByteBuffer#slice() |
| */ |
| public IoBuffer slice() { |
| position.updatePos(); |
| IoBuffer out = new IoBuffer(); |
| out.order(order()); |
| |
| position.getNode().getBuffer().position(position.getPositionInNode()); |
| if (hasRemaining()) { |
| tail.getBuffer().limit(limit.getPositionInNode()); |
| for (BufferNode node = position.getNode(); node != limit.getNode(); node = node.getNext()) { |
| if (node != head) { // NOSONAR, check if instances are the same. |
| node.getBuffer().position(0); |
| } |
| out.add(node.getBuffer()); |
| } |
| if (tail != head) { // NOSONAR, check if instances are the same. |
| tail.getBuffer().position(0); |
| } |
| out.add(tail.getBuffer().slice()); |
| tail.getBuffer().limit(tail.getBuffer().capacity()); |
| } |
| position.getNode().getBuffer().position(0); |
| |
| return out; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(getClass().getName()); |
| sb.append("[pos="); |
| sb.append(position()); |
| sb.append(" lim="); |
| sb.append(limit()); |
| sb.append(" cap="); |
| sb.append(capacity()); |
| sb.append("]"); |
| return sb.toString(); |
| } |
| |
| private static final class BufferNode { |
| private final ByteBuffer buffer; |
| |
| private BufferNode next; |
| |
| private final int offset; |
| |
| public BufferNode(ByteBuffer buffer, int offset) { |
| this.buffer = buffer; |
| this.offset = offset; |
| } |
| |
| public ByteBuffer getBuffer() { |
| return buffer; |
| } |
| |
| public BufferNode getNext() { |
| return next; |
| } |
| |
| public boolean hasNext() { |
| return next != null; |
| } |
| |
| public void setNext(BufferNode next) { |
| this.next = next; |
| } |
| |
| @Override |
| public String toString() { |
| return "BufferNode [offset=" + offset + ", buffer=" + buffer + "]"; |
| } |
| } |
| |
| private final class Pointer { |
| |
| private BufferNode node; |
| |
| private int positionInBuffer; |
| |
| public Pointer(int position) { |
| this(); |
| |
| setPosition(position); |
| } |
| |
| public Pointer() { |
| } |
| |
| public Pointer duplicate() { |
| return new Pointer(getPosition()); |
| } |
| |
| public BufferNode getNode() { |
| return node; |
| } |
| |
| public int getPosition() { |
| return positionInBuffer + (node == null ? 0 : node.offset); |
| } |
| |
| public int getPositionInNode() { |
| updatePos(); |
| return positionInBuffer; |
| } |
| |
| public void incrPosition() { |
| incrementPosition(1); |
| } |
| |
| public void setPosition(int newPosition) { |
| if ((node == null) || (newPosition < node.offset)) { |
| node = head; |
| } |
| |
| positionInBuffer = node == null ? 0 : newPosition - node.offset; |
| } |
| |
| public void incrementPosition(int positionIncrement) { |
| positionInBuffer += positionIncrement; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(getClass().getName()); |
| sb.append("[pos="); |
| sb.append(getPosition()); |
| sb.append(", node="); |
| sb.append(getNode()); |
| sb.append("]"); |
| return sb.toString(); |
| } |
| |
| public void updatePos() { |
| while (node != null && positionInBuffer >= node.getBuffer().capacity() && node.hasNext()) { |
| positionInBuffer -= node.getBuffer().capacity(); |
| node = node.getNext(); |
| } |
| } |
| } |
| } |