package org.threadly.litesockets.buffers; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import org.threadly.util.ArgumentVerifier; /** * This class is used to combine multiple ByteBuffers into 1 simplish to use interface. * It provides most of the features of a single ByteBuffer, but with the ability to perform those * operations spanning many ByteBuffers. * * The idea here is to keep from having to copy around and merge ByteBuffers as much as possible. * * NOTE: This is not threadSafe. It should only be accessed by 1 thread at a time. * */ @SuppressWarnings("deprecation") public abstract class AbstractMergedByteBuffers implements MergedByteBuffers { protected final boolean markReadOnly; public AbstractMergedByteBuffers() { this(true); } public AbstractMergedByteBuffers(boolean readOnly) { this.markReadOnly = readOnly; } public AbstractMergedByteBuffers(boolean readOnly, ByteBuffer ...bbs) { this.markReadOnly = readOnly; add(bbs); } protected abstract void doAppend(final ByteBuffer bb); protected abstract void addToFront(final ByteBuffer bb); protected abstract byte get(int pos); public abstract AbstractMergedByteBuffers duplicate(); public abstract AbstractMergedByteBuffers duplicateAndClean(); public abstract byte get(); public abstract void get(byte[] destBytes, int start, int length); public abstract int nextBufferSize(); public abstract ByteBuffer popBuffer(); public abstract int remaining(); public abstract boolean hasRemaining(); public abstract ByteBuffer pullBuffer(final int size); public abstract void discard(final int size); public abstract long getTotalConsumedBytes(); public abstract boolean isAppendable(); @Override public void add(final byte[] ...bas) { for(byte[] ba: bas) { if(ba.length > 0) { doAppend(ByteBuffer.wrap(ba)); } } } @Override public void add(final ByteBuffer ...buffers) { for(ByteBuffer buffer: buffers) { if(buffer.hasRemaining()) { doAppend(buffer.slice()); } } } @Override public void add(final MergedByteBuffers ...mbbs) { for(MergedByteBuffers mbb: mbbs) { while(mbb.remaining() > 0) { ByteBuffer bb = mbb.popBuffer(); if(bb.hasRemaining()) { doAppend(bb); } } } } @Override public void get(final byte[] destBytes) { ArgumentVerifier.assertNotNull(destBytes, "byte[]"); get(destBytes, 0, destBytes.length); } @Override public int indexOf(final String pattern) { return indexOf(pattern, Charset.forName("US-ASCII"), 0); } @Override public int indexOf(String pattern, int fromPosition) { return indexOf(pattern, Charset.forName("US-ASCII"), fromPosition); } @Override public int indexOf(final String pattern, final Charset charSet) { ArgumentVerifier.assertNotNull(pattern, "String"); return indexOf(pattern.getBytes(charSet), 0); } @Override public int indexOf(String pattern, Charset charSet, int fromPosition) { return indexOf(pattern.getBytes(charSet), fromPosition); } @Override public int indexOf(final byte[] pattern) { return findIndexOf(this, pattern, 0); } public int indexOf(final byte[] pattern, int fromPosition) { return findIndexOf(this, pattern, fromPosition); } @Override public short getUnsignedByte() { return (short)(get() & UNSIGNED_BYTE_MASK); } @Override public int getUnsignedShort() { return getShort() & UNSIGNED_SHORT_MASK; } @Override public short getShort() { if (remaining() < BYTES_IN_SHORT) { throw new BufferUnderflowException(); } return pullBuffer(BYTES_IN_SHORT).getShort(); } @Override public int getInt() { if (remaining() < BYTES_IN_INT) { throw new BufferUnderflowException(); } return pullBuffer(BYTES_IN_INT).getInt(); } @Override public long getUnsignedInt() { return getInt() & UNSIGNED_INT_MASK; } @Override public long getLong() { if (remaining() < BYTES_IN_LONG) { throw new BufferUnderflowException(); } return pullBuffer(BYTES_IN_LONG).getLong(); } public String getAsString(final int size) { return getAsString(size, Charset.forName("US-ASCII")); } public String getAsString(final int size, final Charset charSet) { ArgumentVerifier.assertNotNegative(size, "size"); final byte[] ba = new byte[size]; get(ba); return new String(ba, charSet); } @Override public String toString() { return "MergedByteBuffer size:"+remaining()+": consumed:"+getTotalConsumedBytes(); } protected static int findIndexOf(AbstractMergedByteBuffers abb, final byte[] pattern, int fromPosition) { ArgumentVerifier.assertNotNull(pattern, "byte[]"); final int total = abb.remaining(); if(total < fromPosition){ return -1; } int patPos = 0; int bufPos = fromPosition; byte[] prevMatched = new byte[pattern.length]; while(total-bufPos >= pattern.length-patPos) { prevMatched[patPos] = abb.get(bufPos+patPos); if(pattern[patPos] == prevMatched[patPos]) { if(patPos == pattern.length-1) { return bufPos; } patPos++; } else { bufPos++; patPos = 0; } } return -1; } @Override public int nextPopSize() { return this.nextBufferSize(); } @Override public ByteBuffer pop() { return this.popBuffer(); } @Override public ByteBuffer pull(int size) { return this.pullBuffer(size); } @Override public MergedByteBuffers copy() { return this.duplicate(); } }