001: /*
002: * $RCSfile: SegmentedImageInputStream.java,v $
003: *
004: *
005: * Copyright (c) 2005 Sun Microsystems, Inc. All Rights Reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * - Redistribution of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * - Redistribution in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * Neither the name of Sun Microsystems, Inc. or the names of
020: * contributors may be used to endorse or promote products derived
021: * from this software without specific prior written permission.
022: *
023: * This software is provided "AS IS," without a warranty of any
024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027: * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
028: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
029: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
031: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035: * POSSIBILITY OF SUCH DAMAGES.
036: *
037: * You acknowledge that this software is not designed or intended for
038: * use in the design, construction, operation or maintenance of any
039: * nuclear facility.
040: *
041: * $Revision: 1.2 $
042: * $Date: 2007/08/28 01:12:56 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageio.stream;
046:
047: import java.io.IOException;
048: import javax.imageio.stream.ImageInputStream;
049: import javax.imageio.stream.ImageInputStreamImpl;
050:
051: /**
052: * An implementation of the <code>StreamSegmentMapper</code> interface
053: * that requires an explicit list of the starting locations and
054: * lengths of the source segments.
055: */
056: class StreamSegmentMapperImpl implements StreamSegmentMapper {
057:
058: private long[] segmentPositions;
059:
060: private int[] segmentLengths;
061:
062: public StreamSegmentMapperImpl(long[] segmentPositions,
063: int[] segmentLengths) {
064: this .segmentPositions = (long[]) segmentPositions.clone();
065: this .segmentLengths = (int[]) segmentLengths.clone();
066: }
067:
068: public StreamSegment getStreamSegment(long position, int length) {
069: int numSegments = segmentLengths.length;
070: for (int i = 0; i < numSegments; i++) {
071: int len = segmentLengths[i];
072: if (position < len) {
073: return new StreamSegment(
074: segmentPositions[i] + position, Math.min(len
075: - (int) position, length));
076: }
077: position -= len;
078: }
079:
080: return null;
081: }
082:
083: public void getStreamSegment(long position, int length,
084: StreamSegment seg) {
085: int numSegments = segmentLengths.length;
086: for (int i = 0; i < numSegments; i++) {
087: int len = segmentLengths[i];
088: if (position < len) {
089: seg.setStartPos(segmentPositions[i] + position);
090: seg.setSegmentLength(Math.min(len - (int) position,
091: length));
092: return;
093: }
094: position -= len;
095: }
096:
097: seg.setStartPos(-1);
098: seg.setSegmentLength(-1);
099: return;
100: }
101:
102: long length() {
103: int numSegments = segmentLengths.length;
104: long len = 0L;
105:
106: for (int i = 0; i < numSegments; i++) {
107: len += segmentLengths[i];
108: }
109:
110: return len;
111: }
112: }
113:
114: /**
115: * An implementation of the <code>StreamSegmentMapper</code> interface
116: * for segments of equal length.
117: */
118: class SectorStreamSegmentMapper implements StreamSegmentMapper {
119:
120: long[] segmentPositions;
121: int segmentLength;
122: int totalLength;
123: int lastSegmentLength;
124:
125: public SectorStreamSegmentMapper(long[] segmentPositions,
126: int segmentLength, int totalLength) {
127: this .segmentPositions = (long[]) segmentPositions.clone();
128: this .segmentLength = segmentLength;
129: this .totalLength = totalLength;
130: this .lastSegmentLength = totalLength
131: - (segmentPositions.length - 1) * segmentLength;
132: }
133:
134: public StreamSegment getStreamSegment(long position, int length) {
135: int index = (int) (position / segmentLength);
136:
137: // Compute segment length
138: int len = (index == segmentPositions.length - 1) ? lastSegmentLength
139: : segmentLength;
140:
141: // Compute position within the segment
142: position -= index * segmentLength;
143:
144: // Compute maximum legal length
145: len -= position;
146: if (len > length) {
147: len = length;
148: }
149: return new StreamSegment(segmentPositions[index] + position,
150: len);
151: }
152:
153: public void getStreamSegment(long position, int length,
154: StreamSegment seg) {
155: int index = (int) (position / segmentLength);
156:
157: // Compute segment length
158: int len = (index == segmentPositions.length - 1) ? lastSegmentLength
159: : segmentLength;
160:
161: // Compute position within the segment
162: position -= index * segmentLength;
163:
164: // Compute maximum legal length
165: len -= position;
166: if (len > length) {
167: len = length;
168: }
169:
170: seg.setStartPos(segmentPositions[index] + position);
171: seg.setSegmentLength(len);
172: }
173:
174: long length() {
175: return (long) totalLength;
176: }
177: }
178:
179: /**
180: * A <code>SegmentedImageInputStream</code> provides a view of a
181: * subset of another <code>ImageInputStream</code> consiting of a series
182: * of segments with given starting positions in the source stream and
183: * lengths. The resulting stream behaves like an ordinary
184: * <code>ImageInputStream</code>.
185: *
186: * <p> For example, given a <code>ImageInputStream</code> containing
187: * data in a format consisting of a number of sub-streams stored in
188: * non-contiguous sectors indexed by a directory, it is possible to
189: * construct a set of <code>SegmentedImageInputStream</code>s, one for
190: * each sub-stream, that each provide a view of the sectors comprising
191: * a particular stream by providing the positions and lengths of the
192: * stream's sectors as indicated by the directory. The complex
193: * multi-stream structure of the original stream may be ignored by
194: * users of the <code>SegmentedImageInputStream</code>, who see a
195: * separate <code>ImageInputStream</code> for each sub-stream and do not
196: * need to understand the directory structure at all.
197: *
198: * <p> For further efficiency, a directory structure such as in the
199: * example described above need not be fully parsed in order to build
200: * a <code>SegmentedImageInputStream</code>. Instead, the
201: * <code>StreamSegmentMapper</code> interface allows the association
202: * between a desired region of the output and an input segment to be
203: * provided dynamically. This mapping might be computed by reading
204: * from a directory in piecemeal fashion in order to avoid consuming
205: * memory resources.
206: */
207: public class SegmentedImageInputStream extends ImageInputStreamImpl {
208:
209: private ImageInputStream stream;
210: private StreamSegmentMapper mapper;
211:
212: /**
213: * Constructs a <code>SegmentedImageInputStream</code>
214: * given a <code>ImageInputStream</code> as input
215: * and an instance of <code>StreamSegmentMapper</code>.
216: *
217: * @param stream A source <code>ImageInputStream</code>
218: * @param mapper An instance of the <code>StreamSegmentMapper</code>
219: * interface.
220: */
221: public SegmentedImageInputStream(ImageInputStream stream,
222: StreamSegmentMapper mapper) {
223: super ();
224:
225: this .stream = stream;
226: this .mapper = mapper;
227: }
228:
229: /**
230: * Constructs a <code>SegmentedImageInputStream</code> given a
231: * <code>ImageInputStream</code> as input and a list of the starting
232: * positions and lengths of the segments of the source stream.
233: *
234: * @param stream A source <code>ImageInputStream</code>
235: * @param segmentPositions An array of <code>long</code>s
236: * giving the starting positions of the segments in the
237: * source stream.
238: * @param segmentLengths An array of <code>int</code>s
239: * giving the lengths of segments in the source stream.
240: */
241: public SegmentedImageInputStream(ImageInputStream stream,
242: long[] segmentPositions, int[] segmentLengths) {
243: this (stream, new StreamSegmentMapperImpl(segmentPositions,
244: segmentLengths));
245: }
246:
247: /**
248: * Constructs a <code>SegmentedImageInputStream</code> given a
249: * <code>ImageInputStream</code> as input, a list of the starting
250: * positions of the segments of the source stream, the common
251: * length of each segment, and the total length of the segments.
252: *
253: * <p> This constructor is useful for selecting substreams
254: * of sector-oriented file formats in which each segment
255: * of the substream (except possibly the final segment)
256: * occupies a fixed-length sector.
257: *
258: * @param stream A source <code>ImageInputStream</code>
259: * @param segmentPositions An array of <code>long</code>s
260: * giving the starting positions of the segments in the
261: * source stream.
262: * @param segmentLength The common length of each segment.
263: * @param totalLength The total length of the source segments.
264: */
265: public SegmentedImageInputStream(ImageInputStream stream,
266: long[] segmentPositions, int segmentLength, int totalLength) {
267: this (stream, new SectorStreamSegmentMapper(segmentPositions,
268: segmentLength, totalLength));
269: }
270:
271: private StreamSegment streamSegment = new StreamSegment();
272:
273: /**
274: * Reads the next byte of data from the input stream. The value byte is
275: * returned as an <code>int</code> in the range <code>0</code> to
276: * <code>255</code>. If no byte is available because the end of the stream
277: * has been reached, the value <code>-1</code> is returned. This method
278: * blocks until input data is available, the end of the stream is detected,
279: * or an exception is thrown.
280: *
281: * @return the next byte of data, or <code>-1</code> if the end of the
282: * stream is reached.
283: * @exception IOException if an I/O error occurs.
284: */
285: public int read() throws IOException {
286: mapper.getStreamSegment(streamPos, 1, streamSegment);
287: int streamSegmentLength = streamSegment.getSegmentLength();
288: if (streamSegmentLength < 0) {
289: return -1;
290: }
291: stream.seek(streamSegment.getStartPos());
292:
293: // XXX What happens if streamSegmentLength == 0? Should this
294: // method also return -1 as above for streamSegmentLength < 0?
295: int val = stream.read();
296: ++streamPos;
297: return val;
298: }
299:
300: /**
301: * Reads up to <code>len</code> bytes of data from the input stream into
302: * an array of bytes. An attempt is made to read as many as
303: * <code>len</code> bytes, but a smaller number may be read, possibly
304: * zero. The number of bytes actually read is returned as an integer.
305: *
306: * <p> This method blocks until input data is available, end of stream is
307: * detected, or an exception is thrown.
308: *
309: * <p> If <code>b</code> is <code>null</code>, a
310: * <code>NullPointerException</code> is thrown.
311: *
312: * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
313: * <code>off+len</code> is greater than the length of the array
314: * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
315: * thrown.
316: *
317: * <p> If <code>len</code> is zero, then no bytes are read and
318: * <code>0</code> is returned; otherwise, there is an attempt to read at
319: * least one byte. If no byte is available because the stream is at end of
320: * stream, the value <code>-1</code> is returned; otherwise, at least one
321: * byte is read and stored into <code>b</code>.
322: *
323: * <p> The first byte read is stored into element <code>b[off]</code>, the
324: * next one into <code>b[off+1]</code>, and so on. The number of bytes read
325: * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
326: * bytes actually read; these bytes will be stored in elements
327: * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
328: * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
329: * <code>b[off+len-1]</code> unaffected.
330: *
331: * <p> In every case, elements <code>b[0]</code> through
332: * <code>b[off]</code> and elements <code>b[off+len]</code> through
333: * <code>b[b.length-1]</code> are unaffected.
334: *
335: * <p> If the first byte cannot be read for any reason other than end of
336: * stream, then an <code>IOException</code> is thrown. In particular, an
337: * <code>IOException</code> is thrown if the input stream has been closed.
338: *
339: * @param b the buffer into which the data is read.
340: * @param off the start offset in array <code>b</code>
341: * at which the data is written.
342: * @param len the maximum number of bytes to read.
343: * @return the total number of bytes read into the buffer, or
344: * <code>-1</code> if there is no more data because the end of
345: * the stream has been reached.
346: * @exception IOException if an I/O error occurs.
347: */
348: public int read(byte[] b, int off, int len) throws IOException {
349: if (b == null) {
350: throw new NullPointerException();
351: }
352: if ((off < 0) || (len < 0) || (off + len > b.length)) {
353: throw new IndexOutOfBoundsException();
354: }
355: if (len == 0) {
356: return 0;
357: }
358:
359: mapper.getStreamSegment(streamPos, len, streamSegment);
360: int streamSegmentLength = streamSegment.getSegmentLength();
361: if (streamSegmentLength < 0) {
362: return -1;
363: }
364: stream.seek(streamSegment.getStartPos());
365:
366: int nbytes = stream.read(b, off, streamSegmentLength);
367: streamPos += nbytes;
368: return nbytes;
369: }
370:
371: public long length() {
372: long len;
373: if (mapper instanceof StreamSegmentMapperImpl) {
374: len = ((StreamSegmentMapperImpl) mapper).length();
375: } else if (mapper instanceof SectorStreamSegmentMapper) {
376: len = ((SectorStreamSegmentMapper) mapper).length();
377: } else if (mapper != null) {
378: long pos = len = 0L;
379: StreamSegment seg = mapper.getStreamSegment(pos,
380: Integer.MAX_VALUE);
381: while ((len = seg.getSegmentLength()) > 0) {
382: pos += len;
383: seg.setSegmentLength(0);
384: mapper.getStreamSegment(pos, Integer.MAX_VALUE, seg);
385: }
386: len = pos;
387: } else {
388: len = super.length();
389: }
390:
391: return len;
392: }
393: }
|