package nom.tam.util;
/* Copyright: Thomas McGlynn 1997-1998.
* This code may be used for any purpose, non-commercial
* or commercial so long as this copyright notice is retained
* in the source code or included in or referred to in any
* derived software.
*/
// What do we use in here?
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
/** This class is intended for high performance I/O in scientific applications.
* It combines the functionality of the BufferedOutputStream and the
* DataOutputStream as well as more efficient handling of arrays.
* This minimizes the number of method calls that are required to
* write data. Informal tests of this method show that it can
* be as much as 10 times faster than using a DataOutputStream layered
* on a BufferedOutputStream for writing large arrays. The performance
* gain on scalars or small arrays will be less but there should probably
* never be substantial degradation of performance.
*
* One routine is added to the public interface of DataOutput, writePrimitiveArray.
* This routine provides efficient protocols for writing arrays.
*
* Note that there is substantial duplication of code to minimize method
* invocations.
*/
public class BufferedDataOutputStream
extends BufferedOutputStream
implements DataOutput {
/** Use the BufferedOutputStream constructor
* @param o An open output stream.
*/
public BufferedDataOutputStream(OutputStream o) {
super(o);
}
/** Use the BufferedOutputStream constructor
* @param o An open output stream.
* @param bufLength The buffer size.
*/
public BufferedDataOutputStream(OutputStream o, int bufLength) {
super(o, bufLength);
}
/** Write a boolean value
* @param b The value to be written. Externally true is represented as
* a byte of 1 and false as a byte value of 0.
*/
public void writeBoolean(boolean b) throws IOException {
if (b) {
write( (byte) 1);
} else {
write( (byte) 0);
}
}
/** Write a byte value.
*/
public void writeByte(int b) throws IOException {
write((byte) b);
}
/** Write an integer value.
*/
public void writeInt(int i) throws IOException {
byte[] b = new byte[4];
b[0] = (byte) (i >>> 24);
b[1] = (byte) (i >>> 16);
b[2] = (byte) (i >>> 8);
b[3] = (byte) i;
write(b, 0, 4);
}
/** Write a short value.
*/
public void writeShort(int s) throws IOException {
byte[] b = new byte[2];
b[0] = (byte) (s >>> 8);
b[1] = (byte) s;
write(b, 0, 2);
}
/** Write a char value.
*/
public void writeChar(int c) throws IOException {
byte[] b = new byte[2];
b[0] = (byte) (c >>> 8);
b[1] = (byte) c;
write(b, 0, 2);
}
/** Write a long value.
*/
public void writeLong(long l) throws IOException {
byte[] b = new byte[8];
b[0] = (byte) (l >>> 56);
b[1] = (byte) (l >>> 48);
b[2] = (byte) (l >>> 40);
b[3] = (byte) (l >>> 32);
b[4] = (byte) (l >>> 24);
b[5] = (byte) (l >>> 16);
b[6] = (byte) (l >>> 8);
b[7] = (byte) l;
write(b, 0, 8);
}
/** Write a float value.
*/
public void writeFloat(float f) throws IOException {
int i = Float.floatToIntBits(f);
byte[] b = new byte[4]; // Repeat this from writeInt to save method call.
b[0] = (byte) (i >>> 24);
b[1] = (byte) (i >>> 16);
b[2] = (byte) (i >>> 8);
b[3] = (byte) i;
write(b, 0, 4);
}
/** Write a double value.
*/
public void writeDouble(double d) throws IOException {
long l = Double.doubleToLongBits(d);
byte[] b = new byte[8];
b[0] = (byte) (l >>> 56);
b[1] = (byte) (l >>> 48);
b[2] = (byte) (l >>> 40);
b[3] = (byte) (l >>> 32);
b[4] = (byte) (l >>> 24);
b[5] = (byte) (l >>> 16);
b[6] = (byte) (l >>> 8);
b[7] = (byte) l;
write(b, 0, 8);
}
/** Write a string using the local protocol to convert char's to bytes.
*
* @param s The string to be written.
*/
public void writeBytes(String s) throws IOException {
write(s.getBytes(),0,s.length());
}
/** Write a string as an array of chars.
*/
public void writeChars(String s) throws IOException {
int len = s.length();
char c;
byte[] b = new byte[2*len];
for (int i=0; i<len; i += 1) {
c = s.charAt(i);
b[2*i] = (byte) (c>>8);
b[2*i+1] = (byte) c;
}
write(b, 0, 2*len);
}
/** Write a string as a UTF. Note that this class does not
* handle this situation efficiently since it creates
* new DataOutputStream to handle each call.
*/
public void writeUTF(String s) throws IOException{
// Punt on this one and use standard routines.
DataOutputStream d = new DataOutputStream(this);
d.writeUTF(s);
d.flush();
}
/** This routine provides efficient writing of arrays of any primitive type.
* The String class is also handled but it is an error to invoke this
* method with an object that is not an array of these types. If the
* array is multidimensional, then it calls itself recursively to write
* the entire array. Strings are written using the standard
* 1 byte format (i.e., as in writeBytes).
*
* If the array is an array of objects, then writePrimitiveArray will
* be called for each element of the array.
*
* @param o The object to be written. It must be an array of a primitive
* type, Object, or String.
*/
public void writePrimitiveArray(Object o) throws IOException {
String className = o.getClass().getName();
if (className.charAt(0) != '[') {
throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray:"+className);
}
// Is this a multidimensional array? If so process recursively.
if (className.charAt(1) == '[') {
for (int i=0; i < ((Object[])o).length; i += 1) {
writePrimitiveArray(((Object[])o)[i]);
}
} else {
// This is a one-d array. Process it using our special functions.
switch (className.charAt(1)) {
case 'Z': writeBooleanArray((boolean[])o);
break;
case 'B': write((byte[])o, 0, ((byte[])o).length);
break;
case 'C': writeCharArray((char[])o);
break;
case 'S': writeShortArray((short[])o);
break;
case 'I': writeIntArray((int[])o);
break;
case 'J': writeLongArray((long[])o);
break;
case 'F': writeFloatArray((float[])o);
break;
case 'D': writeDoubleArray((double[])o);
break;
case 'L':
// Handle two exceptions: an array of strings, or an
// array of objects. .
if (className.equals("[Ljava.lang.String;") ) {
writeStringArray((String[])o);
} else if (className.equals("[Ljava.lang.Object;")) {
for (int i=0; i< ((Object[])o).length; i += 1) {
writePrimitiveArray(((Object[])o)[i]);
}
} else {
throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: "+className);
}
break;
default:
throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: "+className);
}
}
}
/** Write an array of booleans.
*/
protected void writeBooleanArray(boolean[] b) throws IOException {
byte[] bx = new byte[b.length];
for (int i=0; i<b.length; i += 1) {
if (b[i]) {
bx[i] = 1;
} else {
bx[i] = 0;
}
}
write(bx, 0, bx.length);
}
/** Write an array of shorts.
*/
protected void writeShortArray(short[] s) throws IOException {
byte[] b = new byte[2*s.length];
for(int i=0; i<s.length; i += 1) {
int t = s[i];
b[2*i] = (byte) (t>>8);
b[2*i + 1] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of char's.
*/
protected void writeCharArray(char[] c) throws IOException {
byte[] b = new byte[2*c.length];
for(int i=0; i<c.length; i += 1) {
int t = c[i];
b[2*i] = (byte) (t>>8);
b[2*i + 1] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of int's.
*/
protected void writeIntArray(int[] i) throws IOException {
byte[] b = new byte[4*i.length];
for (int ii=0; ii<i.length; ii += 1) {
int t = i[ii];
b[4*ii] = (byte)(t >>> 24);
b[4*ii+1] = (byte)(t >>> 16);
b[4*ii+2] = (byte)(t >>> 8);
b[4*ii+3] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of longs.
*/
protected void writeLongArray(long[] l) throws IOException {
byte[] b = new byte[8*l.length];
for (int i=0; i<l.length; i += 1) {
long t = l[i];
b[8*i] = (byte)(t >>> 56);
b[8*i+1] = (byte)(t >>> 48);
b[8*i+2] = (byte)(t >>> 40);
b[8*i+3] = (byte)(t >>> 32);
b[8*i+4] = (byte)(t >>> 24);
b[8*i+5] = (byte)(t >>> 16);
b[8*i+6] = (byte)(t >>> 8);
b[8*i+7] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of floats.
*/
protected void writeFloatArray(float[] f) throws IOException {
byte[] b = new byte[4*f.length];
for (int i=0; i<f.length; i += 1) {
int t = Float.floatToIntBits(f[i]);
b[4*i] = (byte)(t >>> 24);
b[4*i+1] = (byte)(t >>> 16);
b[4*i+2] = (byte)(t >>> 8);
b[4*i+3] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of doubles.
*/
protected void writeDoubleArray(double[] d) throws IOException {
byte[] b = new byte[8*d.length];
for (int i=0; i<d.length; i += 1) {
long t = Double.doubleToLongBits(d[i]);
b[8*i] = (byte)(t >>> 56);
b[8*i+1] = (byte)(t >>> 48);
b[8*i+2] = (byte)(t >>> 40);
b[8*i+3] = (byte)(t >>> 32);
b[8*i+4] = (byte)(t >>> 24);
b[8*i+5] = (byte)(t >>> 16);
b[8*i+6] = (byte)(t >>> 8);
b[8*i+7] = (byte) t;
}
write(b, 0, b.length);
}
/** Write an array of Strings -- equivalent to calling writeBytes for each string.
*/
protected void writeStringArray(String[] s) throws IOException {
// Don't worry about buffering this specially since the
// strings may be of differing lengths.
for (int i=0; i<s.length; i += 1) {
writeBytes(s[i]);
}
}
/** Test this class */
public static void main(String[] args) throws Exception {
// Call the test routines in BufferedDataInputStream.main.
BufferedDataInputStream.main(args);
}
}