1bcf4a0dae04a4ad14287eeb34069a97c96fe9bb1Sam Juddpackage com.bumptech.glide.load.resource.bitmap;
2a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
3a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd/*
4a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  Licensed to the Apache Software Foundation (ASF) under one or more
5a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  contributor license agreements.  See the NOTICE file distributed with
6a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  this work for additional information regarding copyright ownership.
7a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  The ASF licenses this file to You under the Apache License, Version 2.0
8a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  (the "License"); you may not use this file except in compliance with
9a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  the License.  You may obtain a copy of the License at
10a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *
11a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *     http://www.apache.org/licenses/LICENSE-2.0
12a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *
13a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  Unless required by applicable law or agreed to in writing, software
14a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  distributed under the License is distributed on an "AS IS" BASIS,
15a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  See the License for the specific language governing permissions and
17a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *  limitations under the License.
18a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd */
19a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
20a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
21a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Juddimport java.io.FilterInputStream;
22a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Juddimport java.io.IOException;
23a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Juddimport java.io.InputStream;
24a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
25a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd/**
26a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * Wraps an existing {@link InputStream} and <em>buffers</em> the input.
27a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * Expensive interaction with the underlying input stream is minimized, since
28a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * most (smaller) requests can be satisfied by accessing the buffer alone. The
29a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * drawback is that some extra space is required to hold the buffer and that
30a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * copying takes place when filling that buffer, but this is usually outweighed
31a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * by the performance benefits.
32a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *
33a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * <p/>A typical application pattern for the class looks like this:<p/>
34a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *
35a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * <pre>
36a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * BufferedInputStream buf = new BufferedInputStream(new FileInputStream("file.java"));
37a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd * </pre>
38a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd *
39a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd */
40fac5216512ca376a9128307ea812fba51970a7b1Sam Juddpublic class RecyclableBufferedInputStream extends FilterInputStream {
41678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd
42678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd    public static class InvalidMarkException extends RuntimeException {
43678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd        public InvalidMarkException(String detailMessage) {
44678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd            super(detailMessage);
45678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd        }
46678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd    }
47678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd
48a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
49a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * The buffer containing the current bytes read from the target InputStream.
50a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
51a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    protected volatile byte[] buf;
52a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
53a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
54a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * The total number of bytes inside the byte array {@code buf}.
55a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
56a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    protected int count;
57a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
58a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
59a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * The current limit, which when passed, invalidates the current mark.
60a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
61a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    protected int marklimit;
62a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
63a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
64a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * The currently marked position. -1 indicates no mark has been set or the
65a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * mark has been invalidated.
66a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
67a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    protected int markpos = -1;
68a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
69a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
70a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * The current position within the byte array {@code buf}.
71a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
72a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    protected int pos;
73a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
74fac5216512ca376a9128307ea812fba51970a7b1Sam Judd    public RecyclableBufferedInputStream(InputStream in, byte[] buffer) {
75a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        super(in);
76a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (buffer == null || buffer.length == 0) {
77a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw new IllegalArgumentException("buffer is null or empty");
78a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
79a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        buf = buffer;
80a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
81a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
82a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
83a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Returns an estimated number of bytes that can be read or skipped without blocking for more
84a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * input. This method returns the number of bytes available in the buffer
85a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * plus those available in the source stream, but see {@link InputStream#available} for
86a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * important caveats.
87a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
88a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @return the estimated number of bytes available
89a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException if this stream is closed or an error occurs
90a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
91a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
92a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized int available() throws IOException {
93a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        InputStream localIn = in; // 'in' could be invalidated by close()
94a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (buf == null || localIn == null) {
95a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
96a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
97a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        return count - pos + localIn.available();
98a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
99a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
100a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    private IOException streamClosed() throws IOException {
101a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        throw new IOException("BufferedInputStream is closed");
102a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
103a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
104a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
105a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Closes this stream. The source stream is closed and any resources
106a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * associated with it are released.
107a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
108a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException
109a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if an error occurs while closing this stream.
110a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
111a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
112a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public void close() throws IOException {
113a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        buf = null;
114a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        InputStream localIn = in;
115a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        in = null;
116a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localIn != null) {
117a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            localIn.close();
118a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
119a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
120a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
121a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    private int fillbuf(InputStream localIn, byte[] localBuf)
122a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throws IOException {
123a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (markpos == -1 || (pos - markpos >= marklimit)) {
124a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            /* Mark position not set or exceeded readlimit */
125a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            int result = localIn.read(localBuf);
126a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (result > 0) {
127a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                markpos = -1;
128a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                pos = 0;
129a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                count = result == -1 ? 0 : result;
130a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
131a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return result;
132a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
1331224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        //Added count == localBuf.length so that we do not immediately double the buffer size before reading any data
1341224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        // when marklimit > localBuf.length. Instead, we will double the buffer size only after reading the initial
1351224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        // localBuf worth of data without finding what we're looking for in the stream. This allows us to set a
1361224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        // relatively small initial buffer size and a large marklimit for safety without causing an allocation each time
1371224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        // read is called.
1381224f50c5f09df18da720042dca7dd03bf2c6a2eSam Judd        if (markpos == 0 && marklimit > localBuf.length && count == localBuf.length) {
139a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            /* Increase buffer size to accommodate the readlimit */
140a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            int newLength = localBuf.length * 2;
141a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (newLength > marklimit) {
142a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                newLength = marklimit;
143a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
144a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            byte[] newbuf = new byte[newLength];
145a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length);
146a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            // Reassign buf, which will invalidate any local references
147a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            // FIXME: what if buf was null?
148a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            localBuf = buf = newbuf;
149a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        } else if (markpos > 0) {
150a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length
151a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    - markpos);
152a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
153a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        /* Set the new position and mark position */
154a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        pos -= markpos;
155a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        count = markpos = 0;
156a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        int bytesread = localIn.read(localBuf, pos, localBuf.length - pos);
157a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        count = bytesread <= 0 ? pos : pos + bytesread;
158a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        return bytesread;
159a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
160a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
161a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
162a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Sets a mark position in this stream. The parameter {@code readlimit}
163a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * indicates how many bytes can be read before a mark is invalidated.
164a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Calling {@code reset()} will reposition the stream back to the marked
165a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * position if {@code readlimit} has not been surpassed. The underlying
166a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * buffer may be increased in size to allow {@code readlimit} number of
167a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * bytes to be supported.
168a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
169a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @param readlimit
170a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *            the number of bytes that can be read before the mark is
171a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *            invalidated.
172a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @see #reset()
173a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
174a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
175a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized void mark(int readlimit) {
176a1ccb33c449424229fdcbfeb565cca7ba3b1de1dSam Judd        //This is stupid, but BitmapFactory.decodeStream calls mark(1024)
177a1ccb33c449424229fdcbfeb565cca7ba3b1de1dSam Judd        //which is too small for a substantial portion of images. This
178a1ccb33c449424229fdcbfeb565cca7ba3b1de1dSam Judd        //change (using Math.max) ensures that we don't overwrite readlimit
179a1ccb33c449424229fdcbfeb565cca7ba3b1de1dSam Judd        //with a smaller value
180a1ccb33c449424229fdcbfeb565cca7ba3b1de1dSam Judd        marklimit = Math.max(marklimit, readlimit);
181a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        markpos = pos;
182a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
183a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
1849c3d34adfd233c305e878b53238e9cbbcbf11271Sam Judd    public synchronized void clearMark() {
1859c3d34adfd233c305e878b53238e9cbbcbf11271Sam Judd        markpos = -1;
1869c3d34adfd233c305e878b53238e9cbbcbf11271Sam Judd        marklimit = 0;
1879c3d34adfd233c305e878b53238e9cbbcbf11271Sam Judd    }
1889c3d34adfd233c305e878b53238e9cbbcbf11271Sam Judd
189a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
190a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Indicates whether {@code BufferedInputStream} supports the {@code mark()}
191a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * and {@code reset()} methods.
192a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
193a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @return {@code true} for BufferedInputStreams.
194a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @see #mark(int)
195a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @see #reset()
196a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
197a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
198a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public boolean markSupported() {
199a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        return true;
200a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
201a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
202a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
203a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Reads a single byte from this stream and returns it as an integer in the
204a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * range from 0 to 255. Returns -1 if the end of the source string has been
205a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * reached. If the internal buffer does not contain any available bytes then
206a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * it is filled from the source stream and the first byte is returned.
207a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
208a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @return the byte read or -1 if the end of the source stream has been
209a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *         reached.
210a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException
211a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if this stream is closed or another IOException occurs.
212a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
213a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
214a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized int read() throws IOException {
215a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // Use local refs since buf and in may be invalidated by an
216a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // unsynchronized close()
217a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        byte[] localBuf = buf;
218a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        InputStream localIn = in;
219a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localBuf == null || localIn == null) {
220a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
221a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
222a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
223a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        /* Are there buffered bytes available? */
224a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (pos >= count && fillbuf(localIn, localBuf) == -1) {
225a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return -1; /* no, fill buffer */
226a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
227a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // localBuf may have been invalidated by fillbuf
228a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localBuf != buf) {
229a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            localBuf = buf;
230a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (localBuf == null) {
231a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                throw streamClosed();
232a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
233a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
234a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
235a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        /* Did filling the buffer fail with -1 (EOF)? */
236a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (count - pos > 0) {
237a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return localBuf[pos++] & 0xFF;
238a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
239a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        return -1;
240a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
241a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
242a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
243a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Reads at most {@code byteCount} bytes from this stream and stores them in
244a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * byte array {@code buffer} starting at offset {@code offset}. Returns the
245a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * number of bytes actually read or -1 if no bytes were read and the end of
246a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * the stream was encountered. If all the buffered bytes have been used, a
247a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * mark has not been set and the requested number of bytes is larger than
248a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * the receiver&#39;s buffer size, this implementation bypasses the buffer and
249a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * simply places the results directly into {@code buffer}.
250a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
251a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @param buffer
252a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *            the byte array in which to store the bytes read.
253a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @return the number of bytes actually read or -1 if end of stream.
254a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IndexOutOfBoundsException
255a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if {@code offset &lt; 0} or {@code byteCount &lt; 0}, or if
256a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             {@code offset + byteCount} is greater than the size of
257a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             {@code buffer}.
258a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException
259a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if the stream is already closed or another IOException
260a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             occurs.
261a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
262a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
263a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized int read(byte[] buffer, int offset, int byteCount) throws IOException {
264a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // Use local ref since buf may be invalidated by an unsynchronized
265a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // close()
266a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        byte[] localBuf = buf;
267a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localBuf == null) {
268a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
269a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
270a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        //Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
271a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (byteCount == 0) {
272a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return 0;
273a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
274a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        InputStream localIn = in;
275a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localIn == null) {
276a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
277a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
278a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
279a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        int required;
280a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (pos < count) {
281a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            /* There are bytes available in the buffer. */
282a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            int copylength = count - pos >= byteCount ? byteCount : count - pos;
283a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            System.arraycopy(localBuf, pos, buffer, offset, copylength);
284a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            pos += copylength;
285a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (copylength == byteCount || localIn.available() == 0) {
286a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                return copylength;
287a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
288a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            offset += copylength;
289a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            required = byteCount - copylength;
290a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        } else {
291a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            required = byteCount;
292a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
293a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
294a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        while (true) {
295a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            int read;
296a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            /*
297a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd             * If we&#39;re not marked and the required size is greater than the
298a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd             * buffer, simply read the bytes directly bypassing the buffer.
299a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd             */
300a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (markpos == -1 && required >= localBuf.length) {
301a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                read = localIn.read(buffer, offset, required);
302a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                if (read == -1) {
303a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    return required == byteCount ? -1 : byteCount - required;
304a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                }
305a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            } else {
306a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                if (fillbuf(localIn, localBuf) == -1) {
307a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    return required == byteCount ? -1 : byteCount - required;
308a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                }
309a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                // localBuf may have been invalidated by fillbuf
310a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                if (localBuf != buf) {
311a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    localBuf = buf;
312a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    if (localBuf == null) {
313a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                        throw streamClosed();
314a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    }
315a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                }
316a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
317a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                read = count - pos >= required ? required : count - pos;
318a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                System.arraycopy(localBuf, pos, buffer, offset, read);
319a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                pos += read;
320a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
321a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            required -= read;
322a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (required == 0) {
323a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                return byteCount;
324a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
325a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (localIn.available() == 0) {
326a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                return byteCount - required;
327a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
328a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            offset += read;
329a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
330a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
331a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
332a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
333a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Resets this stream to the last marked location.
334a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
335a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException
336a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if this stream is closed, no mark has been set or the mark is
337a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             no longer valid because more than {@code readlimit} bytes
338a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             have been read since setting the mark.
339a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @see #mark(int)
340a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
341a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
342a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized void reset() throws IOException {
343a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (buf == null) {
344a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw new IOException("Stream is closed");
345a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
346a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (-1 == markpos) {
347678c91747b4f8f201767a20f3e0c8beff235d2e3Sam Judd            throw new InvalidMarkException("Mark has been invalidated");
348a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
349a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        pos = markpos;
350a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
351a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
352a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    /**
353a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * Skips {@code byteCount} bytes in this stream. Subsequent calls to
354a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * {@code read} will not return these bytes unless {@code reset} is
355a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * used.
356a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *
357a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @param byteCount
358a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *            the number of bytes to skip. {@code skip} does nothing and
359a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *            returns 0 if {@code byteCount} is less than zero.
360a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @return the number of bytes actually skipped.
361a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     * @throws IOException
362a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     *             if this stream is closed or another IOException occurs.
363a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd     */
364a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    @Override
365a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    public synchronized long skip(long byteCount) throws IOException {
366a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // Use local refs since buf and in may be invalidated by an
367a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        // unsynchronized close()
368a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        byte[] localBuf = buf;
369a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        InputStream localIn = in;
370a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localBuf == null) {
371a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
372a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
373a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (byteCount < 1) {
374a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return 0;
375a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
376a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (localIn == null) {
377a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            throw streamClosed();
378a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
379a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
380a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (count - pos >= byteCount) {
381a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            pos += byteCount;
382a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            return byteCount;
383a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
384a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        long read = count - pos;
385a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        pos = count;
386a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd
387a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        if (markpos != -1) {
388a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            if (byteCount <= marklimit) {
389a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                if (fillbuf(localIn, localBuf) == -1) {
390a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    return read;
391a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                }
392a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                if (count - pos >= byteCount - read) {
393a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    pos += byteCount - read;
394a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                    return byteCount;
395a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                }
396a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                // Couldn&#39;t get all the bytes, skip what we read
397a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                read += (count - pos);
398a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                pos = count;
399a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd                return read;
400a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd            }
401a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        }
402a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd        return read + localIn.skip(byteCount - read);
403a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd    }
404a3a23a8d53117ce1792523f5e6fde126b6efb48cSam Judd}
405