165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/* 265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project 365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License"); 565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License. 665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at 765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * http://www.apache.org/licenses/LICENSE-2.0 965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software 1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS, 1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and 1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License. 1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.util; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.FilterInputStream; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.IOException; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.InputStream; 2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.ArrayList; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.List; 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/** 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * A replacement of BufferedInputStream (no multiple thread): <p> 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * - use list of byte array (chunks) instead of keep growing a single byte array (more efficent) 2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <br> 2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * - support overriding the markLimit passed in mark() call (The value that BitmapFactory 3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * uses 1024 is too small for detecting bitmap bounds and reset()) <br> 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class CachedInputStream extends FilterInputStream { 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int CHUNK_SIZE = ByteArrayPool.CHUNK16K; 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private ArrayList<byte[]> mBufs = new ArrayList<byte[]>(); 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mPos = 0; // current read position inside the chunk buffers 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mCount = 0; // total validate bytes in chunk buffers 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMarkPos = -1; // marked read position in chunk buffers 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mOverrideMarkLimit; // to override readlimit of mark() call 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMarkLimit; // effective marklimit 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private byte[] tmp = new byte[1]; // tmp buffer used in read() 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public CachedInputStream(InputStream in) { 4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super(in); 4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public boolean markSupported() { 5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * set the value that will override small readlimit passed in mark() call. 5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setOverrideMarkLimit(int overrideMarkLimit) { 5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mOverrideMarkLimit = overrideMarkLimit; 5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int getOverrideMarkLimit() { 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mOverrideMarkLimit; 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void mark(int readlimit) { 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane readlimit = readlimit < mOverrideMarkLimit ? mOverrideMarkLimit : readlimit; 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMarkPos >= 0) { 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // for replacing existing mark(), discard anything before mPos 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // and move mMarkPos to mPos 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int chunks = mPos / CHUNK_SIZE; 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (chunks > 0) { 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // trim the header buffers 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int removedBytes = chunks * CHUNK_SIZE; 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane List<byte[]> subList = mBufs.subList(0, chunks); 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane releaseChunks(subList); 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane subList.clear(); 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos = mPos - removedBytes; 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCount = mCount - removedBytes; 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMarkPos = mPos; 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMarkLimit = readlimit; 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void reset() throws IOException { 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMarkPos < 0) { 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new IOException("mark has been invalidated"); 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos = mMarkPos; 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int read() throws IOException { 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // TODO, not efficient, but the function is not called by BitmapFactory 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int r = read(tmp, 0, 1); 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (r <= 0) { 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return -1; 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return tmp[0] & 0xFF; 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void close() throws IOException { 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (in!=null) { 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane in.close(); 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane in = null; 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane releaseChunks(mBufs); 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static void releaseChunks(List<byte[]> bufs) { 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane ByteArrayPool.get16KBPool().releaseChunks(bufs); 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private byte[] allocateChunk() { 11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return ByteArrayPool.get16KBPool().allocateChunk(); 11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean invalidate() { 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mCount - mMarkPos > mMarkLimit) { 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMarkPos = -1; 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCount = 0; 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos = 0; 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane releaseChunks(mBufs); 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBufs.clear(); 12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int read(byte[] buffer, int offset, int count) throws IOException { 13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (in == null) { 13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw streamClosed(); 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMarkPos == -1) { 13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int reads = in.read(buffer, offset, count); 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return reads; 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (count == 0) { 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return 0; 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int copied = copyMarkedBuffer(buffer, offset, count); 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane count -= copied; 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane offset += copied; 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int totalReads = copied; 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane while (count > 0) { 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mPos == mBufs.size() * CHUNK_SIZE) { 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBufs.add(allocateChunk()); 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int currentBuf = mPos / CHUNK_SIZE; 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int indexInBuf = mPos - currentBuf * CHUNK_SIZE; 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane byte[] buf = mBufs.get(currentBuf); 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int end = (currentBuf + 1) * CHUNK_SIZE; 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int leftInBuffer = end - mPos; 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int toRead = count > leftInBuffer ? leftInBuffer : count; 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int reads = in.read(buf, indexInBuf, toRead); 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (reads > 0) { 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane System.arraycopy(buf, indexInBuf, buffer, offset, reads); 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos += reads; 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCount += reads; 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalReads += reads; 16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane offset += reads; 16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane count -= reads; 16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (invalidate()) { 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane reads = in.read(buffer, offset, count); 16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (reads >0 ) { 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalReads += reads; 17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (totalReads == 0) { 17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return -1; 17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return totalReads; 18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int copyMarkedBuffer(byte[] buffer, int offset, int read) { 18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int totalRead = 0; 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane while (read > 0 && mPos < mCount) { 18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int currentBuf = mPos / CHUNK_SIZE; 18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int indexInBuf = mPos - currentBuf * CHUNK_SIZE; 18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane byte[] buf = mBufs.get(currentBuf); 18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int end = (currentBuf + 1) * CHUNK_SIZE; 19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (end > mCount) { 19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane end = mCount; 19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int leftInBuffer = end - mPos; 19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int toRead = read > leftInBuffer ? leftInBuffer : read; 19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane System.arraycopy(buf, indexInBuf, buffer, offset, toRead); 19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane offset += toRead; 19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane read -= toRead; 19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalRead += toRead; 19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos += toRead; 20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return totalRead; 20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int available() throws IOException { 20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (in == null) { 20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw streamClosed(); 20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mCount - mPos + in.available(); 21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public long skip(long byteCount) throws IOException { 21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (in == null) { 21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw streamClosed(); 21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mMarkPos <0) { 21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return in.skip(byteCount); 21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane long totalSkip = 0; 22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalSkip = mCount - mPos; 22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (totalSkip > byteCount) { 22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalSkip = byteCount; 22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos += totalSkip; 22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane byteCount -= totalSkip; 22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane while (byteCount > 0) { 22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mPos == mBufs.size() * CHUNK_SIZE) { 22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBufs.add(allocateChunk()); 23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int currentBuf = mPos / CHUNK_SIZE; 23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int indexInBuf = mPos - currentBuf * CHUNK_SIZE; 23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane byte[] buf = mBufs.get(currentBuf); 23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int end = (currentBuf + 1) * CHUNK_SIZE; 23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int leftInBuffer = end - mPos; 23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int toRead = (int) (byteCount > leftInBuffer ? leftInBuffer : byteCount); 23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int reads = in.read(buf, indexInBuf, toRead); 23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (reads > 0) { 23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPos += reads; 24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCount += reads; 24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane byteCount -= reads; 24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalSkip += reads; 24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (invalidate()) { 24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (byteCount > 0) { 24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane totalSkip += in.skip(byteCount); 24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return totalSkip; 25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static IOException streamClosed() { 25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return new IOException("stream closed"); 25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 261