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