001: package it.unimi.dsi.fastutil.io;
002:
003: /*
004: * fastutil: Fast & compact type-specific collections for Java
005: *
006: * Copyright (C) 2003-2008 Paolo Boldi and Sebastiano Vigna
007: *
008: * This library is free software; you can redistribute it and/or modify it
009: * under the terms of the GNU Lesser General Public License as published by the Free
010: * Software Foundation; either version 2.1 of the License, or (at your option)
011: * any later version.
012: *
013: * This library is distributed in the hope that it will be useful, but
014: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
015: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
016: * for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: *
022: */
023:
024: import java.io.EOFException;
025: import java.io.IOException;
026: import java.io.InputStream;
027:
028: /** Simple, fast and repositionable byte array input stream that multiplexes its content among several arrays.
029: *
030: * This class is significantly slower than {@link FastByteArrayInputStream},
031: * but it can hold 256 PiB of data. The relevant constructor is {@link #FastMultiByteArrayInputStream(InputStream, long)},
032: * which fetches a stream and loads it into a sequence of byte arrays.
033: *
034: * @author Sebastiano Vigna
035: * @author Paolo Boldi
036: s */
037:
038: public class FastMultiByteArrayInputStream extends java.io.InputStream
039: implements RepositionableStream {
040:
041: /** The number of bits of an array slice index. */
042: public final static int SLICE_BITS = 30;
043:
044: /** The maximum length of an array slice. */
045: public final static int SLICE_SIZE = 1 << SLICE_BITS;
046:
047: /** The mask to retrieve a slice offset. */
048: public final static int SLICE_MASK = SLICE_SIZE - 1;
049:
050: /** The array of arrays backing the input stream. */
051: public byte[][] array;
052:
053: /** The number of valid bytes in {@link #array}. */
054: public long length;
055:
056: /** The current position. */
057: private long position;
058:
059: /** The current mark, or -1 if no mark exists. */
060: private long mark;
061:
062: /** Creates a new multi-array input stream loading it from an input stream.
063: *
064: * @param is the input stream that will fill the array.
065: * @param size the number of bytes to be read from <code>is</code>.
066: */
067:
068: public FastMultiByteArrayInputStream(final InputStream is, long size)
069: throws IOException {
070: length = size;
071: array = new byte[(int) ((size + SLICE_SIZE - 1) / SLICE_SIZE)][];
072:
073: for (int i = 0; i < array.length; i++) {
074: array[i] = new byte[size >= SLICE_SIZE ? SLICE_SIZE
075: : (int) size];
076: if (is.read(array[i]) != array[i].length)
077: throw new EOFException();
078: size -= array[i].length;
079: }
080: }
081:
082: /** Creates a new multi-array input stream sharing the backing arrays of another multi-array input stream.
083: *
084: * @param is the multi-array input stream to replicate.
085: */
086: public FastMultiByteArrayInputStream(
087: final FastMultiByteArrayInputStream is) {
088: this .array = is.array;
089: this .length = is.length;
090: }
091:
092: /** Creates a new multi-array input stream using a given array.
093: *
094: * @param array the backing array.
095: */
096: public FastMultiByteArrayInputStream(final byte[] array) {
097: this .array = new byte[1][];
098: this .array[0] = array;
099: this .length = array.length;
100: }
101:
102: public boolean markSupported() {
103: return true;
104: }
105:
106: public void reset() {
107: position = mark;
108: }
109:
110: /** Closing a fast byte array input stream has no effect. */
111: public void close() {
112: }
113:
114: public void mark(final int dummy) {
115: mark = position;
116: }
117:
118: /** Returns the number of bytes that can be read (or skipped over) from this input stream without blocking.
119: *
120: * <P>Note that this number may be smaller than the number of bytes actually
121: * available from the stream if this number exceeds {@link Integer#MAX_VALUE}.
122: *
123: * @return the minimum among the number of available bytes and {@link Integer#MAX_VALUE}.
124: */
125:
126: public int available() {
127: if (length - position > Integer.MAX_VALUE)
128: return Integer.MAX_VALUE;
129: return (int) (length - position);
130: }
131:
132: public long skip(long n) {
133: if (n <= length - position) {
134: position += n;
135:
136: return n;
137: }
138: n = length - position;
139: position = length;
140: return n;
141: }
142:
143: public int read() {
144: if (length == position)
145: return -1;
146: return array[(int) (position >> SLICE_BITS)][(int) (position++ & SLICE_MASK)] & 0xFF;
147: }
148:
149: public int read(final byte[] b, int offset, final int length) {
150: if (this .length == this .position)
151: return length == 0 ? 0 : -1;
152: int res, n = (int) Math
153: .min(length, this .length - this .position), m = n;
154:
155: do {
156: res = Math.min(n,
157: array[(int) (position >> SLICE_BITS)].length
158: - (int) (position & SLICE_MASK));
159: System.arraycopy(array[(int) (position >> SLICE_BITS)],
160: (int) (position & SLICE_MASK), b, offset, res);
161:
162: n -= res;
163: offset += res;
164: position += res;
165:
166: } while (n > 0);
167:
168: return m;
169: }
170:
171: public long position() {
172: return position;
173: }
174:
175: public void position(final long newPosition) {
176: position = Math.min(newPosition, length);
177: }
178: }
|