3 /* Copyright: Thomas McGlynn 1997-1999.
4 * This code may be used for any purpose, non-commercial
5 * or commercial so long as this copyright notice is retained
6 * in the source code or included in or referred to in any
9 // What do we use in here?
10 import java.io.OutputStream;
11 import java.io.BufferedOutputStream;
12 import java.io.DataOutput;
13 import java.io.DataOutputStream;
14 import java.io.IOException;
16 /** This class is intended for high performance I/O in scientific applications.
17 * It combines the functionality of the BufferedOutputStream and the
18 * DataOutputStream as well as more efficient handling of arrays.
19 * This minimizes the number of method calls that are required to
20 * write data. Informal tests of this method show that it can
21 * be as much as 10 times faster than using a DataOutputStream layered
22 * on a BufferedOutputStream for writing large arrays. The performance
23 * gain on scalars or small arrays will be less but there should probably
24 * never be substantial degradation of performance.
26 * Note that there is substantial duplication of code to minimize method
27 * invocations. However simple output methods were used where empirical
28 * tests seemed to indicate that the simpler method did not cost any time.
29 * It seems likely that most of these variations will be
30 * washed out across different compilers and users who wish to tune
31 * the method for their particular system may wish to compare the
32 * the implementation of write(int[], int, int) with write(float[], int, int).
34 * Testing and timing for this class is
35 * peformed in the nom.tam.util.test.BufferedFileTester class.
37 public class BufferedDataOutputStream
38 extends BufferedOutputStream
39 implements ArrayDataOutput {
41 /** Use the BufferedOutputStream constructor
42 * @param o An open output stream.
44 public BufferedDataOutputStream(OutputStream o) {
48 /** Use the BufferedOutputStream constructor
49 * @param o An open output stream.
50 * @param bufLength The buffer size.
52 public BufferedDataOutputStream(OutputStream o, int bufLength) {
56 /** Write a boolean value
57 * @param b The value to be written. Externally true is represented as
58 * a byte of 1 and false as a byte value of 0.
60 public void writeBoolean(boolean b) throws IOException {
70 /** Write a byte value.
72 public void writeByte(int b) throws IOException {
74 buf[count++] = (byte) b;
77 /** Write an integer value.
79 public void writeInt(int i) throws IOException {
82 buf[count++] = (byte) (i >>> 24);
83 buf[count++] = (byte) (i >>> 16);
84 buf[count++] = (byte) (i >>> 8);
85 buf[count++] = (byte) i;
88 /** Write a short value.
90 public void writeShort(int s) throws IOException {
93 buf[count++] = (byte) (s >>> 8);
94 buf[count++] = (byte) s;
98 /** Write a char value.
100 public void writeChar(int c) throws IOException {
103 buf[count++] = (byte) (c >>> 8);
104 buf[count++] = (byte) c;
107 /** Write a long value.
109 public void writeLong(long l) throws IOException {
113 buf[count++] = (byte) (l >>> 56);
114 buf[count++] = (byte) (l >>> 48);
115 buf[count++] = (byte) (l >>> 40);
116 buf[count++] = (byte) (l >>> 32);
117 buf[count++] = (byte) (l >>> 24);
118 buf[count++] = (byte) (l >>> 16);
119 buf[count++] = (byte) (l >>> 8);
120 buf[count++] = (byte) l;
123 /** Write a float value.
125 public void writeFloat(float f) throws IOException {
129 int i = Float.floatToIntBits(f);
131 buf[count++] = (byte) (i >>> 24);
132 buf[count++] = (byte) (i >>> 16);
133 buf[count++] = (byte) (i >>> 8);
134 buf[count++] = (byte) i;
139 /** Write a double value.
141 public void writeDouble(double d) throws IOException {
144 long l = Double.doubleToLongBits(d);
146 buf[count++] = (byte) (l >>> 56);
147 buf[count++] = (byte) (l >>> 48);
148 buf[count++] = (byte) (l >>> 40);
149 buf[count++] = (byte) (l >>> 32);
150 buf[count++] = (byte) (l >>> 24);
151 buf[count++] = (byte) (l >>> 16);
152 buf[count++] = (byte) (l >>> 8);
153 buf[count++] = (byte) l;
157 /** Write a string using the local protocol to convert char's to bytes.
159 * @param s The string to be written.
161 public void writeBytes(String s) throws IOException {
163 write(s.getBytes(), 0, s.length());
166 /** Write a string as an array of chars.
168 public void writeChars(String s) throws IOException {
170 for (int i = 0; i < s.length(); i += 1) {
171 writeChar(s.charAt(i));
175 /** Write a string as a UTF. Note that this class does not
176 * handle this situation efficiently since it creates
177 * new DataOutputStream to handle each call.
179 public void writeUTF(String s) throws IOException {
181 // Punt on this one and use standard routines.
182 DataOutputStream d = new DataOutputStream(this);
188 /** This routine provides efficient writing of arrays of any primitive type.
189 * The String class is also handled but it is an error to invoke this
190 * method with an object that is not an array of these types. If the
191 * array is multidimensional, then it calls itself recursively to write
192 * the entire array. Strings are written using the standard
193 * 1 byte format (i.e., as in writeBytes).
195 * If the array is an array of objects, then writePrimitiveArray will
196 * be called for each element of the array.
198 * @param o The object to be written. It must be an array of a primitive
199 * type, Object, or String.
201 public void writePrimitiveArray(Object o) throws IOException {
205 /** This routine provides efficient writing of arrays of any primitive type.
206 * The String class is also handled but it is an error to invoke this
207 * method with an object that is not an array of these types. If the
208 * array is multidimensional, then it calls itself recursively to write
209 * the entire array. Strings are written using the standard
210 * 1 byte format (i.e., as in writeBytes).
212 * If the array is an array of objects, then writePrimitiveArray will
213 * be called for each element of the array.
215 * @param o The object to be written. It must be an array of a primitive
216 * type, Object, or String.
218 public void writeArray(Object o) throws IOException {
219 String className = o.getClass().getName();
221 if (className.charAt(0) != '[') {
222 throw new IOException("Invalid object passed to BufferedDataOutputStream.write" + className);
225 // Is this a multidimensional array? If so process recursively.
226 if (className.charAt(1) == '[') {
227 for (int i = 0; i < ((Object[]) o).length; i += 1) {
228 writeArray(((Object[]) o)[i]);
232 // This is a one-d array. Process it using our special functions.
233 switch (className.charAt(1)) {
235 write((boolean[]) o, 0, ((boolean[]) o).length);
238 write((byte[]) o, 0, ((byte[]) o).length);
241 write((char[]) o, 0, ((char[]) o).length);
244 write((short[]) o, 0, ((short[]) o).length);
247 write((int[]) o, 0, ((int[]) o).length);
250 write((long[]) o, 0, ((long[]) o).length);
253 write((float[]) o, 0, ((float[]) o).length);
256 write((double[]) o, 0, ((double[]) o).length);
260 // Handle two exceptions: an array of strings, or an
261 // array of objects. .
262 if (className.equals("[Ljava.lang.String;")) {
263 write((String[]) o, 0, ((String[]) o).length);
264 } else if (className.equals("[Ljava.lang.Object;")) {
265 for (int i = 0; i < ((Object[]) o).length; i += 1) {
266 writeArray(((Object[]) o)[i]);
269 throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className);
273 throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className);
279 /** Write an array of booleans.
281 public void write(boolean[] b) throws IOException {
282 write(b, 0, b.length);
285 /** Write a segment of an array of booleans.
287 public void write(boolean[] b, int start, int len) throws IOException {
289 for (int i = start; i < start + len; i += 1) {
291 if (count + 1 > buf.length) {
302 /** Write an array of shorts.
304 public void write(short[] s) throws IOException {
305 write(s, 0, s.length);
308 /** Write a segment of an array of shorts.
310 public void write(short[] s, int start, int len) throws IOException {
312 for (int i = start; i < start + len; i += 1) {
313 if (count + 2 > buf.length) {
316 buf[count++] = (byte) (s[i] >> 8);
317 buf[count++] = (byte) (s[i]);
321 /** Write an array of char's.
323 public void write(char[] c) throws IOException {
324 write(c, 0, c.length);
327 /** Write a segment of an array of char's.
329 public void write(char[] c, int start, int len) throws IOException {
331 for (int i = start; i < start + len; i += 1) {
332 if (count + 2 > buf.length) {
335 buf[count++] = (byte) (c[i] >> 8);
336 buf[count++] = (byte) (c[i]);
340 /** Write an array of int's.
342 public void write(int[] i) throws IOException {
343 write(i, 0, i.length);
346 /** Write a segment of an array of int's.
348 public void write(int[] i, int start, int len) throws IOException {
350 for (int ii = start; ii < start + len; ii += 1) {
351 if (count + 4 > buf.length) {
355 buf[count++] = (byte) (i[ii] >>> 24);
356 buf[count++] = (byte) (i[ii] >>> 16);
357 buf[count++] = (byte) (i[ii] >>> 8);
358 buf[count++] = (byte) (i[ii]);
364 /** Write an array of longs.
366 public void write(long[] l) throws IOException {
367 write(l, 0, l.length);
370 /** Write a segement of an array of longs.
372 public void write(long[] l, int start, int len) throws IOException {
374 for (int i = start; i < start + len; i += 1) {
375 if (count + 8 > buf.length) {
378 int t = (int) (l[i] >>> 32);
380 buf[count++] = (byte) (t >>> 24);
381 buf[count++] = (byte) (t >>> 16);
382 buf[count++] = (byte) (t >>> 8);
383 buf[count++] = (byte) (t);
387 buf[count++] = (byte) (t >>> 24);
388 buf[count++] = (byte) (t >>> 16);
389 buf[count++] = (byte) (t >>> 8);
390 buf[count++] = (byte) (t);
394 /** Write an array of floats.
396 public void write(float[] f) throws IOException {
397 write(f, 0, f.length);
400 public void write(float[] f, int start, int len) throws IOException {
402 for (int i = start; i < start + len; i += 1) {
404 if (count + 4 > buf.length) {
407 int t = Float.floatToIntBits(f[i]);
408 buf[count++] = (byte) (t >>> 24);
409 buf[count++] = (byte) (t >>> 16);
410 buf[count++] = (byte) (t >>> 8);
411 buf[count++] = (byte) t;
415 /** Write an array of doubles.
417 public void write(double[] d) throws IOException {
418 write(d, 0, d.length);
421 public void write(double[] d, int start, int len) throws IOException {
423 for (int i = start; i < start + len; i += 1) {
424 if (count + 8 > buf.length) {
427 long t = Double.doubleToLongBits(d[i]);
429 int ix = (int) (t >>> 32);
431 buf[count++] = (byte) (ix >>> 24);
432 buf[count++] = (byte) (ix >>> 16);
433 buf[count++] = (byte) (ix >>> 8);
434 buf[count++] = (byte) (ix);
438 buf[count++] = (byte) (ix >>> 24);
439 buf[count++] = (byte) (ix >>> 16);
440 buf[count++] = (byte) (ix >>> 8);
441 buf[count++] = (byte) ix;
446 /** Write an array of Strings -- equivalent to calling writeBytes for each string.
448 public void write(String[] s) throws IOException {
449 write(s, 0, s.length);
452 /** Write a segment of an array of Strings.
453 * Equivalent to calling writeBytes for the selected elements.
455 public void write(String[] s, int start, int len) throws IOException {
457 // Do not worry about buffering this specially since the
458 // strings may be of differing lengths.
460 for (int i = 0; i < s.length; i += 1) {
465 /* See if there is enough space to add
466 * something to the buffer.
468 protected void checkBuf(int need) throws IOException {
470 if (count + need > buf.length) {
471 out.write(buf, 0, count);