// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/io/BinaryBufferedFile.java,v $
// $RCSfile: BinaryBufferedFile.java,v $
// $Revision: 1.2.2.4 $
// $Date: 2008/02/25 23:13:13 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.io;
import com.bbn.openmap.MoreMath;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
/**
* This class extends the BinaryFile class, doing buffered reads on the
* underlying input file. The buffer size is not modifiable after construction,
* and the buffer management isn't the greatest.
*/
public class BinaryBufferedFile extends BinaryFile {
/** Where reads get buffered */
private byte buffer[];
/** Where current reads will come from */
private int curptr = 0;
/** how many valid bytes are in the buffer */
private int bytesinbuffer = 0;
/** the byte offset of the first byte in the buffer */
private long firstbyteoffset = 0;
/**
* Constructs a BinaryBufferedFile with a File and a buffer size
*
* @param f the input file
* @param buffersize the size to use for buffering reads
* @exception IOException pass-through errors from opening a BinaryFile with
* f
* @see com.bbn.openmap.io.BinaryFile
*/
public BinaryBufferedFile(File f, int buffersize) throws IOException {
super(f);
buffer = new byte[buffersize];
}
/**
* Constructs a BinaryBufferedFile with a File
*
* @param f the input file
* @exception IOException pass-through errors from opening a BinaryFile with
* f
* @see com.bbn.openmap.io.BinaryFile
*/
public BinaryBufferedFile(File f) throws IOException {
this(f, 4096);
}
/**
* Constructs a BinaryBufferedFile with a filename and a buffersize
*
* @param name the name/path of the input file
* @param buffersize the size to use for buffering reads
* @exception IOException pass-through errors from opening a BinaryFile with
* f
* @see com.bbn.openmap.io.BinaryFile
*/
public BinaryBufferedFile(String name, int buffersize) throws IOException {
super(name);
buffer = new byte[buffersize];
}
/**
* Constructs a BinaryBufferedFile with a filename
*
* @param name the name/path of the input file
* @exception IOException pass-through errors from opening a BinaryFile with
* f
* @see com.bbn.openmap.io.BinaryFile
*/
public BinaryBufferedFile(String name) throws IOException {
this(name, 4096);
}
/**
* A simple factory method that lets you try to create something without
* having to really deal with failure. Returns a BinaryFile if successful,
* null if not.
*/
public static BinaryBufferedFile create(String name) {
return create(name, 4096);
}
/**
* A simple factory method that lets you try to create something without
* having to really deal with failure. Returns a BinaryFile if successful,
* null if not.
*/
public static BinaryBufferedFile create(String name, int buffersize) {
BinaryBufferedFile bf = null;
try {
bf = new BinaryBufferedFile(name, buffersize);
} catch (IOException ioe) {
}
return bf;
}
/**
* Set the input reader used by the BinaryFile. Make sure it's intialized
* properly. Assumes that the pointer is at the beginning of the file.
*/
public void setInputReader(InputReader reader) {
super.setInputReader(reader);
firstbyteoffset = 0;
bytesinbuffer = 0;
curptr = 0;
}
/**
* Throws away whatever data is in the buffer, and refills it.
*
* @exception IOException IO errors encountered while refilling the buffer
* @exception EOFException no data was left in the file
*/
private void refillBuffer() throws IOException, EOFException {
firstbyteoffset += (curptr + bytesinbuffer);
int err = super.read(buffer, 0, buffer.length);
curptr = 0;
if (err == -1)
throw new EOFException();
bytesinbuffer = err;
}
/**
* Forces the buffer to have at least some minimum number of bytes in it.
*
* @param minlength the minimum number of bytes to have in the buffer
* @exception FormatException couldn't get enough bytes (or IO Exception)
* @exception EOFException couldn't get any bytes
*/
private void assertSize(int minlength) throws FormatException, EOFException {
try {
if (bytesinbuffer < minlength) {
if (curptr != 0) {
firstbyteoffset += curptr;
System.arraycopy(buffer, curptr, buffer, 0, bytesinbuffer);
curptr = 0;
}
int err = super.read(buffer, bytesinbuffer, buffer.length
- bytesinbuffer);
if (err == -1) {
if (available() <= 0) {
throw new EOFException("BinaryBufferedFile, no bytes at all, trying to read "
+ minlength);
} else {
throw new FormatException("BinaryBufferedFile: failed to read "
+ minlength
+ " bytes, with "
+ bytesinbuffer
+ " bytes in the buffer and "
+ available()
+ " bytes available, have read "
+ curptr
+ " bytes.");
}
}
bytesinbuffer += err;
assertSize(minlength);
}
} catch (EOFException e) {
throw e;
} catch (IOException i) {
throw new FormatException("assertSize IOException: "
+ i.getMessage());
}
}
public long skipBytes(long n) throws IOException {
if (n < bytesinbuffer) {
bytesinbuffer -= n;
curptr += n;
return n;
}
final long oldbinb = bytesinbuffer;
bytesinbuffer = 0;
curptr = 0;
final int skipcnt = (int) super.skipBytes(n - oldbinb);
firstbyteoffset += skipcnt;
return (oldbinb + skipcnt);
}
public long getFilePointer() throws IOException {
return (firstbyteoffset + curptr);
}
public void seek(long pos) throws IOException {
final long relpos = pos - firstbyteoffset;
if ((relpos >= 0) && (relpos < (curptr + bytesinbuffer))) {
final int relcur = (int) relpos - curptr;
if (relcur != 0) {
bytesinbuffer -= relcur;
curptr = (int) relpos;
} // else we're already at the right place
} else {
super.seek(pos);
firstbyteoffset = pos;
bytesinbuffer = 0;
curptr = 0;
}
}
public long length() throws IOException {
return super.length();
}
public long available() throws IOException {
return (length() - firstbyteoffset - curptr);
}
/**
* Disposes the underlying file input, and releases some resources of the
* class. Calling any other members after this one will return bogus
* results.
*
* @exception IOException IO errors envountered in closing the file
*/
public void dispose() throws IOException {
buffer = null;
super.dispose();
}
public int read() throws IOException {
try {
if (bytesinbuffer == 0)
refillBuffer();
} catch (EOFException e) {
return -1;
}
bytesinbuffer--;
return MoreMath.signedToInt(buffer[curptr++]);
}
/**
* Read from the file
*
* @param b The byte array to read into
* @param off the first array position to read into
* @param len the number of bytes to read
* @return the number of bytes read
* @exception IOException Any IO errors encountered in reading from the file
*/
public int read(byte b[], int off, int len) throws IOException {
int numread = 0;
int copy;
if (len < bytesinbuffer)
copy = len;
else
copy = bytesinbuffer;
numread += copy;
bytesinbuffer -= copy;
System.arraycopy(buffer, curptr, b, off, copy);
curptr += copy;
off += copy;
if (len == copy)
return numread;
len -= copy;
// was not enough stuff in buffer, do some reads...
if (len > 512) {// threshold exceeded, read straight into user
// buffer
final int bcnt = super.read(b, off, len);
firstbyteoffset += (curptr + bcnt);
curptr = 0;
return (numread + bcnt);
} else { // refill buffer and recurse
try {
refillBuffer();
} catch (EOFException e) {
if (numread == 0) {
// If it's really EOF and nothing was ever read (as opposed
// to getting a little out of the buffer before the EOF),
// return EOF value
numread = -1;
}
return numread;
}
return (numread + read(b, off, len));
}
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* Read from the file.
*
* @param howmany the number of bytes to read
* @param allowless if we can return fewer bytes than requested
* @return the array of bytes read.
* @exception FormatException Any IO Exceptions, plus an end-of-file
* encountered after reading some, but now enough, bytes when
* allowless was <code>false</code>
* @exception EOFException Encountered an end-of-file while allowless was
* <code>false</code>, but NO bytes had been read.
*/
public byte[] readBytes(int howmany, boolean allowless)
throws EOFException, FormatException {
byte foo[] = new byte[howmany];
int gotsofar = 0;
try {
while (gotsofar < howmany) {
int err = read(foo, gotsofar, howmany - gotsofar);
if (err == -1) {
if (allowless) {
/*
* return a smaller array, so the caller can tell how
* much they really got
*/
byte retval[] = new byte[gotsofar];
System.arraycopy(foo, 0, retval, 0, gotsofar);
return retval;
} else { // some kind of failure...
if (gotsofar > 0) {
throw new FormatException("EOF while reading");
} else {
throw new EOFException();
}
}
}
gotsofar += err;
}
} catch (IOException i) {
throw new FormatException("IOException reading file: "
+ i.getMessage());
}
return foo;
}
/**
* Reads and returns a single byte, cast to a char.
*
* @return the byte read from the file, cast to a char
* @exception EOFException the end-of-file has been reached, so no chars
* where available
* @exception FormatException a rethrown IOException
*/
public char readChar() throws EOFException, FormatException {
try {
int retv = read();
if (retv == -1) {
throw new EOFException("Error in ReadChar, EOF reached");
}
return (char) retv;
} catch (IOException i) {
throw new FormatException("IOException in ReadChar: "
+ i.getMessage());
}
}
/**
* Reads and returns a short.
*
* @return the 2 bytes merged into a short, according to the current byte
* ordering
* @exception EOFException there were less than 2 bytes left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the short
* @see #read(byte[])
*/
public short readShort() throws EOFException, FormatException {
// MSBFirst must be set when we are called
assertSize(2);
curptr += 2;
bytesinbuffer -= 2;
return MoreMath.BuildShort(buffer, curptr - 2, MSBFirst);
}
/**
* Reads and returns a integer from 2 bytes.
*
* @return the 2 bytes merged into a short, according to the current byte
* ordering, and then unsigned to int.
* @exception EOFException there were less than 2 bytes left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the short
* @see #read(byte[])
*/
public int readUnsignedShort() throws EOFException, FormatException {
// MSBFirst must be set when we are called
return MoreMath.signedToInt(readShort());
}
/**
* Reads an array of shorts.
*
* @param vec the array to write the shorts into
* @param offset the first array index to write to
* @param len the number of shorts to read
* @exception EOFException there were fewer bytes than needed in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the array
*/
public void readShortArray(short vec[], int offset, int len)
throws EOFException, FormatException {
while (len > 0) {
int shortsleft = bytesinbuffer / 2;
if (shortsleft == 0) {
assertSize(2); // force a buffer refill - throws
// exception if it can't
continue;
}
int reallyread = (len < shortsleft) ? len : shortsleft;
if (MSBFirst) {
for (int i = 0; i < reallyread; i++) {
vec[offset++] = MoreMath.BuildShortBE(buffer, curptr);
curptr += 2;
}
} else {
for (int i = 0; i < reallyread; i++) {
vec[offset++] = MoreMath.BuildShortLE(buffer, curptr);
curptr += 2;
}
}
len -= reallyread;
bytesinbuffer -= (2 * reallyread);
}
}
/**
* Reads and returns a long.
*
* @return the 4 bytes merged into a long, according to the current byte
* ordering
* @exception EOFException there were less than 4 bytes left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the integer
*/
public int readInteger() throws EOFException, FormatException {
// MSBFirst must be set when we are called
assertSize(4);
curptr += 4;
bytesinbuffer -= 4;
return MoreMath.BuildInteger(buffer, curptr - 4, MSBFirst);
}
/**
* Reads an array of integers.
*
* @exception EOFException there were fewer bytes than needed in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the array
*/
public void readIntegerArray(int vec[], int offset, int len)
throws EOFException, FormatException {
while (len > 0) {
int intsleft = bytesinbuffer / 4;
if (intsleft == 0) {
assertSize(4); // force a buffer refill
continue;
}
int reallyread = (len < intsleft) ? len : intsleft;
int cursor = curptr;
if (MSBFirst) {
for (int i = 0; i < reallyread; i++) {
vec[offset++] = MoreMath.BuildIntegerBE(buffer, cursor);
cursor += 4;
}
} else {
for (int i = 0; i < reallyread; i++) {
vec[offset++] = MoreMath.BuildIntegerLE(buffer, cursor);
cursor += 4;
}
}
len -= reallyread;
bytesinbuffer -= (4 * reallyread);
curptr = cursor;
}
}
/**
* Reads an array of floats from the input.
*
* @param vec the vector to read into
* @param offset the first float read goes into vec[offset]
* @param len the number of floats to read from the stream
* @exception EOFException not enough bytes were left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the integer
*/
public void readFloatArray(float vec[], int offset, int len)
throws EOFException, FormatException {
while (len > 0) {
int floatsleft = bytesinbuffer / 4;
if (floatsleft == 0) {
assertSize(4); // force a buffer refill
continue;
}
int reallyread = (len < floatsleft) ? len : floatsleft;
int cursor = curptr;
if (MSBFirst) {
for (int i = 0; i < reallyread; i++) {
int floatasint = MoreMath.BuildIntegerBE(buffer, cursor);
vec[offset++] = Float.intBitsToFloat(floatasint);
cursor += 4;
}
} else {
for (int i = 0; i < reallyread; i++) {
int floatasint = MoreMath.BuildIntegerLE(buffer, cursor);
vec[offset++] = Float.intBitsToFloat(floatasint);
cursor += 4;
}
}
len -= reallyread;
bytesinbuffer -= (4 * reallyread);
curptr = cursor;
}
}
/**
* Reads and returns a long.
*
* @return the 8 bytes merged into a long, according to the current byte
* ordering
* @exception EOFException there were less than 8 bytes left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the long
* @see #read(byte[])
*/
public long readLong() throws EOFException, FormatException {
assertSize(8);
curptr += 8;
bytesinbuffer -= 8;
return MoreMath.BuildLong(buffer, curptr - 8, MSBFirst);
}
/**
* Reads <code>length</code> bytes and returns a string composed of the
* bytes cast to chars.
*
* @param length the number of bytes to read into the string
* @return the composed string
* @exception EOFException there were less than <code>length</code> bytes
* left in the file
* @exception FormatException rethrow of IOExceptions encountered while
* reading the bytes for the short
*/
public String readFixedLengthString(int length) throws EOFException,
FormatException {
String retstring;
if (length < buffer.length) {
assertSize(length);
retstring = new String(buffer, curptr, length);
curptr += length;
bytesinbuffer -= length;
} else {
byte foo[] = readBytes(length, false);
retstring = new String(foo, 0, length);
}
return retstring;
}
}