| 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; |
| } |
| } |