/*
* Copyright 1997-2006 Unidata Program Center/University Corporation for
* Atmospheric Research, P.O. Box 3000, Boulder, CO 80307,
* support@unidata.ucar.edu.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* ImageI/O-Ext - OpenSource Java Image translation Library
* http://www.geo-solutions.it/
* http://java.net/projects/imageio-ext/
* (C) 2007 - 2009, GeoSolutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package it.geosolutions.imageio.stream.eraf;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UTFDataFormatException;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
/**
* @author Simone Giannecchini, GeoSolutions.
*
* -- NOTES from a class we derived this class from --
*
*
* RandomAccessFile.java. By Russ Rew, based on BufferedRandomAccessFile by Alex
* McManus, based on Sun's source code for java.io.RandomAccessFile. For Alex
* McManus version from which this derives, see his <a
* href="http://www.aber.ac.uk/~agm/Java.html"> Freeware Java Classes</a>.
*
* A buffered drop-in replacement for java.io.RandomAccessFile. Instances of
* this class realise substantial speed increases over java.io.RandomAccessFile
* through the use of buffering. This is a subclass of Object, as it was not
* possible to subclass java.io.RandomAccessFile because many of the methods are
* final. However, if it is necessary to use RandomAccessFile and
* java.io.RandomAccessFile interchangeably, both classes implement the
* DataInput and DataOutput interfaces.
*
* @author Alex McManus
* @author Russ Rew
* @author john caron
*
* @version $Id: EnhancedRandomAccessFile.java 1117 2007-02-20 09:46:00Z simboss $
* @see DataInput
* @see DataOutput
* @see java.io.RandomAccessFile
* @todo optimize {@link #readLine()}
* @task {@link ByteOrder} is not respected with writing
*/
public class EnhancedRandomAccessFile implements DataInput, DataOutput {
/** _more_ */
static public final int BIG_ENDIAN = 0;
/** _more_ */
static public final int LITTLE_ENDIAN = 1;
// debug leaks - keep track of open files
/** The default buffer size, in bytes. */
public static final int DEFAULT_BUFFER_SIZE = 4096;
/** _more_ */
protected File file;
/** The underlying java.io.RandomAccessFile. */
protected java.io.RandomAccessFile eraf;
/**
* The offset in bytes from the eraf start, of the next read or write
* operation.
*/
protected long filePosition;
/** The buffer used to load the data. */
protected byte buffer[];
/**
* The offset in bytes of the start of the buffer, from the start of the
* eraf.
*/
protected long bufferStart;
/**
* The offset in bytes of the end of the data in the buffer, from the start
* of the eraf. This can be calculated from
* <code>bufferStart + dataSize</code>, but it is cached to speed up the
* read( ) method.
*/
protected long dataEnd;
/**
* The size of the data stored in the buffer, in bytes. This may be less
* than the size of the buffer.
*/
protected int dataSize;
/** True if we are at the end of the eraf. */
protected boolean endOfFile;
/** The access mode of the eraf. */
protected boolean readonly;
/** The current endian (big or little) mode of the eraf. */
protected boolean bigEndian;
/** True if the data in the buffer has been modified. */
boolean bufferModified = false;
/** make sure eraf is this long when closed */
protected long minLength = 0;
/**
* stupid extendMode for truncated, yet valid files - old code allowed
* NOFILL to do this
*/
boolean extendMode = false;
// for HTTPRandomAccessFile
/**
* _more_
*
* @param bufferSize
* _more_
*/
protected EnhancedRandomAccessFile(int bufferSize) {
eraf = null;
readonly = true;
init(bufferSize);
}
/**
* Constructor, default buffer size.
*
* @param file
* file of the eraf
* @param mode
* same as for java.io.RandomAccessFile
* @throws IOException
*/
public EnhancedRandomAccessFile(File file, String mode)
throws IOException {
this(file, mode, DEFAULT_BUFFER_SIZE);
}
/**
* Constructor.
*
* @param file
* file of the eraf
* @param mode
* same as for java.io.RandomAccessFile
* @param bufferSize
* size of buffer to use.
* @throws IOException
*/
public EnhancedRandomAccessFile(File file, String mode, int bufferSize)
throws IOException {
this.file = file;
this.eraf = new java.io.RandomAccessFile(file, mode);
this.readonly = mode.equals("r");
init(bufferSize);
}
public java.io.RandomAccessFile getRandomAccessFile() {
return this.eraf;
}
/**
* _more_
*
* @param bufferSize
* _more_
*/
private void init(int bufferSize) {
// Initialise the buffer
bufferStart = 0;
dataEnd = 0;
dataSize = 0;
filePosition = 0;
buffer = new byte[bufferSize];
endOfFile = false;
}
/**
* Close the eraf, and release any associated system resources.
*
* @exception IOException
* if an I/O error occurrs.
*/
public void close() throws IOException {
if (eraf == null) {
return;
}
// If we are writing and the buffer has been modified, flush the
// contents of the buffer.
if (!readonly && bufferModified) {
eraf.seek(bufferStart);
eraf.write(buffer, 0, dataSize);
}
// may need to extend eraf, in case no fill is neing used
// may need to truncate eraf in case overwriting a longer eraf
// use only if minLength is set (by N3iosp)
if (!readonly && (minLength != 0) && (minLength != eraf.length())) {
eraf.setLength(minLength);
// System.out.println("TRUNCATE!!! minlength="+minLength);
}
// Close the underlying eraf object.
eraf.close();
}
/**
* Close silently the underlying {@link RandomAccessFile}
*
*
*/
public void finalize() {
try {
close();
} catch (IOException ex) {
}
}
/**
* Return true if eraf pointer is at end of eraf.
*
* @return _more_
*/
public boolean isAtEndOfFile() {
return endOfFile;
}
// Create channel from eraf
/**
* _more_
*
* @return _more_
*/
public FileChannel getChannel() {
if (eraf == null) {
return null;
}
try {
eraf.seek(0);
} catch (IOException e) {
e.printStackTrace();
}
return eraf.getChannel();
}
/**
* Set the position in the eraf for the next read or write.
*
* @param pos
* the offset (in bytes) from the start of the eraf.
* @exception IOException
* if an I/O error occurrs.
*/
public void seek(long pos) throws IOException {
// If the seek is into the buffer, just update the eraf pointer.
if ((pos >= bufferStart) && (pos < dataEnd)) {
filePosition = pos;
endOfFile = false;
return;
}
// If the current buffer is modified, write it to disk.
if (bufferModified) {
flush();
}
// need new buffer
bufferStart = pos;
filePosition = pos;
dataSize = read_(pos, buffer, 0, buffer.length);
if (dataSize <= 0) {
dataSize = 0;
endOfFile = true;
} else {
endOfFile = false;
}
// Cache the position of the buffer end.
dataEnd = bufferStart + dataSize;
}
/**
* Returns the current position in the eraf, where the next read or write
* will occur.
*
* @return the offset from the start of the eraf in bytes.
* @exception IOException
* if an I/O error occurrs.
*/
public long getFilePointer() throws IOException {
return filePosition;
}
/**
* Get the eraf file, or name.
*
* @return _more_
*/
public File getFile() {
return file;
}
/**
* Get the length of the eraf. The data in the buffer (which may not have
* been written the disk yet) is taken into account.
*
* @return the length of the eraf in bytes.
* @exception IOException
* if an I/O error occurrs.
*/
public long length() throws IOException {
long fileLength = eraf.length();
if (fileLength < dataEnd) {
return dataEnd;
} else {
return fileLength;
}
}
/**
* Change the current endian mode. Subsequent reads of short, int, float,
* double, long, char will use this. Does not currently effect writes.
*
* @param endian
*
*/
public void setByteOrder(final ByteOrder bo) {
this.bigEndian = (bo == ByteOrder.BIG_ENDIAN);
}
public ByteOrder getByteOrder() {
return bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
}
/**
* Returns the opaque eraf descriptor object associated with this eraf.
*
* @return the eraf descriptor object associated with this eraf.
* @exception IOException
* if an I/O error occurs.
*/
public FileDescriptor getFD() throws IOException {
return (eraf == null) ? null : eraf.getFD();
}
/**
* Copy the contents of the buffer to the disk.
*
* @exception IOException
* if an I/O error occurrs.
*/
public void flush() throws IOException {
if (bufferModified) {
eraf.seek(bufferStart);
eraf.write(buffer, 0, dataSize);
bufferModified = false;
}
}
/**
* Make sure eraf is at least this long when its closed. needed when not
* using fill mode, and not all data is written.
*
* @param minLength
* _more_
*/
public void setMinLength(long minLength) {
this.minLength = minLength;
}
/**
* Set extendMode for truncated, yet valid files - old NetCDF code allowed
* this when NOFILL on, and user doesnt write all variables.
*/
public void setExtendMode() {
this.extendMode = true;
}
// ////////////////////////////////////////////////////////////////////////////////////////////
// Read primitives.
//
/**
* Read a byte of data from the eraf, blocking until data is available.
*
* @return the next byte of data, or -1 if the end of the eraf is reached.
* @exception IOException
* if an I/O error occurrs.
*/
public int read() throws IOException {
// If the eraf position is within the data, return the byte...
if (filePosition < dataEnd) {
int pos = (int) (filePosition - bufferStart);
filePosition++;
return (buffer[pos] & 0xff);
// ...or should we indicate EOF...
} else if (endOfFile) {
return -1;
// ...or seek to fill the buffer, and try again.
} else {
seek(filePosition);
return read();
}
}
/**
* Read up to <code>len</code> bytes into an array, at a specified offset.
* This will block until at least one byte has been read.
*
* @param b
* the byte array to receive the bytes.
* @param off
* the offset in the array where copying will start.
* @param len
* the number of bytes to copy.
* @return the actual number of bytes read, or -1 if there is not more data
* due to the end of the eraf being reached.
* @exception IOException
* if an I/O error occurrs.
*/
protected int readBytes(byte b[], int off, int len) throws IOException {
// Check for end of eraf.
if (endOfFile) {
return -1;
}
// See how many bytes are available in the buffer - if none,
// seek to the eraf position to update the buffer and try again.
int bytesAvailable = (int) (dataEnd - filePosition);
if (bytesAvailable < 1) {
seek(filePosition);
return readBytes(b, off, len);
}
// Copy as much as we can.
int copyLength = (bytesAvailable >= len) ? len : bytesAvailable;
System.arraycopy(buffer, (int) (filePosition - bufferStart), b, off,
copyLength);
filePosition += copyLength;
// If there is more to copy...
if (copyLength < len) {
int extraCopy = len - copyLength;
// If the amount remaining is more than a buffer's length, read it
// directly from the eraf.
if (extraCopy > buffer.length) {
extraCopy = read_(filePosition, b, off + copyLength, len
- copyLength);
// ...or read a new buffer full, and copy as much as possible...
} else {
seek(filePosition);
if (!endOfFile) {
extraCopy = (extraCopy > dataSize) ? dataSize : extraCopy;
System.arraycopy(buffer, 0, b, off + copyLength, extraCopy);
} else {
extraCopy = -1;
}
}
// If we did manage to copy any more, update the eraf position and
// return the amount copied.
if (extraCopy > 0) {
filePosition += extraCopy;
return copyLength + extraCopy;
}
}
// Return the amount copied.
return copyLength;
}
/**
* read directly, without going through the buffer
*
* @param pos
* _more_
* @param b
* _more_
* @param offset
* _more_
* @param len
* _more_
*
* @return _more_
*
* @throws IOException
* _more_
*/
protected int read_(long pos, byte[] b, int offset, int len)
throws IOException {
eraf.seek(pos);
int n = eraf.read(b, offset, len);
if (extendMode && (n < len)) {
n = len;
}
return n;
}
/**
* Read up to <code>len</code> bytes into an array, at a specified offset.
* This will block until at least one byte has been read.
*
* @param b
* the byte array to receive the bytes.
* @param off
* the offset in the array where copying will start.
* @param len
* the number of bytes to copy.
* @return the actual number of bytes read, or -1 if there is not more data
* due to the end of the eraf being reached.
* @exception IOException
* if an I/O error occurrs.
*/
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
/**
* Read up to <code>b.length( )</code> bytes into an array. This will
* block until at least one byte has been read.
*
* @param b
* the byte array to receive the bytes.
* @return the actual number of bytes read, or -1 if there is not more data
* due to the end of the eraf being reached.
* @exception IOException
* if an I/O error occurrs.
*/
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
/**
* Reads <code>b.length</code> bytes from this eraf into the byte array.
* This method reads repeatedly from the eraf 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 read.
* @exception EOFException
* if this eraf reaches the end before reading all the bytes.
* @exception IOException
* if an I/O error occurs.
*/
public void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
/**
* Reads exactly <code>len</code> bytes from this eraf into the byte
* array. This method reads repeatedly from the eraf 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 read.
* @param off
* the start offset of the data.
* @param len
* the number of bytes to read.
* @exception EOFException
* if this eraf reaches the end before reading all the bytes.
* @exception IOException
* if an I/O error occurs.
*/
public void readFully(byte b[], int off, int len) throws IOException {
int n = 0;
while (n < len) {
final int count = this.read(b, off + n, len - n);
if (count < 0) {
throw new EOFException();
}
n += count;
}
}
/**
* Skips exactly <code>n</code> bytes of input. This method blocks until
* all the bytes are skipped, the end of the stream is detected, or an
* exception is thrown.
*
* @param n
* the number of bytes to be skipped.
* @return the number of bytes skipped, which is always <code>n</code>.
* @exception EOFException
* if this eraf reaches the end before skipping all the
* bytes.
* @exception IOException
* if an I/O error occurs.
*/
public int skipBytes(int n) throws IOException {
seek(getFilePointer() + n);
return n;
}
/**
* Skips exactly <code>n</code> bytes of input. This method blocks until
* all the bytes are skipped, the end of the stream is detected, or an
* exception is thrown.
*
* @param n
* the number of bytes to be skipped.
* @return the number of bytes skipped, which is always <code>n</code>.
* @exception EOFException
* if this eraf reaches the end before skipping all the
* bytes.
* @exception IOException
* if an I/O error occurs.
*/
public long skipBytes(long n) throws IOException {
seek(getFilePointer() + n);
return n;
}
/**
* Unread the last byte read. This method should not be used more than once
* between reading operations, or strange things might happen.
*/
public void unread() {
filePosition--;
}
//
// Write primitives.
//
/**
* Write a byte to the eraf. If the eraf has not been opened for writing, an
* IOException will be raised only when an attempt is made to write the
* buffer to the eraf.
* <p>
* Caveat: the effects of seek( )ing beyond the end of the eraf are
* undefined.
*
*
* @param b
* _more_
* @exception IOException
* if an I/O error occurrs.
*/
public void write(int b) throws IOException {
// If the eraf position is within the block of data...
if (filePosition < dataEnd) {
buffer[(int) (filePosition++ - bufferStart)] = (byte) b;
bufferModified = true;
// ...or (assuming that seek will not allow the eraf pointer
// to move beyond the end of the eraf) get the correct block of
// data...
} else {
// If there is room in the buffer, expand it...
if (dataSize != buffer.length) {
buffer[(int) (filePosition++ - bufferStart)] = (byte) b;
bufferModified = true;
dataSize++;
dataEnd++;
// ...or do another seek to get a new buffer, and start again...
} else {
seek(filePosition);
write(b);
}
}
}
/**
* Write <code>len</code> bytes from an array to the eraf.
*
* @param b
* the array containing the data.
* @param off
* the offset in the array to the data.
* @param len
* the length of the data.
* @exception IOException
* if an I/O error occurrs.
*/
public void writeBytes(byte b[], int off, int len) throws IOException {
// If the amount of data is small (less than a full buffer)...
if (len < buffer.length) {
// If any of the data fits within the buffer...
int spaceInBuffer = 0;
int copyLength = 0;
if (filePosition >= bufferStart) {
spaceInBuffer = (int) ((bufferStart + buffer.length) - filePosition);
}
if (spaceInBuffer > 0) {
// Copy as much as possible to the buffer.
copyLength = (spaceInBuffer > len) ? len : spaceInBuffer;
System.arraycopy(b, off, buffer,
(int) (filePosition - bufferStart), copyLength);
bufferModified = true;
long myDataEnd = filePosition + copyLength;
dataEnd = (myDataEnd > dataEnd) ? myDataEnd : dataEnd;
dataSize = (int) (dataEnd - bufferStart);
filePosition += copyLength;
// /System.out.println("--copy to buffer "+copyLength+" "+len);
}
// If there is any data remaining, move to the new position and copy
// to
// the new buffer.
if (copyLength < len) {
// System.out.println("--need more "+copyLength+" "+len+" space=
// "+spaceInBuffer);
seek(filePosition); // triggers a flush
System.arraycopy(b, off + copyLength, buffer,
(int) (filePosition - bufferStart), len - copyLength);
bufferModified = true;
long myDataEnd = filePosition + (len - copyLength);
dataEnd = (myDataEnd > dataEnd) ? myDataEnd : dataEnd;
dataSize = (int) (dataEnd - bufferStart);
filePosition += (len - copyLength);
}
// ...or write a lot of data...
} else {
// Flush the current buffer, and write this data to the eraf.
if (bufferModified) {
flush();
bufferStart = dataEnd = dataSize = 0;
// eraf.seek(filePosition); // JC added Oct 21, 2004
}
eraf.seek(filePosition); // moved per Steve Cerruti; Jan 14, 2005
eraf.write(b, off, len);
// System.out.println("--write at "+filePosition+" "+len);
filePosition += len;
}
}
/**
* Writes <code>b.length</code> bytes from the specified byte array
* starting at offset <code>off</code> to this eraf.
*
* @param b
* the data.
* @exception IOException
* if an I/O error occurs.
*/
public void write(byte b[]) throws IOException {
writeBytes(b, 0, b.length);
}
/**
* Writes <code>len</code> bytes from the specified byte array starting at
* offset <code>off</code> to this eraf.
*
* @param b
* the data.
* @param off
* the start offset in the data.
* @param len
* the number of bytes to write.
* @exception IOException
* if an I/O error occurs.
*/
public void write(byte b[], int off, int len) throws IOException {
writeBytes(b, off, len);
}
//
// DataInput methods.
//
/**
* Reads a <code>boolean</code> from this eraf. This method reads a single
* byte from the eraf. A value of <code>0</code> represents
* <code>false</code>. Any other value represents <code>true</code>.
* This method blocks until the byte is read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the <code>boolean</code> value read.
* @exception EOFException
* if this eraf has reached the end.
* @exception IOException
* if an I/O error occurs.
*/
public boolean readBoolean() throws IOException {
int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return (ch != 0);
}
/**
* Reads a signed 8-bit value from this eraf. This method reads a byte from
* the eraf. If the byte read is <code>b</code>, where
* <code>0 <= b <= 255</code>, then the result
* is:
* <ul>
* <code>
* (byte)(b)
* </code>
* </ul>
* <p>
* This method blocks until the byte is read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the next byte of this eraf as a signed 8-bit <code>byte</code>.
* @exception EOFException
* if this eraf has reached the end.
* @exception IOException
* if an I/O error occurs.
*/
public byte readByte() throws IOException {
int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return (byte) (ch);
}
/**
* Reads an unsigned 8-bit number from this eraf. This method reads a byte
* from this eraf and returns that byte.
* <p>
* This method blocks until the byte is read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the next byte of this eraf, interpreted as an unsigned 8-bit
* number.
* @exception EOFException
* if this eraf has reached the end.
* @exception IOException
* if an I/O error occurs.
*/
public int readUnsignedByte() throws IOException {
int ch = this.read();
if (ch < 0) {
throw new EOFException();
}
return ch;
}
/**
* Reads a signed 16-bit number from this eraf. The method reads 2 bytes
* from this eraf. If the two bytes read, in order, are <code>b1</code>
* and <code>b2</code>, where each of the two values is between
* <code>0</code> and <code>255</code>, inclusive, then the result is
* equal to:
* <ul>
* <code>
* (short)((b1 << 8) | b2)
* </code>
* </ul>
* <p>
* This method blocks until the two bytes are read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the next two bytes of this eraf, interpreted as a signed 16-bit
* number.
* @exception EOFException
* if this eraf reaches the end before reading two bytes.
* @exception IOException
* if an I/O error occurs.
*/
public short readShort() throws IOException {
final byte b[] = new byte[2];
if (read(b, 0, 2) < 0) {
throw new EOFException();
}
if (bigEndian) {
return (short) (((b[0] & 0xFF) << 8) + (b[1] & 0xFF));
} else {
return (short) (((b[1] & 0xFF) << 8) + (b[0] & 0xFF));
}
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void readShort(short[] pa, int start, int n) throws IOException {
for (int i = start, end = n + start; i < end; i++) {
pa[i] = readShort();
}
}
/**
* Reads an unsigned 16-bit number from this eraf. This method reads two
* bytes from the eraf. If the bytes read, in order, are <code>b1</code>
* and <code>b2</code>, where
* <code>0 <= b1, b2 <= 255</code>, then the
* result is equal to:
* <ul>
* <code>
* (b1 << 8) | b2
* </code>
* </ul>
* <p>
* This method blocks until the two bytes are read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the next two bytes of this eraf, interpreted as an unsigned
* 16-bit integer.
* @exception EOFException
* if this eraf reaches the end before reading two bytes.
* @exception IOException
* if an I/O error occurs.
*/
public int readUnsignedShort() throws IOException {
byte b[] = new byte[2];
if (read(b, 0, 2) < 0) {
throw new EOFException();
}
if (bigEndian) {
return ((b[0] & 0xFF) << 8) + (b[1] & 0xFF) & 0xFFFF;
} else {
return ((b[1] & 0xFF) << 8) + (b[0] & 0xFF) & 0xFFFF;
}
}
/**
* Reads a Unicode character from this eraf. This method reads two bytes
* from the eraf. If the bytes read, in order, are <code>b1</code> and
* <code>b2</code>, where
* <code>0 <= b1, b2 <= 255</code>, then
* the result is equal to:
* <ul>
* <code>
* (char)((b1 << 8) | b2)
* </code>
* </ul>
* <p>
* This method blocks until the two bytes are read, the end of the stream is
* detected, or an exception is thrown.
*
* @return the next two bytes of this eraf as a Unicode character.
* @exception EOFException
* if this eraf reaches the end before reading two bytes.
* @exception IOException
* if an I/O error occurs.
*/
public char readChar() throws IOException {
final byte b[] = new byte[2];
if (read(b, 0, 2) < 0) {
throw new EOFException();
}
if (bigEndian) {
return (char) (((b[0] & 0xFF) << 8) + (b[1] & 0xFF));
} else {
return (char) (((b[1] & 0xFF) << 8) + (b[0] & 0xFF));
}
}
/**
* Reads a signed 32-bit integer from this eraf. This method reads 4 bytes
* from the eraf. If the bytes read, in order, are <code>b1</code>,
* <code>b2</code>, <code>b3</code>, and <code>b4</code>, where
* <code>0 <= b1, b2, b3, b4 <= 255</code>,
* then the result is equal to:
* <ul>
* <code>
* (b1 << 24) | (b2 << 16) + (b3 << 8) + b4
* </code>
* </ul>
* <p>
* This method blocks until the four bytes are read, the end of the stream
* is detected, or an exception is thrown.
*
* @return the next four bytes of this eraf, interpreted as an
* <code>int</code>.
* @exception EOFException
* if this eraf reaches the end before reading four bytes.
* @exception IOException
* if an I/O error occurs.
*/
public int readInt() throws IOException {
final byte b[] = new byte[4];
if (read(b, 0, 4) < 0) {
throw new EOFException();
}
if (bigEndian) {
return (((b[0] & 0xFF) << 24) + ((b[1] & 0xFF) << 16)
+ ((b[2] & 0xFF) << 8) + ((b[3] & 0xFF)));
} else {
return (((b[3] & 0xFF) << 24) + ((b[2] & 0xFF) << 16)
+ ((b[1] & 0xFF) << 8) + ((b[0] & 0xFF)));
}
}
public long readUnsignedInt() throws IOException {
// retaining only the first 4 bytes, ignoring sign when extending
return ((long) readInt()) & 0xFFFFFFFFL;
}
/**
* Read an integer at the given position, bypassing all buffering.
*
* @param pos
* read a byte at this position
* @return The int that was read
* @throws IOException
*/
public int readIntUnbuffered(long pos) throws IOException {
byte[] bb = new byte[4];
read_(pos, bb, 0, 4);
int ch1 = bb[0] & 0xFF;
int ch2 = bb[1] & 0xFF;
int ch3 = bb[2] & 0xFF;
int ch4 = bb[3] & 0xFF;
if ((ch1 | ch2 | ch3 | ch4) < 0) {
throw new EOFException();
}
if (bigEndian) {
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
} else {
return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));
}
}
/**
* Reads a signed 24-bit integer from this eraf. This method reads 3 bytes
* from the eraf. If the bytes read, in order, are <code>b1</code>,
* <code>b2</code>, and <code>b3</code>, where
* <code>0 <= b1, b2, b3 <= 255</code>, then
* the result is equal to:
* <ul>
* <code>
* (b1 << 16) | (b2 << 8) + (b3 << 0)
* </code>
* </ul>
* <p>
* This method blocks until the three bytes are read, the end of the stream
* is detected, or an exception is thrown.
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
* @exception EOFException
* if this eraf reaches the end before reading four bytes.
* @exception IOException
* if an I/O error occurs.
*/
public void readInt(int[] pa, int start, int n) throws IOException {
for (int i = start, end = n + start; i < end; i++) {
pa[i] = readInt();
}
}
/**
* Reads a signed 64-bit integer from this eraf. This method reads eight
* bytes from the eraf. If the bytes read, in order, are <code>b1</code>,
* <code>b2</code>, <code>b3</code>, <code>b4</code>,
* <code>b5</code>, <code>b6</code>, <code>b7</code>, and
* <code>b8,</code> where:
* <ul>
* <code>
* 0 <= b1, b2, b3, b4, b5, b6, b7, b8 <=255,
* </code>
* </ul>
* <p>
* then the result is equal to:
* <p>
* <blockquote>
*
* <pre>
* ((long) b1 << 56) + ((long) b2 << 48) + ((long) b3 << 40) + ((long) b4 << 32)
* + ((long) b5 << 24) + ((long) b6 << 16) + ((long) b7 << 8) + b8
* </pre>
*
* </blockquote>
* <p>
* This method blocks until the eight bytes are read, the end of the stream
* is detected, or an exception is thrown.
*
* @return the next eight bytes of this eraf, interpreted as a
* <code>long</code>.
* @exception EOFException
* if this eraf reaches the end before reading eight bytes.
* @exception IOException
* if an I/O error occurs.
*/
public long readLong() throws IOException {
final byte b[] = new byte[8];
if (read(b, 0, 8) < 0) {
throw new EOFException();
}
if (bigEndian) {
return (((b[0] & 0xFFL) << 56) + ((b[1] & 0xFFL) << 48)
+ ((b[2] & 0xFFL) << 40) + ((b[3] & 0xFFL) << 32)
+ ((b[4] & 0xFFL) << 24) + ((b[5] & 0xFFL) << 16)
+ ((b[6] & 0xFFL) << 8) + ((b[7] & 0xFFL)));
} else {
return (((b[7] & 0xFFL) << 56) + ((b[6] & 0xFFL) << 48)
+ ((b[5] & 0xFFL) << 40) + ((b[4] & 0xffL) << 32)
+ ((b[3] & 0xFFL) << 24) + ((b[2] & 0xFFL) << 16)
+ ((b[1] & 0xFFL) << 8) + ((b[0] & 0xFFL)));
}
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void readLong(long[] pa, int start, int n) throws IOException {
for (int i = start, end = n + start; i < end; i++) {
pa[i] = readLong();
}
}
/**
* Reads a <code>float</code> from this eraf. This method reads an
* <code>int</code> value as if by the <code>readInt</code> method and
* then converts that <code>int</code> to a <code>float</code> using the
* <code>intBitsToFloat</code> method in class <code>Float</code>.
* <p>
* This method blocks until the four bytes are read, the end of the stream
* is detected, or an exception is thrown.
*
* @return the next four bytes of this eraf, interpreted as a
* <code>float</code>.
* @exception EOFException
* if this eraf reaches the end before reading four bytes.
* @exception IOException
* if an I/O error occurs.
* @see java.io.RandomAccessFile#readInt()
* @see java.lang.Float#intBitsToFloat(int)
*/
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void readFloat(float[] pa, int start, int n) throws IOException {
for (int i = start, end = n + start; i < end; i++) {
pa[i] = Float.intBitsToFloat(readInt());
}
}
/**
* Reads a <code>double</code> from this eraf. This method reads a
* <code>long</code> value as if by the <code>readLong</code> method and
* then converts that <code>long</code> to a <code>double</code> using
* the <code>longBitsToDouble</code> method in class <code>Double</code>.
* <p>
* This method blocks until the eight bytes are read, the end of the stream
* is detected, or an exception is thrown.
*
* @return the next eight bytes of this eraf, interpreted as a
* <code>double</code>.
* @exception EOFException
* if this eraf reaches the end before reading eight bytes.
* @exception IOException
* if an I/O error occurs.
* @see java.io.RandomAccessFile#readLong()
* @see java.lang.Double#longBitsToDouble(long)
*/
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void readDouble(double[] pa, int start, int n) throws IOException {
for (int i = start, end = n + start; i < end; i++) {
pa[i] = Double.longBitsToDouble(readLong());
}
}
/**
* Reads the next line of text from this eraf. This method successively
* reads bytes from the eraf until it reaches the end of a line of text.
* <p>
*
* A line of text is terminated by a carriage-return character (<code>'\r'</code>),
* a newline character (<code>'\n'</code>), a carriage-return
* character immediately followed by a newline character, or the end of the
* input stream. The line-terminating character(s), if any, are included as
* part of the string returned.
*
* <p>
* This method blocks until a newline character is read, a carriage return
* and the byte following it are read (to see if it is a newline), the end
* of the stream is detected, or an exception is thrown.
*
* @return the next line of text from this eraf.
* @exception IOException
* if an I/O error occurs.
* @task we can optimize this
*/
public String readLine() throws IOException {
StringBuilder input = new StringBuilder();
int c;
while (((c = read()) != -1) && (c != '\n')) {
input.append((char) c);
}
if ((c == -1) && (input.length() == 0)) {
return null;
}
return input.toString();
}
/**
* Reads in a string from this eraf. The string has been encoded using a
* modified UTF-8 format.
* <p>
* The first two bytes are read as if by <code>readUnsignedShort</code>.
* This value gives the number of following bytes that are in the encoded
* string, not the length of the resulting string. The following bytes are
* then interpreted as bytes encoding characters in the UTF-8 format and are
* converted into characters.
* <p>
* This method blocks until all the bytes are read, the end of the stream is
* detected, or an exception is thrown.
*
* @return a Unicode string.
* @exception EOFException
* if this eraf reaches the end before reading all the bytes.
* @exception IOException
* if an I/O error occurs.
* @exception UTFDataFormatException
* if the bytes do not represent valid UTF-8 encoding of a
* Unicode string.
* @see java.io.RandomAccessFile#readUnsignedShort()
*/
public String readUTF() throws IOException {
return DataInputStream.readUTF(this);
}
/**
* Read a String of knoen length.
*
* @param nbytes
* number of bytes to read
* @return String wrapping the bytes.
* @throws IOException
*/
public String readString(int nbytes) throws IOException {
final byte[] data = new byte[nbytes];
readFully(data);
return new String(data);
}
//
// DataOutput methods.
//
/**
* Writes a <code>boolean</code> to the eraf as a 1-byte value. The value
* <code>true</code> is written out as the value <code>(byte)1</code>;
* the value <code>false</code> is written out as the value
* <code>(byte)0</code>.
*
* @param v
* a <code>boolean</code> value to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeBoolean(boolean v) throws IOException {
write(v ? 1 : 0);
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeBoolean(boolean[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeBoolean(pa[i]);
}
}
/**
* Writes a <code>byte</code> to the eraf as a 1-byte value.
*
* @param v
* a <code>byte</code> value to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeByte(int v) throws IOException {
write(v);
}
/**
* Writes a <code>short</code> to the eraf as two bytes, high byte first.
*
* @param v
* a <code>short</code> to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeShort(int v) throws IOException {
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeShort(short[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeShort(pa[i]);
}
}
/**
* Writes a <code>char</code> to the eraf as a 2-byte value, high byte
* first.
*
* @param v
* a <code>char</code> value to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeChar(int v) throws IOException {
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeChar(char[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeChar(pa[i]);
}
}
/**
* Writes an <code>int</code> to the eraf as four bytes, high byte first.
*
* @param v
* an <code>int</code> to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeInt(int v) throws IOException {
write((v >>> 24) & 0xFF);
write((v >>> 16) & 0xFF);
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeInt(int[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeInt(pa[i]);
}
}
/**
* Writes a <code>long</code> to the eraf as eight bytes, high byte first.
*
* @param v
* a <code>long</code> to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeLong(long v) throws IOException {
if (bigEndian) {
write((int) (v >>> 56) & 0xFF);
write((int) (v >>> 48) & 0xFF);
write((int) (v >>> 40) & 0xFF);
write((int) (v >>> 32) & 0xFF);
write((int) (v >>> 24) & 0xFF);
write((int) (v >>> 16) & 0xFF);
write((int) (v >>> 8) & 0xFF);
write((int) (v >>> 0) & 0xFF);
} else {
write((int) (v >>> 0) & 0xFF);
write((int) (v >>> 8) & 0xFF);
write((int) (v >>> 16) & 0xFF);
write((int) (v >>> 24) & 0xFF);
write((int) (v >>> 32) & 0xFF);
write((int) (v >>> 40) & 0xFF);
write((int) (v >>> 48) & 0xFF);
write((int) (v >>> 56) & 0xFF);
}
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeLong(long[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeLong(pa[i]);
}
}
/**
* Converts the float argument to an <code>int</code> using the
* <code>floatToIntBits</code> method in class <code>Float</code>, and
* then writes that <code>int</code> value to the eraf as a 4-byte
* quantity, high byte first.
*
* @param v
* a <code>float</code> value to be written.
* @exception IOException
* if an I/O error occurs.
* @see java.lang.Float#floatToIntBits(float)
*/
public void writeFloat(float v) throws IOException {
writeInt(Float.floatToIntBits(v));
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeFloat(float[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeFloat(pa[i]);
}
}
/**
* Converts the double argument to a <code>long</code> using the
* <code>doubleToLongBits</code> method in class <code>Double</code>,
* and then writes that <code>long</code> value to the eraf as an 8-byte
* quantity, high byte first.
*
* @param v
* a <code>double</code> value to be written.
* @exception IOException
* if an I/O error occurs.
* @see java.lang.Double#doubleToLongBits(double)
*/
public void writeDouble(double v) throws IOException {
writeLong(Double.doubleToLongBits(v));
}
/**
* _more_
*
* @param pa
* _more_
* @param start
* _more_
* @param n
* _more_
*
* @throws IOException
* _more_
*/
public void writeDouble(double[] pa, int start, int n) throws IOException {
for (int i = start, end = start + n; i < end; i++) {
writeDouble(pa[i]);
}
}
/**
* Writes the string to the eraf as a sequence of bytes. Each character in
* the string is written out, in sequence, by discarding its high eight
* bits.
*
* @param s
* a string of bytes to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeBytes(String s) throws IOException {
final int len = s.length();
for (int i = 0; i < len; i++) {
write((byte) s.charAt(i));
}
}
/**
* Writes the character array to the eraf as a sequence of bytes. Each
* character in the string is written out, in sequence, by discarding its
* high eight bits.
*
* @param b
* a character array of bytes to be written.
* @param off
* the index of the first character to write.
* @param len
* the number of characters to write.
* @exception IOException
* if an I/O error occurs.
*/
public void writeBytes(char b[], int off, int len) throws IOException {
for (int i = off; i < len; i++) {
write((byte) b[i]);
}
}
/**
* Writes a string to the eraf as a sequence of characters. Each character
* is written to the data output stream as if by the <code>writeChar</code>
* method.
*
* @param s
* a <code>String</code> value to be written.
* @exception IOException
* if an I/O error occurs.
* @see java.io.RandomAccessFile#writeChar(int)
*/
public void writeChars(String s) throws IOException {
int len = s.length();
for (int i = 0; i < len; i++) {
final int v = s.charAt(i);
write((v >>> 8) & 0xFF);
write((v >>> 0) & 0xFF);
}
}
/**
* Writes a string to the eraf using UTF-8 encoding in a machine-independent
* manner.
* <p>
* First, two bytes are written to the eraf as if by the
* <code>writeShort</code> method giving the number of bytes to follow.
* This value is the number of bytes actually written out, not the length of
* the string. Following the length, each character of the string is output,
* in sequence, using the UTF-8 encoding for each character.
*
* @param str
* a string to be written.
* @exception IOException
* if an I/O error occurs.
*/
public void writeUTF(String str) throws IOException {
final int strlen = str.length();
int utflen = 0;
for (int i = 0; i < strlen; i++) {
int c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
if (utflen > 65535) {
throw new UTFDataFormatException();
}
write((utflen >>> 8) & 0xFF);
write((utflen >>> 0) & 0xFF);
for (int i = 0; i < strlen; i++) {
int c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
write(c);
} else if (c > 0x07FF) {
write(0xE0 | ((c >> 12) & 0x0F));
write(0x80 | ((c >> 6) & 0x3F));
write(0x80 | ((c >> 0) & 0x3F));
} else {
write(0xC0 | ((c >> 6) & 0x1F));
write(0x80 | ((c >> 0) & 0x3F));
}
}
}
/**
* Create a string representation of this object.
*
* @return a string representation of the state of the object.
*/
public String toString() {
return "fp=" + filePosition + ", bs=" + bufferStart + ", de=" + dataEnd
+ ", ds=" + dataSize + ", bl=" + buffer.length + ", readonly="
+ readonly + ", bm=" + bufferModified;
}
/** Support for FileCache. */
protected boolean cached;
protected String location;
/**
* _more_
*
* @param cached
* _more_
*/
public void setCached(boolean cached) {
this.cached = cached;
}
/**
* _more_
*
* @return _more_
*/
public boolean isCached() {
return cached;
}
/**
* _more_
*
* @throws IOException
* _more_
*/
public void synch() throws IOException {
}
public String getLocation() {
return location;
}
}