Home | History | Annotate | Download | only in dec
      1 /* Copyright 2015 Google Inc. All Rights Reserved.
      2 
      3    Distributed under MIT license.
      4    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
      5 */
      6 
      7 package org.brotli.dec;
      8 
      9 import java.io.IOException;
     10 import java.io.InputStream;
     11 
     12 /**
     13  * {@link InputStream} decorator that decompresses brotli data.
     14  *
     15  * <p> Not thread-safe.
     16  */
     17 public class BrotliInputStream extends InputStream {
     18 
     19   public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 16384;
     20 
     21   /**
     22    * Internal buffer used for efficient byte-by-byte reading.
     23    */
     24   private byte[] buffer;
     25 
     26   /**
     27    * Number of decoded but still unused bytes in internal buffer.
     28    */
     29   private int remainingBufferBytes;
     30 
     31   /**
     32    * Next unused byte offset.
     33    */
     34   private int bufferOffset;
     35 
     36   /**
     37    * Decoder state.
     38    */
     39   private final State state = new State();
     40 
     41   /**
     42    * Creates a {@link InputStream} wrapper that decompresses brotli data.
     43    *
     44    * <p> For byte-by-byte reading ({@link #read()}) internal buffer with
     45    * {@link #DEFAULT_INTERNAL_BUFFER_SIZE} size is allocated and used.
     46    *
     47    * <p> Will block the thread until first kilobyte of data of source is available.
     48    *
     49    * @param source underlying data source
     50    * @throws IOException in case of corrupted data or source stream problems
     51    */
     52   public BrotliInputStream(InputStream source) throws IOException {
     53     this(source, DEFAULT_INTERNAL_BUFFER_SIZE);
     54   }
     55 
     56   /**
     57    * Creates a {@link InputStream} wrapper that decompresses brotli data.
     58    *
     59    * <p> For byte-by-byte reading ({@link #read()}) internal buffer of specified size is
     60    * allocated and used.
     61    *
     62    * <p> Will block the thread until first kilobyte of data of source is available.
     63    *
     64    * @param source compressed data source
     65    * @param byteReadBufferSize size of internal buffer used in case of
     66    *        byte-by-byte reading
     67    * @throws IOException in case of corrupted data or source stream problems
     68    */
     69   public BrotliInputStream(InputStream source, int byteReadBufferSize) throws IOException {
     70     if (byteReadBufferSize <= 0) {
     71       throw new IllegalArgumentException("Bad buffer size:" + byteReadBufferSize);
     72     } else if (source == null) {
     73       throw new IllegalArgumentException("source is null");
     74     }
     75     this.buffer = new byte[byteReadBufferSize];
     76     this.remainingBufferBytes = 0;
     77     this.bufferOffset = 0;
     78     try {
     79       Decode.initState(state, source);
     80     } catch (BrotliRuntimeException ex) {
     81       throw new IOException("Brotli decoder initialization failed", ex);
     82     }
     83   }
     84 
     85   /**
     86    * {@inheritDoc}
     87    */
     88   @Override
     89   public void close() throws IOException {
     90     Decode.close(state);
     91   }
     92 
     93   /**
     94    * {@inheritDoc}
     95    */
     96   @Override
     97   public int read() throws IOException {
     98     if (bufferOffset >= remainingBufferBytes) {
     99       remainingBufferBytes = read(buffer, 0, buffer.length);
    100       bufferOffset = 0;
    101       if (remainingBufferBytes == -1) {
    102         return -1;
    103       }
    104     }
    105     return buffer[bufferOffset++] & 0xFF;
    106   }
    107 
    108   /**
    109    * {@inheritDoc}
    110    */
    111   @Override
    112   public int read(byte[] destBuffer, int destOffset, int destLen) throws IOException {
    113     if (destOffset < 0) {
    114       throw new IllegalArgumentException("Bad offset: " + destOffset);
    115     } else if (destLen < 0) {
    116       throw new IllegalArgumentException("Bad length: " + destLen);
    117     } else if (destOffset + destLen > destBuffer.length) {
    118       throw new IllegalArgumentException(
    119           "Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.length);
    120     } else if (destLen == 0) {
    121       return 0;
    122     }
    123     int copyLen = Math.max(remainingBufferBytes - bufferOffset, 0);
    124     if (copyLen != 0) {
    125       copyLen = Math.min(copyLen, destLen);
    126       System.arraycopy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
    127       bufferOffset += copyLen;
    128       destOffset += copyLen;
    129       destLen -= copyLen;
    130       if (destLen == 0) {
    131         return copyLen;
    132       }
    133     }
    134     try {
    135       state.output = destBuffer;
    136       state.outputOffset = destOffset;
    137       state.outputLength = destLen;
    138       state.outputUsed = 0;
    139       Decode.decompress(state);
    140       if (state.outputUsed == 0) {
    141         return -1;
    142       }
    143       return state.outputUsed + copyLen;
    144     } catch (BrotliRuntimeException ex) {
    145       throw new IOException("Brotli stream decoding failed", ex);
    146     }
    147 
    148     // <{[INJECTED CODE]}>
    149   }
    150 }
    151