package net.pms.io; /* * Input stream wrapper with a byte limit. * Copyright (C) 2004 Stephen Ostermiller * http://ostermiller.org/contact.pl?regarding=Java+Utilities * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See COPYING.TXT for details. */ import java.io.IOException; import java.io.InputStream; /** * An input stream wrapper that will read only a set number of bytes from the * underlying stream. * * @author Stephen Ostermiller * http://ostermiller.org/contact.pl?regarding=Java+Utilities * @since ostermillerutils 1.04.00 */ public class SizeLimitInputStream extends InputStream { /** * The input stream that is being protected. All methods should be forwarded * to it, after checking the size that has been read. * * @since ostermillerutils 1.04.00 */ protected InputStream in; /** * The number of bytes to read at most from this Stream. Read methods should * check to ensure that bytesRead never exceeds maxBytesToRead. * * @since ostermillerutils 1.04.00 */ protected long maxBytesToRead = 0; /** * The number of bytes that have been read from this stream. Read methods * should check to ensure that bytesRead never exceeds maxBytesToRead. * * @since ostermillerutils 1.04.00 */ protected long bytesRead = 0; /** * The number of bytes that have been read from this stream since mark() was * called. * * @since ostermillerutils 1.04.00 */ protected long bytesReadSinceMark = 0; /** * The number of bytes the user has request to have been marked for reset. * * @since ostermillerutils 1.04.00 */ protected long markReadLimitBytes = -1; /** * Get the number of bytes actually read from this stream. * * @return number of bytes that have already been taken from this stream. * * @since ostermillerutils 1.04.00 */ public long getBytesRead() { return bytesRead; } /** * Get the maximum number of bytes left to read before the limit (set in the * constructor) is reached. * * @return The number of bytes that (at a maximum) are left to be taken from * this stream. * * @since ostermillerutils 1.04.00 */ public long getBytesLeft() { return maxBytesToRead - bytesRead; } /** * Tell whether the number of bytes specified in the constructor have been * read yet. * * @return true iff the specified number of bytes have all been read. * * @since ostermillerutils 1.04.00 */ public boolean allBytesRead() { return getBytesLeft() == 0; } /** * Get the number of total bytes (including bytes already read) that can be * read from this stream (as set in the constructor). * * @return Maximum bytes that can be read until the size limit runs out * * @since ostermillerutils 1.04.00 */ public long getMaxBytesToRead() { return maxBytesToRead; } /** * Create a new size limit input stream from another stream given a size * limit. * * @param in * The input stream. * @param maxBytesToRead * the max number of bytes to allow to be read from the * underlying stream. * * @since ostermillerutils 1.04.00 */ public SizeLimitInputStream(InputStream in, long maxBytesToRead) { this.in = in; this.maxBytesToRead = maxBytesToRead; } /** * {@inheritDoc} */ @Override public int read() throws IOException { if (bytesRead >= maxBytesToRead) { return -1; } int b = in.read(); if (b != -1) { bytesRead++; bytesReadSinceMark++; } return b; } /** * {@inheritDoc} */ @Override public int read(byte[] b) throws IOException { return this.read(b, 0, b.length); } /** * {@inheritDoc} */ @Override public int read(byte[] b, int off, int len) throws IOException { if (bytesRead >= maxBytesToRead) { return -1; } long bytesLeft = getBytesLeft(); if (len > bytesLeft) { len = (int) bytesLeft; } int bytesJustRead = in.read(b, off, len); bytesRead += bytesJustRead; bytesReadSinceMark += bytesJustRead; return bytesJustRead; } /** * {@inheritDoc} */ @Override public long skip(long n) throws IOException { if (bytesRead >= maxBytesToRead) { return -1; } long bytesLeft = getBytesLeft(); if (n > bytesLeft) { n = bytesLeft; } return in.skip(n); } /** * {@inheritDoc} */ @Override public int available() throws IOException { int available = in.available(); long bytesLeft = getBytesLeft(); if (available > bytesLeft) { available = (int) bytesLeft; } return available; } /** * Close this stream and underlying streams. Calling this method may make * data on the underlying stream unavailable. * <p> * Consider wrapping this stream in a NoCloseStream so that clients can call * close() with no effect. * * @since ostermillerutils 1.04.00 */ @Override public void close() throws IOException { in.close(); } /** * {@inheritDoc} */ @Override public void mark(int readlimit) { if (in.markSupported()) { markReadLimitBytes = readlimit; bytesReadSinceMark = 0; in.mark(readlimit); } } /** * {@inheritDoc} */ @Override public void reset() throws IOException { if (in.markSupported() && bytesReadSinceMark <= markReadLimitBytes) { bytesRead -= bytesReadSinceMark; in.reset(); bytesReadSinceMark = 0; } } /** * {@inheritDoc} */ @Override public boolean markSupported() { return in.markSupported(); } }