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(&quot;file.java&quot;)); 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'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 < 0} or {@code byteCount < 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'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'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