1 /* Copyright 2015 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.dec; 8 9 import java.io.IOException; 10 import java.io.InputStream; 11 12 /** 13 * {@link InputStream} decorator that decompresses brotli data. 14 * 15 * <p> Not thread-safe. 16 */ 17 public class BrotliInputStream extends InputStream { 18 19 public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 16384; 20 21 /** 22 * Internal buffer used for efficient byte-by-byte reading. 23 */ 24 private byte[] buffer; 25 26 /** 27 * Number of decoded but still unused bytes in internal buffer. 28 */ 29 private int remainingBufferBytes; 30 31 /** 32 * Next unused byte offset. 33 */ 34 private int bufferOffset; 35 36 /** 37 * Decoder state. 38 */ 39 private final State state = new State(); 40 41 /** 42 * Creates a {@link InputStream} wrapper that decompresses brotli data. 43 * 44 * <p> For byte-by-byte reading ({@link #read()}) internal buffer with 45 * {@link #DEFAULT_INTERNAL_BUFFER_SIZE} size is allocated and used. 46 * 47 * <p> Will block the thread until first kilobyte of data of source is available. 48 * 49 * @param source underlying data source 50 * @throws IOException in case of corrupted data or source stream problems 51 */ BrotliInputStream(InputStream source)52 public BrotliInputStream(InputStream source) throws IOException { 53 this(source, DEFAULT_INTERNAL_BUFFER_SIZE); 54 } 55 56 /** 57 * Creates a {@link InputStream} wrapper that decompresses brotli data. 58 * 59 * <p> For byte-by-byte reading ({@link #read()}) internal buffer of specified size is 60 * allocated and used. 61 * 62 * <p> Will block the thread until first kilobyte of data of source is available. 63 * 64 * @param source compressed data source 65 * @param byteReadBufferSize size of internal buffer used in case of 66 * byte-by-byte reading 67 * @throws IOException in case of corrupted data or source stream problems 68 */ BrotliInputStream(InputStream source, int byteReadBufferSize)69 public BrotliInputStream(InputStream source, int byteReadBufferSize) throws IOException { 70 if (byteReadBufferSize <= 0) { 71 throw new IllegalArgumentException("Bad buffer size:" + byteReadBufferSize); 72 } else if (source == null) { 73 throw new IllegalArgumentException("source is null"); 74 } 75 this.buffer = new byte[byteReadBufferSize]; 76 this.remainingBufferBytes = 0; 77 this.bufferOffset = 0; 78 try { 79 Decode.initState(state, source); 80 } catch (BrotliRuntimeException ex) { 81 throw new IOException("Brotli decoder initialization failed", ex); 82 } 83 } 84 85 /** 86 * {@inheritDoc} 87 */ 88 @Override close()89 public void close() throws IOException { 90 Decode.close(state); 91 } 92 93 /** 94 * {@inheritDoc} 95 */ 96 @Override read()97 public int read() throws IOException { 98 if (bufferOffset >= remainingBufferBytes) { 99 remainingBufferBytes = read(buffer, 0, buffer.length); 100 bufferOffset = 0; 101 if (remainingBufferBytes == -1) { 102 return -1; 103 } 104 } 105 return buffer[bufferOffset++] & 0xFF; 106 } 107 108 /** 109 * {@inheritDoc} 110 */ 111 @Override read(byte[] destBuffer, int destOffset, int destLen)112 public int read(byte[] destBuffer, int destOffset, int destLen) throws IOException { 113 if (destOffset < 0) { 114 throw new IllegalArgumentException("Bad offset: " + destOffset); 115 } else if (destLen < 0) { 116 throw new IllegalArgumentException("Bad length: " + destLen); 117 } else if (destOffset + destLen > destBuffer.length) { 118 throw new IllegalArgumentException( 119 "Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.length); 120 } else if (destLen == 0) { 121 return 0; 122 } 123 int copyLen = Math.max(remainingBufferBytes - bufferOffset, 0); 124 if (copyLen != 0) { 125 copyLen = Math.min(copyLen, destLen); 126 System.arraycopy(buffer, bufferOffset, destBuffer, destOffset, copyLen); 127 bufferOffset += copyLen; 128 destOffset += copyLen; 129 destLen -= copyLen; 130 if (destLen == 0) { 131 return copyLen; 132 } 133 } 134 try { 135 state.output = destBuffer; 136 state.outputOffset = destOffset; 137 state.outputLength = destLen; 138 state.outputUsed = 0; 139 Decode.decompress(state); 140 if (state.outputUsed == 0) { 141 return -1; 142 } 143 return state.outputUsed + copyLen; 144 } catch (BrotliRuntimeException ex) { 145 throw new IOException("Brotli stream decoding failed", ex); 146 } 147 148 // <{[INJECTED CODE]}> 149 } 150 } 151