All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.grizzly.memory.BuffersBuffer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.grizzly.memory;

import java.io.UnsupportedEncodingException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.InvalidMarkException;
import java.nio.charset.Charset;
import java.util.Arrays;

import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.utils.ArrayUtils;

/**
 *
 * @author Alexey Stashok
 */
public final class BuffersBuffer extends CompositeBuffer {
    public static volatile boolean DEBUG_MODE = false;

    private static final ThreadCache.CachedTypeIndex CACHE_IDX = ThreadCache.obtainIndex(BuffersBuffer.class,
            Integer.getInteger(BuffersBuffer.class.getName() + ".bb-cache-size", 5));

    /**
     * Construct BuffersBuffer.
     * 
     * @return {@link BuffersBuffer}
     */
    public static BuffersBuffer create() {
        return create(MemoryManager.DEFAULT_MEMORY_MANAGER, null, 0, false);
    }

    public static BuffersBuffer create(final MemoryManager memoryManager) {
        return create(memoryManager, null, 0, false);
    }

    public static BuffersBuffer create(final MemoryManager memoryManager, final Buffer... buffers) {
        return create(memoryManager, buffers, buffers.length, false);
    }

    public static BuffersBuffer create(final MemoryManager memoryManager, final Buffer[] buffers, final boolean isReadOnly) {
        return create(memoryManager, buffers, buffers.length, isReadOnly);
    }

    private static BuffersBuffer create(final MemoryManager memoryManager, final Buffer[] buffers, final int buffersSize, final boolean isReadOnly) {
        return create(memoryManager, buffers, buffersSize, ByteOrder.BIG_ENDIAN, isReadOnly);
    }

    private static BuffersBuffer create(final MemoryManager memoryManager, final Buffer[] buffers, final int buffersSize, final ByteOrder byteOrder,
            final boolean isReadOnly) {
        final BuffersBuffer buffer = ThreadCache.takeFromCache(CACHE_IDX);
        if (buffer != null) {
            buffer.isDisposed = false;
            buffer.order(byteOrder);
            buffer.set(memoryManager, buffers, buffersSize, isReadOnly);
            return buffer;
        }

        return new BuffersBuffer(memoryManager, buffers, buffersSize, isReadOnly);
    }

    protected Exception disposeStackTrace;

    private MemoryManager memoryManager;

    private ByteOrder byteOrder = ByteOrder.BIG_ENDIAN; // parity with ByteBuffer

    private boolean bigEndian = true;

    // Allow to dispose this BuffersBuffer
    private boolean allowBufferDispose = false;

    // Allow to try to dispose internal buffers
    private boolean allowInternalBuffersDispose = true;

    private boolean isDisposed;

    private boolean isReadOnly;

    private int mark = -1;

    // absolute position
    private int position;

    // absolute limit
    private int limit;

    // absolute capacity
    private int capacity;

    // List of wrapped buffers
    private int[] bufferBounds;
    private Buffer[] buffers;
    private int buffersSize;

    private int lastSegmentIndex;
    private int lowerBound;
    private int upperBound;
    private int activeBufferLowerBound;
    private Buffer activeBuffer;

    protected BuffersBuffer(final MemoryManager memoryManager, final Buffer[] buffers, final int buffersSize, final boolean isReadOnly) {
        set(memoryManager, buffers, buffersSize, isReadOnly);
    }

    private void set(final MemoryManager memoryManager, final Buffer[] buffers, final int buffersSize, final boolean isReadOnly) {
        this.memoryManager = memoryManager != null ? memoryManager : MemoryManager.DEFAULT_MEMORY_MANAGER;

        if (buffers != null || this.buffers == null) {
            initBuffers(buffers, buffersSize);
            refreshBuffers();
            this.limit = capacity;
        }

        this.isReadOnly = isReadOnly;
    }

    private void initBuffers(final Buffer[] buffers, final int bufferSize) {
        this.buffers = buffers != null ? buffers : new Buffer[4];
        this.buffersSize = bufferSize;

        if (bufferBounds == null || bufferBounds.length < this.buffers.length) {
            bufferBounds = new int[this.buffers.length];
        }
    }

    private BuffersBuffer duplicateFrom(final BuffersBuffer that) {
        this.memoryManager = that.memoryManager;

        final Buffer[] ba = new Buffer[that.buffers.length];
        for (int i = 0, len = that.buffersSize; i < len; i++) {
            ba[i] = that.buffers[i].duplicate();
        }
        initBuffers(ba, that.buffersSize);
        System.arraycopy(that.bufferBounds, 0, this.bufferBounds, 0, that.buffersSize);

        this.position = that.position;
        this.limit = that.limit;
        this.capacity = that.capacity;
        this.isReadOnly = that.isReadOnly;
        this.byteOrder = that.byteOrder;

        return this;
    }

    @Override
    public boolean tryDispose() {
        if (allowBufferDispose) {
            dispose();
            return true;
        } else if (allowInternalBuffersDispose) {
            removeAndDisposeBuffers();
        }

        return false;
    }

    @Override
    public void dispose() {
        checkDispose();
        isDisposed = true;
        removeAndDisposeBuffers();

        if (DEBUG_MODE) { // if debug is on - clear the buffer content
            // Use static logic class to help JIT optimize the code
            DebugLogic.doDebug(this);
        }

        ThreadCache.putToCache(CACHE_IDX, this);
    }

    @Override
    public boolean isComposite() {
        return true;
    }

