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.util.Arrays;
21
22/**
23 * A specialized {@link InputStream } for reading the contents of a byte array.
24 *
25 * @see ByteArrayOutputStream
26 */
27public class ByteArrayInputStream extends InputStream {
28    /**
29     * The {@code byte} array containing the bytes to stream over.
30     */
31    protected byte[] buf;
32
33    /**
34     * The current position within the byte array.
35     */
36    protected int pos;
37
38    /**
39     * The current mark position. Initially set to 0 or the <code>offset</code>
40     * parameter within the constructor.
41     */
42    protected int mark;
43
44    /**
45     * The total number of bytes initially available in the byte array
46     * {@code buf}.
47     */
48    protected int count;
49
50    /**
51     * Constructs a new {@code ByteArrayInputStream} on the byte array
52     * {@code buf}.
53     *
54     * @param buf
55     *            the byte array to stream over.
56     */
57    public ByteArrayInputStream(byte[] buf) {
58        this.mark = 0;
59        this.buf = buf;
60        this.count = buf.length;
61    }
62
63    /**
64     * Constructs a new {@code ByteArrayInputStream} on the byte array
65     * {@code buf} with the initial position set to {@code offset} and the
66     * number of bytes available set to {@code offset} + {@code length}.
67     *
68     * @param buf
69     *            the byte array to stream over.
70     * @param offset
71     *            the initial position in {@code buf} to start streaming from.
72     * @param length
73     *            the number of bytes available for streaming.
74     */
75    public ByteArrayInputStream(byte[] buf, int offset, int length) {
76        this.buf = buf;
77        pos = offset;
78        mark = offset;
79        count = offset + length > buf.length ? buf.length : offset + length;
80    }
81
82    /**
83     * Returns the number of remaining bytes.
84     *
85     * @return {@code count - pos}
86     */
87    @Override
88    public synchronized int available() {
89        return count - pos;
90    }
91
92    /**
93     * Closes this stream and frees resources associated with this stream.
94     *
95     * @throws IOException
96     *             if an I/O error occurs while closing this stream.
97     */
98    @Override
99    public void close() throws IOException {
100        // Do nothing on close, this matches JDK behavior.
101    }
102
103    /**
104     * Sets a mark position in this ByteArrayInputStream. The parameter
105     * {@code readlimit} is ignored. Sending {@code reset()} will reposition the
106     * stream back to the marked position.
107     *
108     * @param readlimit
109     *            ignored.
110     * @see #markSupported()
111     * @see #reset()
112     */
113    @Override
114    public synchronized void mark(int readlimit) {
115        mark = pos;
116    }
117
118    /**
119     * Indicates whether this stream supports the {@code mark()} and
120     * {@code reset()} methods. Returns {@code true} since this class supports
121     * these methods.
122     *
123     * @return always {@code true}.
124     * @see #mark(int)
125     * @see #reset()
126     */
127    @Override
128    public boolean markSupported() {
129        return true;
130    }
131
132    /**
133     * Reads a single byte from the source byte array and returns it as an
134     * integer in the range from 0 to 255. Returns -1 if the end of the source
135     * array has been reached.
136     *
137     * @return the byte read or -1 if the end of this stream has been reached.
138     */
139    @Override
140    public synchronized int read() {
141        return pos < count ? buf[pos++] & 0xFF : -1;
142    }
143
144    @Override public synchronized int read(byte[] buffer, int byteOffset, int byteCount) {
145        Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
146
147        // Are there any bytes available?
148        if (this.pos >= this.count) {
149            return -1;
150        }
151        if (byteCount == 0) {
152            return 0;
153        }
154
155        int copylen = this.count - pos < byteCount ? this.count - pos : byteCount;
156        System.arraycopy(this.buf, pos, buffer, byteOffset, copylen);
157        pos += copylen;
158        return copylen;
159    }
160
161    /**
162     * Resets this stream to the last marked location. This implementation
163     * resets the position to either the marked position, the start position
164     * supplied in the constructor or 0 if neither has been provided.
165     *
166     * @see #mark(int)
167     */
168    @Override
169    public synchronized void reset() {
170        pos = mark;
171    }
172
173    /**
174     * Skips {@code byteCount} bytes in this InputStream. Subsequent
175     * calls to {@code read} will not return these bytes unless {@code reset} is
176     * used. This implementation skips {@code byteCount} number of bytes in the
177     * target stream. It does nothing and returns 0 if {@code byteCount} is negative.
178     *
179     * @return the number of bytes actually skipped.
180     */
181    @Override
182    public synchronized long skip(long byteCount) {
183        if (byteCount <= 0) {
184            return 0;
185        }
186        int temp = pos;
187        pos = this.count - pos < byteCount ? this.count : (int) (pos + byteCount);
188        return pos - temp;
189    }
190}
191