/* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.imagepipeline.memory; import javax.annotation.concurrent.NotThreadSafe; import java.io.InputStream; import com.facebook.common.internal.Preconditions; import com.facebook.common.internal.VisibleForTesting; /** * An InputStream implementation over a {@link PooledByteBuffer} instance */ @NotThreadSafe public class PooledByteBufferInputStream extends InputStream { @VisibleForTesting final PooledByteBuffer mPooledByteBuffer; @VisibleForTesting int mOffset; // current offset in the chunk @VisibleForTesting int mMark; // position of 'mark' if any /** * Creates a new inputstream instance over the specific buffer. * @param pooledByteBuffer the buffer to read from */ public PooledByteBufferInputStream(PooledByteBuffer pooledByteBuffer) { super(); Preconditions.checkArgument(!pooledByteBuffer.isClosed()); mPooledByteBuffer = Preconditions.checkNotNull(pooledByteBuffer); mOffset = 0; mMark = 0; } /** * Returns the number of bytes still available to read */ @Override public int available() { return mPooledByteBuffer.size() - mOffset; } /** * Sets a mark position in this inputstream. * The parameter {@code readlimit} is ignored. * Sending {@link #reset()} will reposition the stream back to the marked position. * @param readlimit ignored. */ @Override public void mark(int readlimit) { mMark = mOffset; } /** * Returns {@code true} since this class supports {@link #mark(int)} and {@link #reset()} * methods */ @Override public boolean markSupported() { return true; } @Override public int read() { if (available() <= 0) { return -1; } return ((int) mPooledByteBuffer.read(mOffset++)) & 0xFF; } @Override public int read(byte[] buffer) { return read(buffer, 0, buffer.length); } /** * Reads at most {@code length} bytes from this stream and stores them in byte array * {@code buffer} starting at {@code offset}. * @param buffer the buffer to read data into * @param offset start offset in the buffer * @param length max number of bytes to read * @return number of bytes read */ @Override public int read(byte[] buffer, int offset, int length) { if (offset < 0 || length < 0 || offset + length > buffer.length) { throw new ArrayIndexOutOfBoundsException( "length=" + buffer.length + "; regionStart=" + offset + "; regionLength=" + length); } final int available = available(); if (available <= 0) { return -1; } if (length <= 0) { return 0; } int numToRead = Math.min(available, length); mPooledByteBuffer.read(mOffset, buffer, offset, numToRead); mOffset += numToRead; return numToRead; } /** * Resets this stream to the last marked location. This implementation * resets the position to either the marked position, the start position * supplied in the constructor or 0 if neither has been provided. */ @Override public void reset() { mOffset = mMark; } /** * Skips byteCount (or however many bytes are available) bytes in the stream * @param byteCount number of bytes to skip * @return number of bytes actually skipped */ @Override public long skip(long byteCount) { Preconditions.checkArgument(byteCount >= 0); int skipped = Math.min((int) byteCount, available()); mOffset += skipped; return skipped; } }