blob: f1a6db33dd37dfb69d3635855a774e3b14ae3e5b [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001package net.floodlightcontroller.debugevent;
2
3
4import java.util.ArrayList;
5import java.util.Iterator;
6import java.util.concurrent.LinkedBlockingDeque;
7
8import org.slf4j.Logger;
9import org.slf4j.LoggerFactory;
10
11public class CircularBuffer<T> implements Iterable<T>{
12 protected static Logger log = LoggerFactory.getLogger(CircularBuffer.class);
13 private final LinkedBlockingDeque<T> buffer;
14
15 public CircularBuffer(int capacity) {
16 this.buffer = new LinkedBlockingDeque<T>(capacity);
17 }
18
19 /**
20 * Adding an element to the circular buffer implies adding the element to
21 * the tail of the deque. In doing so, if the capacity of the buffer has
22 * been exhausted, then the deque's head should be removed to preserve the
23 * circular nature of the buffer. A LinkedBlockingDeque is used because of its
24 * concurrent nature and the fact that adding to tail and removing from head are
25 * both O(1) operations. The removed head is returned to the caller for reuse.
26 *
27 * @param e the element to be added
28 * @return removed element (for reuse) or null
29 */
30 public T add(T e) {
31 T oldE = null;
32 while (!buffer.offerLast(e)) {
33 oldE = buffer.poll();
34 }
35 return oldE;
36 }
37
38 /**
39 * The basic idea here is that an ArrayList has been passed in, which may or may not
40 * have a size bigger that the actual number of elements that are meant to
41 * be flushed to the Circular Buffer. Thus the 'uptoIndex' parameter specifies
42 * the number of elements that need to be flushed starting from index 0.
43 * Note that after flushing, the circular buffer may return a memory unit (of type T)
44 * for reuse in the list, if the circular buffer popped off memory to preserve
45 * its circular nature. Or it may just return null if nothing was popped off.
46 * Either way, the list that is returned by this method, is of the SAME SIZE
47 * as the list passed in, as ArrayLists can hold null elements. The only difference
48 * is that the list returned will have elements that reference old popped-off memory
49 * from the circular-buffer or null.
50 *
51 * @param elist the ArrayList to flush into the circular buffer.
52 * @param uptoIndex flush starting from index 0 upto but not including
53 * index 'uptoIndex'.
54 * @return the 'elist' passed in with members now pointing to
55 * to null or old-memory for reuse. The returned list
56 * if of the same size as 'elist'.
57 */
58 public ArrayList<T> addAll(ArrayList<T> elist, int uptoIndex) {
59 if (uptoIndex > elist.size()) {
60 log.error("uptoIndex is greater than elist size .. aborting addAll");
61 return elist;
62 }
63 for (int index=0; index < uptoIndex; index++) {
64 T e = elist.get(index);
65 if (e != null) {
66 elist.set(index, add(e));
67 }
68 }
69 return elist;
70 }
71
72 /**
73 * Returns an iterator over the elements in the circular buffer in proper sequence.
74 * The elements will be returned in order from most-recent to oldest.
75 * The returned Iterator is a "weakly consistent" iterator that will never
76 * throw ConcurrentModificationException, and guarantees to traverse elements
77 * as they existed upon construction of the iterator, and may (but is not
78 * guaranteed to) reflect any modifications subsequent to construction.
79 */
80 @Override
81 public Iterator<T> iterator() {
82 return buffer.descendingIterator();
83 }
84
85 public int size() {
86 return buffer.size();
87 }
88
89 /**
90 * Atomically removes all elements in the circular buffer
91 */
92 public void clear() {
93 buffer.clear();
94 }
95}