/* * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * -Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * -Redistribution in binary form must reproduct the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that Software is not designed,licensed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ package com.fr.third.JAI; import java.io.IOException; /** * An implementation of the StreamSegmentMapper interface * that requires an explicit list of the starting locations and * lengths of the source segments. */ class StreamSegmentMapperImpl implements StreamSegmentMapper { private long[] segmentPositions; private int[] segmentLengths; public StreamSegmentMapperImpl(long[] segmentPositions, int[] segmentLengths) { this.segmentPositions = (long[])segmentPositions.clone(); this.segmentLengths = (int[])segmentLengths.clone(); } public StreamSegment getStreamSegment(long position, int length) { int numSegments = segmentLengths.length; for (int i = 0; i < numSegments; i++) { int len = segmentLengths[i]; if (position < len) { return new StreamSegment(segmentPositions[i] + position, Math.min(len - (int)position, length)); } position -= len; } return null; } public void getStreamSegment(long position, int length, StreamSegment seg) { int numSegments = segmentLengths.length; for (int i = 0; i < numSegments; i++) { int len = segmentLengths[i]; if (position < len) { seg.setStartPos(segmentPositions[i] + position); seg.setSegmentLength(Math.min(len - (int)position, length)); return; } position -= len; } seg.setStartPos(-1); seg.setSegmentLength(-1); return; } } /** * An implementation of the StreamSegmentMapper interface * for segments of equal length. */ class SectorStreamSegmentMapper implements StreamSegmentMapper { long[] segmentPositions; int segmentLength; int totalLength; int lastSegmentLength; public SectorStreamSegmentMapper(long[] segmentPositions, int segmentLength, int totalLength) { this.segmentPositions = (long[])segmentPositions.clone(); this.segmentLength = segmentLength; this.totalLength = totalLength; this.lastSegmentLength = totalLength - (segmentPositions.length - 1)*segmentLength; } public StreamSegment getStreamSegment(long position, int length) { int index = (int) (position/segmentLength); // Compute segment length int len = (index == segmentPositions.length - 1) ? lastSegmentLength : segmentLength; // Compute position within the segment position -= index*segmentLength; // Compute maximum legal length len -= position; if (len > length) { len = length; } return new StreamSegment(segmentPositions[index] + position, len); } public void getStreamSegment(long position, int length, StreamSegment seg) { int index = (int) (position/segmentLength); // Compute segment length int len = (index == segmentPositions.length - 1) ? lastSegmentLength : segmentLength; // Compute position within the segment position -= index*segmentLength; // Compute maximum legal length len -= position; if (len > length) { len = length; } seg.setStartPos(segmentPositions[index] + position); seg.setSegmentLength(len); } } /** * A SegmentedSeekableStream provides a view of a * subset of another SeekableStream consiting of a series * of segments with given starting positions in the source stream and * lengths. The resulting stream behaves like an ordinary * SeekableStream. * *

For example, given a SeekableStream containing * data in a format consisting of a number of sub-streams stored in * non-contiguous sectors indexed by a directory, it is possible to * construct a set of SegmentedSeekableStreams, one for * each sub-stream, that each provide a view of the sectors comprising * a particular stream by providing the positions and lengths of the * stream's sectors as indicated by the directory. The complex * multi-stream structure of the original stream may be ignored by * users of the SegmentedSeekableStream, who see a * separate SeekableStream for each sub-stream and do not * need to understand the directory structure at all. * *

For further efficiency, a directory structure such as in the * example described above need not be fully parsed in order to build * a SegmentedSeekableStream. Instead, the * StreamSegmentMapper interface allows the association * between a desired region of the output and an input segment to be * provided dynamically. This mapping might be computed by reading * from a directory in piecemeal fashion in order to avoid consuming * memory resources. * *

It is the responsibility of the user of this class to determine * whether backwards seeking should be enabled. If the source stream * supports only forward seeking, backwards seeking must be disabled * and the StreamSegmentMapper must be monotone; that is, * forward motion in the destination must always result in forward * motion within the source. If the source stream supports backwards * seeking, there are no restrictions on the * StreamSegmentMapper and backwards seeking may always * be enabled for the SegmentedSeekableStream. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public class SegmentedSeekableStream extends SeekableStream { private SeekableStream stream; private StreamSegmentMapper mapper; private long pointer = 0; private boolean canSeekBackwards; /** * Constructs a SegmentedSeekableStream * given a SeekableStream as input, * an instance of StreamSegmentMapper, * and a boolean indicating whether the * output SegmentedSeekableStream should * support seeking backwards. If canSeekBackwards * is true, the source stream must itself * support seeking backwards. * * @param stream A source SeekableStream * @param mapper An instance of the StreamSegmentMapper * interface. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, StreamSegmentMapper mapper, boolean canSeekBackwards) { this.stream = stream; this.mapper = mapper; this.canSeekBackwards = canSeekBackwards; if (canSeekBackwards && !stream.canSeekBackwards()) { throw new IllegalArgumentException(JaiI18N.getString("SegmentedSeekableStream0")); } } /** * Constructs a SegmentedSeekableStream given a * SeekableStream as input, a list of the starting * positions and lengths of the segments of the source stream, and * a boolean indicating whether the output * SegmentedSeekableStream should support seeking * backwards. If canSeekBakckwards is * true, the source stream must itself support * seeking backwards. * * @param stream A source SeekableStream * @param segmentPositions An array of longs * giving the starting positions of the segments in the * source stream. * @param segmentLengths An array of ints * giving the lengths of segments in the source stream. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, long[] segmentPositions, int[] segmentLengths, boolean canSeekBackwards) { this(stream, new StreamSegmentMapperImpl(segmentPositions, segmentLengths), canSeekBackwards); } /** * Constructs a SegmentedSeekableStream given a * SeekableStream as input, a list of the starting * positions of the segments of the source stream, the common * length of each segment, the total length of the segments and * a boolean indicating whether the output * SegmentedSeekableStream should support seeking * backwards. If canSeekBakckwards is * true, the source stream must itself support * seeking backwards. * *

This constructor is useful for selecting substreams * of sector-oriented file formats in which each segment * of the substream (except possibly the final segment) * occupies a fixed-length sector. * * @param stream A source SeekableStream * @param segmentPositions An array of longs * giving the starting positions of the segments in the * source stream. * @param segmentLength The common length of each segment. * @param totalLength The total length of the source segments. * @param canSeekBackwards true if the ability to * seek backwards is desired. */ public SegmentedSeekableStream(SeekableStream stream, long[] segmentPositions, int segmentLength, int totalLength, boolean canSeekBackwards) { this(stream, new SectorStreamSegmentMapper(segmentPositions, segmentLength, totalLength), canSeekBackwards); } /** * Returns the current offset in this stream. * * @return the offset from the beginning of the stream, in bytes, * at which the next read occurs. */ public long getFilePointer() { return (long)pointer; } /** * Returns true if seeking backwards is supported. * Support is determined by the value of the * canSeekBackwards parameter at construction time. */ public boolean canSeekBackwards() { return canSeekBackwards; } /** * Sets the offset, measured from the beginning of this * stream, at which the next read occurs. * *

If canSeekBackwards() returns false, * then setting pos to an offset smaller than * the current value of getFilePointer() will have * no effect. * * @param pos the offset position, measured in bytes from the * beginning of the stream, at which to set the stream * pointer. * @exception IOException if pos is less than * 0 or if an I/O error occurs. */ public void seek(long pos) throws IOException { if (pos < 0) { throw new IOException(); } pointer = pos; } private StreamSegment streamSegment = new StreamSegment(); /** * Reads the next byte of data from the input stream. The value byte is * returned as an int in the range 0 to * 255. If no byte is available because the end of the stream * has been reached, the value -1 is returned. This method * blocks until input data is available, the end of the stream is detected, * or an exception is thrown. * * @return the next byte of data, or -1 if the end of the * stream is reached. * @exception IOException if an I/O error occurs. */ public int read() throws IOException { mapper.getStreamSegment(pointer, 1, streamSegment); stream.seek(streamSegment.getStartPos()); int val = stream.read(); ++pointer; return val; } /** * Reads up to len bytes of data from the input stream into * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read, possibly * zero. The number of bytes actually read is returned as an integer. * *

This method blocks until input data is available, end of stream is * detected, or an exception is thrown. * *

If b is null, a * NullPointerException is thrown. * *

If off is negative, or len is negative, or * off+len is greater than the length of the array * b, then an IndexOutOfBoundsException is * thrown. * *

If len is zero, then no bytes are read and * 0 is returned; otherwise, there is an attempt to read at * least one byte. If no byte is available because the stream is at end of * stream, the value -1 is returned; otherwise, at least one * byte is read and stored into b. * *

The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. Let k be the number of * bytes actually read; these bytes will be stored in elements * b[off] through b[off+k-1], * leaving elements b[off+k] through * b[off+len-1] unaffected. * *

In every case, elements b[0] through * b[off] and elements b[off+len] through * b[b.length-1] are unaffected. * *

If the first byte cannot be read for any reason other than end of * stream, then an IOException is thrown. In particular, an * IOException is thrown if the input stream has been closed. * * @param b the buffer into which the data is read. * @param off the start offset in array b * at which the data is written. * @param len the maximum number of bytes to read. * @return the total number of bytes read into the buffer, or * -1 if there is no more data because the end of * the stream has been reached. * @exception IOException if an I/O error occurs. */ public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if ((off < 0) || (len < 0) || (off + len > b.length)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } mapper.getStreamSegment(pointer, len, streamSegment); stream.seek(streamSegment.getStartPos()); int nbytes = stream.read(b, off, streamSegment.getSegmentLength()); pointer += nbytes; return nbytes; } }