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