001: /**
002: * com.mckoi.util.PagedInputStream 18 Feb 2003
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.util;
024:
025: import java.io.InputStream;
026: import java.io.IOException;
027:
028: /**
029: * An implementation of InputStream that reads data from an underlying
030: * representation in fixed sized pages. This object maintains a single buffer
031: * that is the size of a page. This implementation supports 'skip' and
032: * buffered access to the data.
033: * <p>
034: * The only method that needs to be implemented is the 'readPageContent'
035: * method.
036: *
037: * @author Tobias Downer
038: */
039:
040: public abstract class PagedInputStream extends InputStream {
041:
042: /**
043: * The size of the buffer page.
044: */
045: private final int BUFFER_SIZE;
046:
047: /**
048: * The current position in the stream.
049: */
050: private long position;
051:
052: /**
053: * The total size of the underlying dataset.
054: */
055: private long size;
056:
057: /**
058: * The start buffer position.
059: */
060: private long buffer_pos;
061:
062: /**
063: * The buffer.
064: */
065: private final byte[] buf;
066:
067: /**
068: * Last marked position.
069: */
070: private long mark_position;
071:
072: /**
073: * Constructs the input stream.
074: *
075: * @param page_size the size of the pages when accessing the underlying
076: * stream.
077: * @param total_size the total size of the underlying data set.
078: */
079: public PagedInputStream(int page_size, long total_size) {
080: this .BUFFER_SIZE = page_size;
081: this .position = 0;
082: this .size = total_size;
083: this .mark_position = 0;
084: this .buf = new byte[BUFFER_SIZE];
085: buffer_pos = -1;
086: }
087:
088: /**
089: * Reads the page at the given offset in the underlying data into the given
090: * byte[] array. The 'pos' variable given is guarenteed to be a multiple of
091: * buffer_size. For example, the first access will be to pos = 0, the
092: * second access to pos = BUFFER_SIZE, the third access to pos =
093: * BUFFER_SIZE * 2, etc. 'length' will always be either BUFFER_SIZE or a
094: * value smaller than BUFFER_SIZE if the page containing the end of the
095: * stream is read.
096: */
097: protected abstract void readPageContent(byte[] buf, long pos,
098: int length) throws IOException;
099:
100: /**
101: * Fills the buffer with data from the blob at the given position. A buffer
102: * may be partially filled if the end is reached.
103: */
104: private void fillBuffer(long pos) throws IOException {
105: final long read_pos = (pos / BUFFER_SIZE) * BUFFER_SIZE;
106: int to_read = (int) Math.min((long) BUFFER_SIZE,
107: (size - read_pos));
108: if (to_read > 0) {
109: readPageContent(buf, read_pos, to_read);
110: buffer_pos = read_pos;
111: }
112: }
113:
114: // ---------- Implemented from InputStream ----------
115:
116: public int read() throws IOException {
117: if (position >= size) {
118: return -1;
119: }
120:
121: if (buffer_pos == -1) {
122: fillBuffer(position);
123: }
124:
125: int p = (int) (position - buffer_pos);
126: int v = ((int) buf[p]) & 0x0FF;
127:
128: ++position;
129: // Fill the next part of the buffer?
130: if (p + 1 >= BUFFER_SIZE) {
131: fillBuffer(buffer_pos + BUFFER_SIZE);
132: }
133:
134: return v;
135: }
136:
137: public int read(byte[] read_buf, int off, int len)
138: throws IOException {
139: if (len <= 0) {
140: return 0;
141: }
142:
143: if (buffer_pos == -1) {
144: fillBuffer(position);
145: }
146:
147: int p = (int) (position - buffer_pos);
148: long buffer_end = Math.min(buffer_pos + BUFFER_SIZE, size);
149: int to_read = (int) Math.min((long) len, buffer_end - position);
150: if (to_read <= 0) {
151: return -1;
152: }
153: int has_read = 0;
154: while (to_read > 0) {
155: System.arraycopy(buf, p, read_buf, off, to_read);
156: has_read += to_read;
157: p += to_read;
158: off += to_read;
159: len -= to_read;
160: position += to_read;
161: if (p >= BUFFER_SIZE) {
162: fillBuffer(buffer_pos + BUFFER_SIZE);
163: p -= BUFFER_SIZE;
164: }
165: buffer_end = Math.min(buffer_pos + BUFFER_SIZE, size);
166: to_read = (int) Math.min((long) len, buffer_end - position);
167: }
168: return has_read;
169: }
170:
171: public long skip(long n) throws IOException {
172: long act_skip = Math.min(n, size - position);
173:
174: if (n < 0) {
175: throw new IOException("Negative skip");
176: }
177: position += act_skip;
178: if (buffer_pos == -1 || (position - buffer_pos) > BUFFER_SIZE) {
179: fillBuffer((position / BUFFER_SIZE) * BUFFER_SIZE);
180: }
181:
182: return act_skip;
183: }
184:
185: public int available() throws IOException {
186: return (int) Math.min((long) Integer.MAX_VALUE,
187: (size - position));
188: }
189:
190: public void close() throws IOException {
191: }
192:
193: public void mark(int limit) {
194: mark_position = position;
195: }
196:
197: public void reset() {
198: position = mark_position;
199: long fill_pos = (position / BUFFER_SIZE) * BUFFER_SIZE;
200: if (fill_pos != buffer_pos) {
201: try {
202: fillBuffer(fill_pos);
203: } catch (IOException e) {
204: throw new Error(e.getMessage());
205: }
206: }
207: }
208:
209: public boolean markSupported() {
210: return true;
211: }
212:
213: }
|