blob: a7782bcb06cbeb0ebd553732114cf11b61f3414f [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pdfbox.jbig2.io;
import java.io.IOException;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageInputStreamImpl;
/**
* A wrapper for an {@link ImageInputStream} which is able to provide a view of a specific part of
* the wrapped stream. Read accesses to the wrapped stream are synchronized, so that users of this
* stream need to deal with synchronization against other users of the same instance, but not
* against other users of the wrapped stream.
*/
public class SubInputStream extends ImageInputStreamImpl {
protected final ImageInputStream wrappedStream;
/**
* The position in the wrapped stream at which the window starts. Offset is an absolut value.
*/
protected final long offset;
/**
* The length of the window. Length is an relative value.
*/
protected final long length;
/**
* A buffer which is used to improve read performance.
*/
private final byte buffer[] = new byte[4096];
/**
* Location of the first byte in the buffer with respect to the start of the stream.
*/
long bufferBase;
/**
* Location of the last byte in the buffer with respect to the start of the stream.
*/
long bufferTop;
/**
* Construct a new SubInputStream which provides a view of the wrapped stream.
*
* @param iis - The stream to be wrapped.
* @param offset - The absolute position in the wrapped stream at which the sub-stream starts.
* @param length - The length of the sub-stream.
*/
public SubInputStream(ImageInputStream iis, long offset, long length) {
assert null != iis;
assert length >= 0;
assert offset >= 0;
this.wrappedStream = iis;
this.offset = offset;
this.length = length;
}
@Override
public int read() throws IOException {
if (streamPos >= length) {
return -1;
}
if (streamPos >= bufferTop || streamPos < bufferBase) {
if (!fillBuffer()) {
return -1;
}
}
int read = 0xff & buffer[(int) (streamPos - bufferBase)];
streamPos++;
return read;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (streamPos >= length) {
return -1;
}
synchronized (wrappedStream) {
if (wrappedStream.getStreamPosition() != streamPos + offset) {
wrappedStream.seek(streamPos + offset);
}
int toRead = (int) Math.min(len, length - streamPos);
int read = wrappedStream.read(b, off, toRead);
streamPos += read;
return read;
}
}
/**
* Fill the buffer at the current stream position.
*
* @throws IOException
* @return Boolean flag. {@code true} if successful, {@code false} if not.
*/
private boolean fillBuffer() throws IOException {
synchronized (wrappedStream) {
if (wrappedStream.getStreamPosition() != streamPos + offset) {
wrappedStream.seek(streamPos + offset);
}
bufferBase = streamPos;
int toRead = (int) Math.min(buffer.length, length - streamPos);
int read = wrappedStream.read(buffer, 0, toRead);
bufferTop = bufferBase + read;
return read > 0;
}
}
@Override
public long length() {
return length;
}
/**
* Skips remaining bits in the current byte.
*/
public void skipBits() {
if (bitOffset != 0) {
bitOffset = 0;
streamPos++;
}
}
}