blob: 6440bd25fbce3b9881f04a6d565a7f1b15fc6a95 [file] [log] [blame]
package com.google.eclipse.elt.emulator.util;
import org.eclipse.core.runtime.Assert;
/**
* A byte bounded buffer used to synchronize the input and the output stream.
* <p>
* Adapted from {@code BoundedBufferWithStateTracking} http://gee.cs.oswego.edu/dl/cpj/allcode.java
* http://gee.cs.oswego.edu/dl/cpj/
* <p>
* BoundedBufferWithStateTracking is part of the examples for the book Concurrent Programming in Java: Design
* Principles and Patterns by Doug Lea (ISBN 0-201-31009-0). Second edition published by Addison-Wesley, November
* 1999. The code is Copyright(c) Douglas Lea 1996, 1999 and released to the public domain and may be used for any
* purposes whatsoever.
* <p>
* For some reasons a solution based on PipedOutputStream/PipedIntputStream does work *very* slowly:
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4404700
* <p>
*/
public class BoundedByteBuffer {
private final byte[] buffer; // the elements
private int putPosition; // circular indices
private int takePosition;
private int usedSlots; // the count
private boolean closed;
public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
// Make sure we don't deadlock on too small capacity.
if (capacity <= 0) {
throw new IllegalArgumentException("Capacity should be greater than zero");
}
buffer = new byte[capacity];
}
/**
* Returns the bytes available for {@link #read()}.
*
* @return the bytes available for reading.
*/
public int size() {
return usedSlots;
}
/**
* Writes a single byte to the buffer. Blocks if the buffer is full.
*
* @param b byte to write to the buffer.
* @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
*/
public void write(byte b) throws InterruptedException {
while (usedSlots == buffer.length) {
// Wait until not full.
wait();
}
buffer[putPosition] = b;
putPosition = (putPosition + 1) % buffer.length; // cyclically increment
if (usedSlots++ == 0) {
notifyAll();
}
}
public int getFreeSlots() {
return buffer.length - usedSlots;
}
public void write(byte[] b, int off, int len) throws InterruptedException {
Assert.isTrue(len <= getFreeSlots());
while (usedSlots == buffer.length) {
// Wait until not full.
wait();
}
int n = Math.min(len, buffer.length - putPosition);
System.arraycopy(b, off, buffer, putPosition, n);
if (putPosition + len > buffer.length) {
System.arraycopy(b, off + n, buffer, 0, len - n);
}
putPosition = (putPosition + len) % buffer.length; // cyclically increment
boolean wasEmpty = usedSlots == 0;
usedSlots += len;
if (wasEmpty) {
notifyAll();
}
}
/**
* Read a single byte. Blocks until a byte is available.
*
* @return a byte from the buffer.
* @throws InterruptedException when the thread is interrupted while waiting for the buffer to become ready.
*/
public byte read() throws InterruptedException {
while (usedSlots == 0) {
if (closed) {
return -1;
}
// Wait until not empty.
wait();
}
byte b = buffer[takePosition];
takePosition = (takePosition + 1) % buffer.length;
if (usedSlots-- == buffer.length) {
notifyAll();
}
return b;
}
public int read(byte[] b, int off, int len) throws InterruptedException {
Assert.isTrue(len <= size());
while (usedSlots == 0) {
if (closed) {
return 0;
}
// Wait until not empty.
wait();
}
int n = Math.min(len, buffer.length - takePosition);
System.arraycopy(buffer, takePosition, b, off, n);
if (takePosition + len > n) {
System.arraycopy(buffer, 0, b, off + n, len - n);
}
takePosition = (takePosition + len) % buffer.length;
boolean wasFull = usedSlots == buffer.length;
usedSlots -= len;
if (wasFull) {
notifyAll();
}
return len;
}
public void close() {
closed = true;
notifyAll();
}
public boolean isClosed() {
return closed;
}
}