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