blob: 09a53e291c2ae1d63851e14d03af1ab06bc497fe [file] [log] [blame] [edit]
/*
* Copyright (c) 2014-2021 by Wen Yu
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* or any later version.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
*/
package pixy.io;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* Implements a random access input stream
* <p>
* Based on com.sun.media.jai.codec.SeekableStream.
* <p>
* To make it flexible, this class and any of its sub-class doesn't close the underlying
* stream. It's up to the underlying stream creator to close them. This ensures the actual
* stream out-lives the random stream itself in case we need to read more content from the
* underlying stream.
* <p>
* NOTE: for MemoryCacheRandomAccessInputStream, there is the risk of "over read" in which
* more bytes are cached in the buffer than actually needed. In this case, the underlying
* stream might not be usable anymore afterwards.
*
* @author Wen Yu, yuwen_66@yahoo.com
* @version 1.0 01/24/2013
*/
public abstract class RandomAccessInputStream extends InputStream implements DataInput {
private ReadStrategy strategy = ReadStrategyMM.getInstance();
/** The source stream. */
protected InputStream src;
protected boolean closed;
protected RandomAccessInputStream(InputStream src) {
this.src = src;
}
/**
* Closes the RandomAccessInputStream and it's underlying stream
* @throws IOException
*/
public abstract void shallowClose() throws IOException;
/**
* Check to make sure that this stream has not been closed
*/
protected void ensureOpen() throws IOException {
if (closed)
throw new IOException("Stream closed");
}
protected void finalize() throws Throwable {
super.finalize();
close();
}
public short getEndian() {
return strategy instanceof ReadStrategyMM?IOUtils.BIG_ENDIAN:IOUtils.LITTLE_ENDIAN;
}
public abstract long getStreamPointer();
public abstract int read() throws IOException;
public abstract int read(byte[] b, int off, int len) throws IOException;
public final boolean readBoolean() throws IOException {
int ch = this.read();
if (ch < 0)
throw new EOFException();
return (ch != 0);
}
public final byte readByte() throws IOException {
int ch = this.read();
if (ch < 0)
throw new EOFException();
return (byte)ch;
}
public final char readChar() throws IOException {
return (char)(readShort()&0xffff);
}
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
public final void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
public final void readFully(byte[] b, int off, int len) throws IOException {
int n = 0;
do {
int count = this.read(b, off + n, len - n);
if (count < 0)
throw new EOFException();
n += count;
} while (n < len);
}
public final int readInt() throws IOException {
byte[] buf = new byte[4];
readFully(buf);
return strategy.readInt(buf, 0);
}
@Deprecated
public final String readLine() throws IOException {
throw new UnsupportedOperationException(
"readLine is not supported by RandomAccessInputStream."
);
}
public final long readLong() throws IOException {
byte[] buf = new byte[8];
readFully(buf);
return strategy.readLong(buf, 0);
}
public final float readS15Fixed16Number() throws IOException {
byte[] buf = new byte[4];
readFully(buf);
return strategy.readS15Fixed16Number(buf, 0);
}
public final short readShort() throws IOException {
byte[] buf = new byte[2];
readFully(buf);
return strategy.readShort(buf, 0);
}
public final float readU16Fixed16Number() throws IOException {
byte[] buf = new byte[4];
readFully(buf);
return strategy.readU16Fixed16Number(buf, 0);
}
public final float readU8Fixed8Number() throws IOException {
byte[] buf = new byte[2];
readFully(buf);
return strategy.readU8Fixed8Number(buf, 0);
}
public final int readUnsignedByte() throws IOException {
int ch = this.read();
if (ch < 0)
throw new EOFException();
return ch;
}
public final long readUnsignedInt() throws IOException {
return readInt()&0xffffffffL;
}
public final int readUnsignedShort() throws IOException {
return readShort()&0xffff;
}
/**
* Due to the current implementation, writeUTF and readUTF are the
* only methods which are machine or byte sequence independent as
* they are actually both Motorola byte sequence under the hood.
*
* Whereas the following static method is byte sequence dependent
* as it calls readUnsignedShort of RandomAccessInputStream.
*
* <code>DataInputStream.readUTF(this)</code>;
*/
public final String readUTF() throws IOException {
return new DataInputStream(this).readUTF();
}
public abstract void seek(long loc) throws IOException;
public void setReadStrategy(ReadStrategy strategy) {
this.strategy = strategy;
}
public int skipBytes(int n) throws IOException {
if (n <= 0) {
return 0;
}
return (int)skip(n);
}
}