/* Copyright 2017 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ package org.brotli.wrapper.dec; import java.io.IOException; import java.io.InputStream; import java.nio.channels.Channels; /** * InputStream that wraps native brotli decoder. */ public class BrotliInputStream extends InputStream { /** The default internal buffer size used by the decoder. */ private static final int DEFAULT_BUFFER_SIZE = 16384; private final Decoder decoder; /** * Creates a BrotliInputStream. * * @param source underlying source * @param bufferSize intermediate buffer size */ public BrotliInputStream(InputStream source, int bufferSize) throws IOException { this.decoder = new Decoder(Channels.newChannel(source), bufferSize); } public BrotliInputStream(InputStream source) throws IOException { this(source, DEFAULT_BUFFER_SIZE); } public void enableEagerOutput() { decoder.enableEagerOutput(); } @Override public void close() throws IOException { decoder.close(); } @Override public int available() { return (decoder.buffer != null) ? decoder.buffer.remaining() : 0; } @Override public int read() throws IOException { if (decoder.closed) { throw new IOException("read after close"); } int decoded; // Iterate until at leat one byte is decoded, or EOF reached. while (true) { decoded = decoder.decode(); if (decoded != 0) { break; } } if (decoded == -1) { return -1; } return decoder.buffer.get() & 0xFF; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (decoder.closed) { throw new IOException("read after close"); } if (decoder.decode() == -1) { return -1; } int result = 0; while (len > 0) { int limit = Math.min(len, decoder.buffer.remaining()); decoder.buffer.get(b, off, limit); off += limit; len -= limit; result += limit; if (decoder.decode() == -1) { break; } } return result; } @Override public long skip(long n) throws IOException { if (decoder.closed) { throw new IOException("read after close"); } long result = 0; while (n > 0) { if (decoder.decode() == -1) { break; } int limit = (int) Math.min(n, (long) decoder.buffer.remaining()); decoder.discard(limit); result += limit; n -= limit; } return result; } }