/*
* This file is a part of Alchemy OS project.
* Copyright (C) 2011-2013, Sergey Basalaev <sbasalaev@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package alchemy.io;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import javax.microedition.io.StreamConnection;
/**
* Pipe is an one-direction data channel.
* Data sent to the output can then be read from the input.
* <p>
* Warning: using input and output from the same
* thread can result in a deadlock.
*
* @author Sergey Basalaev
*/
public final class Pipe implements StreamConnection {
private static final int BUFFER_SIZE = 1024;
/** Circular pipe buffer. If null the pipe is closed. */
private byte[] buf = new byte[BUFFER_SIZE];
/** Index of the next byte to be written. */
private int next = 0;
/** Number of bytes in this buffer. */
private int count = 0;
private DataInputStream in;
private DataOutputStream out;
public void close() { }
public InputStream openInputStream() throws IOException {
return openDataInputStream();
}
public DataInputStream openDataInputStream() throws IOException {
if (in == null) return in = new DataInputStream(new PipeInputStream());
else throw new IOException("Stream already opened.");
}
public OutputStream openOutputStream() throws IOException {
return openDataOutputStream();
}
public DataOutputStream openDataOutputStream() throws IOException {
if (out == null) return out = new DataOutputStream(new PipeOutputStream());
else throw new IOException("Stream already opened.");
}
private class PipeOutputStream extends OutputStream {
public PipeOutputStream() { }
public void write(int b) throws IOException {
synchronized (Pipe.this) {
if (buf == null) throw new IOException("Pipe is closed");
while (count == BUFFER_SIZE) {
Pipe.this.notify();
try {
Pipe.this.wait(1000);
} catch (InterruptedException ie) {
throw new InterruptedIOException();
}
}
buf[next] = (byte)b;
next = (next+1) % BUFFER_SIZE;
count++;
}
}
public void write(byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
if (buf == null) throw new IOException("Pipe is closed");
while (len > 0) {
// calculate available continuous empty region
int copyoff = next;
int copylen;
if (next >= count) {
copylen = BUFFER_SIZE - next;
} else {
copylen = BUFFER_SIZE - count;
}
if (copylen > len) copylen = len;
// if cannot copy bytes, wait this round
if (copylen == 0) {
Pipe.this.notify();
try {
Pipe.this.wait(1000);
} catch (InterruptedException ie) {
throw new InterruptedIOException();
}
} else {
System.arraycopy(b, off, buf, copyoff, copylen);
next = (next + copylen) % BUFFER_SIZE;
count += copylen;
off += copylen;
len -= copylen;
}
}
}
}
public void flush() throws IOException {
synchronized (Pipe.this) {
Pipe.this.notify();
}
}
public void close() {
synchronized (Pipe.this) {
buf = null;
}
}
}
private class PipeInputStream extends InputStream {
public PipeInputStream() { }
public int read() throws IOException {
synchronized (Pipe.this) {
if (buf == null) return -1;
while (count == 0) {
Pipe.this.notify();
try {
Pipe.this.wait(1000);
} catch (InterruptedException ie) {
throw new InterruptedIOException();
}
}
int index = next - count;
if (index < 0) index += BUFFER_SIZE;
count--;
return buf[index];
}
}
public int read(byte[] b, int off, int len) throws IOException {
synchronized (Pipe.this) {
if (buf == null) return -1;
int readtotal = 0;
while (buf != null && len > 0) {
// calculate available continuous filled region
int copyoff;
int copylen;
if (next >= count) {
copyoff = next - count;
copylen = count;
} else {
copyoff = next - count + BUFFER_SIZE;
copylen = BUFFER_SIZE - copyoff;
}
if (copylen > len) copylen = len;
// if cannot copy bytes, wait this round
if (copylen == 0) {
Pipe.this.notify();
try {
Pipe.this.wait(1000);
} catch (InterruptedException ie) {
throw new InterruptedIOException();
}
} else {
System.arraycopy(buf, copyoff, b, off, copylen);
count -= copylen;
off += copylen;
len -= copylen;
readtotal += len;
}
}
return readtotal;
}
}
public void close() {
synchronized (Pipe.this) {
buf = null;
}
}
public int available() throws IOException {
return count;
}
}
}