package com.limegroup.gnutella.util;
import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException;
import com.limegroup.gnutella.Assert;
/**
* A very simple fixed-size double-ended queue, i.e., a circular buffer.
* The fixed size is intentional, not the result of laziness; use this
* data structure when you want to use a fix amount of resources.
* This is not thread-safe.
* For a minimal amount of efficiency, the internal buffer is only
* allocated on the first insertion or retrieval, allowing lots of
* Buffers to be created that may not be used.
*/
public final class IntBuffer implements Cloneable {
/**
* 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
*/
private final int size;
private int buf[];
private int head;
private int tail;
/**
* @requires size>=1
* @effects creates a new, empty buffer that can hold
* size elements.
*/
public IntBuffer(int size) {
Assert.that(size>=1);
//one element of buf unused
this.size = size+1;
// lazily initialized to preserver memory.
//buf = new int[size+1];
head = 0;
tail = 0;
}
/** "Copy constructor": constructs a new shallow copy of other. */
public IntBuffer(IntBuffer other) {
this.size=other.size;
this.head=other.head;
this.tail=other.tail;
if(other.buf != null) {
this.buf=new int[other.buf.length];
System.arraycopy(other.buf, 0,
this.buf, 0,
other.buf.length);
}
}
private void initialize() {
if(buf == null)
buf = new int[size + 1];
}
/*
public int[] toArray() {
int[] newBuf = new int[size];
int index = tail-head;
System.arraycopy(buf, head,
newBuf, 0,
index);
System.arraycopy(buf, 0,
newBuf, index,
size-index);
}
*/
/** Returns true iff this is empty. */
public boolean isEmpty() {
return head==tail;
}
/** Returns true iff 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 + ", size: " + getSize());
return (i+head) % size;
}
/** If i<0 or i>=getSize(), throws IndexOutOfBoundsException.
* Else returns this[i] */
public int 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, int value) throws IndexOutOfBoundsException {
initialize();
buf[index(i)] = value;
}
/**
* Same as addFirst(x).
*/
public int add(int 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 int addFirst(int x) {
initialize();
int ret = -1;
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 int addLast(int x) {
initialize();
int ret = -1;
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(int x) {
IntBufferIterator iterator = iterator();
while (iterator.hasNext())
if (iterator.nextInt() == x)
return true;
return false;
}
/**
* Returns the head of this, or throws NoSuchElementException if
* this is empty.
*/
public int first() throws NoSuchElementException {
if (isEmpty())
throw new NoSuchElementException();
return buf[head];
}
/**
* Returns the tail of this, or throws NoSuchElementException if
* this is empty.
*/
public int 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 int removeFirst() throws NoSuchElementException {
if (isEmpty())
throw new NoSuchElementException();
int ret=buf[head];
buf[head]=-1; //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 int removeLast() throws NoSuchElementException {
if (isEmpty())
throw new NoSuchElementException();
tail=decrement(tail);
int ret=buf[tail];
buf[tail]=-1; //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 int remove(int i) throws IndexOutOfBoundsException {
int 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);
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 removeValue(int x) {
for (int i=0; i<getSize(); i++) {
if (x == 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(int x) {
boolean ret=false;
for (int i=0; i<getSize(); i++) {
if (x == get(i)) {
remove(i);
i--;
ret=true;
}
}
return ret;
}
/**
* @modifies this
* @effects removes all elements of this.
*/
public void clear() {
head=0;
tail=0;
}
/**
* @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 IntBufferIterator iterator() {
return new IntBufferIterator();
}
/**
* Specialized <tt>Iterator</tt> for <tt>IntBuffer</tt> that iterates
* over an array of ints.
*/
private class IntBufferIterator extends UnmodifiableIterator {
/** The index of the next element to yield. */
int index;
/** Defensive programming; detect modifications while
* iterator in use. */
int oldHead;
int oldTail;
IntBufferIterator() {
index=head;
oldHead=head;
oldTail=tail;
}
public boolean hasNext() {
ensureNoModifications();
return index!=tail;
}
public Object next() throws NoSuchElementException {
throw new UnsupportedOperationException();
}
public int nextInt() throws NoSuchElementException {
ensureNoModifications();
if (!hasNext())
throw new NoSuchElementException();
int ret=buf[index];
index=increment(index);
return ret;
}
private void ensureNoModifications() {
if (oldHead!=head || oldTail!=tail)
throw new ConcurrentModificationException();
}
}
/** Returns a shallow copy of this, of type <tt>IntBuffer</tt> */
public Object clone() {
return new IntBuffer(this);
}
// overrides Object.toString to return more information
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
boolean isFirst=true;
for (IntBufferIterator iter=iterator(); iter.hasNext(); ) {
if (! isFirst)
sb.append(", ");
else
isFirst=false;
sb.append(iter.nextInt());
}
sb.append("]");
return sb.toString();
}
}