Source code: non_com/media/jai/codec/MemoryCacheSeekableStream.java
1 /*
2 * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * -Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * -Redistribution in binary form must reproduct the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
15 * be used to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * This software is provided "AS IS," without a warranty of any kind. ALL
19 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
20 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
21 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
22 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
23 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
24 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
25 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
26 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
27 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
29 *
30 * You acknowledge that Software is not designed,licensed or intended for use in
31 * the design, construction, operation or maintenance of any nuclear facility.
32 */
33 package non_com.media.jai.codec;
34
35 import non_com.media.jai.codec.JaiI18N;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.util.Vector;
39
40 /**
41 * A subclass of <code>SeekableStream</code> that may be used to wrap a regular
42 * <code>InputStream</code>. Seeking backwards is supported by means of an
43 * in-memory cache. For greater efficiency, <code>FileCacheSeekableStream</code>
44 * should be used in circumstances that allow the creation of a temporary file.
45 * <p>
46 *
47 * The <code>mark()</code> and <code>reset()</code> methods are supported. <p>
48 *
49 * <b> This class is not a committed part of the JAI API. It may be removed or
50 * changed in future releases of JAI.</b>
51 */
52 public final class MemoryCacheSeekableStream extends SeekableStream {
53
54 /**
55 * Log_2 of the sector size.
56 */
57 private final static int SECTOR_SHIFT = 9;
58
59 /**
60 * The sector size.
61 */
62 private final static int SECTOR_SIZE = 1 << SECTOR_SHIFT;
63
64 /**
65 * A mask to determine the offset within a sector.
66 */
67 private final static int SECTOR_MASK = SECTOR_SIZE - 1;
68
69 /**
70 * Number of sectors stored.
71 */
72 int sectors = 0;
73
74 /**
75 * Number of bytes read.
76 */
77 int length = 0;
78
79 /**
80 * True if we've previously reached the end of the source stream
81 */
82 boolean foundEOS = false;
83
84 /**
85 * The source input stream.
86 */
87 private InputStream src;
88
89 /**
90 * Position of first unread byte.
91 */
92 private long pointer = 0;
93
94 /**
95 * A Vector of source sectors.
96 */
97 private Vector data = new Vector();
98
99
100 /**
101 * Constructs a <code>MemoryCacheSeekableStream</code> that takes its source
102 * data from a regular <code>InputStream</code>. Seeking backwards is
103 * supported by means of an in-memory cache.
104 *
105 * @param src Description of Parameter
106 */
107 public MemoryCacheSeekableStream(InputStream src) {
108 this.src = src;
109 }
110
111
112 /**
113 * Returns the current offset in this file.
114 *
115 * @return the offset from the beginning of the file, in bytes, at which
116 * the next read occurs.
117 */
118 public long getFilePointer() {
119 return pointer;
120 }
121
122
123 /**
124 * Returns <code>true</code> since all <code>MemoryCacheSeekableStream</code>
125 * instances support seeking backwards.
126 *
127 * @return Description of the Returned Value
128 */
129 public boolean canSeekBackwards() {
130 return true;
131 }
132
133
134 /**
135 * Sets the file-pointer offset, measured from the beginning of this file, at
136 * which the next read occurs.
137 *
138 * @param pos the offset position, measured in bytes from the
139 * beginning of the file, at which to set the file pointer.
140 * @exception IOException if <code>pos</code> is less than <code>0</code> or
141 * if an I/O error occurs.
142 */
143 public void seek(long pos) throws IOException {
144 if (pos < 0) {
145 throw new IOException(JaiI18N.getString("MemoryCacheSeekableStream0"));
146 }
147 pointer = pos;
148 }
149
150
151 /**
152 * Reads the next byte of data from the input stream. The value byte is
153 * returned as an <code>int</code> in the range <code>0</code> to <code>255</code>
154 * . If no byte is available because the end of the stream has been reached,
155 * the value <code>-1</code> is returned. This method blocks until input data
156 * is available, the end of the stream is detected, or an exception is
157 * thrown.
158 *
159 * @return the next byte of data, or <code>-1</code> if the
160 * end of the stream is reached.
161 * @exception IOException Description of Exception
162 */
163 public int read() throws IOException {
164 long next = pointer + 1;
165 long pos = readUntil(next);
166 if (pos >= next) {
167 byte[] buf =
168 (byte[]) data.elementAt((int) (pointer >> SECTOR_SHIFT));
169 return buf[(int) (pointer++ & SECTOR_MASK)] & 0xff;
170 }
171 else {
172 return -1;
173 }
174 }
175
176
177 /**
178 * Reads up to <code>len</code> bytes of data from the input stream into an
179 * array of bytes. An attempt is made to read as many as <code>len</code>
180 * bytes, but a smaller number may be read, possibly zero. The number of
181 * bytes actually read is returned as an integer. <p>
182 *
183 * This method blocks until input data is available, end of file is detected,
184 * or an exception is thrown. <p>
185 *
186 * If <code>b</code> is <code>null</code>, a <code>NullPointerException</code>
187 * is thrown. <p>
188 *
189 * If <code>off</code> is negative, or <code>len</code> is negative, or
190 * <code>off+len</code> is greater than the length of the array <code>b</code>
191 * , then an <code>IndexOutOfBoundsException</code> is thrown. <p>
192 *
193 * If <code>len</code> is zero, then no bytes are read and <code>0</code> is
194 * returned; otherwise, there is an attempt to read at least one byte. If no
195 * byte is available because the stream is at end of file, the value <code>-1</code>
196 * is returned; otherwise, at least one byte is read and stored into <code>b</code>
197 * . <p>
198 *
199 * The first byte read is stored into element <code>b[off]</code>, the next
200 * one into <code>b[off+1]</code>, and so on. The number of bytes read is, at
201 * most, equal to <code>len</code>. Let <i>k</i> be the number of bytes
202 * actually read; these bytes will be stored in elements <code>b[off]</code>
203 * through <code>b[off+</code><i>k</i> <code>-1]</code>, leaving elements
204 * <code>b[off+</code><i>k</i> <code>]</code> through <code>b[off+len-1]</code>
205 * unaffected. <p>
206 *
207 * In every case, elements <code>b[0]</code> through <code>b[off]</code> and
208 * elements <code>b[off+len]</code> through <code>b[b.length-1]</code> are
209 * unaffected. <p>
210 *
211 * If the first byte cannot be read for any reason other than end of file,
212 * then an <code>IOException</code> is thrown. In particular, an <code>IOException</code>
213 * is thrown if the input stream has been closed.
214 *
215 * @param b the buffer into which the data is read.
216 * @param off the start offset in array <code>b</code> at which
217 * the data is written.
218 * @param len the maximum number of bytes to read.
219 * @return the total number of bytes read into the buffer, or
220 * <code>-1</code> if there is no more data because the end of the stream
221 * has been reached.
222 * @exception IOException Description of Exception
223 */
224 public int read(byte[] b, int off, int len) throws IOException {
225 if (b == null) {
226 throw new NullPointerException();
227 }
228 if ((off < 0) || (len < 0) || (off + len > b.length)) {
229 throw new IndexOutOfBoundsException();
230 }
231 if (len == 0) {
232 return 0;
233 }
234
235 long pos = readUntil(pointer + len);
236 // End-of-stream
237 if (pos <= pointer) {
238 return -1;
239 }
240
241 byte[] buf = (byte[]) data.elementAt((int) (pointer >> SECTOR_SHIFT));
242 int nbytes = Math.min(len, SECTOR_SIZE - (int) (pointer & SECTOR_MASK));
243 System.arraycopy(buf, (int) (pointer & SECTOR_MASK),
244 b, off, nbytes);
245 pointer += nbytes;
246 return nbytes;
247 }
248
249
250 /**
251 * Ensures that at least <code>pos</code> bytes are cached, or the end of the
252 * source is reached. The return value is equal to the smaller of <code>pos</code>
253 * and the length of the source stream.
254 *
255 * @param pos Description of Parameter
256 * @return Description of the Returned Value
257 * @exception IOException Description of Exception
258 */
259 private long readUntil(long pos) throws IOException {
260 // We've already got enough data cached
261 if (pos < length) {
262 return pos;
263 }
264 // pos >= length but length isn't getting any bigger, so return it
265 if (foundEOS) {
266 return length;
267 }
268
269 int sector = (int) (pos >> SECTOR_SHIFT);
270
271 // First unread sector
272 int startSector = length >> SECTOR_SHIFT;
273
274 // Read sectors until the desired sector
275 for (int i = startSector; i <= sector; i++) {
276 byte[] buf = new byte[SECTOR_SIZE];
277 data.addElement(buf);
278
279 // Read up to SECTOR_SIZE bytes
280 int len = SECTOR_SIZE;
281 int off = 0;
282 while (len > 0) {
283 int nbytes = src.read(buf, off, len);
284 // Found the end-of-stream
285 if (nbytes == -1) {
286 foundEOS = true;
287 return length;
288 }
289 off += nbytes;
290 len -= nbytes;
291
292 // Record new data length
293 length += nbytes;
294 }
295 }
296
297 return length;
298 }
299 }