package com.yahoo.glimmer.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Detects a given non byte aligned bit sequence in the bytes given in * consecutive calls to append() and invokes the given callback with the byte * and bit offsets at which the bit sequence started. * * @author tep */ public class BitSequenceMonitor { private Callback callback; private long bitSequence; private int bitSequenceLengthInBytes; private long bitSequenceMask; private long byteCount; private long bitPipe; public void setCallback(Callback callback) { this.callback = callback; } public Callback getCallback() { return callback; } public void setBitSequence(long bitSequence, byte lengthInBits) { this.bitSequence = bitSequence; bitSequenceMask = (1l << lengthInBits) - 1; bitSequenceLengthInBytes = lengthInBits / 8; if (lengthInBits % 8 > 0) { bitSequenceLengthInBytes++; } } public OutputStream monitor(final OutputStream out) { return new OutputStream() { @Override public void write(int b) throws IOException { append(b); out.write(b); } @Override public void flush() throws IOException { out.flush(); } @Override public void close() throws IOException { end(); out.close(); } }; } public InputStream monitor(final InputStream in) { return new InputStream() { @Override public int read() throws IOException { int b = in.read(); append(b); return b; } @Override public void close() throws IOException { in.close(); } }; } public void append(int b) { byteCount++; bitPipe <<= 8; bitPipe |= (long) b & 0xFF; if ((bitPipe & bitSequenceMask) == bitSequence) { // the block delimiter is byte aligned. callback.sequenceStart(byteCount - bitSequenceLengthInBytes, 0); } else { long tmp = bitPipe; for (int i = 1; i < 8; i++) { tmp >>>= 1; if ((tmp & bitSequenceMask) == bitSequence) { callback.sequenceStart(byteCount - bitSequenceLengthInBytes - 1, 8 - i); break; } } } } public void end() { callback.close(byteCount); } public interface Callback { public void sequenceStart(long byteOffset, int bitInByte); public void close(long byteOffset); } }