package org.limewire.collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; /** * Provides a simple fixed-size double-ended queue, a circular buffer. * The fixed size is intentional for performance; use <code>Buffer</code> * when you want to use a fix amount of resources. * For a minimal amount of efficiency, the internal buffer is only * allocated on the first insertion or retrieval, allowing lots of * <code>Buffer</code>s to be created that may not be used. * <p> * This class is not thread-safe. */ public class Buffer<E> implements Cloneable, Iterable<E> { /**<pre> * The abstraction function is * [ buf[head], buf[head+1], ..., buf[tail-1] ] if head<=tail * or * [ buf[head], buf[head+1], ..., buf[size-1], * buf[0], buf[1], ..., buf[tail-1] ] otherwise * * Note that buf[head] is the location of the head, and * buf[tail] is just past the location of the tail. This * means that there is always one unused element of the array. * See p. 202 of _Introduction to Algorithms_ by Cormen, * Leiserson, Rivest for details. * * Also note that size is really the MAX size of this+1, i.e., * the capacity, not the current size. * * INVARIANT: buf.length=size * 0<=head, tail<size * size>=2 *<p/re> */ private final int size; protected E buf[]; private int head; private int tail; /** * @requires size>=1. * @effects that it creates a new, empty buffer that can hold * size elements. */ public Buffer(int size) { assert size >= 1; //one element of buf unused this.size=size+1; // lazily allocate buffer. //buf=new Object[size+1]; head=0; tail=0; } /** "Copy constructor": constructs a new shallow copy of other. */ public Buffer(Buffer<? extends E> other) { this.size=other.size; this.head=other.head; this.tail=other.tail; if(other.buf != null) { this.buf = createArray(other.buf.length); System.arraycopy(other.buf, 0, this.buf, 0, other.buf.length); } } @SuppressWarnings("unchecked") protected E[] createArray(int size) { return (E[]) new Object[size]; } /** Initializes the internal buf if necessary. */ protected void initialize() { if(buf == null) buf = createArray(size+1); } /** Returns true if and only if this is empty. */ public boolean isEmpty() { return head==tail; } /** Returns true if and only if this is full, e.g., adding another element * would force another out. */ public boolean isFull() { return increment(tail)==head; } /** Same as getSize(). */ public final int size() { return getSize(); } /** Returns the number of elements in this. Note that this never * exceeds the value returned by getCapacity. */ public int getSize() { if (head<=tail) //tail-1-head+1 [see abstraction function] return tail-head; else //(size-1-head+1) + (tail-1+1) [see abstraction function] return size-head+tail; } /** Returns the number of elements that this can hold, i.e., the * max size that was passed to the constructor. */ public int getCapacity() { return size-1; } private int decrement(int i) { if (i==0) return size-1; else return i-1; } private int increment(int i) { if (i==(size-1)) return 0; else return i+1; } /** Returns the j s.t. buf[j]=this[i]. */ private int index(int i) throws IndexOutOfBoundsException { if (i<0 || i>=getSize()) throw new IndexOutOfBoundsException("index: " + i); return (i+head) % size; } /** If i<0 or i>=getSize(), throws IndexOutOfBoundsException. * Else returns this[i] */ public E get(int i) throws IndexOutOfBoundsException { initialize(); return buf[index(i)]; } /* * @modifies this[i]. * @effects If i<0 or i>=getSize(), throws IndexOutOfBoundsException * and does not modify this. Else this[i]=o. */ public void set(int i, E o) throws IndexOutOfBoundsException { initialize(); buf[index(i)]=o; } /** Same as addFirst(x). */ public E add(E x) { return addFirst(x); } /** * @modifies this. * @effects adds x to the head of this, removing the tail * if necessary so that the number of elements in this is less than * or equal to the maximum size. Returns the element removed, or null * if none was removed. */ public E addFirst(E x) { initialize(); E ret=null; if (isFull()) ret=removeLast(); head=decrement(head); buf[head]=x; return ret; } /** * @modifies this. * @effects adds x to the tail of this, removing the head * if necessary so that the number of elements in this is less than * or equal to the maximum size. Returns the element removed, or null * if none was removed. */ public E addLast(E x) { initialize(); E ret=null; if (isFull()) ret=removeFirst(); buf[tail]=x; tail=increment(tail); return ret; } /** * Returns true if the input object x is in the buffer. */ public boolean contains(Object x) { for(E e : this) { if(e.equals(x)) return true; } return false; } /** * Returns the head of this, or throws NoSuchElementException if * this is empty. */ public E first() throws NoSuchElementException { if (isEmpty()) throw new NoSuchElementException(); return buf[head]; } /** * Returns the tail of this, or throws NoSuchElementException if * this is empty. */ public E last() throws NoSuchElementException { if (isEmpty()) throw new NoSuchElementException(); return buf[decrement(tail)]; } /** * @modifies this. * @effects removes and returns the head of this, or throws * NoSuchElementException if this is empty. */ public E removeFirst() throws NoSuchElementException { if (isEmpty()) throw new NoSuchElementException(); E ret=buf[head]; buf[head]=null; //optimization: don't retain removed values head=increment(head); return ret; } /** * @modifies this. * @effects Removes and returns the tail of this, or throws * NoSuchElementException if this is empty. */ public E removeLast() throws NoSuchElementException { if (isEmpty()) throw new NoSuchElementException(); tail=decrement(tail); E ret=buf[tail]; buf[tail]=null; //optimization: don't retain removed values return ret; } /** * @modifies this. * @effects Removes and returns the i'th element of this, or * throws IndexOutOfBoundsException if i is not a valid index * of this. In the worst case, this runs in linear time with * respect to size(). */ public E remove(int i) throws IndexOutOfBoundsException { E ret=get(i); //Shift all elements to left. This could be micro-optimized. for (int j=index(i); j!=tail; j=increment(j)) { buf[j]=buf[increment(j)]; } //Adjust tail pointer accordingly. tail=decrement(tail); buf[tail] = null; return ret; } /** * @modifies this. * @effects removes the first occurrence of x in this, * if any, as determined by .equals. Returns true if any * elements were removed. In the worst case, this runs in linear * time with respect to size(). */ public boolean remove(Object x) { for (int i=0; i<getSize(); i++) { if (x.equals(get(i))) { remove(i); return true; } } return false; } /** * @modifies this. * @effects removes all occurrences of x in this, * if any, as determined by .equals. Returns true if any * elements were removed. In the worst case, this runs in linear * time with respect to size(). */ public boolean removeAll(Object x) { boolean ret=false; for (int i=0; i<getSize(); i++) { if (x.equals(get(i))) { remove(i); i--; ret=true; } } return ret; } /** * @modifies this. * @effects removes all elements of this. */ public void clear() { while (!isEmpty()) removeFirst(); } /** * @effects returns an iterator that yields the elements of this, in * order, from head to tail. * @requires this not modified will iterator in use. */ public Iterator<E> iterator() { // will either throw NoSuchElementException // or already be initialized. return new BufferIterator(); } private class BufferIterator extends UnmodifiableIterator<E> { /** The index of the next element to yield. */ int i; /** Defensive programming; detect modifications while * iterator in use. */ int oldHead; int oldTail; BufferIterator() { i=head; oldHead=head; oldTail=tail; } public boolean hasNext() { ensureNoModifications(); return i!=tail; } public E next() throws NoSuchElementException { ensureNoModifications(); if (!hasNext()) throw new NoSuchElementException(); E ret=buf[i]; i=increment(i); return ret; } private void ensureNoModifications() { if (oldHead!=head || oldTail!=tail) throw new ConcurrentModificationException(); } } /** Returns a shallow copy of this, of type Buffer. */ @Override public Buffer<E> clone() throws CloneNotSupportedException { return new Buffer<E>(this); } @Override public String toString() { StringBuilder buf=new StringBuilder(); buf.append("["); boolean isFirst=true; for (Object o : this) { if (!isFirst) buf.append(", "); else isFirst = false; buf.append(o.toString()); } buf.append("]"); return buf.toString(); } }