Begin versioning.
[fits.git] / src / nom / tam / util / BufferedDataOutputStream.java
1 package nom.tam.util;
2
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
7  * derived software.
8  */
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;
15
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.
25  * <p>
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).
33  * <p>
34  * Testing and timing for this class is
35  * peformed in the nom.tam.util.test.BufferedFileTester class.
36  */
37 public class BufferedDataOutputStream
38         extends BufferedOutputStream
39         implements ArrayDataOutput {
40
41     /** Use the BufferedOutputStream constructor
42      * @param o An open output stream.
43      */
44     public BufferedDataOutputStream(OutputStream o) {
45         super(o, 32768);
46     }
47
48     /** Use the BufferedOutputStream constructor
49      * @param o           An open output stream.
50      * @param bufLength   The buffer size.
51      */
52     public BufferedDataOutputStream(OutputStream o, int bufLength) {
53         super(o, bufLength);
54     }
55
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.
59      */
60     public void writeBoolean(boolean b) throws IOException {
61
62         checkBuf(1);
63         if (b) {
64             buf[count++] = 1;
65         } else {
66             buf[count++] = 0;
67         }
68     }
69
70     /** Write a byte value.
71      */
72     public void writeByte(int b) throws IOException {
73         checkBuf(1);
74         buf[count++] = (byte) b;
75     }
76
77     /** Write an integer value.
78      */
79     public void writeInt(int i) throws IOException {
80
81         checkBuf(4);
82         buf[count++] = (byte) (i >>> 24);
83         buf[count++] = (byte) (i >>> 16);
84         buf[count++] = (byte) (i >>> 8);
85         buf[count++] = (byte) i;
86     }
87
88     /** Write a short value.
89      */
90     public void writeShort(int s) throws IOException {
91
92         checkBuf(2);
93         buf[count++] = (byte) (s >>> 8);
94         buf[count++] = (byte) s;
95
96     }
97
98     /** Write a char value.
99      */
100     public void writeChar(int c) throws IOException {
101
102         checkBuf(2);
103         buf[count++] = (byte) (c >>> 8);
104         buf[count++] = (byte) c;
105     }
106
107     /** Write a long value.
108      */
109     public void writeLong(long l) throws IOException {
110
111         checkBuf(8);
112
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;
121     }
122
123     /** Write a float value.
124      */
125     public void writeFloat(float f) throws IOException {
126
127         checkBuf(4);
128
129         int i = Float.floatToIntBits(f);
130
131         buf[count++] = (byte) (i >>> 24);
132         buf[count++] = (byte) (i >>> 16);
133         buf[count++] = (byte) (i >>> 8);
134         buf[count++] = (byte) i;
135
136
137     }
138
139     /** Write a double value.
140      */
141     public void writeDouble(double d) throws IOException {
142
143         checkBuf(8);
144         long l = Double.doubleToLongBits(d);
145
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;
154
155     }
156
157     /** Write a string using the local protocol to convert char's to bytes.
158      *
159      * @param s   The string to be written.
160      */
161     public void writeBytes(String s) throws IOException {
162
163         write(s.getBytes(), 0, s.length());
164     }
165
166     /** Write a string as an array of chars.
167      */
168     public void writeChars(String s) throws IOException {
169
170         for (int i = 0; i < s.length(); i += 1) {
171             writeChar(s.charAt(i));
172         }
173     }
174
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.
178      */
179     public void writeUTF(String s) throws IOException {
180
181         // Punt on this one and use standard routines.
182         DataOutputStream d = new DataOutputStream(this);
183         d.writeUTF(s);
184         d.flush();
185         d.close();
186     }
187
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).
194      *
195      * If the array is an array of objects, then writePrimitiveArray will
196      * be called for each element of the array.
197      *
198      * @param o  The object to be written.  It must be an array of a primitive
199      *           type, Object, or String.
200      */
201     public void writePrimitiveArray(Object o) throws IOException {
202         writeArray(o);
203     }
204
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).
211      *
212      * If the array is an array of objects, then writePrimitiveArray will
213      * be called for each element of the array.
214      *
215      * @param o  The object to be written.  It must be an array of a primitive
216      *           type, Object, or String.
217      */
218     public void writeArray(Object o) throws IOException {
219         String className = o.getClass().getName();
220
221         if (className.charAt(0) != '[') {
222             throw new IOException("Invalid object passed to BufferedDataOutputStream.write" + className);
223         }
224
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]);
229             }
230         } else {
231
232             // This is a one-d array.  Process it using our special functions.
233             switch (className.charAt(1)) {
234                 case 'Z':
235                     write((boolean[]) o, 0, ((boolean[]) o).length);
236                     break;
237                 case 'B':
238                     write((byte[]) o, 0, ((byte[]) o).length);
239                     break;
240                 case 'C':
241                     write((char[]) o, 0, ((char[]) o).length);
242                     break;
243                 case 'S':
244                     write((short[]) o, 0, ((short[]) o).length);
245                     break;
246                 case 'I':
247                     write((int[]) o, 0, ((int[]) o).length);
248                     break;
249                 case 'J':
250                     write((long[]) o, 0, ((long[]) o).length);
251                     break;
252                 case 'F':
253                     write((float[]) o, 0, ((float[]) o).length);
254                     break;
255                 case 'D':
256                     write((double[]) o, 0, ((double[]) o).length);
257                     break;
258                 case 'L':
259
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]);
267                         }
268                     } else {
269                         throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className);
270                     }
271                     break;
272                 default:
273                     throw new IOException("Invalid object passed to BufferedDataOutputStream.writeArray: " + className);
274             }
275         }
276
277     }
278
279     /** Write an array of booleans.
280      */
281     public void write(boolean[] b) throws IOException {
282         write(b, 0, b.length);
283     }
284
285     /** Write a segment of an array of booleans.
286      */
287     public void write(boolean[] b, int start, int len) throws IOException {
288
289         for (int i = start; i < start + len; i += 1) {
290
291             if (count + 1 > buf.length) {
292                 checkBuf(1);
293             }
294             if (b[i]) {
295                 buf[count++] = 1;
296             } else {
297                 buf[count++] = 0;
298             }
299         }
300     }
301
302     /** Write an array of shorts.
303      */
304     public void write(short[] s) throws IOException {
305         write(s, 0, s.length);
306     }
307
308     /** Write a segment of an array of shorts.
309      */
310     public void write(short[] s, int start, int len) throws IOException {
311
312         for (int i = start; i < start + len; i += 1) {
313             if (count + 2 > buf.length) {
314                 checkBuf(2);
315             }
316             buf[count++] = (byte) (s[i] >> 8);
317             buf[count++] = (byte) (s[i]);
318         }
319     }
320
321     /** Write an array of char's.
322      */
323     public void write(char[] c) throws IOException {
324         write(c, 0, c.length);
325     }
326
327     /** Write a segment of an array of char's.
328      */
329     public void write(char[] c, int start, int len) throws IOException {
330
331         for (int i = start; i < start + len; i += 1) {
332             if (count + 2 > buf.length) {
333                 checkBuf(2);
334             }
335             buf[count++] = (byte) (c[i] >> 8);
336             buf[count++] = (byte) (c[i]);
337         }
338     }
339
340     /** Write an array of int's.
341      */
342     public void write(int[] i) throws IOException {
343         write(i, 0, i.length);
344     }
345
346     /** Write a segment of an array of int's.
347      */
348     public void write(int[] i, int start, int len) throws IOException {
349
350         for (int ii = start; ii < start + len; ii += 1) {
351             if (count + 4 > buf.length) {
352                 checkBuf(4);
353             }
354
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]);
359
360         }
361
362     }
363
364     /** Write an array of longs.
365      */
366     public void write(long[] l) throws IOException {
367         write(l, 0, l.length);
368     }
369
370     /** Write a segement of an array of longs.
371      */
372     public void write(long[] l, int start, int len) throws IOException {
373
374         for (int i = start; i < start + len; i += 1) {
375             if (count + 8 > buf.length) {
376                 checkBuf(8);
377             }
378             int t = (int) (l[i] >>> 32);
379
380             buf[count++] = (byte) (t >>> 24);
381             buf[count++] = (byte) (t >>> 16);
382             buf[count++] = (byte) (t >>> 8);
383             buf[count++] = (byte) (t);
384
385             t = (int) (l[i]);
386
387             buf[count++] = (byte) (t >>> 24);
388             buf[count++] = (byte) (t >>> 16);
389             buf[count++] = (byte) (t >>> 8);
390             buf[count++] = (byte) (t);
391         }
392     }
393
394     /** Write an array of floats.
395      */
396     public void write(float[] f) throws IOException {
397         write(f, 0, f.length);
398     }
399
400     public void write(float[] f, int start, int len) throws IOException {
401
402         for (int i = start; i < start + len; i += 1) {
403
404             if (count + 4 > buf.length) {
405                 checkBuf(4);
406             }
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;
412         }
413     }
414
415     /** Write an array of doubles.
416      */
417     public void write(double[] d) throws IOException {
418         write(d, 0, d.length);
419     }
420
421     public void write(double[] d, int start, int len) throws IOException {
422
423         for (int i = start; i < start + len; i += 1) {
424             if (count + 8 > buf.length) {
425                 checkBuf(8);
426             }
427             long t = Double.doubleToLongBits(d[i]);
428
429             int ix = (int) (t >>> 32);
430
431             buf[count++] = (byte) (ix >>> 24);
432             buf[count++] = (byte) (ix >>> 16);
433             buf[count++] = (byte) (ix >>> 8);
434             buf[count++] = (byte) (ix);
435
436             ix = (int) t;
437
438             buf[count++] = (byte) (ix >>> 24);
439             buf[count++] = (byte) (ix >>> 16);
440             buf[count++] = (byte) (ix >>> 8);
441             buf[count++] = (byte) ix;
442         }
443
444     }
445
446     /** Write an array of Strings -- equivalent to calling writeBytes for each string.
447      */
448     public void write(String[] s) throws IOException {
449         write(s, 0, s.length);
450     }
451
452     /** Write a segment of an array of Strings.
453      * Equivalent to calling writeBytes for the selected elements.
454      */
455     public void write(String[] s, int start, int len) throws IOException {
456
457         // Do not worry about buffering this specially since the
458         // strings may be of differing lengths.
459
460         for (int i = 0; i < s.length; i += 1) {
461             writeBytes(s[i]);
462         }
463     }
464
465     /* See if there is enough space to add
466      * something to the buffer.
467      */
468     protected void checkBuf(int need) throws IOException {
469
470         if (count + need > buf.length) {
471             out.write(buf, 0, count);
472             count = 0;
473         }
474     }
475 }