update CachedInputStream
[org.ibex.util.git] / src / org / ibex / util / CachedInputStream.java
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.
4
5 package org.ibex.util;
6
7 import java.io.*;
8
9 /** Wraps around an InputStream, caching the stream in a byte[] as it
10  *  is read and permitting multiple simultaneous readers.
11  *
12  *  @author adam@ibex.org, crawshaw@ibex.org
13  */
14 public class CachedInputStream {
15
16     private final InputStream is;
17
18     private byte[] cache = new byte[128];
19     private File cacheFile;
20     private FileOutputStream out;
21
22     /** True iff end of stream has been reached. */
23     private boolean eof = false;
24
25     /** Number of bytes currently cached. */
26     private long size = 0;
27
28     public CachedInputStream(InputStream is) {
29         this.is = is;
30         this.cacheFile = null;
31         this.out = null;
32     }
33     public CachedInputStream(InputStream is, File cacheFile) throws IOException {
34         this.is = is;
35         this.cacheFile = cacheFile;
36         this.out = new FileOutputStream(cacheFile);
37     }
38
39     public InputStream getInputStream() throws IOException { return new SubStream(); }
40
41     private synchronized int fill(int need) throws IOException {
42         int ret;
43
44         if (out == null) {
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);
48                 cache = newCache;
49             }
50             ret = is.read(cache, (int)size, need);
51         } else {
52             ret = is.read(cache, 0, Math.min(cache.length, need));
53             if (ret > 0) out.write(cache, 0, ret);
54         }
55
56         if (ret == -1) { eof = true; if (out != null) { out.close(); out = null; } }
57         else size += ret;
58
59         return ret;
60     }
61
62     private final class SubStream extends InputStream {
63         private final InputStream fis;
64         private long pos = 0;
65         
66         private SubStream() throws IOException {
67             fis = cacheFile != null ? new FileInputStream(cacheFile) : null;
68         }
69
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);
75                 n -= need;
76                 if (fis != null) { long fisn = n; while(fisn > 0) fisn -= fis.skip(fisn); }
77                 pos += n;
78                 return n;
79             }
80         }
81         public long length() { synchronized(CachedInputStream.this) {
82                 return eof ? size : is instanceof SubStream ? ((SubStream)is).length() : 0;
83         } }
84
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;
89         }
90
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);
94                 if (i > 0) len -= i;
95
96                 if (fis != null) {
97                     int fislen = len;
98                     while (fislen > 0) {
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;
102                     }
103                 } else System.arraycopy(cache, (int)pos, b, off, len);
104
105                 pos += len;
106                 return len;
107             }
108         }
109     }
110 }