1
2
3
4
5
6
7 package org.htmlunit.util.brotli;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11
12
13
14
15
16
17 public class BrotliInputStream extends InputStream {
18
19 public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 256;
20
21
22
23
24
25
26
27 private static final int END_OF_STREAM_MARKER = -1;
28
29
30
31
32 private byte[] buffer;
33
34
35
36
37 private int remainingBufferBytes;
38
39
40
41
42 private int bufferOffset;
43
44
45
46
47 private final State state = new State();
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public BrotliInputStream(InputStream source) throws IOException {
62 this(source, DEFAULT_INTERNAL_BUFFER_SIZE);
63 }
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public BrotliInputStream(InputStream source, int byteReadBufferSize) throws IOException {
80 if (byteReadBufferSize <= 0) {
81 throw new IllegalArgumentException("Bad buffer size:" + byteReadBufferSize);
82 } else if (source == null) {
83 throw new IllegalArgumentException("source is null");
84 }
85 this.buffer = new byte[byteReadBufferSize];
86 this.remainingBufferBytes = 0;
87 this.bufferOffset = 0;
88 try {
89 state.input = source;
90 Decode.initState(state);
91 } catch (BrotliRuntimeException ex) {
92 throw new IOException("Brotli decoder initialization failed", ex);
93 }
94 }
95
96 public void attachDictionaryChunk(byte[] data) {
97 Decode.attachDictionaryChunk(state, data);
98 }
99
100 public void enableEagerOutput() {
101 Decode.enableEagerOutput(state);
102 }
103
104 public void enableLargeWindow() {
105 Decode.enableLargeWindow(state);
106 }
107
108
109
110
111 @Override
112 public void close() throws IOException {
113 Decode.close(state);
114 Utils.closeInput(state);
115 }
116
117
118
119
120 @Override
121 public int read() throws IOException {
122 if (bufferOffset >= remainingBufferBytes) {
123 remainingBufferBytes = read(buffer, 0, buffer.length);
124 bufferOffset = 0;
125 if (remainingBufferBytes == END_OF_STREAM_MARKER) {
126
127 return -1;
128 }
129 }
130 return buffer[bufferOffset++] & 0xFF;
131 }
132
133
134
135
136 @Override
137 public int read(byte[] destBuffer, int destOffset, int destLen) throws IOException {
138 if (destOffset < 0) {
139 throw new IllegalArgumentException("Bad offset: " + destOffset);
140 } else if (destLen < 0) {
141 throw new IllegalArgumentException("Bad length: " + destLen);
142 } else if (destOffset + destLen > destBuffer.length) {
143 throw new IllegalArgumentException(
144 "Buffer overflow: " + (destOffset + destLen) + " > " + destBuffer.length);
145 } else if (destLen == 0) {
146 return 0;
147 }
148 int copyLen = Math.max(remainingBufferBytes - bufferOffset, 0);
149 if (copyLen != 0) {
150 copyLen = Math.min(copyLen, destLen);
151 System.arraycopy(buffer, bufferOffset, destBuffer, destOffset, copyLen);
152 bufferOffset += copyLen;
153 destOffset += copyLen;
154 destLen -= copyLen;
155 if (destLen == 0) {
156 return copyLen;
157 }
158 }
159 try {
160 state.output = destBuffer;
161 state.outputOffset = destOffset;
162 state.outputLength = destLen;
163 state.outputUsed = 0;
164 Decode.decompress(state);
165 copyLen += state.outputUsed;
166 copyLen = (copyLen > 0) ? copyLen : END_OF_STREAM_MARKER;
167 return copyLen;
168 } catch (BrotliRuntimeException ex) {
169 throw new IOException("Brotli stream decoding failed", ex);
170 }
171
172
173 }
174 }