/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.apmrouter.util; 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); } /** * 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) * 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 channelBuffer The buffer to search * @return the offset from the beginning of the buffer's readable bytes and the first byte of the byte sequence */ public int findIn(ChannelBuffer channelBuffer, int index) { //int idx = findIn(index, channelBuffer.capacity()-index, channelBuffer); int idx = findIn(index, channelBuffer.readableBytes(), channelBuffer); if(idx==-1) return idx; return idx+index; } /** * 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; } }