View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.commons.compress.archivers.sevenz;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.nio.ByteBuffer;
22  import java.nio.channels.SeekableByteChannel;
23  
24  final class BoundedSeekableByteChannelInputStream extends InputStream {
25      private static final int MAX_BUF_LEN = 8192;
26      private final ByteBuffer buffer;
27      private final SeekableByteChannel channel;
28      private long bytesRemaining;
29  
30      BoundedSeekableByteChannelInputStream(final SeekableByteChannel channel, final long size) {
31          this.channel = channel;
32          this.bytesRemaining = size;
33          if (size < MAX_BUF_LEN && size > 0) {
34              buffer = ByteBuffer.allocate((int) size);
35          } else {
36              buffer = ByteBuffer.allocate(MAX_BUF_LEN);
37          }
38      }
39  
40      @Override
41      public void close() {
42          // the nested channel is controlled externally
43      }
44  
45      @Override
46      public int read() throws IOException {
47          if (bytesRemaining > 0) {
48              --bytesRemaining;
49              final int read = read(1);
50              if (read < 0) {
51                  return read;
52              }
53              return buffer.get() & 0xff;
54          }
55          return -1;
56      }
57  
58      /**
59       * Reads up to len bytes of data from the input stream into an array of bytes.
60       *
61       * <p>
62       * An attempt is made to read as many as len bytes, but a smaller number may be read. The number of bytes actually read is returned as an integer.
63       * </p>
64       *
65       * <p>
66       * This implementation may return 0 if the underlying {@link SeekableByteChannel} is non-blocking and currently hasn't got any bytes available.
67       * </p>
68       */
69      @Override
70      public int read(final byte[] b, final int off, final int len) throws IOException {
71          if (len == 0) {
72              return 0;
73          }
74          if (bytesRemaining <= 0) {
75              return -1;
76          }
77          int bytesToRead = len;
78          if (bytesToRead > bytesRemaining) {
79              bytesToRead = (int) bytesRemaining;
80          }
81          final int bytesRead;
82          final ByteBuffer buf;
83          if (bytesToRead <= buffer.capacity()) {
84              buf = buffer;
85              bytesRead = read(bytesToRead);
86          } else {
87              buf = ByteBuffer.allocate(bytesToRead);
88              bytesRead = channel.read(buf);
89              buf.flip();
90          }
91          if (bytesRead >= 0) {
92              buf.get(b, off, bytesRead);
93              bytesRemaining -= bytesRead;
94          }
95          return bytesRead;
96      }
97  
98      private int read(final int len) throws IOException {
99          buffer.rewind().limit(len);
100         final int read = channel.read(buffer);
101         buffer.flip();
102         return read;
103     }
104 }