Is this LimitedInputStream correct? - java

I've written a class called LimitedInputStream. It wraps around an existing input stream to limit the number of bytes read from it to a specified length. It's meant as an alternative to:
byte[] data = readAll(length);
InputStream ins = new ByteArrayInputStream(data);
Which requires the extra buffer.
This is the class:
public static class LimitedInputStream extends InputStream {
private final InputStream ins;
private int left;
private int mark = -1;
public LimitedInputStream(InputStream ins, int limit) {
this.ins = ins;
left = limit;
}
public void skipRest() throws IOException {
ByteStreams.skipFully(ins, left);
left = 0;
}
#Override
public int read() throws IOException {
if (left == 0) return -1;
final int read = ins.read();
if (read > 0) left--;
return read;
}
#Override
public int read(byte[] b, int off, int len) throws IOException {
if (left == 0) return -1;
if (len > left) len = left;
final int read = ins.read(b, off, len);
if (read > 0) left -= read;
return read;
}
#Override
public int available() throws IOException {
final int a = ins.available();
return a > left ? left : a;
}
#Override
public void mark(int readlimit) {
ins.mark(readlimit);
mark = left;
}
#Override
public void reset() throws IOException {
if (!ins.markSupported()) throw new IOException("Mark not supported");
if (mark == -1) throw new IOException("Mark not set");
ins.reset();
left = mark;
}
#Override
public long skip(long n) throws IOException {
if (n > left) n = left;
long skipped = ins.skip(n);
left -= skipped;
return skipped;
}
}
Use case:
Object readObj() throws IOException {
int len = readInt();
final LimitedInputStream lis = new LimitedInputStream(this, len);
try {
return deserialize(new CompactInputStream(lis));
} finally {
lis.skipRest();
}
}
for (something) {
Object obj;
try {
obj = readObj();
} catch (Exception e) {
obj = null;
}
list.add(obj);
}
Could you code review my class for any serious bugs, e.g. possible mistakes in updating left?

Guava includes a LimitInputStream, so you may want to just use that.

Related

Console input no longer being echo'd after changing stdout

In my program I have changed stdout using System.setOut(); but after that, typing into the console no longer echoes the input back.
I tried the following, with no avail:
originalStdIn = System.in;
stdIn = new InputStream() {
#Override
public int available() throws IOException {
return originalStdIn.available();
}
#Override
public int read() throws IOException {
int chr = originalStdIn.read();
stdOut.write(chr);
return chr;
}
#Override
public int read(byte[] buffer) throws IOException {
return this.read(buffer, 0, buffer.length);
}
#Override
public int read(byte[] buffer, int off, int len) throws IOException {
int readResult = originalStdIn.read(buffer, off, len);
stdOut.write(buffer, off, readResult);
return readResult;
}
};
System.setIn(stdIn);
This is how I read input:
private void handleThread_func() {
Logging.didUpdateInWindow = true;
System.out.print(this.consoleInputPrefix);
while (!this.isClosed) {
try {
if (System.in.available() > 0) {
String input = "";
while (System.in.available() > 0) {
input += this.inputReader.readLine();
}
this.handleInput(input);
Logging.didUpdateInWindow = true;
System.out.print(this.consoleInputPrefix);
}
Thread.sleep(1);
} catch (Exception ex) {
if (ex instanceof InterruptedException) break;
Logging.logSevere("Unable to handle console input: " +
Utils.getExceptionStackTraceAsStr(ex));
}
}
}
I tried changing input += this.inputReader.readLine(); to input += (char)this.inputReader.read() but while it did echo, the input was read in the incorrect order

Java OutputStream reading lines of strings

I use an API (for more info, see below) which accepts an OutputStream to capture data. Instead, I want to provide a Consumer of Strings which consumes the data line after line. Hence I have to write an OutputStream implementation which wraps such a Consumer. This is the easiest I can think of:
import java.io.OutputStream;
import java.util.Objects;
import java.util.function.Consumer;
public class OutputStreamConsumer extends OutputStream {
private final Consumer<String> consumer;
private final StringBuilder sb = new StringBuilder();
public OutputStreamConsumer(Consumer<String> consumer) {
this.consumer = Objects.requireNonNull(consumer);
}
#Override
public void write(int b) {
char c = (char) b;
if (c == '\r') {
return;
}
if (c == '\n') {
consume();
return;
}
this.sb.append(c);
}
#Override
public void close() {
if (sb.length() != 0) {
consume();
}
}
private void consume() {
this.consumer.accept(this.sb.toString());
this.sb.delete(0, Integer.MAX_VALUE);
}
}
However, this is probably not enough for production code in terms of encoding and performance. I think that the necessary logic is already contained in InputStreamReader and BufferedReader but I cannot use these classes in this scenario.
What is the best way to write this kind of OutputStream? What jdk classes can I use to avoid writing a bunch of low level code handling encoding, end of lines etc.
Concrete use-case
In a Gradle plugin project, I start an external process using Gradle's project API: ExecSpec. There I can set OutputStreams using the methods setStandardOutput and setErrorOutput in order to capture the output of the process.
Since there are no answers so far, I will post my "best" solution as the time of writing. Feel free to post better solutions.
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Consumer;
public class LineReadingOutputStream extends OutputStream {
private static final byte CR = '\r';
private static final byte LF = '\n';
private final Consumer<String> consumer;
private final StringBuilder stringBuilder = new StringBuilder();
private boolean lastCR = false;
private LineReadingOutputStream(final Consumer<String> consumer) {
this.consumer = Objects.requireNonNull(consumer);
}
#Override
public void write(final int b) throws IOException {
write(new byte[]{(byte) b});
}
#Override
public void write(final byte[] b, int start, final int len) {
if (b == null) {
throw new NullPointerException();
}
if (len < 0) {
throw new IllegalArgumentException();
}
final int end = start + len;
if ((start < 0) || (start > b.length) || (end < 0) || (end > b.length)) {
throw new IndexOutOfBoundsException();
}
if (this.lastCR && start < end && b[start] == LF) {
start++;
this.lastCR = false;
} else if (start < end) {
this.lastCR = b[end - 1] == CR;
}
int base = start;
for (int i = start; i < end; i++) {
if (b[i] == LF || b[i] == CR) {
final String chunk = asString(b, base, i);
this.stringBuilder.append(chunk);
consume();
}
if (b[i] == LF) {
base = i + 1;
} else if (b[i] == CR) {
if (i < end - 1 && b[i + 1] == LF) {
base = i + 2;
i++;
} else {
base = i + 1;
}
}
}
final String chunk = asString(b, base, end);
this.stringBuilder.append(chunk);
}
#Override
public void close() {
if (this.stringBuilder.length() != 0) {
consume();
}
}
private static String asString(final byte[] b, final int start, final int end) {
if (start > end) {
throw new IllegalArgumentException();
}
if (start == end) {
return "";
}
final byte[] copy = Arrays.copyOfRange(b, start, end);
return new String(copy, StandardCharsets.UTF_8);
}
private void consume() {
this.consumer.accept(this.stringBuilder.toString());
this.stringBuilder.delete(0, Integer.MAX_VALUE);
}
}

Run JTape Library on Java (java library path)

