// Copyright 2014 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland // www.source-code.biz, www.inventec.ch/chdh // // This module is multi-licensed and may be used under the terms // of any of the following licenses: // // EPL, Eclipse Public License, V1.0 or later, http://www.eclipse.org/legal // LGPL, GNU Lesser General Public License, V2.1 or later, http://www.gnu.org/licenses/lgpl.html // // Please contact the author if you need another license. // This module is provided "as is", without warranties of any kind. /** * A ring buffer (circular buffer, FIFO) for bytes. * *

All methods of this class are non-blocking and not synchronized (not thread-safe). */ public class ByteRingBuffer { private byte[] rBuf; // ring buffer data private int rBufSize; // ring buffer size private int rBufPos; // position of first (oldest) data byte within the ring buffer private int rBufUsed; // number of used data bytes within the ring buffer /** * Creates a ring buffer. */ public ByteRingBuffer (int size) { if (size <= 0) { throw new IllegalArgumentException(); } rBufSize = size; rBuf = new byte[rBufSize]; } /** * Resizes the ring buffer by preserving it's data. * *

If the new size is not enough to keep all used data in the ring buffer, * the excess old data is discarded. */ public void resize (int newSize) { if (newSize <= 0) { throw new IllegalArgumentException(); } if (newSize < rBufUsed) { // if new buffer is too small to contain all data discard(rBufUsed - newSize); } // discard oldest data byte[] newBuf = new byte[newSize]; int newBufUsed = read(newBuf, 0, newSize); // transfer data to new buffer rBuf = newBuf; rBufSize = newSize; rBufPos = 0; rBufUsed = newBufUsed; } /** * Returns the size of the ring buffer. */ public int getSize() { return rBufSize; } /** * Returns the number of free bytes within the ring buffer. */ public int getFree() { return rBufSize - rBufUsed; } /** * Returns the number of used bytes within the ring buffer. */ public int getUsed() { return rBufUsed; } /** * Clears the ring buffer. */ public void clear() { rBufPos = 0; rBufUsed = 0; } /** * Discards the oldest len bytes within the ring buffer. * This has the same effect as calling read(new byte[len], 0, len). * * @param len * The number of bytes to be discarded. */ public void discard (int len) { if (len < 0) { throw new IllegalArgumentException(); } int trLen = Math.min(len, rBufUsed); rBufPos = clip(rBufPos + trLen); rBufUsed -= trLen; } /** * Writes data to the ring buffer. * * @return * The number of bytes written. * This is guaranteed to be min(len, getFree()). */ public int write (byte[] buf, int pos, int len) { if (len < 0) { throw new IllegalArgumentException(); } if (rBufUsed == 0) { rBufPos = 0; } // (speed optimization) int p1 = rBufPos + rBufUsed; if (p1 < rBufSize) { // free space in two pieces int trLen1 = Math.min(len, rBufSize - p1); append(buf, pos, trLen1); int trLen2 = Math.min(len - trLen1, rBufPos); append(buf, pos + trLen1, trLen2); return trLen1 + trLen2; } else { // free space in one piece int trLen = Math.min(len, rBufSize - rBufUsed); append(buf, pos, trLen); return trLen; }} /** * Writes data to the ring buffer. * *

Convenience method for: write(buf, 0, buf.length) */ public int write (byte[] buf) { return write(buf, 0, buf.length); } private void append (byte[] buf, int pos, int len) { if (len == 0) { return; } if (len < 0) { throw new AssertionError(); } int p = clip(rBufPos + rBufUsed); System.arraycopy(buf, pos, rBuf, p, len); rBufUsed += len; } /** * Reads data from the ring buffer. * * @return * The number of bytes read. * This is guaranteed to be min(len, getUsed()). */ public int read (byte[] buf, int pos, int len) { if (len < 0) { throw new IllegalArgumentException(); } int trLen1 = Math.min(len, Math.min(rBufUsed, rBufSize - rBufPos)); remove(buf, pos, trLen1); int trLen2 = Math.min(len - trLen1, rBufUsed); remove(buf, pos + trLen1, trLen2); return trLen1 + trLen2; } /** * Reads data from the ring buffer. * *

Convenience method for: read(buf, 0, buf.length) */ public int read (byte[] buf) { return read(buf, 0, buf.length); } private void remove (byte[] buf, int pos, int len) { if (len == 0) { return; } if (len < 0) { throw new AssertionError(); } System.arraycopy(rBuf, rBufPos, buf, pos, len); rBufPos = clip(rBufPos + len); rBufUsed -= len; } private int clip (int p) { return (p < rBufSize) ? p : (p - rBufSize); } }