1 // Copyright (C) 2003 Adam Megacz <adam@xwt.org> all rights reserved.
3 // You may modify, copy, and redistribute this code under the terms of
4 // the GNU Library Public License version 2.1, with the exception of
5 // the portion of clause 6a after the semicolon (aka the "obnoxious
11 // FEATURE: don't use a byte[] if we have a diskCache file
13 * Wraps around an InputStream, caching the stream in a byte[] as it
14 * is read and permitting multiple simultaneous readers
16 public class CachedInputStream {
18 boolean filling = false; ///< true iff some thread is blocked on us waiting for input
19 boolean eof = false; ///< true iff end of stream has been reached
20 byte[] cache = new byte[1024 * 128];
25 public CachedInputStream(InputStream is) { this(is, null); }
26 public CachedInputStream(InputStream is, File diskCache) {
28 this.diskCache = diskCache;
30 public InputStream getInputStream() throws IOException {
31 System.out.println("diskCache == " + diskCache);
32 System.out.println("diskCache.exists() == " + diskCache.exists());
33 if (diskCache != null && diskCache.exists()) return new FileInputStream(diskCache);
34 return new SubStream();
37 public void grow(int newLength) {
38 if (newLength < cache.length) return;
39 byte[] newCache = new byte[cache.length + 2 * (newLength - cache.length)];
40 System.arraycopy(cache, 0, newCache, 0, size);
44 synchronized void fillCache(int howMuch) throws IOException {
45 if (filling) { try { wait(); } catch (InterruptedException e) { }; return; }
48 int ret = is.read(cache, size, howMuch);
51 // FIXME: probably a race here
52 if (diskCache != null && !diskCache.exists())
54 File cacheFile = new File(diskCache + ".tmp");
55 FileOutputStream cacheFileStream = new FileOutputStream(cacheFile);
56 cacheFileStream.write(cache, 0, size);
57 cacheFileStream.close();
58 cacheFile.renameTo(diskCache);
59 } catch (IOException e) {
60 Log.log(this, "exception thrown while writing disk cache");
69 private class SubStream extends InputStream implements KnownLength {
71 public int available() { return Math.max(0, size - pos); }
72 public long skip(long n) throws IOException { pos += (int)n; return n; } // FEATURE: don't skip past EOF
73 public int getLength() { return eof ? size : is instanceof KnownLength ? ((KnownLength)is).getLength() : 0; }
74 public int read() throws IOException { // FEATURE: be smarter here
75 byte[] b = new byte[1];
76 int ret = read(b, 0, 1);
77 return ret == -1 ? -1 : b[0]&0xff;
79 public int read(byte[] b, int off, int len) throws IOException {
80 synchronized(CachedInputStream.this) {
81 while (pos >= size && !eof) fillCache(pos + len - size);
82 if (eof && pos == size) return -1;
83 int count = Math.min(size - pos, len);
84 System.arraycopy(cache, pos, b, off, count);