Begin versioning.
[fits.git] / src / nom / tam / util / BufferedDataInputStream.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.InputStream;
11 import java.io.InputStreamReader;
12 import java.io.BufferedReader;
13 import java.io.BufferedInputStream;
14 import java.io.DataInputStream;
15 import java.io.IOException;
16 import java.io.EOFException;
17
18 /** This class is intended for high performance I/O in scientific applications.
19  * It combines the functionality of the BufferedInputStream and the
20  * DataInputStream as well as more efficient handling of arrays.
21  * This minimizes the number of method calls that are required to
22  * read data.  Informal tests of this method show that it can
23  * be as much as 10 times faster than using a DataInputStream layered
24  * on a BufferedInputStream for writing large arrays.  The performance
25  * gain on scalars or small arrays will be less but there should probably
26  * never be substantial degradation of performance.
27  * <p>
28  * Many new read calls are added to allow efficient reading
29  * off array data.  The read(Object o) call provides
30  * for reading a primitive array of arbitrary type or
31  * dimensionality.  There are also reads for each type
32  * of one dimensional array.
33  * <p>
34  * Note that there is substantial duplication of code to minimize method
35  * invocations.  E.g., the floating point read routines read the data
36  * as integer values and then convert to float.  However the integer
37  * code is duplicated rather than invoked.  There has been
38  * considerable effort expended to ensure that these routines are
39  * efficient, but they could easily be superceded if
40  * an efficient underlying I/O package were ever delivered
41  * as part of the basic Java libraries.
42  * [This has subsequently happened with the NIO package
43  *  and in an ideal universe these classes would be
44  *  rewritten to take advantage of NIO.]
45  * <p>
46  * Testing and timing routines are provided in the
47  * nom.tam.util.test.BufferedFileTester class.
48  *
49  * Version 1.1: October 12, 2000: Fixed handling of EOF to return
50  * partially read arrays when EOF is detected.
51  * Version 1.2: July 20, 2009: Added handling of very large Object
52  * arrays.  Additional work is required to handle very large arrays
53  * generally.
54  */
55 public class BufferedDataInputStream
56         extends BufferedInputStream
57         implements ArrayDataInput {
58
59     private long primitiveArrayCount;
60     private byte[] bb = new byte[8];
61
62     /** Use the BufferedInputStream constructor
63      */
64     public BufferedDataInputStream(InputStream o) {
65         super(o, 32768);
66     }
67
68     /** Use the BufferedInputStream constructor
69      */
70     public BufferedDataInputStream(InputStream o, int bufLength) {
71         super(o, bufLength);
72     }
73
74     /** Read a byte array.  This is the only method
75      *  for reading arrays in the fundamental I/O classes.
76      *  @param obuf    The byte array.
77      *  @param offset  The starting offset into the array.
78      *  @param len     The number of bytes to read.
79      *  @return The actual number of bytes read.
80      */
81     public int read(byte[] obuf, int offset, int len) throws IOException {
82
83         int total = 0;
84
85         while (len > 0) {
86
87             // Use just the buffered I/O to get needed info.
88
89             int xlen = super.read(obuf, offset, len);
90             if (xlen <= 0) {
91                 if (total == 0) {
92                     throw new EOFException();
93                 } else {
94                     return total;
95                 }
96             } else {
97                 len -= xlen;
98                 total += xlen;
99                 offset += xlen;
100             }
101         }
102         return total;
103
104     }
105
106     /** Read a boolean value.
107      * @return b  The value read.
108      */
109     public boolean readBoolean() throws IOException {
110
111         int b = read();
112         if (b == 1) {
113             return true;
114         } else {
115             return false;
116         }
117     }
118
119     /** Read a byte value in the range -128 to 127.
120      * @return The byte value as a byte (see read() to return the value
121      * as an integer.
122      */
123     public byte readByte() throws IOException {
124         return (byte) read();
125     }
126
127     /** Read a byte value in the range 0-255.
128      *  @return The byte value as an integer.
129      */
130     public int readUnsignedByte() throws IOException {
131         return read() | 0x00ff;
132     }
133
134     /** Read an integer.
135      *  @return The integer value.
136      */
137     public int readInt() throws IOException {
138
139         if (read(bb, 0, 4) < 4) {
140             throw new EOFException();
141         }
142         int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
143         return i;
144     }
145
146     /** Read a 2-byte value as a short (-32788 to 32767)
147      *  @return The short value.
148      */
149     public short readShort() throws IOException {
150
151         if (read(bb, 0, 2) < 2) {
152             throw new EOFException();
153         }
154
155         short s = (short) (bb[0] << 8 | (bb[1] & 0xFF));
156         return s;
157     }
158
159     /** Read a 2-byte value in the range 0-65536.
160      * @return the value as an integer.
161      */
162     public int readUnsignedShort() throws IOException {
163
164         if (read(bb, 0, 2) < 2) {
165             throw new EOFException();
166         }
167
168         return (bb[0] & 0xFF) << 8 | (bb[1] & 0xFF);
169     }
170
171     /** Read a 2-byte value as a character.
172      * @return The character read.
173      */
174     public char readChar() throws IOException {
175         byte[] b = new byte[2];
176
177         if (read(b, 0, 2) < 2) {
178             throw new EOFException();
179         }
180
181         char c = (char) (b[0] << 8 | (b[1] & 0xFF));
182         return c;
183     }
184
185     /** Read a long.
186      *  @return The value read.
187      */
188     public long readLong() throws IOException {
189
190         // use two ints as intermediarys to
191         // avoid casts of bytes to longs...
192         if (read(bb, 0, 8) < 8) {
193             throw new EOFException();
194         }
195         int i1 = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
196         int i2 = bb[4] << 24 | (bb[5] & 0xFF) << 16 | (bb[6] & 0xFF) << 8 | (bb[7] & 0xFF);
197         return (((long) i1) << 32) | (((long) i2) & 0x00000000ffffffffL);
198     }
199
200     /** Read a 4 byte real number.
201      *  @return The value as a float.
202      */
203     public float readFloat() throws IOException {
204
205         if (read(bb, 0, 4) < 4) {
206             throw new EOFException();
207         }
208
209         int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
210         return Float.intBitsToFloat(i);
211
212     }
213
214     /** Read an 8 byte real number.
215      *  @return The value as a double.
216      */
217     public double readDouble() throws IOException {
218
219         if (read(bb, 0, 8) < 8) {
220             throw new EOFException();
221         }
222
223         int i1 = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
224         int i2 = bb[4] << 24 | (bb[5] & 0xFF) << 16 | (bb[6] & 0xFF) << 8 | (bb[7] & 0xFF);
225
226         return Double.longBitsToDouble(((long) i1) << 32 | ((long) i2 & 0x00000000ffffffffL));
227     }
228
229     /** Read a buffer and signal an EOF if the buffer
230      *  cannot be fully read.
231      *  @param b  The buffer to be read.
232      */
233     public void readFully(byte[] b) throws IOException {
234         readFully(b, 0, b.length);
235     }
236
237     /** Read a buffer and signal an EOF if the requested elements
238      *  cannot be read.
239      *
240      *  This differs from read(b,off,len) since that call
241      *  will not signal and end of file unless no bytes can
242      *  be read.  However both of these routines will attempt
243      *  to fill their buffers completely.
244      *  @param b   The input buffer.
245      *  @param off The requested offset into the buffer.
246      *  @param len The number of bytes requested.
247      */
248     public void readFully(byte[] b, int off, int len) throws IOException {
249
250         if (off < 0 || len < 0 || off + len > b.length) {
251             throw new IOException("Attempt to read outside byte array");
252         }
253
254         if (read(b, off, len) < len) {
255             throw new EOFException();
256         }
257     }
258     /** Skip the requested number of bytes.
259      *  This differs from the skip call in that
260      *  it takes an long argument and will throw
261      *  an end of file if the full number of bytes cannot be skipped.
262      *  @param toSkip The number of bytes to skip.
263      */
264     private byte[] skipBuf = null;
265
266     public int skipBytes(int toSkip) throws IOException {
267         return (int) skipBytes((long) toSkip);
268     }
269
270     public long skipBytes(long toSkip) throws IOException {
271
272         long need = toSkip;
273
274         while (need > 0) {
275
276             try {
277                 long got = skip(need);
278                 if (got > 0) {
279                     need -= got;
280                 } else {
281                     break;
282                 }
283             } catch (IOException e) {
284                 // Some input streams (process outputs) don't allow
285                 // skipping.  The kludgy solution here is to
286                 // try to do a read when we get an error in the skip....
287                 // Real IO errors will presumably casue an error
288                 // in these reads too.
289                 if (skipBuf == null) {
290                     skipBuf = new byte[8192];
291                 }
292                 while (need > 8192) {
293                     int got = read(skipBuf, 0, 8192);
294                     if (got <= 0) {
295                         break;
296                     }
297                     need -= got;
298                 }
299                 while (need > 0) {
300                     int got = read(skipBuf, 0, (int) need);
301                     if (got <= 0) {
302                         break;
303                     }
304                     need -= got;
305                 }
306             }
307
308         }
309
310         if (need > 0) {
311             throw new EOFException();
312         } else {
313             return toSkip;
314         }
315     }
316
317     /** Read a String in the UTF format.
318      *  The implementation of this is very inefficient and
319      *  use of this class is not recommended for applications
320      *  which will use this routine heavily.
321      *  @return The String that was read.
322      */
323     public String readUTF() throws IOException {
324
325         // Punt on this one and use DataInputStream routines.
326         DataInputStream d = new DataInputStream(this);
327         return d.readUTF();
328
329     }
330
331     /**
332      * Emulate the deprecated DataInputStream.readLine() method.
333      * Originally we used the method itself, but Alan Brighton
334      * suggested using a BufferedReader to eliminate the deprecation warning.
335      * This method is slow regardless.
336      *
337      * @return The String read.
338      * @deprecated Use BufferedReader methods.
339      */
340     public String readLine() throws IOException {
341         // Punt on this and use BufferedReader routines.
342         BufferedReader d = new BufferedReader(new InputStreamReader(this));
343         return d.readLine();
344     }
345
346     /** This routine provides efficient reading of arrays of any primitive type.
347      * It is an error to invoke this method with an object that is not an array
348      * of some primitive type.  Note that there is no corresponding capability
349      * to writePrimitiveArray in BufferedDataOutputStream to read in an
350      * array of Strings.
351      *
352      * @param o  The object to be read.  It must be an array of a primitive type,
353      *           or an array of Object's.
354      * @deprecated  See readLArray(Object o).
355      */
356     public int readPrimitiveArray(Object o) throws IOException {
357
358         // Note that we assume that only a single thread is
359         // doing a primitive Array read at any given time.  Otherwise
360         // primitiveArrayCount can be wrong and also the
361         // input data can be mixed up.
362
363         primitiveArrayCount = 0;
364         return (int) readLArray(o);
365     }
366
367     /** Read an object.  An EOF will be signaled if the
368      *  object cannot be fully read.  The getPrimitiveArrayCount()
369      *  method may then be used to get a minimum number of bytes read.
370      *  @param o  The object to be read.  This object should
371      *            be a primitive (possibly multi-dimensional) array.
372      *
373      *  @returns  The number of bytes read.
374      *  @deprecated See readLArray(Object) which handles large arrays properly.
375      */
376     public int readArray(Object o) throws IOException {
377         return (int) readLArray(o);
378     }
379
380     /** Read an object.  An EOF will be signaled if the
381      *  object cannot be fully read.  The getPrimitiveArrayCount()
382      *  method may then be used to get a minimum number of bytes read.
383      *  @param o  The object to be read.  This object should
384      *            be a primitive (possibly multi-dimensional) array.
385      *
386      *  @returns  The number of bytes read.
387      */
388     public long readLArray(Object o) throws IOException {
389         primitiveArrayCount = 0;
390         return primitiveArrayRecurse(o);
391     }
392
393     /** Read recursively over a multi-dimensional array.
394      *  @return The number of bytes read.
395      */
396     protected long primitiveArrayRecurse(Object o) throws IOException {
397
398         if (o == null) {
399             return primitiveArrayCount;
400         }
401
402         String className = o.getClass().getName();
403
404         if (className.charAt(0) != '[') {
405             throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className);
406         }
407
408         // Is this a multidimensional array?  If so process recursively.
409         if (className.charAt(1) == '[') {
410             for (int i = 0; i < ((Object[]) o).length; i += 1) {
411                 primitiveArrayRecurse(((Object[]) o)[i]);
412             }
413         } else {
414
415             // This is a one-d array.  Process it using our special functions.
416             switch (className.charAt(1)) {
417                 case 'Z':
418                     primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length);
419                     break;
420                 case 'B':
421                     int len = read((byte[]) o, 0, ((byte[]) o).length);
422                     primitiveArrayCount += len;
423
424                     if (len < ((byte[]) o).length) {
425                         throw new EOFException();
426                     }
427                     break;
428                 case 'C':
429                     primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length);
430                     break;
431                 case 'S':
432                     primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length);
433                     break;
434                 case 'I':
435                     primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length);
436                     break;
437                 case 'J':
438                     primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length);
439                     break;
440                 case 'F':
441                     primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length);
442                     break;
443                 case 'D':
444                     primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length);
445                     break;
446                 case 'L':
447
448                     // Handle an array of Objects by recursion.  Anything
449                     // else is an error.
450                     if (className.equals("[Ljava.lang.Object;")) {
451                         for (int i = 0; i < ((Object[]) o).length; i += 1) {
452                             primitiveArrayRecurse(((Object[]) o)[i]);
453                         }
454                     } else {
455                         throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);
456                     }
457                     break;
458                 default:
459                     throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);
460             }
461         }
462         return primitiveArrayCount;
463     }
464
465     /** Ensure that the requested number of bytes
466      *  are available in the buffer or throw an EOF
467      *  if they cannot be obtained.  Note that this
468      *  routine will try to fill the buffer completely.
469      *
470      *  @param The required number of bytes.
471      */
472     private void fillBuf(int need) throws IOException {
473
474         if (count > pos) {
475             System.arraycopy(buf, pos, buf, 0, count - pos);
476             count -= pos;
477             need -= count;
478             pos = 0;
479         } else {
480             count = 0;
481             pos = 0;
482         }
483
484         while (need > 0) {
485
486
487             int len = in.read(buf, count, buf.length - count);
488             if (len <= 0) {
489                 throw new EOFException();
490             }
491             count += len;
492             need -= len;
493         }
494     }
495
496     /** Read a boolean array */
497     public int read(boolean[] b) throws IOException {
498         return read(b, 0, b.length);
499     }
500
501     /** Read a boolean array.
502      */
503     public int read(boolean[] b, int start, int len) throws IOException {
504
505         int i = start;
506         try {
507             for (; i < start + len; i += 1) {
508
509                 if (pos >= count) {
510                     fillBuf(1);
511                 }
512
513                 if (buf[pos] == 1) {
514                     b[i] = true;
515                 } else {
516                     b[i] = false;
517                 }
518                 pos += 1;
519             }
520         } catch (EOFException e) {
521             return eofCheck(e, i, start, 1);
522         }
523         return len;
524     }
525
526     /** Read a short array */
527     public int read(short[] s) throws IOException {
528         return read(s, 0, s.length);
529     }
530
531     /** Read a short array */
532     public int read(short[] s, int start, int len) throws IOException {
533
534         int i = start;
535         try {
536             for (; i < start + len; i += 1) {
537                 if (count - pos < 2) {
538                     fillBuf(2);
539                 }
540                 s[i] = (short) (buf[pos] << 8 | (buf[pos + 1] & 0xFF));
541                 pos += 2;
542             }
543         } catch (EOFException e) {
544             return eofCheck(e, i, start, 2);
545         }
546         return 2 * len;
547     }
548
549     /** Read a character array */
550     public int read(char[] c) throws IOException {
551         return read(c, 0, c.length);
552     }
553
554     /** Read a character array */
555     public int read(char[] c, int start, int len) throws IOException {
556
557         int i = start;
558         try {
559             for (; i < start + len; i += 1) {
560                 if (count - pos < 2) {
561                     fillBuf(2);
562                 }
563                 c[i] = (char) (buf[pos] << 8 | (buf[pos + 1] & 0xFF));
564                 pos += 2;
565             }
566         } catch (EOFException e) {
567             return eofCheck(e, i, start, 2);
568         }
569         return 2 * len;
570     }
571
572     /** Read an integer array */
573     public int read(int[] i) throws IOException {
574         return read(i, 0, i.length);
575     }
576
577     /** Read an integer array */
578     public int read(int[] i, int start, int len) throws IOException {
579
580         int ii = start;
581         try {
582             for (; ii < start + len; ii += 1) {
583
584                 if (count - pos < 4) {
585                     fillBuf(4);
586                 }
587
588                 i[ii] = buf[pos] << 24
589                         | (buf[pos + 1] & 0xFF) << 16
590                         | (buf[pos + 2] & 0xFF) << 8
591                         | (buf[pos + 3] & 0xFF);
592                 pos += 4;
593             }
594         } catch (EOFException e) {
595             return eofCheck(e, ii, start, 4);
596         }
597         return i.length * 4;
598     }
599
600     /** Read a long array */
601     public int read(long[] l) throws IOException {
602         return read(l, 0, l.length);
603     }
604
605     /** Read a long array */
606     public int read(long[] l, int start, int len) throws IOException {
607
608         int i = start;
609         try {
610             for (; i < start + len; i += 1) {
611                 if (count - pos < 8) {
612                     fillBuf(8);
613                 }
614                 int i1 = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF);
615                 int i2 = buf[pos + 4] << 24 | (buf[pos + 5] & 0xFF) << 16 | (buf[pos + 6] & 0xFF) << 8 | (buf[pos + 7] & 0xFF);
616                 l[i] = ((long) i1) << 32 | ((long) i2 & 0x00000000FFFFFFFFL);
617                 pos += 8;
618             }
619
620         } catch (EOFException e) {
621             return eofCheck(e, i, start, 8);
622         }
623         return 8 * len;
624     }
625
626     /** Read a float array */
627     public int read(float[] f) throws IOException {
628         return read(f, 0, f.length);
629     }
630
631     /** Read a float array */
632     public int read(float[] f, int start, int len) throws IOException {
633
634         int i = start;
635         try {
636             for (; i < start + len; i += 1) {
637                 if (count - pos < 4) {
638                     fillBuf(4);
639                 }
640                 int t = buf[pos] << 24
641                         | (buf[pos + 1] & 0xFF) << 16
642                         | (buf[pos + 2] & 0xFF) << 8
643                         | (buf[pos + 3] & 0xFF);
644                 f[i] = Float.intBitsToFloat(t);
645                 pos += 4;
646             }
647         } catch (EOFException e) {
648             return eofCheck(e, i, start, 4);
649         }
650         return 4 * len;
651     }
652
653     /** Read a double array */
654     public int read(double[] d) throws IOException {
655         return read(d, 0, d.length);
656     }
657
658     /** Read a double array */
659     public int read(double[] d, int start, int len) throws IOException {
660
661         int i = start;
662         try {
663             for (; i < start + len; i += 1) {
664
665                 if (count - pos < 8) {
666                     fillBuf(8);
667                 }
668                 int i1 = buf[pos] << 24 | (buf[pos + 1] & 0xFF) << 16 | (buf[pos + 2] & 0xFF) << 8 | (buf[pos + 3] & 0xFF);
669                 int i2 = buf[pos + 4] << 24 | (buf[pos + 5] & 0xFF) << 16 | (buf[pos + 6] & 0xFF) << 8 | (buf[pos + 7] & 0xFF);
670                 d[i] = Double.longBitsToDouble(
671                         ((long) i1) << 32 | ((long) i2 & 0x00000000FFFFFFFFL));
672                 pos += 8;
673             }
674         } catch (EOFException e) {
675             return eofCheck(e, i, start, 8);
676         }
677         return 8 * len;
678     }
679
680     /** For array reads return an EOF if unable to
681      *  read any data.
682      */
683     private int eofCheck(EOFException e, int i, int start, int length)
684             throws EOFException {
685
686         if (i == start) {
687             throw e;
688         } else {
689             return (i - start) * length;
690         }
691     }
692
693     /** Represent the stream as a string */
694     public String toString() {
695         return super.toString() + "[count=" + count + ",pos=" + pos + "]";
696     }
697 }