/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.util;
/**
* A simple fixed-length Queue for buffering bytes. This queue is designed to
* never block on the input side. If the queue is full when 'push' is called,
* a byte will be discarded from the head of the queue to make space at the
* tail for the new byte.
*
* @author epr
*/
public class ByteQueue {
// FIXME ... Looking at the way this class is used, I think it may needs an
// atomic drain operation and/or a close operation.
// FIXME ... Make the ByteQueue API and behavior mirror the Queue API and behavior.
/**
* The default queue size.
*/
static final int Q_SIZE = 10;
private final byte[] data;
private final int size;
private int top = 0;
private int bottom = 0;
/**
* Create a queue with the default size.
*/
public ByteQueue() {
this(Q_SIZE);
}
/**
* Create a queue with the supplied size.
* @param size the queue size in bytes; should be >= 1.
*/
public ByteQueue(int size) {
this.data = new byte[size + 1];
this.size = size;
}
/**
* Add a byte at the tail of the queue. This method does not block.
* If the queue is full when 'push' is called, space for the byte is
* made by removing (and discarding) the byte at the head of the queue.
* @param o the byte to be added to the queue.
*/
public synchronized void enQueue(byte o) {
data[bottom] = o;
bottom++;
if (bottom >= size) {
/* overflow */
bottom = 0;
}
/* if wrapped around, advance top so it
points to the old value */
if (top == bottom) {
top++;
}
notifyAll();
}
/**
* Remove a byte from the head of the queue, blocking until one becomes
* available. If the thread calling this method is interrupted while waiting
* for data, the method returns a zero byte.
*
* @return the byte removed, or zero if the method call was interrupted.
*/
public synchronized byte deQueue() {
while (top == bottom) { /* Q is empty */
try {
wait();
} catch (InterruptedException ie) {
// FIXME ... this approach to handling interrupts is broken. The
// exception should be allowed to propagate
return 0;
}
} /* wait for push to fill Q */
byte r = data[top];
top++;
if (top >= size) {
top = 0;
} /* end overflow */
return r;
}
/**
* Remove a byte from the head of the queue, blocking with a timeout if data is
* not immediately available. Unlike {@link #deQueue()}, this method does <b>not</b>
* return zero when interrupted!
*
* @param timeout the maximum time (in milliseconds) to wait for data to become
* available. If zero, the method will wait as long as necessary.
* @return the byte removed from the queue.
* @throws InterruptedException if the method call is interrupted.
* @throws TimeoutException if no data is available within the required time.
*/
public synchronized byte deQueue(long timeout)
throws TimeoutException, InterruptedException {
while (top == bottom) { /* Q is empty */
wait(timeout);
if ((timeout > 0) && (top == bottom)) {
throw new TimeoutException();
}
} /* wait for push to fill Q */
byte r = data[top];
top++;
if (top >= size) {
top = 0;
} /* end overflow */
return r;
}
/**
* Return the byte at the head of the queue without removing it. If data is
* not immediately available, the method will block (with a timeout) until
* data is available. Unlike {@link #deQueue()}, this method does <b>not</b>
* return zero when interrupted!
*
* @param timeout the maximum time (in milliseconds) to wait for data to become
* available. If zero, the method will wait as long as necessary.
* @return the byte removed from the queue.
* @throws InterruptedException if the method call is interrupted.
* @throws TimeoutException if no data is available within the required time.
*/
public synchronized byte peek(long timeout)
throws TimeoutException, InterruptedException {
while (top == bottom) { /* Q is empty */
wait(timeout);
if ((timeout > 0) && (top == bottom)) {
throw new TimeoutException();
}
} /* wait for push to fill Q */
return data[top];
}
/**
* Test if there is no data in the queue.
* @return {@code true} if the queue is empty, {@code false} otherwise.
*/
public synchronized boolean isEmpty() {
// FIXME ... this should be synchronized.
return (top == bottom);
}
/**
* Clears the queue.
*/
public synchronized void clear() {
top = bottom = 0;
}
}