    @Override
    public BuffersBuffer append(final Buffer buffer) {
        if (buffer == this) {
            throw new IllegalArgumentException("CompositeBuffer can not append itself");
        }

        checkDispose();
        checkReadOnly();

        ensureBuffersCapacity(1);

        buffer.order(byteOrder); // assign the correct ByteOrder
        capacity += buffer.remaining();
        bufferBounds[buffersSize] = capacity;
        buffers[buffersSize++] = buffer;

        limit = capacity;

        resetLastLocation();

        return this;
    }

    @Override
    public BuffersBuffer prepend(final Buffer buffer) {
        if (buffer == this) {
            throw new IllegalArgumentException("CompositeBuffer can not append itself");
        }

        checkDispose();
        checkReadOnly();

        ensureBuffersCapacity(1);

        buffer.order(byteOrder); // assign the correct ByteOrder
        System.arraycopy(buffers, 0, buffers, 1, buffersSize);
        buffers[0] = buffer;

        buffersSize++;
        refreshBuffers();
        position = 0;
        limit += buffer.remaining();

        resetLastLocation();

        return this;
    }

    @Override
    public boolean replace(final Buffer oldBuffer, final Buffer newBuffer) {
        if (newBuffer == this) {
            throw new IllegalArgumentException("CompositeBuffer can not append itself");
        }

        for (int i = 0; i < buffersSize; i++) {
            final Buffer b = buffers[i];
            if (b == oldBuffer) {
                buffers[i] = newBuffer;
                refreshBuffers();
                limit = capacity;

                if (position > limit) {
                    position = limit;
                }

                resetLastLocation();

                return true;
            } else if (b.isComposite()) {
                if (((CompositeBuffer) b).replace(oldBuffer, newBuffer)) {
                    break;
                }
            }
        }

        return false;
    }

    private void ensureBuffersCapacity(final int newElementsNum) {
        final int newSize = buffersSize + newElementsNum;

        if (newSize > buffers.length) {
            final int newCapacity = Math.max(newSize, buffers.length * 3 / 2 + 1);

            buffers = Arrays.copyOf(buffers, newCapacity);
            bufferBounds = Arrays.copyOf(bufferBounds, newCapacity);
        }
    }

    @Override
    public Buffer[] underlying() {
        checkDispose();
        return buffers;
    }

    @Override
    public int position() {
        checkDispose();
        return position;
    }

    @Override
    public BuffersBuffer position(final int newPosition) {
        checkDispose();
        setPosLim(newPosition, limit);
        if (mark > position) {
            mark = -1;
        }
        return this;
    }

    @Override
    public int limit() {
        checkDispose();
        return limit;
    }

    @Override
    public BuffersBuffer limit(final int newLimit) {
        checkDispose();
        setPosLim(position <= newLimit ? position : newLimit, newLimit);
        if (mark > limit) {
            mark = -1;
        }
        return this;
    }

    @Override
    public int capacity() {
        checkDispose();
        return capacity;
    }

    @Override
    public BuffersBuffer mark() {
        mark = position;
        return this;
    }

    @Override
    public BuffersBuffer reset() {
        int m = mark;
        if (m < 0) {
            throw new InvalidMarkException();
        }
        position = m;
        return this;
    }

    @Override
    public boolean isDirect() {
        return buffers[0].isDirect();
    }

    @Override
    public BuffersBuffer clear() {
        checkDispose();
        refreshBuffers();
        setPosLim(0, capacity);
        mark = -1;
        return this;
    }

    @Override
    public BuffersBuffer flip() {
        checkDispose();
        setPosLim(0, position);
        mark = -1;
        return this;
    }

    @Override
    public BuffersBuffer rewind() {
        checkDispose();
        setPosLim(0, limit);
        mark = -1;
        return this;
    }

    @Override
    public int remaining() {
        checkDispose();
        return limit - position;
    }

    @Override
    public boolean hasRemaining() {
        checkDispose();
        return limit > position;
    }

    @Override
    public boolean isReadOnly() {
        checkDispose();
        return isReadOnly;
    }

    @Override
    public BuffersBuffer asReadOnlyBuffer() {
        checkDispose();
        final BuffersBuffer buffer = create().duplicateFrom(this);
        buffer.isReadOnly = true;

        return buffer;
    }

    @Override
    public Buffer split(final int splitPosition) {
        checkDispose();
        if (splitPosition < 0 || splitPosition > capacity) {
            throw new IllegalArgumentException("Invalid splitPosition value, should be 0 <= splitPosition <= capacity");
        }

        if (mark >= splitPosition) {
            mark = -1;
        }

        final int oldPosition = position;
        final int oldLimit = limit;

        if (splitPosition == capacity) {
            return Buffers.EMPTY_BUFFER;
        } else if (splitPosition == 0) {
            final BuffersBuffer slice2Buffer = BuffersBuffer.create(memoryManager, buffers, buffersSize, byteOrder, isReadOnly);
            slice2Buffer.setPosLim(position, limit);
            initBuffers(null, 0);
            position = 0;
            limit = 0;
            capacity = 0;
            resetLastLocation();

            return slice2Buffer;
        }

        checkIndex(splitPosition);

        final int splitBufferIdx = lastSegmentIndex;

        final int splitBufferPos = toActiveBufferPos(splitPosition);

        final BuffersBuffer slice2Buffer = BuffersBuffer.create(memoryManager, null, 0, byteOrder, false);

        final Buffer splitBuffer = activeBuffer;

        int newSize = splitBufferIdx + 1;

        if (splitBufferPos == 0) {
            slice2Buffer.append(splitBuffer);
            buffers[splitBufferIdx] = null;
            newSize = splitBufferIdx;
        } else if (splitBufferPos < splitBuffer.limit()) {
            final Buffer splitBuffer2 = splitBuffer.split(splitBufferPos);
            slice2Buffer.append(splitBuffer2);
        }

        for (int i = splitBufferIdx + 1; i < buffersSize; i++) {
            slice2Buffer.append(buffers[i]);
            buffers[i] = null;
        }

        buffersSize = newSize;

        refreshBuffers();

        if (oldPosition < splitPosition) {
            position = oldPosition;
        } else {
            position = capacity;
            slice2Buffer.position(oldPosition - splitPosition);
        }

        if (oldLimit < splitPosition) {
            limit = oldLimit;
            slice2Buffer.limit(0);
        } else {
            slice2Buffer.limit(oldLimit - splitPosition);
            limit = capacity;
        }

        resetLastLocation();

        return slice2Buffer;
    }

