Skip to content

Instantly share code, notes, and snippets.

@nickman
Last active December 16, 2015 21:49
Show Gist options
  • Save nickman/5502143 to your computer and use it in GitHub Desktop.
Save nickman/5502143 to your computer and use it in GitHub Desktop.
A Netty ChannelBufferIndexFinder that finds byte arrays withing a channel buffer. Still working on some odd bugs. Not thread safe either.
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferIndexFinder;
/**
* <p>Title: ByteSequenceIndexFinder</p>
* <p>Description: A {@link ChannelBufferIndexFinder} implementation for finding a sequence of bytes within a {@link ChannelBuffer}</p>
* <p>This class is <b><i>not</i></b> thread-safe and use by multiple threads will almost certainly cause it to explode.</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.util.ByteSequenceIndexFinder</code></p>
*/
public class ByteSequenceIndexFinder implements ChannelBufferIndexFinder {
/** The sequence of bytes to find */
private final byte[] sequence;
/** The length of the sequence */
private final int slength;
/** The position within the sequence that has been matched up to */
private int pos = 0;
/** The adjusted result of the last find */
private int lastResult = -1;
/**
* Creates a new ByteSequenceIndexFinder
* @param sequence The sequence we're searching for
*/
public ByteSequenceIndexFinder(byte[] sequence) {
if(sequence==null || sequence.length==0) throw new IllegalArgumentException("The passed byte sequence was null or zero-length", new Throwable());
this.sequence = sequence;
slength = this.sequence.length;
}
/**
* {@inheritDoc}
* @see org.jboss.netty.buffer.ChannelBufferIndexFinder#find(org.jboss.netty.buffer.ChannelBuffer, int)
*/
@Override
public boolean find(ChannelBuffer buffer, int guessedIndex) {
byte nextByte = buffer.getByte(guessedIndex);
boolean found = nextByte==sequence[pos];
if(found) pos++;
else {
pos = 0;
if(nextByte==sequence[pos]) pos++;
}
if(pos==slength) {
pos = 0;
lastResult = guessedIndex-slength+1;
return true;
}
return false;
}
/**
* Locates the first starting offset in the passed {@link ChannelBuffer}
* where the specified byte sequence was located.
* The search starts the specified {@code index} (inclusive)
* and lasts for the specified {@code length}.
* This method does not modify the {@code readerIndex} or {@code writerIndex}
* of the passed channelBuffer.
* The finder is {@link #reset()} once this method is complete.
*
* @param index The offset where the search starts
* @param length The maximum number of bytes to search
* @param channelBuffer The buffer to search
* @return the number of bytes between the specified {@code index}
* and the first starting offset where the {@code indexFinder} returned
* {@code true}. {@code -1} if the {@code indexFinder} did not
* return {@code true} at all.
*/
public int findIn(int index, int length, ChannelBuffer channelBuffer) {
checkReadableBytes(channelBuffer, length);
try {
int result = channelBuffer.bytesBefore(index, length, this);
return result==-1 ? -1 : result-slength+1;
} finally {
reset();
}
}
/**
* Locates the first starting offset in the passed {@link ChannelBuffer}
* where the specified byte sequence was located.
* The search starts the current {@link ChannelBuffer#readerIndex()} (inclusive)
* and lasts for the specified {@code length}.
* This method does not modify the {@code readerIndex} or {@code writerIndex}
* of the passed channelBuffer.
* The finder is {@link #reset()} once this method is complete.
*
* @param length The maximum number of bytes to search
* @param channelBuffer The buffer to search
* @return the number of bytes between the specified {@code index}
* and the first starting offset where the {@code indexFinder} returned
* {@code true}. {@code -1} if the {@code indexFinder} did not
* return {@code true} at all.
*/
public int findIn(int length, ChannelBuffer channelBuffer) {
return findIn(channelBuffer.readerIndex(), length, channelBuffer);
}
/**
* Locates the first starting offset in the passed {@link ChannelBuffer}
* where the specified byte sequence was located.
* The search starts the current {@link ChannelBuffer#readerIndex()} (inclusive)
* to the current {@link ChannelBuffer#writerIndex()}.
*
* This method does not modify the {@code readerIndex} or {@code writerIndex}
* of the passed channelBuffer.
* The finder is {@link #reset()} once this method is complete.
*
* @param channelBuffer The buffer to search
* @return the number of bytes between the specified {@code index}
* and the first starting offset where the {@code indexFinder} returned
* {@code true}. {@code -1} if the {@code indexFinder} did not
* return {@code true} at all.
*/
public int findIn(ChannelBuffer channelBuffer) {
return findIn(channelBuffer.readerIndex(), channelBuffer.readableBytes(), channelBuffer);
}
/**
* Throws an {@link IndexOutOfBoundsException} if the current
* {@linkplain ChannelBuffer#readableBytes() readable bytes} of this buffer is less
* than the specified value.
* @param channelBuffer The channel buffer to test
* @param minimumReadableBytes
*/
protected void checkReadableBytes(ChannelBuffer channelBuffer, int minimumReadableBytes) {
if(channelBuffer==null) throw new IllegalArgumentException("The passed channel buffer was null");
if (channelBuffer.readableBytes() < minimumReadableBytes) {
throw new IndexOutOfBoundsException("Not enough readable bytes - Need "
+ minimumReadableBytes + ", maximum is " + channelBuffer.readableBytes());
}
}
/**
* Resets this finder.
*/
public void reset() {
pos = 0;
lastResult = -1;
}
/**
* Returns the sequence length adjusted result of the most recent {@link ChannelBuffer#bytesBefore(ChannelBufferIndexFinder)} invocation.
* If a find has not been executed, this will be <code>-1</code>. Calling {@link #reset()} resets the last result to <code>-1</code>.
* @return the result of the most recent find.
*/
public int getLastResult() {
return lastResult;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment