5 * Wraps around an InputStream, caching the stream in a byte[] as it
6 * is read and permitting multiple simultaneous readers
8 public class CachedInputStream {
10 boolean filling = false; ///< true iff some thread is blocked on us waiting for input
11 boolean eof = false; ///< true iff end of stream has been reached
12 byte[] cache = new byte[1024 * 128];
16 public CachedInputStream(InputStream is) { this.is = is; }
17 public InputStream getInputStream() { return new SubStream(); }
19 public void grow(int newLength) {
20 if (newLength < cache.length) return;
21 byte[] newCache = new byte[cache.length + 2 * (newLength - cache.length)];
22 System.arraycopy(cache, 0, newCache, 0, size);
26 synchronized void fillCache(int howMuch) throws IOException {
27 if (filling) { try { wait(); } catch (InterruptedException e) { }; return; }
30 int ret = is.read(cache, size, howMuch);
31 if (ret == -1) eof = true;
37 private class SubStream extends InputStream {
39 public int available() { return Math.max(0, size - pos); }
40 public long skip(long n) throws IOException { pos += (int)n; return n; } // FEATURE: don't skip past EOF
41 public int read() throws IOException { // FEATURE: be smarter here
42 byte[] b = new byte[1];
43 int ret = read(b, 0, 1);
44 return ret == -1 ? -1 : b[0];
46 public int read(byte[] b, int off, int len) throws IOException {
47 synchronized(CachedInputStream.this) {
48 while (pos >= size && !eof) fillCache(pos + len - size);
49 if (eof && pos == size) return -1;
50 int count = Math.min(size - pos, len);
51 System.arraycopy(cache, pos, b, off, count);