diff --git a/src/zutil/io/InputStreamCloser.java b/src/zutil/io/InputStreamCloser.java index b220e47..7b42037 100755 --- a/src/zutil/io/InputStreamCloser.java +++ b/src/zutil/io/InputStreamCloser.java @@ -25,38 +25,28 @@ package zutil.io; import java.io.Closeable; +import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; /** - * A simple Class that mirrors a InputStream but - * also has an additional Closeable objects that - * will be closed when the this object is closed. + * A simple Class that mirrors a InputStream but also has additional Closeable objects that + * will be closed at the same time as the {@link #close()} method is called. * * @author Ziver */ -public class InputStreamCloser extends InputStream{ - private Closeable[] c; - private InputStream in; +public class InputStreamCloser extends FilterInputStream { + private Closeable[] additionalClosable; - public InputStreamCloser(InputStream in, Closeable... c) { - this.c = c; - this.in = in; + public InputStreamCloser(InputStream in, Closeable... additionalClosable) { + super(in); + this.additionalClosable = additionalClosable; } public void close() throws IOException { - in.close(); - for (Closeable stream : c) + super.close(); + + for (Closeable stream : additionalClosable) stream.close(); } - - // Mirror functions - public int read() throws IOException { return in.read(); } - public int read(byte b[]) throws IOException { return in.read(b); } - public int read(byte b[], int off, int len) throws IOException { return in.read(b, off, len); } - public long skip(long n) throws IOException { return in.skip(n); } - public int available() throws IOException { return in.available(); } - public synchronized void mark(int readlimit) { in.mark(readlimit); } - public synchronized void reset() throws IOException { in.reset(); } - public boolean markSupported() { return in.markSupported(); } } \ No newline at end of file diff --git a/src/zutil/io/PositionalInputStream.java b/src/zutil/io/PositionalInputStream.java new file mode 100644 index 0000000..204b89f --- /dev/null +++ b/src/zutil/io/PositionalInputStream.java @@ -0,0 +1,82 @@ +package zutil.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A InputStream wrapper that counts the current position in the data stream which equals the amount of data read. + */ +public class PositionalInputStream extends FilterInputStream { + + private long pos = 0; + private long mark = 0; + + + /** + * @param in the underlying input stream. + */ + protected PositionalInputStream(InputStream in) { + super(in); + } + + + /** + * @return the current position in the data stream + */ + public synchronized long getPosition() { + return pos; + } + + + @Override + public int read() throws IOException { + int b = super.read(); + + synchronized(this) { + if (b >= 0) pos += 1; + } + return b; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int n = super.read(b, off, len); + + synchronized(this) { + if (n > 0) pos += n; + } + return n; + } + + @Override + public long skip(long skip) throws IOException { + long n = super.skip(skip); + + synchronized(this) { + if (n > 0) pos += n; + } + return n; + } + + @Override + public void mark(int readlimit) { + super.mark(readlimit); + + synchronized(this) { + mark = pos; + } + } + + @Override + public synchronized void reset() throws IOException { + super.reset(); + + synchronized(this) { + // Only update the position if mark is supported, + // as reset succeeds even if mark is not supported. + if (markSupported()) + pos = mark; + } + } +} diff --git a/src/zutil/io/StringInputStream.java b/src/zutil/io/StringInputStream.java index dd09930..19ba225 100755 --- a/src/zutil/io/StringInputStream.java +++ b/src/zutil/io/StringInputStream.java @@ -25,30 +25,30 @@ package zutil.io; import java.io.InputStream; -import java.nio.charset.StandardCharsets; /** - * This class saves all the input data in to an StringBuffer + * This InputStream uses a string as the data source. * * @author Ziver * */ -public class StringInputStream extends InputStream{ - // The buffer - protected StringBuilder buffer; +public class StringInputStream extends InputStream { + + private StringBuilder buffer = new StringBuilder(); + private int pos; + private int mark; + /** - * Creates an new instance of this class + * Creates a new instance of this class */ - public StringInputStream() { - clear(); - } + public StringInputStream() { } public StringInputStream(String data) { - clear(); add(data); } + /** * Returns an estimate of the number of bytes * that can be read (or skipped over) from this @@ -56,44 +56,37 @@ public class StringInputStream extends InputStream{ * invocation of a method for this input stream. */ public int available() { - return buffer.length(); + return buffer.length() - pos; } /** * Reads the next byte of data from the input stream. */ - public int read() { - if (buffer.length() == 0) + public synchronized int read() { + if (available() <= 0) return -1; - int ret = buffer.charAt(0); - buffer.deleteCharAt(0); + int ret = buffer.charAt(pos); + pos++; return ret; } /** - * Reads some number of bytes from the input stream - * and stores them into the buffer array b. + * Reads up to len bytes of data from the input stream into an array of bytes. */ - public int read(byte[] b) { - return read(b, 0, b.length); - } - - /** - * Reads up to len bytes of data from the input stream - * into an array of bytes. - */ - public int read(byte[] b, int off, int len) { - if (buffer.length() == 0) + public synchronized int read(byte[] b, int off, int len) { + if (available() <= 0) return -1; - if (buffer.length() < len) { - len = buffer.length(); + if (available() < len) + len = available(); + + for (int i=0; i { str.append('.'); if ((c & 0b1100_0000) == 0b1100_0000) { - // This a pointer + // This a offset pointer to the String data int offset = (c & 0b0011_1111) << 8; offset |= in.read() & 0b1111_1111; str.append(offset); + break; // PTR is always the last part of the FQDN } else { - // Normal String + // Normal String data for (int i = 0; i < c; ++i) { str.append((char) in.read()); } diff --git a/test/zutil/io/PositionalInputStreamTest.java b/test/zutil/io/PositionalInputStreamTest.java new file mode 100644 index 0000000..798202f --- /dev/null +++ b/test/zutil/io/PositionalInputStreamTest.java @@ -0,0 +1,75 @@ +package zutil.io; + +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + + +public class PositionalInputStreamTest { + + @Test + public void read() throws IOException { + PositionalInputStream in = new PositionalInputStream(new StringInputStream("hello")); + + assertEquals(0, in.getPosition()); + assertEquals('h', (char) in.read()); + assertEquals(1, in.getPosition()); + assertEquals('e', (char) in.read()); + assertEquals(2, in.getPosition()); + assertEquals('l', (char) in.read()); + assertEquals(3, in.getPosition()); + assertEquals('l', (char) in.read()); + assertEquals(4, in.getPosition()); + assertEquals('o', (char) in.read()); + assertEquals(5, in.getPosition()); + assertEquals(-1, in.read()); + assertEquals(5, in.getPosition()); + assertEquals(-1, in.read()); + assertEquals(5, in.getPosition()); + } + + @Test + public void readArray() throws IOException { + PositionalInputStream in = new PositionalInputStream(new StringInputStream("hello world")); + + byte[] buffer = new byte[20]; + assertEquals(5, in.read(buffer, 0, 5)); + assertEquals("hello", new String(buffer, 0, 5)); + assertEquals(5, in.getPosition()); + + assertEquals(' ', (char) in.read()); + assertEquals(6, in.getPosition()); + + assertEquals(5, in.read(buffer)); + assertEquals("world", new String(buffer, 0, 5)); + assertEquals(11, in.getPosition()); + + assertEquals(-1, in.read()); + assertEquals(11, in.getPosition()); + } + + @Test + public void skip() throws IOException { + PositionalInputStream in = new PositionalInputStream(new StringInputStream("hello world")); + + assertEquals(5, in.skip(5)); + assertEquals(5, in.getPosition()); + assertEquals(6, in.skip(20)); + assertEquals(11, in.getPosition()); + } + + @Test + public void mark() throws IOException { + PositionalInputStream in = new PositionalInputStream(new StringInputStream("hello world")); + + in.mark(20); + assertEquals(6, in.skip(6)); + assertEquals(6, in.getPosition()); + in.reset(); + assertEquals(0, in.getPosition()); + assertEquals('h', (char) in.read()); + assertEquals(1, in.getPosition()); + } +} \ No newline at end of file diff --git a/test/zutil/io/StringInputStreamTest.java b/test/zutil/io/StringInputStreamTest.java new file mode 100644 index 0000000..42766e3 --- /dev/null +++ b/test/zutil/io/StringInputStreamTest.java @@ -0,0 +1,58 @@ +package zutil.io; + +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + + +public class StringInputStreamTest { + + @Test + public void read() throws IOException { + StringInputStream in = new StringInputStream("hello"); + + assertEquals('h', (char) in.read()); + assertEquals('e', (char) in.read()); + assertEquals('l', (char) in.read()); + assertEquals('l', (char) in.read()); + assertEquals('o', (char) in.read()); + assertEquals(-1, in.read()); + assertEquals(-1, in.read()); + } + + @Test + public void readArray() throws IOException { + StringInputStream in = new StringInputStream("hello world"); + + byte[] buffer = new byte[20]; + assertEquals(5, in.read(buffer, 0, 5)); + assertEquals("hello", new String(buffer, 0, 5)); + + assertEquals(' ', (char) in.read()); + + assertEquals(5, in.read(buffer)); + assertEquals("world", new String(buffer, 0, 5)); + + assertEquals(-1, in.read()); + } + + @Test + public void skip() { + StringInputStream in = new StringInputStream("hello world"); + + assertEquals(5, in.skip(5)); + assertEquals(6, in.skip(20)); + } + + @Test + public void mark() throws IOException { + StringInputStream in = new StringInputStream("hello world"); + + in.mark(20); + assertEquals(6, in.skip(6)); + in.reset(); + assertEquals('h', (char) in.read()); + } +} \ No newline at end of file