001: /*
002: * $RCSfile: FileSeekableStream.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.2 $
009: * $Date: 2005/11/15 00:39:10 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.codec;
013:
014: import java.io.File;
015: import java.io.IOException;
016: import java.io.RandomAccessFile;
017:
018: /**
019: * A subclass of <code>SeekableStream</code> that takes its input
020: * from a <code>File</code> or <code>RandomAccessFile</code>.
021: * Backwards seeking is supported. The <code>mark()</code> and
022: * <code>reset()</code> methods are supported.
023: *
024: * <p><b> This class is not a committed part of the JAI API. It may
025: * be removed or changed in future releases of JAI.</b>
026: */
027: public class FileSeekableStream extends SeekableStream {
028:
029: private RandomAccessFile file;
030: private long markPos = -1;
031:
032: // Base 2 logarithm of the cache page size
033: private static final int PAGE_SHIFT = 9;
034:
035: // The page size, derived from PAGE_SHIFT
036: private static final int PAGE_SIZE = 1 << PAGE_SHIFT;
037:
038: // Binary mask to find the offset of a pointer within a cache page
039: private static final int PAGE_MASK = PAGE_SIZE - 1;
040:
041: // Number of pages to cache
042: private static final int NUM_PAGES = 32;
043:
044: // Reads longer than this bypass the cache
045: private static final int READ_CACHE_LIMIT = PAGE_SIZE;
046:
047: // The page cache
048: private byte[][] pageBuf = new byte[PAGE_SIZE][NUM_PAGES];
049:
050: // The index of the file page held in a given cache entry,
051: // -1 = invalid.
052: private int[] currentPage = new int[NUM_PAGES];
053:
054: private long length = 0L;
055:
056: private long pointer = 0L;
057:
058: /**
059: * Constructs a <code>FileSeekableStream</code> from a
060: * <code>RandomAccessFile</code>.
061: */
062: public FileSeekableStream(RandomAccessFile file) throws IOException {
063: this .file = file;
064: file.seek(0L);
065: this .length = file.length();
066:
067: // Allocate the cache pages and mark them as invalid
068: for (int i = 0; i < NUM_PAGES; i++) {
069: pageBuf[i] = new byte[PAGE_SIZE];
070: currentPage[i] = -1;
071: }
072: }
073:
074: /**
075: * Constructs a <code>FileSeekableStream</code> from a
076: * <code>File</code>.
077: */
078: public FileSeekableStream(File file) throws IOException {
079: this (new RandomAccessFile(file, "r"));
080: }
081:
082: /**
083: * Constructs a <code>FileSeekableStream</code> from a
084: * <code>String</code> path name.
085: */
086: public FileSeekableStream(String name) throws IOException {
087: this (new RandomAccessFile(name, "r"));
088: }
089:
090: /** Returns true since seeking backwards is supported. */
091: public final boolean canSeekBackwards() {
092: return true;
093: }
094:
095: /**
096: * Returns the current offset in this stream.
097: *
098: * @return the offset from the beginning of the stream, in bytes,
099: * at which the next read occurs.
100: * @exception IOException if an I/O error occurs.
101: */
102: public final long getFilePointer() throws IOException {
103: return pointer;
104: }
105:
106: public final void seek(long pos) throws IOException {
107: if (pos < 0) {
108: throw new IOException(JaiI18N
109: .getString("FileSeekableStream0"));
110: }
111: pointer = pos;
112: }
113:
114: public final int skip(int n) throws IOException {
115: pointer += n;
116: return n;
117: }
118:
119: private byte[] readPage(long pointer) throws IOException {
120: int page = (int) (pointer >> PAGE_SHIFT);
121:
122: for (int i = 0; i < NUM_PAGES; i++) {
123: if (currentPage[i] == page) {
124: return pageBuf[i];
125: }
126: }
127:
128: // Use random replacement for now
129: int index = (int) (Math.random() * NUM_PAGES);
130: currentPage[index] = page;
131:
132: long pos = ((long) page) << PAGE_SHIFT;
133: long remaining = length - pos;
134: int len = PAGE_SIZE < remaining ? PAGE_SIZE : (int) remaining;
135: file.seek(pos);
136: file.readFully(pageBuf[index], 0, len);
137:
138: return pageBuf[index];
139: }
140:
141: /** Forwards the request to the real <code>File</code>. */
142: public final int read() throws IOException {
143: if (pointer >= length) {
144: return -1;
145: }
146:
147: byte[] buf = readPage(pointer);
148: return buf[(int) (pointer++ & PAGE_MASK)] & 0xff;
149: }
150:
151: /** Forwards the request to the real <code>File</code>. */
152: public final int read(byte[] b, int off, int len)
153: throws IOException {
154: if (b == null) {
155: throw new NullPointerException();
156: }
157: if ((off < 0) || (len < 0) || (off + len > b.length)) {
158: throw new IndexOutOfBoundsException();
159: }
160: if (len == 0) {
161: return 0;
162: }
163:
164: len = (int) Math.min((long) len, length - pointer);
165: if (len <= 0) {
166: return -1;
167: }
168:
169: // If the read is large, don't bother to cache it.
170: if (len > READ_CACHE_LIMIT) {
171: file.seek(pointer);
172: int nbytes = file.read(b, off, len);
173: pointer += nbytes;
174: return nbytes;
175: } else {
176: byte[] buf = readPage(pointer);
177:
178: // Compute length to end of page
179: int remaining = PAGE_SIZE - (int) (pointer & PAGE_MASK);
180: int newLen = len < remaining ? len : remaining;
181: System.arraycopy(buf, (int) (pointer & PAGE_MASK), b, off,
182: newLen);
183:
184: pointer += newLen;
185: return newLen;
186: }
187: }
188:
189: /** Forwards the request to the real <code>File</code>. */
190: public final void close() throws IOException {
191: file.close();
192: }
193:
194: /**
195: * Marks the current file position for later return using
196: * the <code>reset()</code> method.
197: */
198: public synchronized final void mark(int readLimit) {
199: markPos = pointer;
200: }
201:
202: /**
203: * Returns the file position to its position at the time of
204: * the immediately previous call to the <code>mark()</code>
205: * method.
206: */
207: public synchronized final void reset() throws IOException {
208: if (markPos != -1) {
209: pointer = markPos;
210: }
211: }
212:
213: /** Returns <code>true</code> since marking is supported. */
214: public boolean markSupported() {
215: return true;
216: }
217: }
|