package org.threadly.litesockets.buffers; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import org.threadly.util.ArgumentVerifier; /** * This is a lower overhead Implementation of {@link MergedByteBuffers}. * It is not appendable and can only process the buffers it is contructed with. * */ public class SimpleMergedByteBuffers extends AbstractMergedByteBuffers { private static final ByteBuffer[] EMPTY_BUFFER_ARRAY = new ByteBuffer[] {EMPTY_BYTEBUFFER}; private final ByteBuffer[] bba; private int currentBuffer = 0; protected long consumedSize = 0; public SimpleMergedByteBuffers(boolean readOnly, ByteBuffer ...bbs) { super(readOnly); for(ByteBuffer bb: bbs) { if(bb == null) { throw new IllegalArgumentException("Can not add null buffers!"); } } if(bbs.length > 0) { bba = bbs; } else { bba = EMPTY_BUFFER_ARRAY; } } public SimpleMergedByteBuffers(boolean readOnly, SimpleMergedByteBuffers smbb, ByteBuffer ...bbs) { super(readOnly); bba = new ByteBuffer[smbb.bba.length-smbb.currentBuffer+ bbs.length]; int count = 0; while(smbb.hasRemaining()) { bba[count] = smbb.popBuffer(); count++; } for(ByteBuffer bb: bbs) { if(bb == null) { throw new IllegalArgumentException("Can not add null buffers!"); } bba[count] = bb; count++; } } private void doGet(final byte[] destBytes) { doGet(destBytes, 0, destBytes.length); } private void doGet(final byte[] destBytes, int start, int len) { int remainingToCopy = len; while (remainingToCopy > 0) { final ByteBuffer buf = getNextBuffer(); final int toCopy = Math.min(buf.remaining(), remainingToCopy); buf.get(destBytes, start+destBytes.length - remainingToCopy, toCopy); remainingToCopy -= toCopy; } } private ByteBuffer getNextBuffer() { while(!this.bba[this.currentBuffer].hasRemaining() && bba.length > currentBuffer+1 ) { this.bba[this.currentBuffer] = null; currentBuffer++; } if(bba[this.currentBuffer].hasRemaining()) { return bba[currentBuffer]; } return EMPTY_BYTEBUFFER; } @Override protected void doAppend(ByteBuffer bb) { throw new UnsupportedOperationException("Can not add to this buffer!"); } @Override protected void addToFront(ByteBuffer bb) { throw new UnsupportedOperationException("Can not add to this buffer!"); } @Override public SimpleMergedByteBuffers duplicate() { ByteBuffer[] bba2 = new ByteBuffer[bba.length-currentBuffer]; for(int i=currentBuffer; i<bba.length; i++) { bba2[i-currentBuffer] = bba[i].duplicate(); } return new SimpleMergedByteBuffers(markReadOnly, bba2); } @Override public SimpleMergedByteBuffers duplicateAndClean() { SimpleMergedByteBuffers smbb = duplicate(); currentBuffer = bba.length; for(int i=0; i<bba.length; i++) { bba[i] = null; } return smbb; } @Override public byte get() { ByteBuffer bb = getNextBuffer(); if(!bb.hasRemaining()){ throw new BufferUnderflowException(); } if(bb.hasRemaining()) { consumedSize++; return bb.get(); } return 0; } @Override public void get(byte[] destBytes, int start, int length) { ArgumentVerifier.assertNotNull(destBytes, "byte[]"); if (remaining() < destBytes.length) { throw new BufferUnderflowException(); } doGet(destBytes, start, length); consumedSize += destBytes.length; } @Override public int nextBufferSize() { return getNextBuffer().remaining(); } @Override public ByteBuffer popBuffer() { ByteBuffer bb = getNextBuffer(); consumedSize += bb.remaining(); currentBuffer++; return bb.slice(); } @Override public int remaining() { int left = 0; for(int i=currentBuffer; i<bba.length; i++) { left+=bba[i].remaining(); } return left; } @Override public boolean hasRemaining() { for(int i=currentBuffer; i<bba.length; i++) { if(bba[i].hasRemaining()) { return true; } } return false; } @Override public ByteBuffer pullBuffer(int size) { ArgumentVerifier.assertNotNegative(size, "size"); if (size == 0) { return EMPTY_BYTEBUFFER; } if (remaining() < size) { throw new BufferUnderflowException(); } consumedSize += size; final ByteBuffer first = getNextBuffer(); if(first.remaining() == size) { currentBuffer++; return first.slice(); } else if(first.remaining() > size) { final ByteBuffer bb = first.duplicate().slice(); bb.limit(bb.position()+size); first.position(first.position()+size); return bb; } else { final byte[] result = new byte[size]; doGet(result); return ByteBuffer.wrap(result); } } @Override public void discard(int size) { ArgumentVerifier.assertNotNegative(size, "size"); if (remaining() < size) { throw new BufferUnderflowException(); } //We have logic here since we dont need to do any copying and we just drop the bytes int toRemoveAmount = size; while (toRemoveAmount > 0) { final ByteBuffer buf = getNextBuffer(); final int bufRemaining = buf.remaining(); if (bufRemaining > toRemoveAmount) { buf.position(buf.position() + toRemoveAmount); toRemoveAmount = 0; } else { currentBuffer++; toRemoveAmount -= bufRemaining; } } consumedSize += size; } @Override protected byte get(int pos) { int currentPos = 0; for(ByteBuffer bb: this.bba) { if(bb != null && bb.remaining() > pos-currentPos) { return bb.get(pos-currentPos); } else { currentPos+=bb.remaining(); } } return 0; } @Override public long getTotalConsumedBytes() { return this.consumedSize; } @Override public boolean isAppendable() { return false; } }