package net.sf.openrocket.logging;
import java.util.AbstractQueue;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A cyclic buffer with a fixed size. When more data is inserted, the newest
* data will overwrite the oldest data.
* <p>
* Though this class implements the Queue interface, it specifically breaks the
* contract by overwriting (removing) data without specific removal. It also
* currently does not support removing arbitrary elements from the set.
* <p>
* The methods in this class are synchronized for concurrent modification.
* However, iterating over the set is not thread-safe. To obtain a snapshot
* of the state of the buffer, use {@link #asList()}.
*
* @param <E> the object type that is stored.
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
public class CyclicBuffer<E> extends AbstractQueue<E> {
private final ArrayList<E> buffer;
private final int maxSize;
private int startPosition = 0;
private int size = 0;
private int overwriteCount = 0;
private int modCount = 0;
/**
* Create a cyclic buffer of the specified size.
*
* @param size the size of the cyclic buffer.
*/
public CyclicBuffer(int size) {
this.buffer = new ArrayList<E>(size);
for (int i=0; i<size; i++) {
this.buffer.add(null);
}
this.maxSize = size;
}
@Override
public synchronized boolean offer(E e) {
buffer.set((startPosition + size) % maxSize, e);
if (size < maxSize) {
size++;
} else {
startPosition = next(startPosition);
overwriteCount++;
}
modCount++;
return true;
}
@Override
public synchronized E peek() {
if (size == 0)
return null;
return buffer.get(startPosition);
}
@Override
public synchronized E poll() {
if (size == 0)
return null;
E element = buffer.get(startPosition);
startPosition = next(startPosition);
size--;
modCount++;
return element;
}
@Override
public synchronized int size() {
return size;
}
@Override
public synchronized Iterator<E> iterator() {
return new CyclicBufferIterator();
}
/**
* Return a snapshot of the current buffered objects in the order they
* were placed in the buffer. The list is independent of the buffer.
*
* @return a list of the buffered objects.
*/
public synchronized List<E> asList() {
ArrayList<E> list = new ArrayList<E>(size);
if (startPosition + size > maxSize) {
list.addAll(buffer.subList(startPosition, maxSize));
list.addAll(buffer.subList(0, startPosition + size - maxSize));
} else {
list.addAll(buffer.subList(startPosition, startPosition+size));
}
return list;
}
/**
* Return the number of elements that have been overwritten in the buffer.
* The overwritten elements are the elements that have been added to the
* buffer, have not been explicitly removed but are not present in the list.
*
* @return the number of overwritten elements this far.
*/
public synchronized int getOverwriteCount() {
return overwriteCount;
}
private int next(int n) {
return (n+1) % maxSize;
}
private class CyclicBufferIterator implements Iterator<E> {
private int expectedModCount;
private int n = 0;
public CyclicBufferIterator() {
this.expectedModCount = modCount;
}
@Override
public boolean hasNext() {
synchronized (CyclicBuffer.this) {
if (expectedModCount != modCount) {
throw new ConcurrentModificationException("expectedModCount="+
expectedModCount+" modCount=" + modCount);
}
return (n < size);
}
}
@Override
public E next() {
synchronized (CyclicBuffer.this) {
if (expectedModCount != modCount) {
throw new ConcurrentModificationException("expectedModCount="+
expectedModCount+" modCount=" + modCount);
}
if (n >= size) {
throw new NoSuchElementException("n="+n+" size="+size);
}
n++;
return buffer.get((startPosition + n-1) % maxSize);
}
}
@Override
public void remove() {
throw new UnsupportedOperationException("random remove not supported");
}
}
}