Home | History | Annotate | Download | only in dec
      1 /* Copyright 2017 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.wrapper.dec;
      8 
      9 import java.io.IOException;
     10 import java.nio.ByteBuffer;
     11 import java.nio.channels.ReadableByteChannel;
     12 import java.util.ArrayList;
     13 
     14 /**
     15  * Base class for InputStream / Channel implementations.
     16  */
     17 public class Decoder {
     18   private final ReadableByteChannel source;
     19   private final DecoderJNI.Wrapper decoder;
     20   ByteBuffer buffer;
     21   boolean closed;
     22 
     23   /**
     24    * Creates a Decoder wrapper.
     25    *
     26    * @param source underlying source
     27    * @param inputBufferSize read buffer size
     28    */
     29   public Decoder(ReadableByteChannel source, int inputBufferSize)
     30       throws IOException {
     31     if (inputBufferSize <= 0) {
     32       throw new IllegalArgumentException("buffer size must be positive");
     33     }
     34     if (source == null) {
     35       throw new NullPointerException("source can not be null");
     36     }
     37     this.source = source;
     38     this.decoder = new DecoderJNI.Wrapper(inputBufferSize);
     39   }
     40 
     41   private void fail(String message) throws IOException {
     42     try {
     43       close();
     44     } catch (IOException ex) {
     45       /* Ignore */
     46     }
     47     throw new IOException(message);
     48   }
     49 
     50   /**
     51    * Continue decoding.
     52    *
     53    * @return -1 if stream is finished, or number of bytes available in read buffer (> 0)
     54    */
     55   int decode() throws IOException {
     56     while (true) {
     57       if (buffer != null) {
     58         if (!buffer.hasRemaining()) {
     59           buffer = null;
     60         } else {
     61           return buffer.remaining();
     62         }
     63       }
     64 
     65       switch (decoder.getStatus()) {
     66         case DONE:
     67           return -1;
     68 
     69         case OK:
     70           decoder.push(0);
     71           break;
     72 
     73         case NEEDS_MORE_INPUT:
     74           ByteBuffer inputBuffer = decoder.getInputBuffer();
     75           inputBuffer.clear();
     76           int bytesRead = source.read(inputBuffer);
     77           if (bytesRead == -1) {
     78             fail("unexpected end of input");
     79           }
     80           decoder.push(bytesRead);
     81           break;
     82 
     83         case NEEDS_MORE_OUTPUT:
     84           buffer = decoder.pull();
     85           break;
     86 
     87         default:
     88           fail("corrupted input");
     89       }
     90     }
     91   }
     92 
     93   void discard(int length) {
     94     buffer.position(buffer.position() + length);
     95     if (!buffer.hasRemaining()) {
     96       buffer = null;
     97     }
     98   }
     99 
    100   int consume(ByteBuffer dst) {
    101     ByteBuffer slice = buffer.slice();
    102     int limit = Math.min(slice.remaining(), dst.remaining());
    103     slice.limit(limit);
    104     dst.put(slice);
    105     discard(limit);
    106     return limit;
    107   }
    108 
    109   void close() throws IOException {
    110     if (closed) {
    111       return;
    112     }
    113     closed = true;
    114     decoder.destroy();
    115     source.close();
    116   }
    117 
    118   /**
    119    * Decodes the given data buffer.
    120    */
    121   public static byte[] decompress(byte[] data) throws IOException {
    122     DecoderJNI.Wrapper decoder = new DecoderJNI.Wrapper(data.length);
    123     ArrayList<byte[]> output = new ArrayList<byte[]>();
    124     int totalOutputSize = 0;
    125     try {
    126       decoder.getInputBuffer().put(data);
    127       decoder.push(data.length);
    128       while (decoder.getStatus() != DecoderJNI.Status.DONE) {
    129         switch (decoder.getStatus()) {
    130           case OK:
    131             decoder.push(0);
    132             break;
    133 
    134           case NEEDS_MORE_OUTPUT:
    135             ByteBuffer buffer = decoder.pull();
    136             byte[] chunk = new byte[buffer.remaining()];
    137             buffer.get(chunk);
    138             output.add(chunk);
    139             totalOutputSize += chunk.length;
    140             break;
    141 
    142           default:
    143             throw new IOException("corrupted input");
    144         }
    145       }
    146     } finally {
    147       decoder.destroy();
    148     }
    149     if (output.size() == 1) {
    150       return output.get(0);
    151     }
    152     byte[] result = new byte[totalOutputSize];
    153     int offset = 0;
    154     for (byte[] chunk : output) {
    155       System.arraycopy(chunk, 0, result, offset, chunk.length);
    156       offset += chunk.length;
    157     }
    158     return result;
    159   }
    160 }
    161