jj2000.j2k.util.ISRandomAccessIO Maven / Gradle / Ivy
Show all versions of jai-imageio-jpeg2000 Show documentation
/*
* $RCSfile: ISRandomAccessIO.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:25 $
* $State: Exp $
*
* Class: ISRandomAccessIO
*
* Description: Turns an InsputStream into a read-only
* RandomAccessIO, using buffering.
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package jj2000.j2k.util;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import jj2000.j2k.io.EndianType;
import jj2000.j2k.io.RandomAccessIO;
/**
* This class implements a wrapper to turn an InputStream into a
* RandomAccessIO. To provide random access the input data from the
* InputStream is cached in an in-memory buffer. The in-memory buffer size can
* be limited to a specified size. The data is read into the cache on a as
* needed basis, blocking only when necessary.
*
* The cache grows automatically as necessary. However, if the data length
* is known prior to the creation of a ISRandomAccessIO object, it is best to
* specify that as the initial in-memory buffer size. That will minimize data
* copying and multiple allocation.
*
*
Multi-byte data is read in big-endian order. The in-memory buffer
* storage is released when 'close()' is called. This class can only be used
* for data input, not output. The wrapped InputStream is closed when all the
* input data is cached or when 'close()' is called.
*
*
If an out of memory condition is encountered when growing the
* in-memory buffer an IOException is thrown instead of an
* OutOfMemoryError. The exception message is "Out of memory to cache input
* data".
*
*
This class is intended for use as a "quick and dirty" way to give
* network connectivity to RandomAccessIO based classes. It is not intended as
* an efficient means of implementing network connectivity. Doing such
* requires reimplementing the RandomAccessIO based classes to directly use
* network connections.
*
*
This class does not use temporary files as buffers, because that would
* preclude the use in unsigned applets.
* */
public class ISRandomAccessIO implements RandomAccessIO {
/** The InputStream that is wrapped */
private InputStream is;
/* Tha maximum size, in bytes, of the in memory buffer. The maximum size
* includes the EOF. */
private int maxsize;
/* The increment, in bytes, for the in-memory buffer size */
private int inc;
/* The in-memory buffer to cache received data */
private byte buf[];
/* The length of the already received data */
private int len;
/* The position of the next byte to be read from the in-memory buffer */
private int pos;
/* Flag to indicate if all the data has been received. That is, if the EOF
* has been reached. */
private boolean complete;
/**
* Creates a new RandomAccessIO wrapper for the given InputStream
* 'is'. The internal cache buffer will have size 'size' and will
* increment by 'inc' each time it is needed. The maximum buffer size is
* limited to 'maxsize'.
*
* @param is The input from where to get the data.
*
* @param size The initial size for the cache buffer, in bytes.
*
* @param inc The size increment for the cache buffer, in bytes.
*
* @param maxsize The maximum size for the cache buffer, in bytes.
*/
public ISRandomAccessIO(InputStream is, int size, int inc, int maxsize) {
if (size < 0 || inc <= 0 || maxsize <= 0 || is == null) {
throw new IllegalArgumentException();
}
this.is = is;
// Increase size by one to count in EOF
if (size < Integer.MAX_VALUE) size++;
buf = new byte[size];
this.inc = inc;
// The maximum size is one byte more, to allow reading the EOF.
if (maxsize < Integer.MAX_VALUE) maxsize++;
this.maxsize = maxsize;
pos = 0;
len = 0;
complete = false;
}
/**
* Creates a new RandomAccessIO wrapper for the given InputStream
* 'is'. The internal cache buffer size and increment is to to 256 kB. The
* maximum buffer size is set to Integer.MAX_VALUE (2 GB).
*
* @param is The input from where to get the data.
*
*/
public ISRandomAccessIO(InputStream is) {
this(is,1<<18,1<<18,Integer.MAX_VALUE);
}
/**
* Grows the cache buffer by 'inc', upto a maximum of 'maxsize'. The
* buffer size will be increased by at least one byte, if no exception is
* thrown.
*
* @exception IOException If the maximum cache size is reached or if not
* enough memory is available to grow the buffer.
*/
private void growBuffer() throws IOException {
byte newbuf[];
int effinc; // effective increment
effinc = inc;
if (buf.length+effinc > maxsize) effinc = maxsize-buf.length;
if (effinc <= 0) {
throw new IOException("Reached maximum cache size ("+maxsize+")");
}
try {
newbuf = new byte[buf.length+inc];
} catch (OutOfMemoryError e) {
throw new IOException("Out of memory to cache input data");
}
System.arraycopy(buf,0,newbuf,0,len);
buf = newbuf;
}
/**
* Reads data from the wrapped InputStream and places it in the cache
* buffer. Reads all input data that will not cause it to block, but at
* least on byte is read (even if it blocks), unless EOF is reached. This
* method can not be called if EOF has been already reached
* (i.e. 'complete' is true).
*
* @exception IOException An I/O error occurred, out of meory to grow
* cache or maximum cache size reached.
* */
private void readInput() throws IOException {
int n;
int b;
int k;
if (complete) {
throw new IllegalArgumentException("Already reached EOF");
}
// may need reflection to call this method
// n = is.available(); /* how much can we read without blocking? */
n = 0; /* how much can we read without blocking? */
if (n == 0) n = 1; /* read at least one byte (even if it blocks) */
while (len+n > buf.length) { /* Ensure buffer size */
growBuffer();
}
/* Read the data. Loop to be sure that we do read 'n' bytes */
do {
k = is.read(buf,len,n);
if (k > 0) { /* Some data was read */
len += k;
n -= k;
}
} while (n > 0 && k > 0);
if (k <= 0) { /* we reached EOF */
complete = true;
is = null;
}
}
/**
* Closes this object for reading as well as the wrapped InputStream, if
* not already closed. The memory used by the cache is released.
*
* @exception IOException If an I/O error occurs while closing the
* underlying InputStream. */
public void close() throws IOException {
buf = null;
if (!complete) {
is.close();
is = null;
}
}
/**
* Returns the current position in the stream, which is the
* position from where the next byte of data would be read. The first
* byte in the stream is in position 0.
*
* @exception IOException If an I/O error occurred.
* */
public int getPos() throws IOException {
return pos;
}
/**
* Moves the current position for the next read operation to
* offset. The offset is measured from the beginning of the stream. If the
* offset is set beyond the currently cached data, the missing data will
* be read only when a read operation is performed. Setting
* the offset beyond the end of the data will cause an EOFException only
* if the data length is currently known, otherwise an IOException will
* occur when a read operation is attempted at that position.
*
* @param off The offset where to move to.
*
* @exception EOFException If seeking beyond EOF and the data length is
* known.
*
* @exception IOException If an I/O error ocurred.
* */
public void seek(int off) throws IOException {
if (complete) { /* we know the length, check seek is within length */
if (off > len) {
throw new EOFException();
}
}
pos = off;
}
/**
* Returns the length of the stream. This will cause all the data to be
* read. This method will block until all the data is read, which can be
* lengthy across the network.
*
* @return The length of the stream, in bytes.
*
* @exception IOException If an I/O error ocurred.
*/
public int length() throws IOException {
if (Integer.MAX_VALUE != maxsize)
return maxsize - 1;
while (!complete) { // read until we reach EOF
readInput();
}
return len;
}
/**
* Reads a byte of data from the stream.
*
* @return The byte read, as an int in the range [0-255].
*
* @exception EOFException If the end-of file was reached.
*
* @exception IOException If an I/O error ocurred.
*
*/
public int read() throws IOException {
if (pos < len) { // common, fast case
return 0xFF & (int)buf[pos++];
}
// general case
while (!complete && pos >= len) {
readInput();
}
if (pos == len) {
throw new EOFException();
} else if (pos > len) {
throw new IOException("Position beyond EOF");
}
return 0xFF & (int)buf[pos++];
}
/**
* Reads 'len' bytes of data from this file into an array of bytes. This
* method reads repeatedly from the stream until all the bytes are
* read. This method blocks until all the bytes are read, the end of the
* stream is detected, or an exception is thrown.
*
* @param b The buffer into which the data is to be read. It must be long
* enough.
*
* @param off The index in 'b' where to place the first byte read.
*
* @param len The number of bytes to read.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
*
* */
public void readFully(byte b[], int off, int n) throws IOException {
if (pos+n <= len) { // common, fast case
System.arraycopy(buf,pos,b,off,n);
pos += n;
return;
}
// general case
while (!complete && pos+n > len) {
readInput();
}
if (pos+n > len) {
throw new EOFException();
}
System.arraycopy(buf,pos,b,off,n);
pos += n;
}
/**
* Returns the endianess (i.e., byte ordering) of multi-byte I/O
* operations. Always EndianType.BIG_ENDIAN since this class implements
* only big-endian.
*
* @return Always EndianType.BIG_ENDIAN.
*
* @see EndianType
*
*/
public int getByteOrdering() {
return EndianType.BIG_ENDIAN;
}
/**
* Reads a signed byte (8 bit) from the input.
*
* @return The next byte-aligned signed byte (8 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public byte readByte() throws IOException {
if (pos < len) { // common, fast case
return buf[pos++];
}
// general case
return (byte) read();
}
/**
* Reads an unsigned byte (8 bit) from the input.
*
* @return The next byte-aligned unsigned byte (8 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public int readUnsignedByte() throws IOException {
if (pos < len) { // common, fast case
return 0xFF & buf[pos++];
}
// general case
return read();
}
/**
* Reads a signed short (16 bit) from the input.
*
* @return The next byte-aligned signed short (16 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public short readShort() throws IOException {
if (pos+1 < len) { // common, fast case
return (short) ((buf[pos++]<<8) | (0xFF & buf[pos++]));
}
// general case
return (short) ((read()<<8) | read());
}
/**
* Reads an unsigned short (16 bit) from the input.
*
* @return The next byte-aligned unsigned short (16 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public int readUnsignedShort() throws IOException {
if (pos+1 < len) { // common, fast case
return ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++]);
}
// general case
return (read()<<8) | read();
}
/**
* Reads a signed int (32 bit) from the input.
*
* @return The next byte-aligned signed int (32 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public int readInt() throws IOException {
if (pos+3 < len) { // common, fast case
return ((buf[pos++]<<24) | ((0xFF & buf[pos++])<<16)
| ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++]));
}
// general case
return (read()<<24) | (read()<<16) | (read()<<8) | read();
}
/**
* Reads a unsigned int (32 bit) from the input.
*
* @return The next byte-aligned unsigned int (32 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public long readUnsignedInt() throws IOException {
if (pos+3 < len) { // common, fast case
return (0xFFFFFFFFL
& (long)((buf[pos++]<<24) | ((0xFF & buf[pos++])<<16)
| ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++])));
}
// general case
return (0xFFFFFFFFL
& (long)((read()<<24) | (read()<<16) | (read()<<8) | read()));
}
/**
* Reads a signed long (64 bit) from the input.
*
* @return The next byte-aligned signed long (64 bit) from the
* input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public long readLong() throws IOException {
if (pos+7 < len) { // common, fast case
return (((long)buf[pos++]<<56)
| ((long)(0xFF&buf[pos++])<<48)
| ((long)(0xFF&buf[pos++])<<40)
| ((long)(0xFF&buf[pos++])<<32)
| ((long)(0xFF&buf[pos++])<<24)
| ((long)(0xFF&buf[pos++])<<16)
| ((long)(0xFF&buf[pos++])<<8)
| (long)(0xFF&buf[pos++]));
}
// general case
return (((long)read()<<56)
| ((long)read()<<48)
| ((long)read()<<40)
| ((long)read()<<32)
| ((long)read()<<24)
| ((long)read()<<16)
| ((long)read()<<8)
| (long)read());
}
/**
* Reads an IEEE single precision (i.e., 32 bit) floating-point number
* from the input.
*
* @return The next byte-aligned IEEE float (32 bit) from the input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public float readFloat() throws IOException {
if (pos+3 < len) { // common, fast case
return Float.intBitsToFloat((buf[pos++]<<24)
| ((0xFF & buf[pos++])<<16)
| ((0xFF & buf[pos++])<<8)
| (0xFF & buf[pos++]));
}
// general case
return Float.intBitsToFloat((read()<<24) | (read()<<16)
| (read()<<8) | read());
}
/**
* Reads an IEEE double precision (i.e., 64 bit) floating-point number
* from the input.
*
* @return The next byte-aligned IEEE double (64 bit) from the input.
*
* @exception EOFException If the end-of file was reached before
* getting all the necessary data.
*
* @exception IOException If an I/O error ocurred.
* */
public double readDouble() throws IOException {
if (pos+7 < len) { // common, fast case
return Double.longBitsToDouble(((long)buf[pos++]<<56)
| ((long)(0xFF&buf[pos++])<<48)
| ((long)(0xFF&buf[pos++])<<40)
| ((long)(0xFF&buf[pos++])<<32)
| ((long)(0xFF&buf[pos++])<<24)
| ((long)(0xFF&buf[pos++])<<16)
| ((long)(0xFF&buf[pos++])<<8)
| (long)(0xFF&buf[pos++]));
}
// general case
return Double.longBitsToDouble(((long)read()<<56)
| ((long)read()<<48)
| ((long)read()<<40)
| ((long)read()<<32)
| ((long)read()<<24)
| ((long)read()<<16)
| ((long)read()<<8)
| (long)read());
}
/**
* Skips 'n' bytes from the input.
*
* @param n The number of bytes to skip
*
* @return Always n.
*
* @exception EOFException If the end-of file was reached before
* all the bytes could be skipped.
*
* @exception IOException If an I/O error ocurred.
* */
public int skipBytes(int n) throws IOException {
if (complete) { /* we know the length, check skip is within length */
if (pos+n > len) {
throw new EOFException();
}
}
pos += n;
return n;
}
/**
* Does nothing since this class does not implement data output.
*/
public void flush() { /* no-op */
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void write(int b) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeByte(int v) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeShort(int v) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeInt(int v) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeLong(long v) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeFloat(float v) throws IOException {
throw new IOException("read-only");
}
/**
* Throws an IOException since this class does not implement data output.
*/
public void writeDouble(double v) throws IOException {
throw new IOException("read-only");
}
}