BoundedSeekableByteChannelInputStream.java

  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. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.nio.ByteBuffer;
  21. import java.nio.channels.SeekableByteChannel;

  22. final class BoundedSeekableByteChannelInputStream extends InputStream {
  23.     private static final int MAX_BUF_LEN = 8192;
  24.     private final ByteBuffer buffer;
  25.     private final SeekableByteChannel channel;
  26.     private long bytesRemaining;

  27.     BoundedSeekableByteChannelInputStream(final SeekableByteChannel channel, final long size) {
  28.         this.channel = channel;
  29.         this.bytesRemaining = size;
  30.         this.buffer = ByteBuffer.allocate(size < MAX_BUF_LEN && size > 0 ? (int) size : MAX_BUF_LEN);
  31.     }

  32.     @Override
  33.     public void close() {
  34.         // the nested channel is controlled externally
  35.     }

  36.     @Override
  37.     public int read() throws IOException {
  38.         if (bytesRemaining > 0) {
  39.             --bytesRemaining;
  40.             final int read = read(1);
  41.             if (read < 0) {
  42.                 return read;
  43.             }
  44.             return buffer.get() & 0xff;
  45.         }
  46.         return -1;
  47.     }

  48.     /**
  49.      * Reads up to len bytes of data from the input stream into an array of bytes.
  50.      *
  51.      * <p>
  52.      * 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.
  53.      * </p>
  54.      *
  55.      * <p>
  56.      * This implementation may return 0 if the underlying {@link SeekableByteChannel} is non-blocking and currently hasn't got any bytes available.
  57.      * </p>
  58.      */
  59.     @Override
  60.     public int read(final byte[] b, final int off, final int len) throws IOException {
  61.         if (len == 0) {
  62.             return 0;
  63.         }
  64.         if (bytesRemaining <= 0) {
  65.             return -1;
  66.         }
  67.         int bytesToRead = len;
  68.         if (bytesToRead > bytesRemaining) {
  69.             bytesToRead = (int) bytesRemaining;
  70.         }
  71.         final int bytesRead;
  72.         final ByteBuffer buf;
  73.         if (bytesToRead <= buffer.capacity()) {
  74.             buf = buffer;
  75.             bytesRead = read(bytesToRead);
  76.         } else {
  77.             buf = ByteBuffer.allocate(bytesToRead);
  78.             bytesRead = channel.read(buf);
  79.             buf.flip();
  80.         }
  81.         if (bytesRead >= 0) {
  82.             buf.get(b, off, bytesRead);
  83.             bytesRemaining -= bytesRead;
  84.         }
  85.         return bytesRead;
  86.     }

  87.     private int read(final int len) throws IOException {
  88.         buffer.rewind().limit(len);
  89.         final int read = channel.read(buffer);
  90.         buffer.flip();
  91.         return read;
  92.     }
  93. }