1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.commons.compress.utils;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.ByteBuffer;
24
25 /**
26 * NIO backed bounded input stream for reading a predefined amount of data.
27 *
28 * @ThreadSafe this base class is thread safe but implementations must not be.
29 * @since 1.21
30 */
31 public abstract class BoundedArchiveInputStream extends InputStream {
32
33 private final long end;
34 private ByteBuffer singleByteBuffer;
35 private long loc;
36
37 /**
38 * Constructs a new bounded input stream.
39 *
40 * @param start position in the stream from where the reading of this bounded stream starts.
41 * @param remaining amount of bytes which are allowed to read from the bounded stream.
42 */
43 public BoundedArchiveInputStream(final long start, final long remaining) {
44 this.end = start + remaining;
45 if (this.end < start) {
46 // check for potential vulnerability due to overflow
47 throw new IllegalArgumentException("Invalid length of stream at offset=" + start + ", length=" + remaining);
48 }
49 loc = start;
50 }
51
52 @Override
53 public synchronized int read() throws IOException {
54 if (loc >= end) {
55 return -1;
56 }
57 if (singleByteBuffer == null) {
58 singleByteBuffer = ByteBuffer.allocate(1);
59 } else {
60 singleByteBuffer.rewind();
61 }
62 final int read = read(loc, singleByteBuffer);
63 if (read < 1) {
64 return -1;
65 }
66 loc++;
67 return singleByteBuffer.get() & 0xff;
68 }
69
70 @Override
71 public synchronized int read(final byte[] b, final int off, final int len) throws IOException {
72 if (loc >= end) {
73 return -1;
74 }
75 final long maxLen = Math.min(len, end - loc);
76 if (maxLen <= 0) {
77 return 0;
78 }
79 if (off < 0 || off > b.length || maxLen > b.length - off) {
80 throw new IndexOutOfBoundsException("offset or len are out of bounds");
81 }
82
83 final ByteBuffer buf = ByteBuffer.wrap(b, off, (int) maxLen);
84 final int ret = read(loc, buf);
85 if (ret > 0) {
86 loc += ret;
87 }
88 return ret;
89 }
90
91 /**
92 * Reads content of the stream into a {@link ByteBuffer}.
93 *
94 * @param pos position to start the read.
95 * @param buf buffer to add the read content.
96 * @return number of read bytes.
97 * @throws IOException if I/O fails.
98 */
99 protected abstract int read(long pos, ByteBuffer buf) throws IOException;
100 }