I am trying to use the JTape Library to read some data from a DDS4 magnetic tape.
I want to use eclipse to run my code under Linux 12.04 LTS
The problem is that I cannot let eclipse reference the TapeLinux.c Library in any way.
PROBLEM:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no TapeLinux in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)
at java.lang.Runtime.loadLibrary0(Runtime.java:849)
at java.lang.System.loadLibrary(System.java:1088)
at BasicTapeDevice.<clinit>(BasicTapeDevice.java:169)
at TestEOD.main(TestEOD.java:12)
This are my classes:
/* TestEOD.java */
import java.io.*;
public class TestEOD {
public static void main(String[] args) throws IOException {
/* if (args.length != 1) {
System.err.println("Usage: java TestEOD <path to device>");
System.exit(1);
}*/
BasicTapeDevice d = new BasicTapeDevice("/dev/nst0");
System.out.print("Rewinding...");
System.out.flush();
d.rewind();
System.out.println("done!");
System.out.print("Spacing to end of data...");
System.out.flush();
d.spaceEOD();
System.out.println("done!");
}
}
/* BasicTapeDevice.java */
import java.io.*;
public class BasicTapeDevice {
private FileDescriptor fd;
private InputStream in;
private OutputStream out;
private boolean eof;
private boolean eom;
private boolean ignoreEOM;
public BasicTapeDevice(String pathName) throws IOException {
fd = new FileDescriptor();
tapeOpen(pathName);
in = new TapeInputStream();
out = new TapeOutputStream();
eof = false;
eom = false;
ignoreEOM = false;
}
public synchronized void close() throws IOException {
if (fd != null) {
try {
if (fd.valid()) {
tapeClose();
}
} finally {
fd = null;
}
}
}
public InputStream getInputStream() throws IOException {
ensureOpen();
return in;
}
public OutputStream getOutputStream() throws IOException {
ensureOpen();
return out;
}
public int getBlockSize() throws IOException {
ensureOpen();
return tapeGetBlockSize();
}
public void setBlockSize(int bs) throws IOException {
ensureOpen();
tapeSetBlockSize(bs);
}
public void rewind() throws IOException {
ensureOpen();
tapeRewind();
}
public void spaceEOD() throws IOException {
ensureOpen();
tapeSpaceEOD();
}
public void clearEOF() throws IOException {
ensureOpen();
if (eof) {
eof = false;
/* assume that the file mark has already been skipped */
} else {
throw new IOException("not at end of file");
}
}
public void clearEOM() throws IOException {
ensureOpen();
if (eom) {
ignoreEOM = true;
} else {
throw new IOException("not at logical end of media");
}
}
class TapeInputStream extends InputStream {
private byte[] temp = new byte[1];
public int read() throws IOException {
int n = read(temp, 0, 1);
if (n <= 0) {
return -1;
}
return temp[0] & 0xff;
}
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off+len > b.length) {
throw new IndexOutOfBoundsException();
}
if (len == 0) {
return 0;
}
if (eof) {
return -1;
}
ensureOpen();
int n = tapeRead(b, off, len);
if (n <= 0) {
return -1;
}
return n;
}
public long skip(long numbytes) throws IOException {
return 0;
}
public void close() throws IOException {
BasicTapeDevice.this.close();
}
}
class TapeOutputStream extends OutputStream {
private byte[] temp = new byte[1];
public void write(int b) throws IOException {
temp[0] = (byte) b;
write(temp, 0, 1);
}
public void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off+len > b.length) {
throw new IndexOutOfBoundsException();
}
if (eom && !ignoreEOM) {
throw new LogicalEOMException("logical end-of-media");
}
int n = tapeWrite(b, off, len);
while (n < len) {
n += tapeWrite(b, off + n, len - n);
}
}
public void close() throws IOException {
BasicTapeDevice.this.close();
}
}
protected void finalize() {
try {
close();
} catch (IOException ex) {
}
}
private void ensureOpen() throws IOException {
if (fd == null || !fd.valid()) {
throw new IOException("tape device is not open");
}
}
private static native void initFields();
private native void tapeOpen(String pathName) throws IOException;
private native void tapeClose() throws IOException;
private native int tapeRead(byte[] b, int off, int len) throws IOException;
private native int tapeWrite(byte[] b, int off, int len) throws IOException;
private native int tapeGetBlockSize() throws IOException;
private native void tapeSetBlockSize(int bs) throws IOException;
private native void tapeRewind() throws IOException;
private native void tapeSpaceEOD() throws IOException;
/* load the JNI library specific for this platform */
static {
StringBuffer buf = new StringBuffer("Tape");
String osName = System.getProperty("os.name");
if (osName.equals("Windows NT") || osName.equals("Windows 2000")) {
buf.append("WinNT");
} else {
buf.append(osName);
}
System.loadLibrary(buf.toString());
initFields();
}
}
WHAT I HAVE TRIED
I have looked around that what I need is to include the path of the folder which contains the file TapeLinux.c
I have tried all these answers and nothing change
Can you please help me to figure out how I can use JNI in this situation and what I should configure to run my code
Thanks
Since you are on Linux, the message ...main java.lang.UnsatisfiedLinkError: no TapeLinux in java... likely means the library named libTapeLinux.so could not be found.
Seems you are searching for a solution by trying to locate TapeLinux.c and you should be searching for libTapeLinux.so and once you find it make sure libTapeLinux.so is on the load path.

How do you merge two input streams in Java?

Having two InputStreams in Java, is there a way to merge them so you end with one InputStream that gives you the output of both streams? How?
As commented, it's not clear what you mean by merge.
Taking available input "randomly" from either is complicated by InputStream.available not necessarily giving you a useful answer and blocking behaviour of streams. You would need two threads to be reading from the streams and then passing back data through, say, java.io.Piped(In|Out)putStream (although those classes have issues). Alternatively for some types of stream it may be possible to use a different interface, for instance java.nio non-blocking channels.
If you want the full contents of the first input stream followed by the second: new java.io.SequenceInputStream(s1, s2).
java.io.SequenceInputStream might be what you need. It accepts an enumeration of streams, and will output the contents of the first stream, then the second, and so on until all streams are empty.
You can write a custom InputStream implementation that does this. Example:
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
public class CatInputStream extends InputStream {
private final Deque<InputStream> streams;
public CatInputStream(InputStream... streams) {
this.streams = new LinkedList<InputStream>();
Collections.addAll(this.streams, streams);
}
private void nextStream() throws IOException {
streams.removeFirst().close();
}
#Override
public int read() throws IOException {
int result = -1;
while (!streams.isEmpty()
&& (result = streams.getFirst().read()) == -1) {
nextStream();
}
return result;
}
#Override
public int read(byte b[], int off, int len) throws IOException {
int result = -1;
while (!streams.isEmpty()
&& (result = streams.getFirst().read(b, off, len)) == -1) {
nextStream();
}
return result;
}
#Override
public long skip(long n) throws IOException {
long skipped = 0L;
while (skipped < n && !streams.isEmpty()) {
int thisSkip = streams.getFirst().skip(n - skipped);
if (thisSkip > 0)
skipped += thisSkip;
else
nextStream();
}
return skipped;
}
#Override
public int available() throws IOException {
return streams.isEmpty() ? 0 : streams.getFirst().available();
}
#Override
public void close() throws IOException {
while (!streams.isEmpty())
nextStream();
}
}
This code isn't tested, so your mileage may vary.
Not that I can think of. You would probably have to read the contents of the two stream into a byte[] and then create a ByteArrayInputStream from that.
Here is an MVar implementation specific to byte arrays (make sure to add your own package definition). From here, it is trivial to write an input stream on merged streams. I can post that too if requested.
import java.nio.ByteBuffer;
public final class MVar {
private static enum State {
EMPTY, ONE, MANY
}
private final Object lock;
private State state;
private byte b;
private ByteBuffer bytes;
private int length;
public MVar() {
lock = new Object();
state = State.EMPTY;
}
public final void put(byte b) {
synchronized (lock) {
while (state != State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
this.b = b;
state = State.ONE;
lock.notifyAll();
}
}
public final void put(byte[] bytes, int offset, int length) {
if (length == 0) {
return;
}
synchronized (lock) {
while (state != State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
this.bytes = ByteBuffer.allocateDirect(length);
this.bytes.put(bytes, offset, length);
this.bytes.position(0);
this.length = length;
state = State.MANY;
lock.notifyAll();
}
}
public final byte take() {
synchronized (lock) {
while (state == State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
switch (state) {
case ONE: {
state = State.EMPTY;
byte b = this.b;
lock.notifyAll();
return b;
}
case MANY: {
byte b = bytes.get();
state = --length <= 0 ? State.EMPTY : State.MANY;
lock.notifyAll();
return b;
}
default:
throw new AssertionError();
}
}
}
public final int take(byte[] bytes, int offset, int length) {
if (length == 0) {
return 0;
}
synchronized (lock) {
while (state == State.EMPTY) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
switch (state) {
case ONE:
bytes[offset] = b;
state = State.EMPTY;
lock.notifyAll();
return 1;
case MANY:
if (this.length > length) {
this.bytes.get(bytes, offset, length);
this.length = this.length - length;
synchronized (lock) {
lock.notifyAll();
}
return length;
}
this.bytes.get(bytes, offset, this.length);
this.bytes = null;
state = State.EMPTY;
length = this.length;
lock.notifyAll();
return length;
default:
throw new AssertionError();
}
}
}
}

What is the best way to create a java.util.stream.Stream from an InputStream?

As I understand, an InputStream is a stream of bytes. I am interested in converting an InputStream object to a stream of bytes. Basically, an implementation of the following method.
public Stream<byte[]> toStream(final InputStream is, final int bufferSize);
What would be the best way to get this done? The buffer size is the number of bytes read from the InputStream at a time.
You need to write your own Spliterator, something like this:
public final class ChunkingInputStreamSpliterator implements Spliterator<byte[]> {
private final InputStream is;
private final int bufferSize;
public ChunkingInputStreamSpliterator(InputStream is, int bufferSize) {
this.is = is;
this.bufferSize = bufferSize;
}
#Override
public boolean tryAdvance(Consumer<? super byte[]> action) {
byte[] bytes;
try {
bytes = this.is.readNBytes(this.bufferSize);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (bytes.length == 0)
return false;
action.accept(bytes);
return true;
}
#Override
public Spliterator<byte[]> trySplit() {
return null; // cannot split an InputStream
}
#Override
public long estimateSize() {
return Long.MAX_VALUE; // unknown
}
#Override
public int characteristics() {
return Spliterator.ORDERED | Spliterator.NONNULL;
}
}
Then implement your method like this:
public static Stream<byte[]> toStream(InputStream is, int bufferSize) {
return StreamSupport.stream(new ChunkingInputStreamSpliterator(is, bufferSize), false);
}
If you don't have Java 11, so you don't have the very convenient readNBytes method, then do that part yourself like this:
public boolean tryAdvance(Consumer<? super byte[]> action) {
byte[] bytes = new byte[this.bufferSize];
int len = 0;
try {
for (int read; len < bytes.length; len += read)
if ((read = this.is.read(bytes, len, bytes.length - len)) <= 0)
break;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (len == 0)
return false;
if (len < bytes.length)
bytes = Arrays.copyOfRange(bytes, 0, len);
action.accept(bytes);
return true;
}

Categories