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.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;
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.
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.
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.]
46 * Testing and timing routines are provided in the
47 * nom.tam.util.test.BufferedFileTester class.
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
55 public class BufferedDataInputStream
56 extends BufferedInputStream
57 implements ArrayDataInput {
59 private long primitiveArrayCount;
60 private byte[] bb = new byte[8];
62 /** Use the BufferedInputStream constructor
64 public BufferedDataInputStream(InputStream o) {
68 /** Use the BufferedInputStream constructor
70 public BufferedDataInputStream(InputStream o, int bufLength) {
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.
81 public int read(byte[] obuf, int offset, int len) throws IOException {
87 // Use just the buffered I/O to get needed info.
89 int xlen = super.read(obuf, offset, len);
92 throw new EOFException();
106 /** Read a boolean value.
107 * @return b The value read.
109 public boolean readBoolean() throws IOException {
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
123 public byte readByte() throws IOException {
124 return (byte) read();
127 /** Read a byte value in the range 0-255.
128 * @return The byte value as an integer.
130 public int readUnsignedByte() throws IOException {
131 return read() | 0x00ff;
135 * @return The integer value.
137 public int readInt() throws IOException {
139 if (read(bb, 0, 4) < 4) {
140 throw new EOFException();
142 int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
146 /** Read a 2-byte value as a short (-32788 to 32767)
147 * @return The short value.
149 public short readShort() throws IOException {
151 if (read(bb, 0, 2) < 2) {
152 throw new EOFException();
155 short s = (short) (bb[0] << 8 | (bb[1] & 0xFF));
159 /** Read a 2-byte value in the range 0-65536.
160 * @return the value as an integer.
162 public int readUnsignedShort() throws IOException {
164 if (read(bb, 0, 2) < 2) {
165 throw new EOFException();
168 return (bb[0] & 0xFF) << 8 | (bb[1] & 0xFF);
171 /** Read a 2-byte value as a character.
172 * @return The character read.
174 public char readChar() throws IOException {
175 byte[] b = new byte[2];
177 if (read(b, 0, 2) < 2) {
178 throw new EOFException();
181 char c = (char) (b[0] << 8 | (b[1] & 0xFF));
186 * @return The value read.
188 public long readLong() throws IOException {
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();
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);
200 /** Read a 4 byte real number.
201 * @return The value as a float.
203 public float readFloat() throws IOException {
205 if (read(bb, 0, 4) < 4) {
206 throw new EOFException();
209 int i = bb[0] << 24 | (bb[1] & 0xFF) << 16 | (bb[2] & 0xFF) << 8 | (bb[3] & 0xFF);
210 return Float.intBitsToFloat(i);
214 /** Read an 8 byte real number.
215 * @return The value as a double.
217 public double readDouble() throws IOException {
219 if (read(bb, 0, 8) < 8) {
220 throw new EOFException();
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);
226 return Double.longBitsToDouble(((long) i1) << 32 | ((long) i2 & 0x00000000ffffffffL));
229 /** Read a buffer and signal an EOF if the buffer
230 * cannot be fully read.
231 * @param b The buffer to be read.
233 public void readFully(byte[] b) throws IOException {
234 readFully(b, 0, b.length);
237 /** Read a buffer and signal an EOF if the requested elements
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.
248 public void readFully(byte[] b, int off, int len) throws IOException {
250 if (off < 0 || len < 0 || off + len > b.length) {
251 throw new IOException("Attempt to read outside byte array");
254 if (read(b, off, len) < len) {
255 throw new EOFException();
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.
264 private byte[] skipBuf = null;
266 public int skipBytes(int toSkip) throws IOException {
267 return (int) skipBytes((long) toSkip);
270 public long skipBytes(long toSkip) throws IOException {
277 long got = skip(need);
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];
292 while (need > 8192) {
293 int got = read(skipBuf, 0, 8192);
300 int got = read(skipBuf, 0, (int) need);
311 throw new EOFException();
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.
323 public String readUTF() throws IOException {
325 // Punt on this one and use DataInputStream routines.
326 DataInputStream d = new DataInputStream(this);
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.
337 * @return The String read.
338 * @deprecated Use BufferedReader methods.
340 public String readLine() throws IOException {
341 // Punt on this and use BufferedReader routines.
342 BufferedReader d = new BufferedReader(new InputStreamReader(this));
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
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).
356 public int readPrimitiveArray(Object o) throws IOException {
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.
363 primitiveArrayCount = 0;
364 return (int) readLArray(o);
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.
373 * @returns The number of bytes read.
374 * @deprecated See readLArray(Object) which handles large arrays properly.
376 public int readArray(Object o) throws IOException {
377 return (int) readLArray(o);
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.
386 * @returns The number of bytes read.
388 public long readLArray(Object o) throws IOException {
389 primitiveArrayCount = 0;
390 return primitiveArrayRecurse(o);
393 /** Read recursively over a multi-dimensional array.
394 * @return The number of bytes read.
396 protected long primitiveArrayRecurse(Object o) throws IOException {
399 return primitiveArrayCount;
402 String className = o.getClass().getName();
404 if (className.charAt(0) != '[') {
405 throw new IOException("Invalid object passed to BufferedDataInputStream.readArray:" + className);
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]);
415 // This is a one-d array. Process it using our special functions.
416 switch (className.charAt(1)) {
418 primitiveArrayCount += read((boolean[]) o, 0, ((boolean[]) o).length);
421 int len = read((byte[]) o, 0, ((byte[]) o).length);
422 primitiveArrayCount += len;
424 if (len < ((byte[]) o).length) {
425 throw new EOFException();
429 primitiveArrayCount += read((char[]) o, 0, ((char[]) o).length);
432 primitiveArrayCount += read((short[]) o, 0, ((short[]) o).length);
435 primitiveArrayCount += read((int[]) o, 0, ((int[]) o).length);
438 primitiveArrayCount += read((long[]) o, 0, ((long[]) o).length);
441 primitiveArrayCount += read((float[]) o, 0, ((float[]) o).length);
444 primitiveArrayCount += read((double[]) o, 0, ((double[]) o).length);
448 // Handle an array of Objects by recursion. Anything
450 if (className.equals("[Ljava.lang.Object;")) {
451 for (int i = 0; i < ((Object[]) o).length; i += 1) {
452 primitiveArrayRecurse(((Object[]) o)[i]);
455 throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);
459 throw new IOException("Invalid object passed to BufferedDataInputStream.readArray: " + className);
462 return primitiveArrayCount;
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.
470 * @param The required number of bytes.
472 private void fillBuf(int need) throws IOException {
475 System.arraycopy(buf, pos, buf, 0, count - pos);
487 int len = in.read(buf, count, buf.length - count);
489 throw new EOFException();
496 /** Read a boolean array */
497 public int read(boolean[] b) throws IOException {
498 return read(b, 0, b.length);
501 /** Read a boolean array.
503 public int read(boolean[] b, int start, int len) throws IOException {
507 for (; i < start + len; i += 1) {
520 } catch (EOFException e) {
521 return eofCheck(e, i, start, 1);
526 /** Read a short array */
527 public int read(short[] s) throws IOException {
528 return read(s, 0, s.length);
531 /** Read a short array */
532 public int read(short[] s, int start, int len) throws IOException {
536 for (; i < start + len; i += 1) {
537 if (count - pos < 2) {
540 s[i] = (short) (buf[pos] << 8 | (buf[pos + 1] & 0xFF));
543 } catch (EOFException e) {
544 return eofCheck(e, i, start, 2);
549 /** Read a character array */
550 public int read(char[] c) throws IOException {
551 return read(c, 0, c.length);
554 /** Read a character array */
555 public int read(char[] c, int start, int len) throws IOException {
559 for (; i < start + len; i += 1) {
560 if (count - pos < 2) {
563 c[i] = (char) (buf[pos] << 8 | (buf[pos + 1] & 0xFF));
566 } catch (EOFException e) {
567 return eofCheck(e, i, start, 2);
572 /** Read an integer array */
573 public int read(int[] i) throws IOException {
574 return read(i, 0, i.length);
577 /** Read an integer array */
578 public int read(int[] i, int start, int len) throws IOException {
582 for (; ii < start + len; ii += 1) {
584 if (count - pos < 4) {
588 i[ii] = buf[pos] << 24
589 | (buf[pos + 1] & 0xFF) << 16
590 | (buf[pos + 2] & 0xFF) << 8
591 | (buf[pos + 3] & 0xFF);
594 } catch (EOFException e) {
595 return eofCheck(e, ii, start, 4);
600 /** Read a long array */
601 public int read(long[] l) throws IOException {
602 return read(l, 0, l.length);
605 /** Read a long array */
606 public int read(long[] l, int start, int len) throws IOException {
610 for (; i < start + len; i += 1) {
611 if (count - pos < 8) {
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);
620 } catch (EOFException e) {
621 return eofCheck(e, i, start, 8);
626 /** Read a float array */
627 public int read(float[] f) throws IOException {
628 return read(f, 0, f.length);
631 /** Read a float array */
632 public int read(float[] f, int start, int len) throws IOException {
636 for (; i < start + len; i += 1) {
637 if (count - pos < 4) {
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);
647 } catch (EOFException e) {
648 return eofCheck(e, i, start, 4);
653 /** Read a double array */
654 public int read(double[] d) throws IOException {
655 return read(d, 0, d.length);
658 /** Read a double array */
659 public int read(double[] d, int start, int len) throws IOException {
663 for (; i < start + len; i += 1) {
665 if (count - pos < 8) {
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));
674 } catch (EOFException e) {
675 return eofCheck(e, i, start, 8);
680 /** For array reads return an EOF if unable to
683 private int eofCheck(EOFException e, int i, int start, int length)
684 throws EOFException {
689 return (i - start) * length;
693 /** Represent the stream as a string */
694 public String toString() {
695 return super.toString() + "[count=" + count + ",pos=" + pos + "]";