    @Override
    public void shrink() {
        checkDispose();

        if (position == limit) {
            removeAndDisposeBuffers();
            return;
        }

        checkIndex(position);
        final int posBufferIndex = lastSegmentIndex;
        final int posBufferPosition = toActiveBufferPos(position);

        checkIndex(limit - 1);
        final int limitBufferIndex = lastSegmentIndex;

        final int rightTrim = buffersSize - limitBufferIndex - 1;

        int shift = 0;

        // Shift buffers left to position
        for (int i = 0; i < posBufferIndex; i++) {
            final Buffer buffer = buffers[i];
            shift += buffer.remaining();

            if (allowInternalBuffersDispose) {
                buffer.tryDispose();
            }
        }

        // Shift the position buffer
        final Buffer posBuffer = buffers[posBufferIndex];
        final int diff = posBufferPosition - posBuffer.position();
        if (diff > 0) {
            posBuffer.position(posBufferPosition);
            posBuffer.shrink();
            shift += diff;
        }

        setPosLim(position - shift, limit - shift);
        if (mark > position) {
            mark = -1;
        }

        for (int i = 0; i < rightTrim; i++) {
            final int idx = buffersSize - i - 1;
            final Buffer buffer = buffers[idx];
            buffers[idx] = null;

            if (allowInternalBuffersDispose) {
                buffer.tryDispose();
            }
        }

        buffersSize -= posBufferIndex + rightTrim;

        if (posBufferIndex > 0) {
            System.arraycopy(buffers, posBufferIndex, buffers, 0, buffersSize);
            Arrays.fill(buffers, buffersSize, buffersSize + posBufferIndex, null);
        }

        refreshBuffers();
        resetLastLocation();
    }

    @Override
    public void trim() {
        flip();

        if (limit == 0) {
            removeRightBuffers(0);
            capacity = 0;
        } else {
            checkIndex(limit - 1);
            capacity -= removeRightBuffers(lastSegmentIndex + 1);
        }

        resetLastLocation();
    }

    protected int removeRightBuffers(int startIndex) {
        int removedBytes = 0;

        for (int i = startIndex; i < buffersSize; i++) {
            final Buffer buffer = buffers[i];
            buffers[i] = null;
            removedBytes += buffer.remaining();

            if (allowInternalBuffersDispose) {
                buffer.tryDispose();
            }
        }

        buffersSize = startIndex;

        return removedBytes;
    }

    @Override
    public Buffer slice() {
        return slice(position, limit);
    }

    @Override
    public Buffer slice(final int position, final int limit) {
        checkDispose();

        if (buffersSize == 0 || position == limit) {
            return Buffers.EMPTY_BUFFER;
        } else if (buffersSize == 1) {
            return buffers[0].slice(position, limit);
        }

        checkIndex(position);
        final int posBufferIndex = lastSegmentIndex;
        final int posBufferPosition = toActiveBufferPos(position);

        checkIndex(limit - 1);
        final int limitBufferIndex = lastSegmentIndex;
        final int limitBufferPosition = toActiveBufferPos(limit);

        // noinspection ConstantConditions
        if (posBufferIndex == limitBufferIndex) {
            return buffers[posBufferIndex].slice(posBufferPosition, limitBufferPosition);
        } else {
            final Buffer[] newList = new Buffer[limitBufferIndex - posBufferIndex + 1];

            final Buffer posBuffer = buffers[posBufferIndex];
            newList[0] = posBuffer.slice(posBufferPosition, posBuffer.limit());

            int index = 1;
            for (int i = posBufferIndex + 1; i < limitBufferIndex; i++) {
                newList[index++] = buffers[i].slice();
            }

            final Buffer limitBuffer = buffers[limitBufferIndex];
            newList[index] = limitBuffer.slice(limitBuffer.position(), limitBufferPosition);

            return BuffersBuffer.create(memoryManager, newList, newList.length, byteOrder, isReadOnly);
        }
    }

    @Override
    public BuffersBuffer duplicate() {
        checkDispose();
        return create().duplicateFrom(this);
    }

    @Override
    public BuffersBuffer compact() {
        checkDispose();
        if (buffersSize == 0) {
            return this;
        } else if (buffersSize == 1) {
            final Buffer buffer = buffers[0];
            Buffers.setPositionLimit(buffer, buffer.position() + position, buffer.position() + limit);
            buffer.compact();
        } else {
            checkIndex(position);
            final int posBufferIndex = lastSegmentIndex;
            activeBuffer.position(toActiveBufferPos(position));

            checkIndex(limit - 1);
            final int limitBufferIndex = lastSegmentIndex;
            activeBuffer.limit(toActiveBufferPos(limit));

            for (int i = posBufferIndex; i <= limitBufferIndex; i++) {
                final Buffer b1 = buffers[i - posBufferIndex];
                buffers[i - posBufferIndex] = buffers[i];
                buffers[i] = b1;
            }
        }

        setPosLim(0, position);
        refreshBuffers();

        resetLastLocation();
        return this;
    }

