1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import java.nio.ByteOrder;
21import java.nio.charset.ModifiedUtf8;
22import libcore.io.Memory;
23import libcore.io.Streams;
24import libcore.io.SizeOf;
25
26/**
27 * Wraps an existing {@link InputStream} and reads big-endian typed data from it.
28 * Typically, this stream has been written by a DataOutputStream. Types that can
29 * be read include byte, 16-bit short, 32-bit int, 32-bit float, 64-bit long,
30 * 64-bit double, byte strings, and strings encoded in
31 * {@link DataInput modified UTF-8}.
32 *
33 * @see DataOutputStream
34 */
35public class DataInputStream extends FilterInputStream implements DataInput {
36
37    private final byte[] scratch = new byte[8];
38
39    /**
40     * Constructs a new DataInputStream on the InputStream {@code in}. All
41     * reads are then filtered through this stream. Note that data read by this
42     * stream is not in a human readable format and was most likely created by a
43     * DataOutputStream.
44     *
45     * <p><strong>Warning:</strong> passing a null source creates an invalid
46     * {@code DataInputStream}. All operations on such a stream will fail.
47     *
48     * @param in
49     *            the source InputStream the filter reads from.
50     * @see DataOutputStream
51     * @see RandomAccessFile
52     */
53    public DataInputStream(InputStream in) {
54        super(in);
55    }
56
57    // overridden to add 'final'
58    @Override public final int read(byte[] buffer) throws IOException {
59        return super.read(buffer);
60    }
61
62    /**
63     * Reads at most {@code length} bytes from this stream and stores them in
64     * the byte array {@code buffer} starting at {@code offset}. Returns the
65     * number of bytes that have been read or -1 if no bytes have been read and
66     * the end of the stream has been reached.
67     *
68     * @param buffer
69     *            the byte array in which to store the bytes read.
70     * @param offset
71     *            the initial position in {@code buffer} to store the bytes
72     *            read from this stream.
73     * @param length
74     *            the maximum number of bytes to store in {@code buffer}.
75     * @return the number of bytes that have been read or -1 if the end of the
76     *         stream has been reached.
77     * @throws IOException
78     *             if a problem occurs while reading from this stream.
79     * @see DataOutput#write(byte[])
80     * @see DataOutput#write(byte[], int, int)
81     */
82    @Override
83    public final int read(byte[] buffer, int offset, int length) throws IOException {
84        return in.read(buffer, offset, length);
85    }
86
87    public final boolean readBoolean() throws IOException {
88        int temp = in.read();
89        if (temp < 0) {
90            throw new EOFException();
91        }
92        return temp != 0;
93    }
94
95    public final byte readByte() throws IOException {
96        int temp = in.read();
97        if (temp < 0) {
98            throw new EOFException();
99        }
100        return (byte) temp;
101    }
102
103    public final char readChar() throws IOException {
104        return (char) readShort();
105    }
106
107    public final double readDouble() throws IOException {
108        return Double.longBitsToDouble(readLong());
109    }
110
111    public final float readFloat() throws IOException {
112        return Float.intBitsToFloat(readInt());
113    }
114
115    public final void readFully(byte[] dst) throws IOException {
116        readFully(dst, 0, dst.length);
117    }
118
119    public final void readFully(byte[] dst, int offset, int byteCount) throws IOException {
120        Streams.readFully(in, dst, offset, byteCount);
121    }
122
123    public final int readInt() throws IOException {
124        Streams.readFully(in, scratch, 0, SizeOf.INT);
125        return Memory.peekInt(scratch, 0, ByteOrder.BIG_ENDIAN);
126    }
127
128    @Deprecated
129    public final String readLine() throws IOException {
130        StringBuilder line = new StringBuilder(80); // Typical line length
131        boolean foundTerminator = false;
132        while (true) {
133            int nextByte = in.read();
134            switch (nextByte) {
135                case -1:
136                    if (line.length() == 0 && !foundTerminator) {
137                        return null;
138                    }
139                    return line.toString();
140                case (byte) '\r':
141                    if (foundTerminator) {
142                        ((PushbackInputStream) in).unread(nextByte);
143                        return line.toString();
144                    }
145                    foundTerminator = true;
146                    /* Have to be able to peek ahead one byte */
147                    if (!(in.getClass() == PushbackInputStream.class)) {
148                        in = new PushbackInputStream(in);
149                    }
150                    break;
151                case (byte) '\n':
152                    return line.toString();
153                default:
154                    if (foundTerminator) {
155                        ((PushbackInputStream) in).unread(nextByte);
156                        return line.toString();
157                    }
158                    line.append((char) nextByte);
159            }
160        }
161    }
162
163    public final long readLong() throws IOException {
164        Streams.readFully(in, scratch, 0, SizeOf.LONG);
165        return Memory.peekLong(scratch, 0, ByteOrder.BIG_ENDIAN);
166    }
167
168    public final short readShort() throws IOException {
169        Streams.readFully(in, scratch, 0, SizeOf.SHORT);
170        return Memory.peekShort(scratch, 0, ByteOrder.BIG_ENDIAN);
171    }
172
173    public final int readUnsignedByte() throws IOException {
174        int temp = in.read();
175        if (temp < 0) {
176            throw new EOFException();
177        }
178        return temp;
179    }
180
181    public final int readUnsignedShort() throws IOException {
182        return ((int) readShort()) & 0xffff;
183    }
184
185    public final String readUTF() throws IOException {
186        return decodeUTF(readUnsignedShort());
187    }
188
189    String decodeUTF(int utfSize) throws IOException {
190        return decodeUTF(utfSize, this);
191    }
192
193    private static String decodeUTF(int utfSize, DataInput in) throws IOException {
194        byte[] buf = new byte[utfSize];
195        in.readFully(buf, 0, utfSize);
196        return ModifiedUtf8.decode(buf, new char[utfSize], 0, utfSize);
197    }
198
199    public static final String readUTF(DataInput in) throws IOException {
200        return decodeUTF(in.readUnsignedShort(), in);
201    }
202
203    /**
204     * Skips {@code count} number of bytes in this stream. Subsequent {@code
205     * read()}s will not return these bytes unless {@code reset()} is used.
206     *
207     * This method will not throw an {@link EOFException} if the end of the
208     * input is reached before {@code count} bytes where skipped.
209     *
210     * @param count
211     *            the number of bytes to skip.
212     * @return the number of bytes actually skipped.
213     * @throws IOException
214     *             if a problem occurs during skipping.
215     * @see #mark(int)
216     * @see #reset()
217     */
218    public final int skipBytes(int count) throws IOException {
219        int skipped = 0;
220        long skip;
221        while (skipped < count && (skip = in.skip(count - skipped)) != 0) {
222            skipped += skip;
223        }
224        return skipped;
225    }
226}
227