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 */ 28 public BrotliInputStream(InputStream source, int bufferSize) 29 throws IOException { 30 this.decoder = new Decoder(Channels.newChannel(source), bufferSize); 31 } 32 33 public BrotliInputStream(InputStream source) throws IOException { 34 this(source, DEFAULT_BUFFER_SIZE); 35 } 36 37 @Override 38 public void close() throws IOException { 39 decoder.close(); 40 } 41 42 @Override 43 public int available() { 44 return (decoder.buffer != null) ? decoder.buffer.remaining() : 0; 45 } 46 47 @Override 48 public int read() throws IOException { 49 if (decoder.closed) { 50 throw new IOException("read after close"); 51 } 52 if (decoder.decode() == -1) { 53 return -1; 54 } 55 return decoder.buffer.get() & 0xFF; 56 } 57 58 @Override 59 public int read(byte[] b) throws IOException { 60 return read(b, 0, b.length); 61 } 62 63 @Override 64 public int read(byte[] b, int off, int len) throws IOException { 65 if (decoder.closed) { 66 throw new IOException("read after close"); 67 } 68 if (decoder.decode() == -1) { 69 return -1; 70 } 71 int result = 0; 72 while (len > 0) { 73 int limit = Math.min(len, decoder.buffer.remaining()); 74 decoder.buffer.get(b, off, limit); 75 off += limit; 76 len -= limit; 77 result += limit; 78 if (decoder.decode() == -1) { 79 break; 80 } 81 } 82 return result; 83 } 84 85 @Override 86 public long skip(long n) throws IOException { 87 if (decoder.closed) { 88 throw new IOException("read after close"); 89 } 90 long result = 0; 91 while (n > 0) { 92 if (decoder.decode() == -1) { 93 break; 94 } 95 int limit = (int) Math.min(n, (long) decoder.buffer.remaining()); 96 decoder.discard(limit); 97 result += limit; 98 n -= limit; 99 } 100 return result; 101 } 102 } 103