package org.ovirt.engine.ui.common.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Simple ring buffer implementation that only permits adding new elements.
* <p>
* A ring buffer is <em>First In, First Out</em> (FIFO) data structure with fixed capacity.
* When adding new element into full buffer, the oldest element is replaced with newly added
* element. Since elements are removed in same order in which they were added, there's no need
* to re-arrange elements within the underlying (linear buffer) data representation.
*
* @param <T>
* Type of element managed by the buffer.
*/
public class AddOnlyRingBuffer<T> {
/**
* Delegate interface representing a linear buffer to store the actual data.
*
* @param <T>
* Type of element managed by the buffer.
*/
public interface LinearBuffer<T> {
/**
* Write an element at given index.
*/
void write(int index, T element);
/**
* Read an element at given index.
*/
T read(int index);
}
private final int capacity;
private final LinearBuffer<T> delegate;
private int head = 0;
private int size = 0;
public AddOnlyRingBuffer(int capacity, LinearBuffer<T> delegate) {
if (delegate == null) {
throw new NullPointerException("delegate cannot be null"); //$NON-NLS-1$
} else if (capacity < 1) {
throw new IllegalArgumentException("capacity must be positive"); //$NON-NLS-1$
}
this.capacity = capacity;
this.delegate = delegate;
}
/**
* Adds the given element to the ring buffer.
* <p>
* Performs in constant time O(1).
*
* @return Oldest element removed due to buffer being full, {@code null} otherwise.
*/
public T add(T element) {
T old = null;
// Buffer not full yet, don't move the head
if (size < capacity) {
int index = head + size;
size += 1;
delegate.write(index, element);
}
// Buffer is full, need to move the head
else {
int index = head;
head = (head + 1) % capacity;
old = delegate.read(index);
delegate.write(index, element);
}
return old;
}
/**
* Returns the list of elements present in the ring buffer.
* <p>
* Performs in linear time O(n).
*/
public List<T> list() {
List<T> result = new ArrayList<>(size);
for (int offset = 0; offset < size; offset += 1) {
int index = (head + offset) % capacity;
result.add(delegate.read(index));
}
return result;
}
/**
* Returns the head index pointing to oldest element in the ring buffer.
*/
public int head() {
return head;
}
/**
* Returns the number of elements present in the ring buffer.
*/
public int size() {
return size;
}
/**
* Returns {@code true} if the ring buffer size is zero.
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* Returns {@code true} if the ring buffer reached its capacity.
*/
public boolean isFull() {
return size() == capacity;
}
/**
* Resets the state of the ring buffer.
*/
public void reset(int newHead, int newSize) {
// 0 <= newHead < capacity
if (newHead < 0 || newHead >= capacity) {
throw new IllegalArgumentException("newHead out of bounds"); //$NON-NLS-1$
}
// 0 <= newSize <= capacity
else if (newSize < 0 || newSize > capacity) {
throw new IllegalArgumentException("newSize out of bounds"); //$NON-NLS-1$
}
this.head = newHead;
this.size = newSize;
}
}