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.io.InputStream;
11 import java.nio.channels.Channels;
12 
13 /**
14  * InputStream that wraps native brotli decoder.
15  */
16 public class BrotliInputStream extends InputStream {
17   /** The default internal buffer size used by the decoder. */
18   private static final int DEFAULT_BUFFER_SIZE = 16384;
19 
20   private final Decoder decoder;
21 
22   /**
23    * Creates a BrotliInputStream.
24    *
25    * @param source underlying source
26    * @param bufferSize intermediate buffer size
27    */
BrotliInputStream(InputStream source, int bufferSize)28   public BrotliInputStream(InputStream source, int bufferSize)
29       throws IOException {
30     this.decoder = new Decoder(Channels.newChannel(source), bufferSize);
31   }
32 
BrotliInputStream(InputStream source)33   public BrotliInputStream(InputStream source) throws IOException {
34     this(source, DEFAULT_BUFFER_SIZE);
35   }
36 
enableEagerOutput()37   public void enableEagerOutput() {
38     decoder.enableEagerOutput();
39   }
40 
41   @Override
close()42   public void close() throws IOException {
43     decoder.close();
44   }
45 
46   @Override
available()47   public int available() {
48     return (decoder.buffer != null) ? decoder.buffer.remaining() : 0;
49   }
50 
51   @Override
read()52   public int read() throws IOException {
53     if (decoder.closed) {
54       throw new IOException("read after close");
55     }
56     int decoded;
57     // Iterate until at leat one byte is decoded, or EOF reached.
58     while (true) {
59       decoded = decoder.decode();
60       if (decoded != 0) {
61         break;
62       }
63     }
64 
65     if (decoded == -1) {
66       return -1;
67     }
68     return decoder.buffer.get() & 0xFF;
69   }
70 
71   @Override
read(byte[] b)72   public int read(byte[] b) throws IOException {
73     return read(b, 0, b.length);
74   }
75 
76   @Override
read(byte[] b, int off, int len)77   public int read(byte[] b, int off, int len) throws IOException {
78     if (decoder.closed) {
79       throw new IOException("read after close");
80     }
81     if (decoder.decode() == -1) {
82       return -1;
83     }
84     int result = 0;
85     while (len > 0) {
86       int limit = Math.min(len, decoder.buffer.remaining());
87       decoder.buffer.get(b, off, limit);
88       off += limit;
89       len -= limit;
90       result += limit;
91       if (decoder.decode() == -1) {
92         break;
93       }
94     }
95     return result;
96   }
97 
98   @Override
skip(long n)99   public long skip(long n) throws IOException {
100     if (decoder.closed) {
101       throw new IOException("read after close");
102     }
103     long result = 0;
104     while (n > 0) {
105       if (decoder.decode() == -1) {
106         break;
107       }
108       int limit = (int) Math.min(n, (long) decoder.buffer.remaining());
109       decoder.discard(limit);
110       result += limit;
111       n -= limit;
112     }
113     return result;
114   }
115 }
116