1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.io.DataInputStream;
13 
14 import std.conv;
15 import hunt.io.FilterInputStream;
16 import hunt.io.DataInput;
17 import hunt.Double;
18 import hunt.Float;
19 import hunt.io.Common;
20 import hunt.Exceptions;
21 import hunt.io.PushbackInputStream;
22 
23 public
24 class DataInputStream : FilterInputStream , DataInput {
25 
26     /**
27      * Creates a DataInputStream that uses the specified
28      * underlying InputStream.
29      *
30      * @param  inputStream   the specified input stream
31      */
32     public this(InputStream inputStream) {
33         super(inputStream);
34     }
35 
36     /**
37      * working arrays initialized on demand by readUTF
38      */
39     private byte[] bytearr = new byte[80];
40     private char[] chararr = new char[80];
41 
42     /**
43      * Reads some number of bytes from the contained input stream and
44      * stores them into the buffer array <code>b</code>. The number of
45      * bytes actually read is returned as an integer. This method blocks
46      * until input data is available, end of file is detected, or an
47      * exception is thrown.
48      *
49      * <p>If <code>b</code> is null, a <code>NullPointerException</code> is
50      * thrown. If the length of <code>b</code> is zero, then no bytes are
51      * read and <code>0</code> is returned; otherwise, there is an attempt
52      * to read at least one byte. If no byte is available because the
53      * stream is at end of file, the value <code>-1</code> is returned;
54      * otherwise, at least one byte is read and stored into <code>b</code>.
55      *
56      * <p>The first byte read is stored into element <code>b[0]</code>, the
57      * next one into <code>b[1]</code>, and so on. The number of bytes read
58      * is, at most, equal to the length of <code>b</code>. Let <code>k</code>
59      * be the number of bytes actually read; these bytes will be stored inputStream
60      * elements <code>b[0]</code> through <code>b[k-1]</code>, leaving
61      * elements <code>b[k]</code> through <code>b[b.length-1]</code>
62      * unaffected.
63      *
64      * <p>The <code>read(b)</code> method has the same effect as:
65      * <blockquote><pre>
66      * read(b, 0, b.length)
67      * </pre></blockquote>
68      *
69      * @param      b   the buffer into which the data is read.
70      * @return     the total number of bytes read into the buffer, or
71      *             <code>-1</code> if there is no more data because the end
72      *             of the stream has been reached.
73      * @exception  IOException if the first byte cannot be read for any reason
74      * other than end of file, the stream has been closed and the underlying
75      * input stream does not support reading after close, or another I/O
76      * error occurs.
77      * @see        java.io.FilterInputStream#inputStream
78      * @see        java.io.InputStream#read(byte[], int, int)
79      */
80      override
81     public final int read(byte[] b)  {
82         return inputStream.read(b, 0, cast(int)(b.length));
83     }
84 
85     /**
86      * Reads up to <code>len</code> bytes of data from the contained
87      * input stream into an array of bytes.  An attempt is made to read
88      * as many as <code>len</code> bytes, but a smaller number may be read,
89      * possibly zero. The number of bytes actually read is returned as an
90      * integer.
91      *
92      * <p> This method blocks until input data is available, end of file is
93      * detected, or an exception is thrown.
94      *
95      * <p> If <code>len</code> is zero, then no bytes are read and
96      * <code>0</code> is returned; otherwise, there is an attempt to read at
97      * least one byte. If no byte is available because the stream is at end of
98      * file, the value <code>-1</code> is returned; otherwise, at least one
99      * byte is read and stored into <code>b</code>.
100      *
101      * <p> The first byte read is stored into element <code>b[off]</code>, the
102      * next one into <code>b[off+1]</code>, and so on. The number of bytes read
103      * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
104      * bytes actually read; these bytes will be stored inputStream elements
105      * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
106      * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
107      * <code>b[off+len-1]</code> unaffected.
108      *
109      * <p> In every case, elements <code>b[0]</code> through
110      * <code>b[off]</code> and elements <code>b[off+len]</code> through
111      * <code>b[b.length-1]</code> are unaffected.
112      *
113      * @param      b     the buffer into which the data is read.
114      * @param off the start offset inputStream the destination array <code>b</code>
115      * @param      len   the maximum number of bytes read.
116      * @return     the total number of bytes read into the buffer, or
117      *             <code>-1</code> if there is no more data because the end
118      *             of the stream has been reached.
119      * @exception  NullPointerException If <code>b</code> is <code>null</code>.
120      * @exception  IndexOutOfBoundsException If <code>off</code> is negative,
121      * <code>len</code> is negative, or <code>len</code> is greater than
122      * <code>b.length - off</code>
123      * @exception  IOException if the first byte cannot be read for any reason
124      * other than end of file, the stream has been closed and the underlying
125      * input stream does not support reading after close, or another I/O
126      * error occurs.
127      * @see        java.io.FilterInputStream#inputStream
128      * @see        java.io.InputStream#read(byte[], int, int)
129      */
130     override
131     public final int read(byte[] b, int off, int len)  {
132         return inputStream.read(b, off, len);
133     }
134 
135     /**
136      * See the general contract of the {@code readFully}
137      * method of {@code DataInput}.
138      * <p>
139      * Bytes
140      * for this operation are read from the contained
141      * input stream.
142      *
143      * @param   b   the buffer into which the data is read.
144      * @throws  NullPointerException if {@code b} is {@code null}.
145      * @throws  EOFException  if this input stream reaches the end before
146      *          reading all the bytes.
147      * @throws  IOException   the stream has been closed and the contained
148      *          input stream does not support reading after close, or
149      *          another I/O error occurs.
150      * @see     java.io.FilterInputStream#inputStream
151      */
152     public final void readFully(byte[] b)  {
153         readFully(b, 0, cast(int)(b.length));
154     }
155 
156     /**
157      * See the general contract of the {@code readFully}
158      * method of {@code DataInput}.
159      * <p>
160      * Bytes
161      * for this operation are read from the contained
162      * input stream.
163      *
164      * @param      b     the buffer into which the data is read.
165      * @param      off   the start offset inputStream the data array {@code b}.
166      * @param      len   the number of bytes to read.
167      * @exception  NullPointerException if {@code b} is {@code null}.
168      * @exception  IndexOutOfBoundsException if {@code off} is negative,
169      *             {@code len} is negative, or {@code len} is greater than
170      *             {@code b.length - off}.
171      * @exception  EOFException  if this input stream reaches the end before
172      *             reading all the bytes.
173      * @exception  IOException   the stream has been closed and the contained
174      *             input stream does not support reading after close, or
175      *             another I/O error occurs.
176      * @see        java.io.FilterInputStream#inputStream
177      */
178     public final void readFully(byte[] b, int off, int len)  {
179         if (len < 0)
180             throw new IndexOutOfBoundsException();
181         int n = 0;
182         while (n < len) {
183             int count = inputStream.read(b, off + n, len - n);
184             if (count < 0)
185                 throw new EOFException();
186             n += count;
187         }
188     }
189 
190     /**
191      * See the general contract of the <code>skipBytes</code>
192      * method of <code>DataInput</code>.
193      * <p>
194      * Bytes for this operation are read from the contained
195      * input stream.
196      *
197      * @param      n   the number of bytes to be skipped.
198      * @return     the actual number of bytes skipped.
199      * @exception  IOException  if the contained input stream does not support
200      *             seek, or the stream has been closed and
201      *             the contained input stream does not support
202      *             reading after close, or another I/O error occurs.
203      */
204     public final int skipBytes(int n)  {
205         int total = 0;
206         int cur = 0;
207 
208         while ((total<n) && ((cur = cast(int) inputStream.skip(n-total)) > 0)) {
209             total += cur;
210         }
211 
212         return total;
213     }
214 
215     /**
216      * See the general contract of the <code>readBoolean</code>
217      * method of <code>DataInput</code>.
218      * <p>
219      * Bytes for this operation are read from the contained
220      * input stream.
221      *
222      * @return     the <code>bool</code> value read.
223      * @exception  EOFException  if this input stream has reached the end.
224      * @exception  IOException   the stream has been closed and the contained
225      *             input stream does not support reading after close, or
226      *             another I/O error occurs.
227      * @see        java.io.FilterInputStream#inputStream
228      */
229     public final bool readBoolean()  {
230         int ch = inputStream.read();
231         if (ch < 0)
232             throw new EOFException();
233         return (ch != 0);
234     }
235 
236     /**
237      * See the general contract of the <code>readByte</code>
238      * method of <code>DataInput</code>.
239      * <p>
240      * Bytes
241      * for this operation are read from the contained
242      * input stream.
243      *
244      * @return     the next byte of this input stream as a signed 8-bit
245      *             <code>byte</code>.
246      * @exception  EOFException  if this input stream has reached the end.
247      * @exception  IOException   the stream has been closed and the contained
248      *             input stream does not support reading after close, or
249      *             another I/O error occurs.
250      * @see        java.io.FilterInputStream#inputStream
251      */
252     public final byte readByte()  {
253         int ch = inputStream.read();
254         if (ch < 0)
255             throw new EOFException();
256         return cast(byte)(ch);
257     }
258 
259     /**
260      * See the general contract of the <code>readUnsignedByte</code>
261      * method of <code>DataInput</code>.
262      * <p>
263      * Bytes
264      * for this operation are read from the contained
265      * input stream.
266      *
267      * @return     the next byte of this input stream, interpreted as an
268      *             unsigned 8-bit number.
269      * @exception  EOFException  if this input stream has reached the end.
270      * @exception  IOException   the stream has been closed and the contained
271      *             input stream does not support reading after close, or
272      *             another I/O error occurs.
273      * @see         java.io.FilterInputStream#inputStream
274      */
275     public final int readUnsignedByte()  {
276         int ch = inputStream.read();
277         if (ch < 0)
278             throw new EOFException();
279         return ch;
280     }
281 
282     /**
283      * See the general contract of the <code>readShort</code>
284      * method of <code>DataInput</code>.
285      * <p>
286      * Bytes
287      * for this operation are read from the contained
288      * input stream.
289      *
290      * @return     the next two bytes of this input stream, interpreted as a
291      *             signed 16-bit number.
292      * @exception  EOFException  if this input stream reaches the end before
293      *               reading two bytes.
294      * @exception  IOException   the stream has been closed and the contained
295      *             input stream does not support reading after close, or
296      *             another I/O error occurs.
297      * @see        java.io.FilterInputStream#inputStream
298      */
299     public final short readShort()  {
300         int ch1 = inputStream.read();
301         int ch2 = inputStream.read();
302         if ((ch1 | ch2) < 0)
303             throw new EOFException();
304         return cast(short)((ch1 << 8) + (ch2 << 0));
305     }
306 
307     /**
308      * See the general contract of the <code>readUnsignedShort</code>
309      * method of <code>DataInput</code>.
310      * <p>
311      * Bytes
312      * for this operation are read from the contained
313      * input stream.
314      *
315      * @return     the next two bytes of this input stream, interpreted as an
316      *             unsigned 16-bit integer.
317      * @exception  EOFException  if this input stream reaches the end before
318      *             reading two bytes.
319      * @exception  IOException   the stream has been closed and the contained
320      *             input stream does not support reading after close, or
321      *             another I/O error occurs.
322      * @see        java.io.FilterInputStream#inputStream
323      */
324     public final int readUnsignedShort()  {
325         int ch1 = inputStream.read();
326         int ch2 = inputStream.read();
327         if ((ch1 | ch2) < 0)
328             throw new EOFException();
329         return (ch1 << 8) + (ch2 << 0);
330     }
331 
332     /**
333      * See the general contract of the <code>readChar</code>
334      * method of <code>DataInput</code>.
335      * <p>
336      * Bytes
337      * for this operation are read from the contained
338      * input stream.
339      *
340      * @return     the next two bytes of this input stream, interpreted as a
341      *             <code>char</code>.
342      * @exception  EOFException  if this input stream reaches the end before
343      *               reading two bytes.
344      * @exception  IOException   the stream has been closed and the contained
345      *             input stream does not support reading after close, or
346      *             another I/O error occurs.
347      * @see        java.io.FilterInputStream#inputStream
348      */
349     public final char readChar()  {
350         return readByte();
351         // int ch1 = inputStream.read();
352         // return cast(char)ch1;
353         // int ch2 = inputStream.read();
354         // if ((ch1 | ch2) < 0)
355         //     throw new EOFException();
356         // return cast(char)((ch1 << 8) + (ch2 << 0));
357     }
358 
359     /**
360      * See the general contract of the <code>readInt</code>
361      * method of <code>DataInput</code>.
362      * <p>
363      * Bytes
364      * for this operation are read from the contained
365      * input stream.
366      *
367      * @return     the next four bytes of this input stream, interpreted as an
368      *             <code>int</code>.
369      * @exception  EOFException  if this input stream reaches the end before
370      *               reading four bytes.
371      * @exception  IOException   the stream has been closed and the contained
372      *             input stream does not support reading after close, or
373      *             another I/O error occurs.
374      * @see        java.io.FilterInputStream#inputStream
375      */
376     public final int readInt()  {
377         int ch1 = inputStream.read();
378         int ch2 = inputStream.read();
379         int ch3 = inputStream.read();
380         int ch4 = inputStream.read();
381         if ((ch1 | ch2 | ch3 | ch4) < 0)
382             throw new EOFException();
383         return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
384     }
385 
386     private byte[] readBuffer = new byte[8];
387 
388     /**
389      * See the general contract of the <code>readLong</code>
390      * method of <code>DataInput</code>.
391      * <p>
392      * Bytes
393      * for this operation are read from the contained
394      * input stream.
395      *
396      * @return     the next eight bytes of this input stream, interpreted as a
397      *             <code>long</code>.
398      * @exception  EOFException  if this input stream reaches the end before
399      *               reading eight bytes.
400      * @exception  IOException   the stream has been closed and the contained
401      *             input stream does not support reading after close, or
402      *             another I/O error occurs.
403      * @see        java.io.FilterInputStream#inputStream
404      */
405     public final long readLong()  {
406         readFully(readBuffer, 0, 8);
407         return ((cast(long)readBuffer[0] << 56) +
408                 (cast(long)(readBuffer[1] & 255) << 48) +
409                 (cast(long)(readBuffer[2] & 255) << 40) +
410                 (cast(long)(readBuffer[3] & 255) << 32) +
411                 (cast(long)(readBuffer[4] & 255) << 24) +
412                 ((readBuffer[5] & 255) << 16) +
413                 ((readBuffer[6] & 255) <<  8) +
414                 ((readBuffer[7] & 255) <<  0));
415     }
416 
417     /**
418      * See the general contract of the <code>readFloat</code>
419      * method of <code>DataInput</code>.
420      * <p>
421      * Bytes
422      * for this operation are read from the contained
423      * input stream.
424      *
425      * @return     the next four bytes of this input stream, interpreted as a
426      *             <code>float</code>.
427      * @exception  EOFException  if this input stream reaches the end before
428      *               reading four bytes.
429      * @exception  IOException   the stream has been closed and the contained
430      *             input stream does not support reading after close, or
431      *             another I/O error occurs.
432      * @see        java.io.DataInputStream#readInt()
433      * @see        java.lang.Float#intBitsToFloat(int)
434      */
435     public final float readFloat()  {
436         return Float.intBitsToFloat(readInt());
437     }
438 
439     /**
440      * See the general contract of the <code>readDouble</code>
441      * method of <code>DataInput</code>.
442      * <p>
443      * Bytes
444      * for this operation are read from the contained
445      * input stream.
446      *
447      * @return     the next eight bytes of this input stream, interpreted as a
448      *             <code>double</code>.
449      * @exception  EOFException  if this input stream reaches the end before
450      *               reading eight bytes.
451      * @exception  IOException   the stream has been closed and the contained
452      *             input stream does not support reading after close, or
453      *             another I/O error occurs.
454      * @see        java.io.DataInputStream#readLong()
455      * @see        java.lang.Double#longBitsToDouble(long)
456      */
457     public final double readDouble()  {
458         return Double.longBitsToDouble(readLong());
459     }
460 
461     private char[] lineBuffer;
462 
463     /**
464      * See the general contract of the <code>readLine</code>
465      * method of <code>DataInput</code>.
466      * <p>
467      * Bytes
468      * for this operation are read from the contained
469      * input stream.
470      *
471      * @deprecated This method does not properly convert bytes to characters.
472      * As of JDK&nbsp;1.1, the preferred way to read lines of text is via the
473      * <code>BufferedReader.readLine()</code> method.  Programs that use the
474      * <code>DataInputStream</code> class to read lines can be converted to use
475      * the <code>BufferedReader</code> class by replacing code of the form:
476      * <blockquote><pre>
477      *     DataInputStream d =&nbsp;new&nbsp;DataInputStream(inputStream);
478      * </pre></blockquote>
479      * with:
480      * <blockquote><pre>
481      *     BufferedReader d
482      *          =&nbsp;new&nbsp;BufferedReader(new&nbsp;InputStreamReader(inputStream));
483      * </pre></blockquote>
484      *
485      * @return     the next line of text from this input stream.
486      * @exception  IOException  if an I/O error occurs.
487      * @see        java.io.BufferedReader#readLine()
488      * @see        java.io.FilterInputStream#inputStream
489      */
490     // @Deprecated
491     public final string readLine()  {
492         char[] buf = lineBuffer;
493 
494         if (buf is null) {
495             buf = lineBuffer = new char[128];
496         }
497 
498         int room = cast(int)(buf.length);
499         int offset = 0;
500         int c;
501 
502 loop:   while (true) {
503             switch (c = inputStream.read()) {
504               case -1:
505               case '\n':
506                 break loop;
507 
508               case '\r':
509                 int c2 = inputStream.read();
510                 if ((c2 != '\n') && (c2 != -1)) {
511                     if (!(cast(PushbackInputStream)inputStream !is null)) {
512                         this.inputStream = new PushbackInputStream(inputStream);
513                     }
514                     (cast(PushbackInputStream)inputStream).unread(c2);
515                 }
516                 break loop;
517 
518               default:
519                 if (--room < 0) {
520                     buf = new char[offset + 128];
521                     room = cast(int)(buf.length) - offset - 1;
522                     // System.arraycopy(lineBuffer, 0, buf, 0, offset);
523                     buf[0 .. offset ] = lineBuffer[0..offset];
524                     lineBuffer = buf;
525                 }
526                 buf[offset++] = cast(char) c;
527                 break;
528             }
529         }
530         if ((c == -1) && (offset == 0)) {
531             return null;
532         }
533         return cast(string)buf[0..offset];
534     }
535 
536     /**
537      * See the general contract of the <code>readUTF</code>
538      * method of <code>DataInput</code>.
539      * <p>
540      * Bytes
541      * for this operation are read from the contained
542      * input stream.
543      *
544      * @return     a Unicode string.
545      * @exception  EOFException  if this input stream reaches the end before
546      *               reading all the bytes.
547      * @exception  IOException   the stream has been closed and the contained
548      *             input stream does not support reading after close, or
549      *             another I/O error occurs.
550      * @exception  UTFDataFormatException if the bytes do not represent a valid
551      *             modified UTF-8 encoding of a string.
552      * @see        java.io.DataInputStream#readUTF(java.io.DataInput)
553      */
554     public final string readUTF()  {
555         return readUTF(this);
556     }
557 
558     /**
559      * Reads from the
560      * stream <code>inputStream</code> a representation
561      * of a Unicode  character string encoded inputStream
562      * <a href="DataInput.html#modified-utf-8">modified UTF-8</a> format;
563      * this string of characters is then returned as a <code>String</code>.
564      * The details of the modified UTF-8 representation
565      * are  exactly the same as for the <code>readUTF</code>
566      * method of <code>DataInput</code>.
567      *
568      * @param      inputStream   a data input stream.
569      * @return     a Unicode string.
570      * @exception  EOFException            if the input stream reaches the end
571      *               before all the bytes.
572      * @exception  IOException   the stream has been closed and the contained
573      *             input stream does not support reading after close, or
574      *             another I/O error occurs.
575      * @exception  UTFDataFormatException  if the bytes do not represent a
576      *               valid modified UTF-8 encoding of a Unicode string.
577      * @see        java.io.DataInputStream#readUnsignedShort()
578      */
579     public static final string readUTF(DataInput inputStream)  {
580         int utflen = inputStream.readUnsignedShort();
581         byte[] bytearr = null;
582         import hunt.logging;
583             // trace("11111111=>", utflen);
584         // char[] chararr = null;
585         if (cast(DataInputStream)inputStream !is null ) {
586             // trace("xxxx=>");
587         //     DataInputStream dis = cast(DataInputStream)inputStream;
588         //     if (dis.bytearr.length < utflen){
589         //         dis.bytearr = new byte[utflen*2];
590         //         dis.chararr = new char[utflen*2];
591         //     }
592         //     chararr = dis.chararr;
593         //     bytearr = dis.bytearr;
594         } else {
595             // trace("yyyyyyyy=>");
596         //     bytearr = new byte[utflen];
597         //     chararr = new char[utflen];
598         }
599 
600         bytearr = new byte[utflen];
601 
602         // int c, char2, char3;
603         // int count = 0;
604         // int chararr_count=0;
605 
606         inputStream.readFully(bytearr, 0, utflen);
607 
608         // while (count < utflen) {
609         //     c = cast(int) bytearr[count] & 0xff;
610         //     if (c > 127) break;
611         //     count++;
612         //     auto t = cast(char)c;
613         //     chararr[chararr_count++]= t;
614         // }
615 
616         // trace("ccc=>", cast(string)bytearr);
617 
618         return cast(string)bytearr;
619 
620         // while (count < utflen) {
621         //     c = cast(int) bytearr[count] & 0xff;
622         //     switch (c >> 4) {
623         //         case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
624         //             /* 0xxxxxxx*/
625         //             count++;
626         //             chararr[chararr_count++]=cast(char)c;
627         //             break;
628         //         case 12: case 13:
629         //             /* 110x xxxx   10xx xxxx*/
630         //             count += 2;
631         //             if (count > utflen)
632         //                 throw new Exception(
633         //                     "malformed input: partial character at end");
634         //             char2 = cast(int) bytearr[count-1];
635         //             if ((char2 & 0xC0) != 0x80)
636         //                 throw new Exception(
637         //                     "malformed input around byte " ~ count.to!string);
638         //             chararr[chararr_count++]=cast(char)(((c & 0x1F) << 6) |
639         //                                             (char2 & 0x3F));
640         //             break;
641         //         case 14:
642         //             /* 1110 xxxx  10xx xxxx  10xx xxxx */
643         //             count += 3;
644         //             if (count > utflen)
645         //                 throw new Exception(
646         //                     "malformed input: partial character at end");
647         //             char2 = cast(int) bytearr[count-2];
648         //             char3 = cast(int) bytearr[count-1];
649         //             if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
650         //                 throw new Exception(
651         //                     "malformed input around byte " ~ (count-1).to!string);
652         //             chararr[chararr_count++]=cast(char)(((c     & 0x0F) << 12) |
653         //                                             ((char2 & 0x3F) << 6)  |
654         //                                             ((char3 & 0x3F) << 0));
655         //             break;
656         //         default:
657         //             /* 10xx xxxx,  1111 xxxx */
658         //             throw new Exception(
659         //                 "malformed input around byte " ~ count.to!string);
660         //     }
661         // }
662         // The number of chars produced may be less than utflen
663         // return cast(string)chararr[0..chararr_count];
664     }
665 }