    @Override
    public ByteOrder order() {
        checkDispose();
        return byteOrder;
    }

    @Override
    public BuffersBuffer order(final ByteOrder bo) {
        checkDispose();

        if (bo != byteOrder) {
            this.byteOrder = bo;
            this.bigEndian = bo == ByteOrder.BIG_ENDIAN;

            // propagate ByteOrder to underlying Buffers
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].order(bo);
            }
        }

        return this;
    }

    @Override
    public boolean allowBufferDispose() {
        return allowBufferDispose;
    }

    @Override
    public void allowBufferDispose(boolean allow) {
        this.allowBufferDispose = allow;
    }

    @Override
    public boolean allowInternalBuffersDispose() {
        return allowInternalBuffersDispose;
    }

    @Override
    public void allowInternalBuffersDispose(boolean allowInternalBuffersDispose) {
        this.allowInternalBuffersDispose = allowInternalBuffersDispose;
    }

    @Override
    public byte get() {
        if (!hasRemaining()) {
            throw new BufferUnderflowException();
        }
        return get(position++);
    }

    @Override
    public BuffersBuffer put(byte b) {
        return put(position++, b);
    }

    @Override
    public byte get(final int index) {
        checkDispose();

        checkIndex(index);

        return activeBuffer.get(toActiveBufferPos(index));

    }

    @Override
    public BuffersBuffer put(int index, byte b) {
        checkDispose();
        checkReadOnly();

        checkIndex(index);

        activeBuffer.put(toActiveBufferPos(index), b);

        return this;
    }

    private void checkIndex(final int index) {
        if (index >= lowerBound & index < upperBound) {
            return;
        }

        recalcIndex(index);
    }

    private void recalcIndex(final int index) {
        final int idx = index < bufferBounds[0] ? 0 : ArrayUtils.binarySearch(bufferBounds, 0, buffersSize - 1, index + 1);

        activeBuffer = buffers[idx];

        upperBound = bufferBounds[idx];
        lowerBound = upperBound - activeBuffer.remaining();
        lastSegmentIndex = idx;

        activeBufferLowerBound = lowerBound - activeBuffer.position();
    }

    private int toActiveBufferPos(final int index) {
        return index - activeBufferLowerBound;
    }

    @Override
    public BuffersBuffer get(final byte[] dst) {
        return get(dst, 0, dst.length);
    }

    @Override
    public BuffersBuffer get(final byte[] dst, int offset, int length) {
        checkDispose();
        if (length == 0) {
            return this;
        }

        if (remaining() < length) {
            throw new BufferUnderflowException();
        }

        checkIndex(position);

        int bufferIdx = lastSegmentIndex;
        Buffer buffer = activeBuffer;
        int bufferPosition = toActiveBufferPos(position);

        while (true) {
            int oldPos = buffer.position();
            buffer.position(bufferPosition);
            final int bytesToCopy = Math.min(buffer.remaining(), length);
            buffer.get(dst, offset, bytesToCopy);
            buffer.position(oldPos);

            length -= bytesToCopy;
            offset += bytesToCopy;
            position += bytesToCopy;

            if (length == 0) {
                break;
            }

            bufferIdx++;
            buffer = buffers[bufferIdx];
            bufferPosition = buffer.position();
        }

        return this;
    }

    @Override
    public BuffersBuffer put(final byte[] src) {
        return put(src, 0, src.length);
    }

    @Override
    public BuffersBuffer put(final byte[] src, int offset, int length) {
        checkDispose();
        checkReadOnly();

        if (remaining() < length) {
            throw new BufferOverflowException();
        }

        checkIndex(position);

        int bufferIdx = lastSegmentIndex;
        Buffer buffer = activeBuffer;
        int bufferPosition = toActiveBufferPos(position);

        while (true) {
            int oldPos = buffer.position();
            buffer.position(bufferPosition);
            int bytesToCopy = Math.min(buffer.remaining(), length);
            buffer.put(src, offset, bytesToCopy);
            buffer.position(oldPos);

            length -= bytesToCopy;
            offset += bytesToCopy;
            position += bytesToCopy;

            if (length == 0) {
                break;
            }

            bufferIdx++;
            buffer = buffers[bufferIdx];
            bufferPosition = buffer.position();
        }

        return this;
    }

    @Override
    public BuffersBuffer put8BitString(final String s) {
        final int len = s.length();
        if (remaining() < len) {
            throw new BufferOverflowException();
        }

        for (int i = 0; i < len; i++) {
            put((byte) s.charAt(i));
        }

        return this;
    }

    @Override
    public BuffersBuffer get(final ByteBuffer dst) {
        get(dst, dst.position(), dst.remaining());
        dst.position(dst.limit());

        return this;
    }

    @Override
    public BuffersBuffer get(final ByteBuffer dst, int offset, int length) {

        if (length == 0) {
            return this;
        }
        checkDispose();
        checkReadOnly();

        if (remaining() < length) {
            throw new BufferOverflowException();
        }

        checkIndex(position);

        int bufferIdx = lastSegmentIndex;
        Buffer buffer = activeBuffer;
        int bufferPosition = toActiveBufferPos(position);

        while (true) {
            int oldPos = buffer.position();
            buffer.position(bufferPosition);
            final int bytesToCopy = Math.min(buffer.remaining(), length);
            buffer.get(dst, offset, bytesToCopy);
            buffer.position(oldPos);

            length -= bytesToCopy;
            offset += bytesToCopy;
            position += bytesToCopy;

            if (length == 0) {
                break;
            }

            bufferIdx++;
            buffer = buffers[bufferIdx];
            bufferPosition = buffer.position();
        }

        return this;
    }

    @Override
    public BuffersBuffer put(final ByteBuffer src) {
        put(src, 0, src.remaining());
        src.position(src.limit());

        return this;
    }

    @Override
    public BuffersBuffer put(final ByteBuffer src, int offset, int length) {
        checkDispose();
        checkReadOnly();

        if (remaining() < length) {
            throw new BufferOverflowException();
        }

        checkIndex(position);

        int bufferIdx = lastSegmentIndex;
        Buffer buffer = activeBuffer;
        int bufferPosition = toActiveBufferPos(position);

        while (true) {
            int oldPos = buffer.position();
            buffer.position(bufferPosition);
            int bytesToCopy = Math.min(buffer.remaining(), length);
            buffer.put(src, offset, bytesToCopy);
            buffer.position(oldPos);

            length -= bytesToCopy;
            offset += bytesToCopy;
            position += bytesToCopy;

            if (length == 0) {
                break;
            }

            bufferIdx++;
            buffer = buffers[bufferIdx];
            bufferPosition = buffer.position();
        }

        return this;
    }

    @Override
    public BuffersBuffer put(Buffer src) {
        put(src, src.position(), src.remaining());
        src.position(src.limit());
        return this;
    }

    @Override
    public Buffer put(Buffer src, int position, int length) {
        checkDispose();
        checkReadOnly();

        Buffers.put(src, position, length, this);

        return this;
    }

    @Override
    public char getChar() {
        final char value = getChar(position);
        position += 2;

        return value;
    }

    @Override
    public BuffersBuffer putChar(final char value) {
        putChar(position, value);
        position += 2;
        return this;
    }

    @Override
    public char getChar(final int index) {
        checkDispose();

        checkIndex(index);

        if (upperBound - index >= 2) {
            return activeBuffer.getChar(toActiveBufferPos(index));
        } else {
            return bigEndian ? makeCharB(index) : makeCharL(index);
        }
    }

    @Override
    public BuffersBuffer putChar(int index, final char value) {
        checkDispose();
        checkReadOnly();

        checkIndex(index);

        if (upperBound - index >= 2) {
            activeBuffer.putChar(toActiveBufferPos(index), value);
        } else {
            if (bigEndian) {
                putCharB(index, value);
            } else {
                putCharL(index, value);
            }
        }

        return this;
    }

    @Override
    public short getShort() {
        final short value = getShort(position);
        position += 2;

        return value;
    }

    @Override
    public BuffersBuffer putShort(final short value) {
        putShort(position, value);
        position += 2;
        return this;
    }

    @Override
    public short getShort(final int index) {
        checkDispose();

        checkIndex(index);

        if (upperBound - index >= 2) {
            return activeBuffer.getShort(toActiveBufferPos(index));
        } else {
            return bigEndian ? makeShortB(index) : makeShortL(index);
        }
    }

    @Override
    public BuffersBuffer putShort(int index, final short value) {
        checkDispose();
        checkReadOnly();

        checkIndex(index);

        if (upperBound - index >= 2) {
            activeBuffer.putShort(toActiveBufferPos(index), value);
        } else {
            if (bigEndian) {
                putShortB(index, value);
            } else {
                putShortL(index, value);
            }
        }

        return this;
    }

    @Override
    public int getInt() {
        final int value = getInt(position);
        position += 4;

        return value;
    }

    @Override
    public BuffersBuffer putInt(final int value) {
        putInt(position, value);
        position += 4;
        return this;
    }

    @Override
    public int getInt(final int index) {
        checkDispose();

        checkIndex(index);

        if (upperBound - index >= 4) {
            return activeBuffer.getInt(toActiveBufferPos(index));
        } else {
            return bigEndian ? makeIntB(index) : makeIntL(index);
        }
    }

    @Override
    public BuffersBuffer putInt(int index, final int value) {
        checkDispose();
        checkReadOnly();

        checkIndex(index);

        if (upperBound - index >= 4) {
            activeBuffer.putInt(toActiveBufferPos(index), value);
        } else {
            if (bigEndian) {
                putIntB(index, value);
            } else {
                putIntL(index, value);
            }
        }

        return this;
    }

    @Override
    public long getLong() {
        final long value = getLong(position);
        position += 8;

        return value;
    }

    @Override
    public BuffersBuffer putLong(final long value) {
        putLong(position, value);
        position += 8;

        return this;
    }

    @Override
    public long getLong(final int index) {
        checkDispose();

        checkIndex(index);

        if (upperBound - index >= 8) {
            return activeBuffer.getLong(toActiveBufferPos(index));
        } else {
            return bigEndian ? makeLongB(index) : makeLongL(index);
        }
    }

    @Override
    public BuffersBuffer putLong(final int index, final long value) {
        checkDispose();
        checkReadOnly();

        checkIndex(index);

        if (upperBound - index >= 8) {
            activeBuffer.putLong(toActiveBufferPos(index), value);
        } else {
            if (bigEndian) {
                putLongB(index, value);
            } else {
                putLongL(index, value);
            }
        }

        return this;
    }

    @Override
    public float getFloat() {
        return Float.intBitsToFloat(getInt());
    }

    @Override
    public BuffersBuffer putFloat(float value) {
        return putInt(Float.floatToIntBits(value));
    }

    @Override
    public float getFloat(int index) {
        return Float.intBitsToFloat(getInt(index));
    }

    @Override
    public BuffersBuffer putFloat(int index, float value) {
        return putInt(index, Float.floatToIntBits(value));
    }

    @Override
    public double getDouble() {
        return Double.longBitsToDouble(getLong());
    }

    @Override
    public BuffersBuffer putDouble(double value) {
        return putLong(Double.doubleToLongBits(value));
    }

    @Override
    public double getDouble(int index) {
        return Double.longBitsToDouble(getLong(index));
    }

    @Override
    public BuffersBuffer putDouble(int index, double value) {
        return putLong(index, Double.doubleToLongBits(value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int bulk(final BulkOperation operation) {
        return bulk(operation, position, limit);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int bulk(final BulkOperation operation, final int position, final int limit) {

        checkDispose();

        int length = limit - position;
        if (length == 0) {
            return -1;
        }

        int offset = position;

        checkIndex(position);

        int bufferIdx = lastSegmentIndex;
        Buffer buffer = activeBuffer;
        int bufferPosition = toActiveBufferPos(position);

        while (true) {
            final int bytesToProcess = Math.min(buffer.limit() - bufferPosition, length);

            if (buffer.isComposite()) {
                final int findPos = ((CompositeBuffer) buffer).bulk(operation, bufferPosition, bufferPosition + bytesToProcess);

                if (findPos != -1) {
                    return offset + findPos - bufferPosition;
                }
            } else {
                setter.buffer = buffer;
                for (int i = bufferPosition; i < bufferPosition + bytesToProcess; i++) {
                    setter.position = i;
                    if (operation.processByte(buffer.get(i), setter)) {
                        return offset + i - bufferPosition;
                    }
                }
            }

            length -= bytesToProcess;

            if (length == 0) {
                return -1;
            }

            offset += bytesToProcess;

            bufferIdx++;
            buffer = buffers[bufferIdx];
            bufferPosition = buffer.position();
        }
    }

    private final SetterImpl setter = new SetterImpl();

    private final static class SetterImpl implements Setter {
        private Buffer buffer;
        private int position;

        @Override
        public void set(final byte value) {
            buffer.put(position, value);
        }

    }

    @Override
    public int compareTo(Buffer that) {
        checkDispose();

        int n = this.position() + Math.min(this.remaining(), that.remaining());
        for (int i = this.position(), j = that.position(); i < n; i++, j++) {
            byte v1 = this.get(i);
            byte v2 = that.get(j);
            if (v1 == v2) {
                continue;
            }
            if (v1 < v2) {
                return -1;
            }
            return +1;
        }
        return this.remaining() - that.remaining();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("BuffersBuffer (" + System.identityHashCode(this) + ") [");
        sb.append("pos=").append(position);
        sb.append(" lim=").append(limit);
        sb.append(" cap=").append(capacity);
        sb.append(" bufferSize=").append(buffersSize);
        sb.append(" buffers=").append(Arrays.toString(buffers));
        sb.append(']');
        return sb.toString();
    }

    @Override
    public String toStringContent() {
        return toStringContent(null, position, limit);
    }

    @Override
    public String toStringContent(Charset charset) {
        return toStringContent(charset, position, limit);
    }

    @Override
    public String toStringContent(Charset charset, final int position, final int limit) {
        checkDispose();

        if (charset == null) {
            charset = Charset.defaultCharset();
        }

        final byte[] tmpBuffer = new byte[limit - position];

        int oldPosition = this.position;
        int oldLimit = this.limit;

        setPosLim(position, limit);
        get(tmpBuffer);
        setPosLim(oldPosition, oldLimit);

        try {
            return new String(tmpBuffer, charset.name());
        } catch (UnsupportedEncodingException e) {
            // Should never get here
            throw new IllegalStateException("We took charset name from Charset, why it's not unsupported?", e);
        }
    }

    @Override
    public void dumpHex(java.lang.Appendable appendable) {
        Buffers.dumpBuffer(appendable, this);
    }

    @Override
    public ByteBuffer toByteBuffer() {
        return toByteBuffer(position, limit);
    }

    @Override
    public ByteBuffer toByteBuffer(int position, int limit) {
        if (position < 0 || position > capacity || limit < 0 || limit > capacity) {
            throw new IndexOutOfBoundsException("position=" + position + " limit=" + limit + "on " + toString());
        }

        if (buffersSize == 0 || position == limit) {
            return Buffers.EMPTY_BYTE_BUFFER;
        } else if (buffersSize == 1) {
            final Buffer buffer = buffers[0];
            final int bufferPos = buffer.position();
            return buffer.toByteBuffer(bufferPos + position, bufferPos + limit);
        }

        checkIndex(position);
        final int pos1 = lastSegmentIndex;
        final int bufPosition = toActiveBufferPos(position);

        checkIndex(limit - 1);
        final int pos2 = lastSegmentIndex;
        final int bufLimit = toActiveBufferPos(limit);

        // noinspection ConstantConditions
        if (pos1 == pos2) {
            final Buffer buffer = buffers[pos1];
            return buffer.toByteBuffer(bufPosition, bufLimit);
        }

        final ByteBuffer resultByteBuffer = MemoryUtils.allocateByteBuffer(memoryManager, limit - position);

        final Buffer startBuffer = buffers[pos1];
        final ByteBufferArray array = ByteBufferArray.create();

        fillByteBuffer(resultByteBuffer, startBuffer.toByteBufferArray(array, bufPosition, startBuffer.limit()));

        for (int i = pos1 + 1; i < pos2; i++) {
            fillByteBuffer(resultByteBuffer, buffers[i].toByteBufferArray(array));
        }

        final Buffer endBuffer = buffers[pos2];
        fillByteBuffer(resultByteBuffer, endBuffer.toByteBufferArray(array, endBuffer.position(), bufLimit));

        array.restore();
        array.recycle();

        return (ByteBuffer) resultByteBuffer.flip();
    }

    @Override
    public ByteBufferArray toByteBufferArray() {
        return toByteBufferArray(position, limit);
    }

    @Override
    public ByteBufferArray toByteBufferArray(final ByteBufferArray array) {
        if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toByteBufferArray(array);
            }

            return array;
        } else {
            return toByteBufferArray(array, position, limit);
        }
    }

    @Override
    public ByteBufferArray toByteBufferArray(final int position, final int limit) {
        final ByteBufferArray array = ByteBufferArray.create();

        if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toByteBufferArray(array);
            }

            return array;
        } else {
            return toByteBufferArray(array, position, limit);
        }
    }

    @Override
    public ByteBufferArray toByteBufferArray(final ByteBufferArray array, final int position, final int limit) {

        if (position < 0 || position > capacity || limit < 0 || limit > capacity) {
            throw new IndexOutOfBoundsException("position=" + position + " limit=" + limit + "on " + toString());
        }

        if (buffersSize == 0 || position == limit) {
            return array;
        } else if (buffersSize == 1) {
            final Buffer b = buffers[0];
            final int startPos = b.position();
            return b.toByteBufferArray(array, position + startPos, limit + startPos);
        } else if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toByteBufferArray(array);
            }

            return array;
        }

        checkIndex(position);
        final int pos1 = lastSegmentIndex;
        final int bufPosition = toActiveBufferPos(position);

        checkIndex(limit - 1);
        final int pos2 = lastSegmentIndex;
        final int bufLimit = toActiveBufferPos(limit);

        // noinspection ConstantConditions
        if (pos1 == pos2) {
            final Buffer buffer = buffers[pos1];
            return buffer.toByteBufferArray(array, bufPosition, bufLimit);
        }

        final Buffer startBuffer = buffers[pos1];
        startBuffer.toByteBufferArray(array, bufPosition, startBuffer.limit());

        for (int i = pos1 + 1; i < pos2; i++) {
            final Buffer srcBuffer = buffers[i];
            srcBuffer.toByteBufferArray(array);
        }

        final Buffer endBuffer = buffers[pos2];
        endBuffer.toByteBufferArray(array, endBuffer.position(), bufLimit);

        return array;
    }

    @Override
    public BufferArray toBufferArray() {
        return toBufferArray(position, limit);
    }

    @Override
    public BufferArray toBufferArray(final BufferArray array) {
        if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toBufferArray(array);
            }

            return array;
        } else {
            return toBufferArray(array, position, limit);
        }
    }

    @Override
    public BufferArray toBufferArray(final int position, final int limit) {
        final BufferArray array = BufferArray.create();

        if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toBufferArray(array);
            }

            return array;
        } else {
            return toBufferArray(array, position, limit);
        }
    }

    @Override
    public BufferArray toBufferArray(final BufferArray array, final int position, final int limit) {

        if (position < 0 || position > capacity || limit < 0 || limit > capacity) {
            throw new IndexOutOfBoundsException("position=" + position + " limit=" + limit + "on " + toString());
        }

        if (buffersSize == 0 || position == limit) {
            return array;
        } else if (buffersSize == 1) {
            final Buffer b = buffers[0];
            final int startPos = b.position();
            return b.toBufferArray(array, position + startPos, limit + startPos);
        } else if (position == 0 && limit == capacity) {
            for (int i = 0; i < buffersSize; i++) {
                buffers[i].toBufferArray(array);
            }

            return array;
        }

        checkIndex(position);
        final int pos1 = lastSegmentIndex;
        final int bufPosition = toActiveBufferPos(position);

        checkIndex(limit - 1);
        final int pos2 = lastSegmentIndex;
        final int bufLimit = toActiveBufferPos(limit);

        // noinspection ConstantConditions
        if (pos1 == pos2) {
            final Buffer buffer = buffers[pos1];
            return buffer.toBufferArray(array, bufPosition, bufLimit);
        }

        final Buffer startBuffer = buffers[pos1];
        startBuffer.toBufferArray(array, bufPosition, startBuffer.limit());

        for (int i = pos1 + 1; i < pos2; i++) {
            final Buffer srcBuffer = buffers[i];
            srcBuffer.toBufferArray(array);
        }

        final Buffer endBuffer = buffers[pos2];
        endBuffer.toBufferArray(array, endBuffer.position(), bufLimit);

        return array;
    }

    @Override
    public void removeAll() {
        position = 0;
        limit = 0;
        capacity = 0;
        Arrays.fill(buffers, 0, buffersSize, null);

        buffersSize = 0;
        resetLastLocation();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Buffer) {
            Buffer that = (Buffer) obj;
            if (this.remaining() != that.remaining()) {
                return false;
            }
            int p = this.position();
            for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--) {
                byte v1 = this.get(i);
                byte v2 = that.get(j);
                if (v1 != v2) {
                    return false;
                }
            }
            return true;
        }

        return false;
    }

    @Override
    public boolean hasArray() {
        return false;
    }

    @Override
    public byte[] array() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int arrayOffset() {
        throw new UnsupportedOperationException();
    }

    /**
     * Returns the current hash code of this buffer.
     *
     * 

* The hash code of a byte buffer depends only upon its remaining elements; that is, upon the elements from * position() up to, and including, the element at limit() - 1. * *

* Because buffer hash codes are content-dependent, it is inadvisable to use buffers as keys in hash maps or similar * data structures unless it is known that their contents will not change. *

* * @return The current hash code of this buffer */ @Override public int hashCode() { int h = 1; int p = position(); for (int i = limit() - 1; i >= p; i--) { h = 31 * h + get(i); } h = 31 * h + mark; return h; } @Override public boolean release() { return tryDispose(); } /** * {@inheritDoc} */ @Override public boolean isExternal() { return false; } // --------------------------------------------------------- Private Methods private void fillByteBuffer(final ByteBuffer bb, final ByteBufferArray array) { final ByteBuffer[] bbs = array.getArray(); final int size = array.size(); for (int i = 0; i < size; i++) { final ByteBuffer srcByteBuffer = bbs[i]; bb.put(srcByteBuffer); } } private void removeAndDisposeBuffers() { boolean isNulled = false; if (allowInternalBuffersDispose) { if (disposeOrder != DisposeOrder.FIRST_TO_LAST) { for (int i = buffersSize - 1; i >= 0; i--) { final Buffer buffer = buffers[i]; buffer.tryDispose(); buffers[i] = null; } } else { for (int i = 0; i < buffersSize; i++) { final Buffer buffer = buffers[i]; buffer.tryDispose(); buffers[i] = null; } } isNulled = true; } position = 0; limit = 0; capacity = 0; mark = -1; if (!isNulled) { Arrays.fill(buffers, 0, buffersSize, null); } buffersSize = 0; disposeOrder = DisposeOrder.LAST_TO_FIRST; allowBufferDispose = false; allowInternalBuffersDispose = true; resetLastLocation(); } private void setPosLim(final int position, final int limit) { if (position > limit) { throw new IllegalArgumentException("Position exceeds a limit: " + position + '>' + limit); } this.position = position; this.limit = limit; } private void checkDispose() { if (isDisposed) { throw new IllegalStateException("CompositeBuffer has already been disposed", disposeStackTrace); } } private void checkReadOnly() { if (isReadOnly) { throw new IllegalStateException("Buffer is in read-only mode"); } } private void refreshBuffers() { int currentCapacity = 0; for (int i = 0; i < buffersSize; i++) { final Buffer buffer = buffers[i]; currentCapacity += buffer.remaining(); bufferBounds[i] = currentCapacity; buffer.order(byteOrder); } capacity = currentCapacity; } private void resetLastLocation() { lowerBound = 0; upperBound = 0; activeBuffer = null; } private long makeLongL(int index) { index += 7; checkIndex(index); final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b3 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b4 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b5 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b6 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b7 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b8 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeLong(b1, b2, b3, b4, b5, b6, b7, b8); } private long makeLongB(int index) { final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b3 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b4 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b5 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b6 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b7 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b8 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeLong(b1, b2, b3, b4, b5, b6, b7, b8); } private void putLongL(int index, long value) { index += 7; checkIndex(index); activeBuffer.put(toActiveBufferPos(index), Bits.long7(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long6(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long5(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long4(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long3(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long2(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long1(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.long0(value)); } private void putLongB(int index, long value) { activeBuffer.put(toActiveBufferPos(index), Bits.long7(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long6(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long5(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long4(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long3(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long2(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long1(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.long0(value)); } private void putIntL(int index, int value) { index += 3; checkIndex(index); activeBuffer.put(toActiveBufferPos(index), Bits.int3(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.int2(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.int1(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.int0(value)); } private void putIntB(int index, int value) { activeBuffer.put(toActiveBufferPos(index), Bits.int3(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.int2(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.int1(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.int0(value)); } private int makeIntL(int index) { index += 3; checkIndex(index); final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b3 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(--index); final byte b4 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeInt(b1, b2, b3, b4); } private int makeIntB(int index) { final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b3 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b4 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeInt(b1, b2, b3, b4); } private void putShortL(int index, short value) { checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.short0(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.short1(value)); } private void putShortB(int index, short value) { activeBuffer.put(toActiveBufferPos(index), Bits.short1(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.short0(value)); } private short makeShortL(int index) { final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b1 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeShort(b2, b1); } private short makeShortB(int index) { final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeShort(b1, b2); } private void putCharL(int index, char value) { checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.char0(value)); checkIndex(--index); activeBuffer.put(toActiveBufferPos(index), Bits.char1(value)); } private void putCharB(int index, char value) { activeBuffer.put(toActiveBufferPos(index), Bits.char1(value)); checkIndex(++index); activeBuffer.put(toActiveBufferPos(index), Bits.char0(value)); } private char makeCharL(int index) { final byte b2 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b1 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeChar(b2, b1); } private char makeCharB(int index) { final byte b1 = activeBuffer.get(toActiveBufferPos(index)); checkIndex(++index); final byte b2 = activeBuffer.get(toActiveBufferPos(index)); return Bits.makeChar(b1, b2); } // ---------------------------------------------------------- Nested Classes private static class DebugLogic { static void doDebug(BuffersBuffer buffersBuffer) { buffersBuffer.disposeStackTrace = new Exception("BuffersBuffer was disposed from: "); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy