X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=src%2Fnet%2Fsf%2Fopenrocket%2Flogging%2FCyclicBuffer.java;fp=src%2Fnet%2Fsf%2Fopenrocket%2Flogging%2FCyclicBuffer.java;h=31328ba23f50e866d6518ca190d589cdfc89f999;hb=e298a9509613f232227d16d28310611b33c3aa03;hp=0000000000000000000000000000000000000000;hpb=c71eeba85a8a25e1bd43b27ad09cb2238139b737;p=debian%2Fopenrocket diff --git a/src/net/sf/openrocket/logging/CyclicBuffer.java b/src/net/sf/openrocket/logging/CyclicBuffer.java new file mode 100644 index 00000000..31328ba2 --- /dev/null +++ b/src/net/sf/openrocket/logging/CyclicBuffer.java @@ -0,0 +1,176 @@ +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. + *

+ * 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. + *

+ * 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 the object type that is stored. + * @author Sampo Niskanen + */ +public class CyclicBuffer extends AbstractQueue { + + private final ArrayList 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(size); + for (int i=0; i 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 asList() { + ArrayList list = new ArrayList(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 { + + 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"); + } + } +}