1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
9 /** Wraps around an InputStream, caching the stream in a byte[] as it
10 * is read and permitting multiple simultaneous readers.
12 * @author adam@ibex.org, crawshaw@ibex.org
14 public class CachedInputStream {
16 private final InputStream is;
18 private byte[] cache = new byte[128];
19 private File cacheFile;
20 private FileOutputStream out;
22 /** True iff end of stream has been reached. */
23 private boolean eof = false;
25 /** Number of bytes currently cached. */
26 private long size = 0;
28 public CachedInputStream(InputStream is) {
30 this.cacheFile = null;
33 public CachedInputStream(InputStream is, File cacheFile) throws IOException {
35 this.cacheFile = cacheFile;
36 this.out = new FileOutputStream(cacheFile);
39 public InputStream getInputStream() throws IOException { return new SubStream(); }
41 private synchronized int fill(int need) throws IOException {
45 if (size + need > cache.length) {
46 byte[] newCache = new byte[Math.max(cache.length * 2, (int)(size + need))];
47 System.arraycopy(cache, 0, newCache, 0, (int)size);
50 ret = is.read(cache, (int)size, need);
52 ret = is.read(cache, 0, Math.min(cache.length, need));
53 if (ret > 0) out.write(cache, 0, ret);
56 if (ret == -1) { eof = true; if (out != null) { out.close(); out = null; } }
62 private final class SubStream extends InputStream {
63 private final InputStream fis;
66 private SubStream() throws IOException {
67 fis = cacheFile != null ? new FileInputStream(cacheFile) : null;
70 public int available() { synchronized(CachedInputStream.this) { return (int)(size - pos); } }
71 public long skip(long n) throws IOException {
72 synchronized(CachedInputStream.this) {
73 int need = (int)(pos + n - size);
74 while (need > 0 && !eof) need -= fill(need);
76 if (fis != null) { long fisn = n; while(fisn > 0) fisn -= fis.skip(fisn); }
81 public long length() { synchronized(CachedInputStream.this) {
82 return eof ? size : is instanceof SubStream ? ((SubStream)is).length() : 0;
85 private final byte[] single = new byte[1];
86 public int read() throws IOException {
87 int ret = read(single, 0, 1);
88 return ret == -1 ? -1 : single[0]&0xff;
91 public int read(byte[] b, int off, int len) throws IOException {
92 synchronized(CachedInputStream.this) {
93 int i = (int)(pos - size) + len; while (i > 0 && !eof) i -= (int)fill(i);
99 int j = fis.read(b, off, fislen);
100 if (j == -1) throw new IOException("less data retrieved from disk than written to it");
101 off += j; fislen -= j;
103 } else System.arraycopy(cache, (int)pos, b, off, len);