Skip to content

Commit 3fa2c7c

Browse files
committed
add current state of brotli source code (https://github.com/google/brotli/tree/master/java/org/brotli/dec) as this contains at least one important fix (see #1009)
1 parent 3120f37 commit 3fa2c7c

12 files changed

Lines changed: 3015 additions & 0 deletions
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
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.htmlunit.util.brotli;
8+
9+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR;
10+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_CORRUPTED_PADDING_BITS;
11+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_READ_AFTER_END;
12+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_TRUNCATED_INPUT;
13+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_ERROR_UNUSED_BYTES_AFTER_END;
14+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_OK;
15+
import static org.htmlunit.util.brotli.BrotliError.BROTLI_PANIC_UNALIGNED_COPY_BYTES;
16+
17+
/**
18+
* Bit reading helpers.
19+
*/
20+
final class BitReader {
21+
22+
// Possible values: {5, 6}. 5 corresponds to 32-bit build, 6 to 64-bit. This value is used for
23+
// JIT conditional compilation.
24+
private static final int LOG_BITNESS = Utils.getLogBintness();
25+
26+
// Not only Java compiler prunes "if (const false)" code, but JVM as well.
27+
// Code under "if (BIT_READER_DEBUG != 0)" have zero performance impact (outside unit tests).
28+
private static final int BIT_READER_DEBUG = Utils.isDebugMode();
29+
30+
static final int BITNESS = 1 << LOG_BITNESS;
31+
32+
private static final int BYTENESS = BITNESS / 8;
33+
private static final int CAPACITY = 4096;
34+
// After encountering the end of the input stream, this amount of zero bytes will be appended.
35+
private static final int SLACK = 64;
36+
private static final int BUFFER_SIZE = CAPACITY + SLACK;
37+
// Don't bother to replenish the buffer while this number of bytes is available.
38+
private static final int SAFEGUARD = 36;
39+
private static final int WATERLINE = CAPACITY - SAFEGUARD;
40+
41+
// "Half" refers to "half of native integer type", i.e. on 64-bit machines it is 32-bit type,
42+
// on 32-bit machines it is 16-bit.
43+
private static final int HALF_BITNESS = BITNESS / 2;
44+
private static final int HALF_SIZE = BYTENESS / 2;
45+
private static final int HALVES_CAPACITY = CAPACITY / HALF_SIZE;
46+
private static final int HALF_BUFFER_SIZE = BUFFER_SIZE / HALF_SIZE;
47+
48+
private static final int LOG_HALF_SIZE = LOG_BITNESS - 4;
49+
50+
static final int HALF_WATERLINE = WATERLINE / HALF_SIZE;
51+
52+
/**
53+
* Fills up the input buffer.
54+
*
55+
* <p> Should not be called if there are at least 36 bytes present after current position.
56+
*
57+
* <p> After encountering the end of the input stream, 64 additional zero bytes are copied to the
58+
* buffer.
59+
*/
60+
static int readMoreInput(State s) {
61+
if (s.endOfStreamReached != 0) {
62+
if (halfAvailable(s) >= -2) {
63+
return BROTLI_OK;
64+
}
65+
return Utils.makeError(s, BROTLI_ERROR_TRUNCATED_INPUT);
66+
}
67+
final int readOffset = s.halfOffset << LOG_HALF_SIZE;
68+
int bytesInBuffer = CAPACITY - readOffset;
69+
// Move unused bytes to the head of the buffer.
70+
Utils.copyBytesWithin(s.byteBuffer, 0, readOffset, CAPACITY);
71+
s.halfOffset = 0;
72+
while (bytesInBuffer < CAPACITY) {
73+
final int spaceLeft = CAPACITY - bytesInBuffer;
74+
final int len = Utils.readInput(s, s.byteBuffer, bytesInBuffer, spaceLeft);
75+
// EOF is -1 in Java, but 0 in C#.
76+
if (len < BROTLI_ERROR) {
77+
return len;
78+
}
79+
if (len <= 0) {
80+
s.endOfStreamReached = 1;
81+
s.tailBytes = bytesInBuffer;
82+
bytesInBuffer += HALF_SIZE - 1;
83+
break;
84+
}
85+
bytesInBuffer += len;
86+
}
87+
bytesToNibbles(s, bytesInBuffer);
88+
return BROTLI_OK;
89+
}
90+
91+
static int checkHealth(State s, int endOfStream) {
92+
if (s.endOfStreamReached == 0) {
93+
return BROTLI_OK;
94+
}
95+
final int byteOffset = (s.halfOffset << LOG_HALF_SIZE) + ((s.bitOffset + 7) >> 3) - BYTENESS;
96+
if (byteOffset > s.tailBytes) {
97+
return Utils.makeError(s, BROTLI_ERROR_READ_AFTER_END);
98+
}
99+
if ((endOfStream != 0) && (byteOffset != s.tailBytes)) {
100+
return Utils.makeError(s, BROTLI_ERROR_UNUSED_BYTES_AFTER_END);
101+
}
102+
return BROTLI_OK;
103+
}
104+
105+
static void assertAccumulatorHealthy(State s) {
106+
if (s.bitOffset > BITNESS) {
107+
throw new IllegalStateException("Accumulator underloaded: " + s.bitOffset);
108+
}
109+
}
110+
111+
static void fillBitWindow(State s) {
112+
if (BIT_READER_DEBUG != 0) {
113+
assertAccumulatorHealthy(s);
114+
}
115+
if (s.bitOffset >= HALF_BITNESS) {
116+
// Same as doFillBitWindow. JVM fails to inline it.
117+
if (BITNESS == 64) {
118+
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
119+
| Utils.shr64(s.accumulator64, HALF_BITNESS);
120+
} else {
121+
s.accumulator32 = (s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
122+
| Utils.shr32(s.accumulator32, HALF_BITNESS);
123+
}
124+
s.bitOffset -= HALF_BITNESS;
125+
}
126+
}
127+
128+
static void doFillBitWindow(State s) {
129+
if (BIT_READER_DEBUG != 0) {
130+
assertAccumulatorHealthy(s);
131+
}
132+
if (BITNESS == 64) {
133+
s.accumulator64 = ((long) s.intBuffer[s.halfOffset++] << HALF_BITNESS)
134+
| Utils.shr64(s.accumulator64, HALF_BITNESS);
135+
} else {
136+
s.accumulator32 = (s.shortBuffer[s.halfOffset++] << HALF_BITNESS)
137+
| Utils.shr32(s.accumulator32, HALF_BITNESS);
138+
}
139+
s.bitOffset -= HALF_BITNESS;
140+
}
141+
142+
static int peekBits(State s) {
143+
if (BITNESS == 64) {
144+
return (int) Utils.shr64(s.accumulator64, s.bitOffset);
145+
}
146+
return Utils.shr32(s.accumulator32, s.bitOffset);
147+
}
148+
149+
/**
150+
* Fetches bits from accumulator.
151+
*
152+
* WARNING: accumulator MUST contain at least the specified amount of bits,
153+
* otherwise BitReader will become broken.
154+
*/
155+
static int readFewBits(State s, int n) {
156+
final int v = peekBits(s) & ((1 << n) - 1);
157+
s.bitOffset += n;
158+
return v;
159+
}
160+
161+
static int readBits(State s, int n) {
162+
if (HALF_BITNESS >= 24) {
163+
return readFewBits(s, n);
164+
}
165+
return (n <= 16) ? readFewBits(s, n) : readManyBits(s, n);
166+
}
167+
168+
private static int readManyBits(State s, int n) {
169+
final int low = readFewBits(s, 16);
170+
doFillBitWindow(s);
171+
return low | (readFewBits(s, n - 16) << 16);
172+
}
173+
174+
static int initBitReader(State s) {
175+
s.byteBuffer = new byte[BUFFER_SIZE];
176+
if (BITNESS == 64) {
177+
s.accumulator64 = 0;
178+
s.intBuffer = new int[HALF_BUFFER_SIZE];
179+
} else {
180+
s.accumulator32 = 0;
181+
s.shortBuffer = new short[HALF_BUFFER_SIZE];
182+
}
183+
s.bitOffset = BITNESS;
184+
s.halfOffset = HALVES_CAPACITY;
185+
s.endOfStreamReached = 0;
186+
return prepare(s);
187+
}
188+
189+
private static int prepare(State s) {
190+
if (s.halfOffset > BitReader.HALF_WATERLINE) {
191+
final int result = readMoreInput(s);
192+
if (result != BROTLI_OK) {
193+
return result;
194+
}
195+
}
196+
int health = checkHealth(s, 0);
197+
if (health != BROTLI_OK) {
198+
return health;
199+
}
200+
doFillBitWindow(s);
201+
doFillBitWindow(s);
202+
return BROTLI_OK;
203+
}
204+
205+
static int reload(State s) {
206+
if (s.bitOffset == BITNESS) {
207+
return prepare(s);
208+
}
209+
return BROTLI_OK;
210+
}
211+
212+
static int jumpToByteBoundary(State s) {
213+
final int padding = (BITNESS - s.bitOffset) & 7;
214+
if (padding != 0) {
215+
final int paddingBits = readFewBits(s, padding);
216+
if (paddingBits != 0) {
217+
return Utils.makeError(s, BROTLI_ERROR_CORRUPTED_PADDING_BITS);
218+
}
219+
}
220+
return BROTLI_OK;
221+
}
222+
223+
static int halfAvailable(State s) {
224+
int limit = HALVES_CAPACITY;
225+
if (s.endOfStreamReached != 0) {
226+
limit = (s.tailBytes + (HALF_SIZE - 1)) >> LOG_HALF_SIZE;
227+
}
228+
return limit - s.halfOffset;
229+
}
230+
231+
static int copyRawBytes(State s, byte[] data, int offset, int length) {
232+
int pos = offset;
233+
int len = length;
234+
if ((s.bitOffset & 7) != 0) {
235+
return Utils.makeError(s, BROTLI_PANIC_UNALIGNED_COPY_BYTES);
236+
}
237+
238+
// Drain accumulator.
239+
while ((s.bitOffset != BITNESS) && (len != 0)) {
240+
data[pos++] = (byte) peekBits(s);
241+
s.bitOffset += 8;
242+
len--;
243+
}
244+
if (len == 0) {
245+
return BROTLI_OK;
246+
}
247+
248+
// Get data from shadow buffer with "sizeof(int)" granularity.
249+
final int copyNibbles = Utils.min(halfAvailable(s), len >> LOG_HALF_SIZE);
250+
if (copyNibbles > 0) {
251+
final int readOffset = s.halfOffset << LOG_HALF_SIZE;
252+
final int delta = copyNibbles << LOG_HALF_SIZE;
253+
Utils.copyBytes(data, pos, s.byteBuffer, readOffset, readOffset + delta);
254+
pos += delta;
255+
len -= delta;
256+
s.halfOffset += copyNibbles;
257+
}
258+
if (len == 0) {
259+
return BROTLI_OK;
260+
}
261+
262+
// Read tail bytes.
263+
if (halfAvailable(s) > 0) {
264+
// length = 1..3
265+
fillBitWindow(s);
266+
while (len != 0) {
267+
data[pos++] = (byte) peekBits(s);
268+
s.bitOffset += 8;
269+
len--;
270+
}
271+
return checkHealth(s, 0);
272+
}
273+
274+
// Now it is possible to copy bytes directly.
275+
while (len > 0) {
276+
final int chunkLen = Utils.readInput(s, data, pos, len);
277+
// EOF is -1 in Java, but 0 in C#.
278+
if (len < BROTLI_ERROR) {
279+
return len;
280+
}
281+
if (chunkLen <= 0) {
282+
return Utils.makeError(s, BROTLI_ERROR_TRUNCATED_INPUT);
283+
}
284+
pos += chunkLen;
285+
len -= chunkLen;
286+
}
287+
return BROTLI_OK;
288+
}
289+
290+
/**
291+
* Translates bytes to halves (int/short).
292+
*/
293+
static void bytesToNibbles(State s, int byteLen) {
294+
final byte[] byteBuffer = s.byteBuffer;
295+
final int halfLen = byteLen >> LOG_HALF_SIZE;
296+
if (BITNESS == 64) {
297+
final int[] intBuffer = s.intBuffer;
298+
for (int i = 0; i < halfLen; ++i) {
299+
intBuffer[i] = (byteBuffer[i * 4] & 0xFF)
300+
| ((byteBuffer[(i * 4) + 1] & 0xFF) << 8)
301+
| ((byteBuffer[(i * 4) + 2] & 0xFF) << 16)
302+
| ((byteBuffer[(i * 4) + 3] & 0xFF) << 24);
303+
}
304+
} else {
305+
final short[] shortBuffer = s.shortBuffer;
306+
for (int i = 0; i < halfLen; ++i) {
307+
shortBuffer[i] = (short) ((byteBuffer[i * 2] & 0xFF)
308+
| ((byteBuffer[(i * 2) + 1] & 0xFF) << 8));
309+
}
310+
}
311+
}
312+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/* Copyright 2025 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.htmlunit.util.brotli;
8+
9+
/**
10+
* Possible errors from decoder.
11+
*/
12+
public class BrotliError {
13+
public static final int BROTLI_OK = 0;
14+
public static final int BROTLI_OK_DONE = BROTLI_OK + 1;
15+
public static final int BROTLI_OK_NEED_MORE_OUTPUT = BROTLI_OK + 2;
16+
17+
// It is important that actual error codes are LESS than -1!
18+
public static final int BROTLI_ERROR = -1;
19+
public static final int BROTLI_ERROR_CORRUPTED_CODE_LENGTH_TABLE = BROTLI_ERROR - 1;
20+
public static final int BROTLI_ERROR_CORRUPTED_CONTEXT_MAP = BROTLI_ERROR - 2;
21+
public static final int BROTLI_ERROR_CORRUPTED_HUFFMAN_CODE_HISTOGRAM = BROTLI_ERROR - 3;
22+
public static final int BROTLI_ERROR_CORRUPTED_PADDING_BITS = BROTLI_ERROR - 4;
23+
public static final int BROTLI_ERROR_CORRUPTED_RESERVED_BIT = BROTLI_ERROR - 5;
24+
public static final int BROTLI_ERROR_DUPLICATE_SIMPLE_HUFFMAN_SYMBOL = BROTLI_ERROR - 6;
25+
public static final int BROTLI_ERROR_EXUBERANT_NIBBLE = BROTLI_ERROR - 7;
26+
public static final int BROTLI_ERROR_INVALID_BACKWARD_REFERENCE = BROTLI_ERROR - 8;
27+
public static final int BROTLI_ERROR_INVALID_METABLOCK_LENGTH = BROTLI_ERROR - 9;
28+
public static final int BROTLI_ERROR_INVALID_WINDOW_BITS = BROTLI_ERROR - 10;
29+
public static final int BROTLI_ERROR_NEGATIVE_DISTANCE = BROTLI_ERROR - 11;
30+
public static final int BROTLI_ERROR_READ_AFTER_END = BROTLI_ERROR - 12;
31+
public static final int BROTLI_ERROR_READ_FAILED = BROTLI_ERROR - 13;
32+
public static final int BROTLI_ERROR_SYMBOL_OUT_OF_RANGE = BROTLI_ERROR - 14;
33+
public static final int BROTLI_ERROR_TRUNCATED_INPUT = BROTLI_ERROR - 15;
34+
public static final int BROTLI_ERROR_UNUSED_BYTES_AFTER_END = BROTLI_ERROR - 16;
35+
public static final int BROTLI_ERROR_UNUSED_HUFFMAN_SPACE = BROTLI_ERROR - 17;
36+
37+
public static final int BROTLI_PANIC = -21;
38+
public static final int BROTLI_PANIC_ALREADY_CLOSED = BROTLI_PANIC - 1;
39+
public static final int BROTLI_PANIC_MAX_DISTANCE_TOO_SMALL = BROTLI_PANIC - 2;
40+
public static final int BROTLI_PANIC_STATE_NOT_FRESH = BROTLI_PANIC - 3;
41+
public static final int BROTLI_PANIC_STATE_NOT_INITIALIZED = BROTLI_PANIC - 4;
42+
public static final int BROTLI_PANIC_STATE_NOT_UNINITIALIZED = BROTLI_PANIC - 5;
43+
public static final int BROTLI_PANIC_TOO_MANY_DICTIONARY_CHUNKS = BROTLI_PANIC - 6;
44+
public static final int BROTLI_PANIC_UNEXPECTED_STATE = BROTLI_PANIC - 7;
45+
public static final int BROTLI_PANIC_UNREACHABLE = BROTLI_PANIC - 8;
46+
public static final int BROTLI_PANIC_UNALIGNED_COPY_BYTES = BROTLI_PANIC - 9;
47+
}

0 commit comments

Comments
 (0)