blob: f1a6db33dd37dfb69d3635855a774e3b14ae3e5b [file] [log] [blame]
package net.floodlightcontroller.debugevent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingDeque;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CircularBuffer<T> implements Iterable<T>{
protected static Logger log = LoggerFactory.getLogger(CircularBuffer.class);
private final LinkedBlockingDeque<T> buffer;
public CircularBuffer(int capacity) {
this.buffer = new LinkedBlockingDeque<T>(capacity);
}
/**
* Adding an element to the circular buffer implies adding the element to
* the tail of the deque. In doing so, if the capacity of the buffer has
* been exhausted, then the deque's head should be removed to preserve the
* circular nature of the buffer. A LinkedBlockingDeque is used because of its
* concurrent nature and the fact that adding to tail and removing from head are
* both O(1) operations. The removed head is returned to the caller for reuse.
*
* @param e the element to be added
* @return removed element (for reuse) or null
*/
public T add(T e) {
T oldE = null;
while (!buffer.offerLast(e)) {
oldE = buffer.poll();
}
return oldE;
}
/**
* The basic idea here is that an ArrayList has been passed in, which may or may not
* have a size bigger that the actual number of elements that are meant to
* be flushed to the Circular Buffer. Thus the 'uptoIndex' parameter specifies
* the number of elements that need to be flushed starting from index 0.
* Note that after flushing, the circular buffer may return a memory unit (of type T)
* for reuse in the list, if the circular buffer popped off memory to preserve
* its circular nature. Or it may just return null if nothing was popped off.
* Either way, the list that is returned by this method, is of the SAME SIZE
* as the list passed in, as ArrayLists can hold null elements. The only difference
* is that the list returned will have elements that reference old popped-off memory
* from the circular-buffer or null.
*
* @param elist the ArrayList to flush into the circular buffer.
* @param uptoIndex flush starting from index 0 upto but not including
* index 'uptoIndex'.
* @return the 'elist' passed in with members now pointing to
* to null or old-memory for reuse. The returned list
* if of the same size as 'elist'.
*/
public ArrayList<T> addAll(ArrayList<T> elist, int uptoIndex) {
if (uptoIndex > elist.size()) {
log.error("uptoIndex is greater than elist size .. aborting addAll");
return elist;
}
for (int index=0; index < uptoIndex; index++) {
T e = elist.get(index);
if (e != null) {
elist.set(index, add(e));
}
}
return elist;
}
/**
* Returns an iterator over the elements in the circular buffer in proper sequence.
* The elements will be returned in order from most-recent to oldest.
* The returned Iterator is a "weakly consistent" iterator that will never
* throw ConcurrentModificationException, and guarantees to traverse elements
* as they existed upon construction of the iterator, and may (but is not
* guaranteed to) reflect any modifications subsequent to construction.
*/
@Override
public Iterator<T> iterator() {
return buffer.descendingIterator();
}
public int size() {
return buffer.size();
}
/**
* Atomically removes all elements in the circular buffer
*/
public void clear() {
buffer.clear();
}
}