All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xmlgraphics.image.codec.util.MemoryCacheSeekableStream Maven / Gradle / Ivy

There is a newer version: 1.2.2.1-jre17
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: MemoryCacheSeekableStream.java 1732018 2016-02-24 04:51:06Z gadams $ */

package org.apache.xmlgraphics.image.codec.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * A subclass of SeekableStream that may be used to wrap
 * a regular InputStream.  Seeking backwards is supported
 * by means of an in-memory cache.  For greater efficiency,
 * FileCacheSeekableStream should be used in
 * circumstances that allow the creation of a temporary file.
 *
 * 

The mark() and reset() methods are * supported. * *

This class is not a committed part of the JAI API. It may * be removed or changed in future releases of JAI. */ public final class MemoryCacheSeekableStream extends SeekableStream { /** The source input stream. */ private InputStream src; /** Position of first unread byte. */ private long pointer; /** Log_2 of the sector size. */ private static final int SECTOR_SHIFT = 9; /** The sector size. */ private static final int SECTOR_SIZE = 1 << SECTOR_SHIFT; /** A mask to determine the offset within a sector. */ private static final int SECTOR_MASK = SECTOR_SIZE - 1; /** A Vector of source sectors. */ private List data = new ArrayList(); /** Number of sectors stored. */ // int sectors = 0; /** Number of bytes read. */ int length; /** True if we've previously reached the end of the source stream */ boolean foundEOS; /** * Constructs a MemoryCacheSeekableStream that takes * its source data from a regular InputStream. * Seeking backwards is supported by means of an in-memory cache. */ public MemoryCacheSeekableStream(InputStream src) { this.src = src; } /** * Ensures that at least pos bytes are cached, * or the end of the source is reached. The return value * is equal to the smaller of pos and the * length of the source stream. */ private long readUntil(long pos) throws IOException { // We've already got enough data cached if (pos < length) { return pos; } // pos >= length but length isn't getting any bigger, so return it if (foundEOS) { return length; } int sector = (int)(pos >> SECTOR_SHIFT); // First unread sector int startSector = length >> SECTOR_SHIFT; // Read sectors until the desired sector for (int i = startSector; i <= sector; i++) { byte[] buf = new byte[SECTOR_SIZE]; data.add(buf); // Read up to SECTOR_SIZE bytes int len = SECTOR_SIZE; int off = 0; while (len > 0) { int nbytes = src.read(buf, off, len); // Found the end-of-stream if (nbytes == -1) { foundEOS = true; return length; } off += nbytes; len -= nbytes; // Record new data length length += nbytes; } } return length; } /** * Returns true since all * MemoryCacheSeekableStream instances support seeking * backwards. */ public boolean canSeekBackwards() { return true; } /** * Returns the current offset in this file. * * @return the offset from the beginning of the file, in bytes, * at which the next read occurs. */ public long getFilePointer() { return pointer; } /** * Sets the file-pointer offset, measured from the beginning of this * file, at which the next read occurs. * * @param pos the offset position, measured in bytes from the * beginning of the file, at which to set the file * 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(PropertyUtil.getString("MemoryCacheSeekableStream0")); } pointer = pos; } /** * 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. */ public int read() throws IOException { long next = pointer + 1; long pos = readUntil(next); if (pos >= next) { byte[] buf = (byte[])data.get((int)(pointer >> SECTOR_SHIFT)); return buf[(int)(pointer++ & SECTOR_MASK)] & 0xff; } else { return -1; } } /** * 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 file 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 * file, 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 * file, 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. */ 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; } long pos = readUntil(pointer + len); // End-of-stream if (pos <= pointer) { return -1; } byte[] buf = (byte[])data.get((int)(pointer >> SECTOR_SHIFT)); int nbytes = Math.min(len, SECTOR_SIZE - (int)(pointer & SECTOR_MASK)); System.arraycopy(buf, (int)(pointer & SECTOR_MASK), b, off, nbytes); pointer += nbytes; return nbytes; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy