mirror of
https://github.com/google/brotli.git
synced 2024-11-21 19:20:09 +00:00
Update c- and java-decoder: (#536)
* speedup java decoder * avoid masking * avoid excessive fillBits * streamline uncompressed block processing * make java decoder more transpilation-friendly * avoid non-essential goto in c-decoder
This commit is contained in:
parent
e12a7a2d28
commit
21c118ba77
282
dec/decode.c
282
dec/decode.c
@ -584,16 +584,23 @@ static BrotliDecoderErrorCode ReadSymbolCodeLengths(
|
||||
static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
uint32_t alphabet_size, BrotliDecoderState* s) {
|
||||
BrotliBitReader* br = &s->br;
|
||||
BROTLI_BOOL get_byte = BROTLI_FALSE;
|
||||
while (s->symbol < alphabet_size && s->space > 0) {
|
||||
const HuffmanCode* p = s->table;
|
||||
uint32_t code_len;
|
||||
uint32_t available_bits;
|
||||
uint32_t bits = 0;
|
||||
uint32_t available_bits = BrotliGetAvailableBits(br);
|
||||
if (get_byte && !BrotliPullByte(br)) return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
get_byte = BROTLI_FALSE;
|
||||
available_bits = BrotliGetAvailableBits(br);
|
||||
if (available_bits != 0) {
|
||||
bits = (uint32_t)BrotliGetBitsUnmasked(br);
|
||||
}
|
||||
p += bits & BitMask(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH);
|
||||
if (p->bits > available_bits) goto pullMoreInput;
|
||||
if (p->bits > available_bits) {
|
||||
get_byte = BROTLI_TRUE;
|
||||
continue;
|
||||
}
|
||||
code_len = p->value; /* code_len == 0..17 */
|
||||
if (code_len < BROTLI_REPEAT_PREVIOUS_CODE_LENGTH) {
|
||||
BrotliDropBits(br, p->bits);
|
||||
@ -603,19 +610,16 @@ static BrotliDecoderErrorCode SafeReadSymbolCodeLengths(
|
||||
} else { /* code_len == 16..17, extra_bits == 2..3 */
|
||||
uint32_t extra_bits = code_len - 14U;
|
||||
uint32_t repeat_delta = (bits >> p->bits) & BitMask(extra_bits);
|
||||
if (available_bits < p->bits + extra_bits) goto pullMoreInput;
|
||||
if (available_bits < p->bits + extra_bits) {
|
||||
get_byte = BROTLI_TRUE;
|
||||
continue;
|
||||
}
|
||||
BrotliDropBits(br, p->bits + extra_bits);
|
||||
ProcessRepeatedCodeLength(code_len, repeat_delta, alphabet_size,
|
||||
&s->symbol, &s->repeat, &s->space, &s->prev_code_len,
|
||||
&s->repeat_code_len, s->symbol_lists, s->code_length_histo,
|
||||
s->next_symbol);
|
||||
}
|
||||
continue;
|
||||
|
||||
pullMoreInput:
|
||||
if (!BrotliPullByte(br)) {
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
}
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
@ -686,113 +690,115 @@ static BrotliDecoderErrorCode ReadHuffmanCode(uint32_t alphabet_size,
|
||||
/* Unnecessary masking, but might be good for safety. */
|
||||
alphabet_size &= 0x3ff;
|
||||
/* State machine */
|
||||
switch (s->substate_huffman) {
|
||||
case BROTLI_STATE_HUFFMAN_NONE:
|
||||
if (!BrotliSafeReadBits(br, 2, &s->sub_loop_counter)) {
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BROTLI_LOG_UINT(s->sub_loop_counter);
|
||||
/* The value is used as follows:
|
||||
1 for simple code;
|
||||
0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */
|
||||
if (s->sub_loop_counter != 1) {
|
||||
s->space = 32;
|
||||
s->repeat = 0; /* num_codes */
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo[0]) *
|
||||
(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1));
|
||||
memset(&s->code_length_code_lengths[0], 0,
|
||||
sizeof(s->code_length_code_lengths));
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
goto Complex;
|
||||
}
|
||||
/* No break, transit to the next state. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_SIZE:
|
||||
/* Read symbols, codes & code lengths directly. */
|
||||
if (!BrotliSafeReadBits(br, 2, &s->symbol)) { /* num_symbols */
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->sub_loop_counter = 0;
|
||||
/* No break, transit to the next state. */
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_READ: {
|
||||
BrotliDecoderErrorCode result =
|
||||
ReadSimpleHuffmanSymbols(alphabet_size, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
/* No break, transit to the next state. */
|
||||
}
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: {
|
||||
uint32_t table_size;
|
||||
if (s->symbol == 3) {
|
||||
uint32_t bits;
|
||||
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD;
|
||||
for (;;) {
|
||||
switch (s->substate_huffman) {
|
||||
case BROTLI_STATE_HUFFMAN_NONE:
|
||||
if (!BrotliSafeReadBits(br, 2, &s->sub_loop_counter)) {
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->symbol += bits;
|
||||
BROTLI_LOG_UINT(s->sub_loop_counter);
|
||||
/* The value is used as follows:
|
||||
1 for simple code;
|
||||
0 for no skipping, 2 skips 2 code lengths, 3 skips 3 code lengths */
|
||||
if (s->sub_loop_counter != 1) {
|
||||
s->space = 32;
|
||||
s->repeat = 0; /* num_codes */
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo[0]) *
|
||||
(BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH + 1));
|
||||
memset(&s->code_length_code_lengths[0], 0,
|
||||
sizeof(s->code_length_code_lengths));
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_COMPLEX;
|
||||
continue;
|
||||
}
|
||||
/* No break, transit to the next state. */
|
||||
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_SIZE:
|
||||
/* Read symbols, codes & code lengths directly. */
|
||||
if (!BrotliSafeReadBits(br, 2, &s->symbol)) { /* num_symbols */
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_SIZE;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->sub_loop_counter = 0;
|
||||
/* No break, transit to the next state. */
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_READ: {
|
||||
BrotliDecoderErrorCode result =
|
||||
ReadSimpleHuffmanSymbols(alphabet_size, s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
/* No break, transit to the next state. */
|
||||
}
|
||||
BROTLI_LOG_UINT(s->symbol);
|
||||
table_size = BrotliBuildSimpleHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbols_lists_array, s->symbol);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
case BROTLI_STATE_HUFFMAN_SIMPLE_BUILD: {
|
||||
uint32_t table_size;
|
||||
if (s->symbol == 3) {
|
||||
uint32_t bits;
|
||||
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_SIMPLE_BUILD;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
s->symbol += bits;
|
||||
}
|
||||
BROTLI_LOG_UINT(s->symbol);
|
||||
table_size = BrotliBuildSimpleHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbols_lists_array, s->symbol);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
|
||||
/* Decode Huffman-coded code lengths. */
|
||||
case BROTLI_STATE_HUFFMAN_COMPLEX: {
|
||||
uint32_t i;
|
||||
BrotliDecoderErrorCode result = ReadCodeLengthCodeLengths(s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
BrotliBuildCodeLengthsHuffmanTable(s->table,
|
||||
s->code_length_code_lengths,
|
||||
s->code_length_histo);
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo));
|
||||
for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) {
|
||||
s->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||
s->symbol_lists[s->next_symbol[i]] = 0xFFFF;
|
||||
}
|
||||
|
||||
s->symbol = 0;
|
||||
s->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH;
|
||||
s->repeat = 0;
|
||||
s->repeat_code_len = 0;
|
||||
s->space = 32768;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS;
|
||||
/* No break, transit to the next state. */
|
||||
}
|
||||
case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: {
|
||||
uint32_t table_size;
|
||||
BrotliDecoderErrorCode result = ReadSymbolCodeLengths(alphabet_size, s);
|
||||
if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) {
|
||||
result = SafeReadSymbolCodeLengths(alphabet_size, s);
|
||||
}
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (s->space != 0) {
|
||||
BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", s->space));
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE);
|
||||
}
|
||||
table_size = BrotliBuildHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbol_lists, s->code_length_histo);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
|
||||
}
|
||||
|
||||
Complex: /* Decode Huffman-coded code lengths. */
|
||||
case BROTLI_STATE_HUFFMAN_COMPLEX: {
|
||||
uint32_t i;
|
||||
BrotliDecoderErrorCode result = ReadCodeLengthCodeLengths(s);
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
BrotliBuildCodeLengthsHuffmanTable(s->table,
|
||||
s->code_length_code_lengths,
|
||||
s->code_length_histo);
|
||||
memset(&s->code_length_histo[0], 0, sizeof(s->code_length_histo));
|
||||
for (i = 0; i <= BROTLI_HUFFMAN_MAX_CODE_LENGTH; ++i) {
|
||||
s->next_symbol[i] = (int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||
s->symbol_lists[(int)i - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1)] = 0xFFFF;
|
||||
}
|
||||
|
||||
s->symbol = 0;
|
||||
s->prev_code_len = BROTLI_INITIAL_REPEATED_CODE_LENGTH;
|
||||
s->repeat = 0;
|
||||
s->repeat_code_len = 0;
|
||||
s->space = 32768;
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS;
|
||||
/* No break, transit to the next state. */
|
||||
}
|
||||
case BROTLI_STATE_HUFFMAN_LENGTH_SYMBOLS: {
|
||||
uint32_t table_size;
|
||||
BrotliDecoderErrorCode result = ReadSymbolCodeLengths(alphabet_size, s);
|
||||
if (result == BROTLI_DECODER_NEEDS_MORE_INPUT) {
|
||||
result = SafeReadSymbolCodeLengths(alphabet_size, s);
|
||||
}
|
||||
if (result != BROTLI_DECODER_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (s->space != 0) {
|
||||
BROTLI_LOG(("[ReadHuffmanCode] space = %d\n", s->space));
|
||||
return BROTLI_FAILURE(BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE);
|
||||
}
|
||||
table_size = BrotliBuildHuffmanTable(
|
||||
table, HUFFMAN_TABLE_BITS, s->symbol_lists, s->code_length_histo);
|
||||
if (opt_table_size) {
|
||||
*opt_table_size = table_size;
|
||||
}
|
||||
s->substate_huffman = BROTLI_STATE_HUFFMAN_NONE;
|
||||
return BROTLI_DECODER_SUCCESS;
|
||||
}
|
||||
|
||||
default:
|
||||
return
|
||||
BROTLI_FAILURE(BROTLI_DECODER_ERROR_UNREACHABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -972,27 +978,29 @@ static BrotliDecoderErrorCode DecodeContextMap(uint32_t context_map_size,
|
||||
uint32_t max_run_length_prefix = s->max_run_length_prefix;
|
||||
uint8_t* context_map = *context_map_arg;
|
||||
uint32_t code = s->code;
|
||||
if (code != 0xFFFF) {
|
||||
goto rleCode;
|
||||
}
|
||||
while (context_index < context_map_size) {
|
||||
if (!SafeReadSymbol(s->context_map_table, br, &code)) {
|
||||
s->code = 0xFFFF;
|
||||
s->context_index = context_index;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BROTLI_LOG_UINT(code);
|
||||
BROTLI_BOOL skip_preamble = (code != 0xFFFF);
|
||||
while (context_index < context_map_size || skip_preamble) {
|
||||
if (!skip_preamble) {
|
||||
if (!SafeReadSymbol(s->context_map_table, br, &code)) {
|
||||
s->code = 0xFFFF;
|
||||
s->context_index = context_index;
|
||||
return BROTLI_DECODER_NEEDS_MORE_INPUT;
|
||||
}
|
||||
BROTLI_LOG_UINT(code);
|
||||
|
||||
if (code == 0) {
|
||||
context_map[context_index++] = 0;
|
||||
continue;
|
||||
if (code == 0) {
|
||||
context_map[context_index++] = 0;
|
||||
continue;
|
||||
}
|
||||
if (code > max_run_length_prefix) {
|
||||
context_map[context_index++] =
|
||||
(uint8_t)(code - max_run_length_prefix);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
skip_preamble = BROTLI_FALSE;
|
||||
}
|
||||
if (code > max_run_length_prefix) {
|
||||
context_map[context_index++] =
|
||||
(uint8_t)(code - max_run_length_prefix);
|
||||
continue;
|
||||
}
|
||||
rleCode:
|
||||
/* RLE sub-stage. */
|
||||
{
|
||||
uint32_t reps;
|
||||
if (!BrotliSafeReadBits(br, code, &reps)) {
|
||||
@ -1707,17 +1715,17 @@ CommandPostDecodeLiterals:
|
||||
s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS;
|
||||
}
|
||||
if (s->distance_code >= 0) {
|
||||
/* Implicit distance case. */
|
||||
s->distance_context = s->distance_code ? 0 : 1;
|
||||
--s->dist_rb_idx;
|
||||
s->distance_code = s->dist_rb[s->dist_rb_idx & 3];
|
||||
goto postReadDistance; /* We already have the implicit distance */
|
||||
} else {
|
||||
/* Read distance code in the command, unless it was implicitly zero. */
|
||||
if (BROTLI_PREDICT_FALSE(s->block_length[2] == 0)) {
|
||||
BROTLI_SAFE(DecodeDistanceBlockSwitch(s));
|
||||
}
|
||||
BROTLI_SAFE(ReadDistance(s, br));
|
||||
}
|
||||
/* Read distance code in the command, unless it was implicitly zero. */
|
||||
if (BROTLI_PREDICT_FALSE(s->block_length[2] == 0)) {
|
||||
BROTLI_SAFE(DecodeDistanceBlockSwitch(s));
|
||||
}
|
||||
BROTLI_SAFE(ReadDistance(s, br));
|
||||
postReadDistance:
|
||||
BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n",
|
||||
pos, s->distance_code));
|
||||
if (s->max_distance != s->max_backward_distance) {
|
||||
|
@ -18,11 +18,15 @@ final class BitReader {
|
||||
* Input byte buffer, consist of a ring-buffer and a "slack" region where bytes from the start of
|
||||
* the ring-buffer are copied.
|
||||
*/
|
||||
private static final int BUF_SIZE = IntReader.CAPACITY << 2;
|
||||
private static final int READ_SIZE = BUF_SIZE - 64;
|
||||
private static final int CAPACITY = 1024;
|
||||
private static final int SLACK = 16;
|
||||
private static final int INT_BUFFER_SIZE = CAPACITY + SLACK;
|
||||
private static final int BYTE_READ_SIZE = CAPACITY << 2;
|
||||
private static final int BYTE_BUFFER_SIZE = INT_BUFFER_SIZE << 2;
|
||||
|
||||
private final byte[] byteBuffer = new byte[BYTE_BUFFER_SIZE];
|
||||
private final int[] intBuffer = new int[INT_BUFFER_SIZE];
|
||||
private final IntReader intReader = new IntReader();
|
||||
private final byte[] shadowBuffer = new byte[BUF_SIZE];
|
||||
|
||||
private InputStream input;
|
||||
|
||||
@ -42,9 +46,9 @@ final class BitReader {
|
||||
int bitOffset;
|
||||
|
||||
/**
|
||||
* Number of 32-bit integers available for reading.
|
||||
* Offset of next item in intBuffer.
|
||||
*/
|
||||
private int available;
|
||||
private int intOffset;
|
||||
|
||||
/* Number of bytes in unfinished "int" item. */
|
||||
private int tailBytes = 0;
|
||||
@ -59,26 +63,26 @@ final class BitReader {
|
||||
*/
|
||||
// TODO: Split to check and read; move read outside of decoding loop.
|
||||
static void readMoreInput(BitReader br) {
|
||||
if (br.available > 9) {
|
||||
if (br.intOffset <= CAPACITY - 9) {
|
||||
return;
|
||||
}
|
||||
if (br.endOfStreamReached) {
|
||||
if (br.available > 4) {
|
||||
if (intAvailable(br) >= -2) {
|
||||
return;
|
||||
}
|
||||
throw new BrotliRuntimeException("No more input");
|
||||
}
|
||||
int readOffset = IntReader.position(br.intReader) << 2;
|
||||
int bytesRead = READ_SIZE - readOffset;
|
||||
System.arraycopy(br.shadowBuffer, readOffset, br.shadowBuffer, 0, bytesRead);
|
||||
int readOffset = br.intOffset << 2;
|
||||
int bytesRead = BYTE_READ_SIZE - readOffset;
|
||||
System.arraycopy(br.byteBuffer, readOffset, br.byteBuffer, 0, bytesRead);
|
||||
br.intOffset = 0;
|
||||
try {
|
||||
while (bytesRead < READ_SIZE) {
|
||||
int len = br.input.read(br.shadowBuffer, bytesRead, READ_SIZE - bytesRead);
|
||||
while (bytesRead < BYTE_READ_SIZE) {
|
||||
int len = br.input.read(br.byteBuffer, bytesRead, BYTE_READ_SIZE - bytesRead);
|
||||
if (len == -1) {
|
||||
br.endOfStreamReached = true;
|
||||
Utils.fillWithZeroes(br.shadowBuffer, bytesRead, 64);
|
||||
bytesRead += 64;
|
||||
br.tailBytes = bytesRead & 3;
|
||||
br.tailBytes = bytesRead;
|
||||
bytesRead += 3;
|
||||
break;
|
||||
}
|
||||
bytesRead += len;
|
||||
@ -86,21 +90,20 @@ final class BitReader {
|
||||
} catch (IOException e) {
|
||||
throw new BrotliRuntimeException("Failed to read input", e);
|
||||
}
|
||||
IntReader.reload(br.intReader, br.shadowBuffer, 0, bytesRead >> 2);
|
||||
br.available = bytesRead >> 2;
|
||||
IntReader.convert(br.intReader, bytesRead >> 2);
|
||||
}
|
||||
|
||||
static void checkHealth(BitReader br) {
|
||||
static void checkHealth(BitReader br, boolean endOfStream) {
|
||||
if (!br.endOfStreamReached) {
|
||||
return;
|
||||
}
|
||||
/* When end of stream is reached, we "borrow" up to 64 zeroes to bit reader.
|
||||
* If compressed stream is valid, then borrowed zeroes should remain unused. */
|
||||
int unusedBytes = (br.available << 2) + ((64 - br.bitOffset) >> 3);
|
||||
int borrowedBytes = 64 - br.tailBytes;
|
||||
if (unusedBytes != borrowedBytes) {
|
||||
int byteOffset = (br.intOffset << 2) + ((br.bitOffset + 7) >> 3) - 8;
|
||||
if (byteOffset > br.tailBytes) {
|
||||
throw new BrotliRuntimeException("Read after end");
|
||||
}
|
||||
if (endOfStream && (byteOffset != br.tailBytes)) {
|
||||
throw new BrotliRuntimeException("Unused bytes after end");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,9 +111,8 @@ final class BitReader {
|
||||
*/
|
||||
static void fillBitWindow(BitReader br) {
|
||||
if (br.bitOffset >= 32) {
|
||||
br.accumulator = ((long) IntReader.read(br.intReader) << 32) | (br.accumulator >>> 32);
|
||||
br.accumulator = ((long) br.intBuffer[br.intOffset++] << 32) | (br.accumulator >>> 32);
|
||||
br.bitOffset -= 32;
|
||||
br.available--;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,19 +139,26 @@ final class BitReader {
|
||||
if (br.input != null) {
|
||||
throw new IllegalStateException("Bit reader already has associated input stream");
|
||||
}
|
||||
IntReader.init(br.intReader, br.byteBuffer, br.intBuffer);
|
||||
br.input = input;
|
||||
br.accumulator = 0;
|
||||
IntReader.setPosition(br.intReader, READ_SIZE >> 2);
|
||||
br.bitOffset = 64;
|
||||
br.available = 0;
|
||||
br.intOffset = CAPACITY;
|
||||
br.endOfStreamReached = false;
|
||||
prepare(br);
|
||||
}
|
||||
|
||||
private static void prepare(BitReader br) {
|
||||
readMoreInput(br);
|
||||
/* This situation is impossible in current implementation. */
|
||||
if (br.available == 0) {
|
||||
throw new BrotliRuntimeException("Can't initialize reader");
|
||||
checkHealth(br, false);
|
||||
fillBitWindow(br);
|
||||
fillBitWindow(br);
|
||||
}
|
||||
|
||||
static void reload(BitReader br) {
|
||||
if (br.bitOffset == 64) {
|
||||
prepare(br);
|
||||
}
|
||||
fillBitWindow(br);
|
||||
fillBitWindow(br);
|
||||
}
|
||||
|
||||
static void close(BitReader br) throws IOException {
|
||||
@ -165,9 +174,72 @@ final class BitReader {
|
||||
if (padding != 0) {
|
||||
int paddingBits = BitReader.readBits(br, padding);
|
||||
if (paddingBits != 0) {
|
||||
throw new BrotliRuntimeException("Corrupted padding bits ");
|
||||
throw new BrotliRuntimeException("Corrupted padding bits");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int intAvailable(BitReader br) {
|
||||
int limit = CAPACITY;
|
||||
if (br.endOfStreamReached) {
|
||||
limit = (br.tailBytes + 3) >> 2;
|
||||
}
|
||||
return limit - br.intOffset;
|
||||
}
|
||||
|
||||
static void copyBytes(BitReader br, byte[] data, int offset, int length) {
|
||||
if ((br.bitOffset & 7) != 0) {
|
||||
throw new BrotliRuntimeException("Unaligned copyBytes");
|
||||
}
|
||||
|
||||
// Drain accumulator.
|
||||
while ((br.bitOffset != 64) && (length != 0)) {
|
||||
data[offset++] = (byte) (br.accumulator >>> br.bitOffset);
|
||||
br.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get data from shadow buffer with "sizeof(int)" granularity.
|
||||
int copyInts = Math.min(intAvailable(br), length >> 2);
|
||||
if (copyInts > 0) {
|
||||
int readOffset = br.intOffset << 2;
|
||||
System.arraycopy(br.byteBuffer, readOffset, data, offset, copyInts << 2);
|
||||
offset += copyInts << 2;
|
||||
length -= copyInts << 2;
|
||||
br.intOffset += copyInts;
|
||||
}
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read tail bytes.
|
||||
if (intAvailable(br) > 0) {
|
||||
// length = 1..3
|
||||
fillBitWindow(br);
|
||||
while (length != 0) {
|
||||
data[offset++] = (byte) (br.accumulator >>> br.bitOffset);
|
||||
br.bitOffset += 8;
|
||||
length--;
|
||||
}
|
||||
checkHealth(br, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now it is possible to copy bytes directly.
|
||||
try {
|
||||
while (length > 0) {
|
||||
int len = br.input.read(data, offset, length);
|
||||
if (len == -1) {
|
||||
throw new BrotliRuntimeException("Unexpected end of input");
|
||||
}
|
||||
offset += len;
|
||||
length -= len;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new BrotliRuntimeException("Failed to read input", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -17,11 +19,17 @@ import org.junit.runners.JUnit4;
|
||||
@RunWith(JUnit4.class)
|
||||
public class BitReaderTest {
|
||||
|
||||
@Test(expected = BrotliRuntimeException.class)
|
||||
@Test
|
||||
public void testReadAfterEos() {
|
||||
BitReader reader = new BitReader();
|
||||
BitReader.init(reader, new ByteArrayInputStream(new byte[1]));
|
||||
BitReader.readBits(reader, 9);
|
||||
BitReader.checkHealth(reader);
|
||||
try {
|
||||
BitReader.checkHealth(reader, false);
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
// This exception is expected.
|
||||
return;
|
||||
}
|
||||
fail("BrotliRuntimeException should have been thrown by BitReader.checkHealth");
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public class BrotliInputStream extends InputStream {
|
||||
try {
|
||||
State.setInput(state, source);
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
throw new IOException(ex);
|
||||
throw new IOException("Brotli decoder initialization failed", ex);
|
||||
}
|
||||
if (customDictionary != null) {
|
||||
Decode.setCustomDictionary(state, customDictionary);
|
||||
@ -117,18 +117,14 @@ public class BrotliInputStream extends InputStream {
|
||||
*/
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
try {
|
||||
if (bufferOffset >= remainingBufferBytes) {
|
||||
remainingBufferBytes = read(buffer, 0, buffer.length);
|
||||
bufferOffset = 0;
|
||||
if (remainingBufferBytes == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (bufferOffset >= remainingBufferBytes) {
|
||||
remainingBufferBytes = read(buffer, 0, buffer.length);
|
||||
bufferOffset = 0;
|
||||
if (remainingBufferBytes == -1) {
|
||||
return -1;
|
||||
}
|
||||
return buffer[bufferOffset++] & 0xFF;
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
throw new IOException(ex);
|
||||
}
|
||||
return buffer[bufferOffset++] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,7 +164,7 @@ public class BrotliInputStream extends InputStream {
|
||||
}
|
||||
return state.outputUsed + copyLen;
|
||||
} catch (BrotliRuntimeException ex) {
|
||||
throw new IOException(ex);
|
||||
throw new IOException("Brotli stream decoding failed", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,4 @@ class BrotliRuntimeException extends RuntimeException {
|
||||
BrotliRuntimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
BrotliRuntimeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
@ -117,19 +117,22 @@ final class Decode {
|
||||
* Decodes the next Huffman code from bit-stream.
|
||||
*/
|
||||
private static int readSymbol(int[] table, int offset, BitReader br) {
|
||||
BitReader.fillBitWindow(br);
|
||||
offset += (int) (br.accumulator >>> br.bitOffset) & HUFFMAN_TABLE_MASK;
|
||||
int n = (table[offset] >> 16) - HUFFMAN_TABLE_BITS;
|
||||
if (n > 0) {
|
||||
br.bitOffset += HUFFMAN_TABLE_BITS;
|
||||
offset += table[offset] & 0xFFFF;
|
||||
offset += (br.accumulator >>> br.bitOffset) & ((1 << n) - 1);
|
||||
int val = (int) (br.accumulator >>> br.bitOffset);
|
||||
offset += val & HUFFMAN_TABLE_MASK;
|
||||
int bits = table[offset] >> 16;
|
||||
int sym = table[offset] & 0xFFFF;
|
||||
if (bits <= HUFFMAN_TABLE_BITS) {
|
||||
br.bitOffset += bits;
|
||||
return sym;
|
||||
}
|
||||
br.bitOffset += table[offset] >> 16;
|
||||
offset += sym;
|
||||
offset += (val & ((1L << bits) - 1)) >>> HUFFMAN_TABLE_BITS;
|
||||
br.bitOffset += ((table[offset] >> 16) + HUFFMAN_TABLE_BITS);
|
||||
return table[offset] & 0xFFFF;
|
||||
}
|
||||
|
||||
private static int readBlockLength(int[] table, int offset, BitReader br) {
|
||||
BitReader.fillBitWindow(br);
|
||||
int code = readSymbol(table, offset, br);
|
||||
int n = Prefix.BLOCK_LENGTH_N_BITS[code];
|
||||
return Prefix.BLOCK_LENGTH_OFFSET[code] + BitReader.readBits(br, n);
|
||||
@ -242,7 +245,8 @@ final class Decode {
|
||||
maxBitsCounter >>= 1;
|
||||
maxBits++;
|
||||
}
|
||||
Utils.fillWithZeroes(codeLengths, 0, alphabetSize);
|
||||
// TODO: uncomment when codeLengths is reused.
|
||||
// Utils.fillWithZeroes(codeLengths, 0, alphabetSize);
|
||||
for (int i = 0; i < numSymbols; i++) {
|
||||
symbols[i] = BitReader.readBits(br, maxBits) % alphabetSize;
|
||||
codeLengths[symbols[i]] = 2;
|
||||
@ -259,6 +263,7 @@ final class Decode {
|
||||
ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[1] != symbols[2];
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
ok = symbols[0] != symbols[1] && symbols[0] != symbols[2] && symbols[0] != symbols[3]
|
||||
&& symbols[1] != symbols[2] && symbols[1] != symbols[3] && symbols[2] != symbols[3];
|
||||
if (BitReader.readBits(br, 1) == 1) {
|
||||
@ -313,6 +318,7 @@ final class Decode {
|
||||
readHuffmanCode(numTrees + maxRunLengthPrefix, table, 0, br);
|
||||
for (int i = 0; i < contextMapSize; ) {
|
||||
BitReader.readMoreInput(br);
|
||||
BitReader.fillBitWindow(br);
|
||||
int code = readSymbol(table, 0, br);
|
||||
if (code == 0) {
|
||||
contextMap[i] = 0;
|
||||
@ -342,6 +348,7 @@ final class Decode {
|
||||
final BitReader br = state.br;
|
||||
final int[] ringBuffers = state.blockTypeRb;
|
||||
final int offset = treeType * 2;
|
||||
BitReader.fillBitWindow(br);
|
||||
int blockType = readSymbol(
|
||||
state.blockTypeTrees, treeType * Huffman.HUFFMAN_MAX_TABLE_SIZE, br);
|
||||
state.blockLength[treeType] = readBlockLength(state.blockLenTrees,
|
||||
@ -429,7 +436,7 @@ final class Decode {
|
||||
|
||||
if (state.inputEnd) {
|
||||
state.nextRunningState = FINISHED;
|
||||
state.bytesToWrite = state.pos & (state.ringBufferSize - 1);
|
||||
state.bytesToWrite = state.pos;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = WRITE;
|
||||
return;
|
||||
@ -536,21 +543,27 @@ final class Decode {
|
||||
private static void copyUncompressedData(State state) {
|
||||
final BitReader br = state.br;
|
||||
final byte[] ringBuffer = state.ringBuffer;
|
||||
final int ringBufferMask = state.ringBufferSize - 1;
|
||||
|
||||
while (state.metaBlockLength > 0) {
|
||||
BitReader.readMoreInput(br);
|
||||
// Optimize
|
||||
ringBuffer[state.pos & ringBufferMask] = (byte) (BitReader.readBits(br, 8));
|
||||
state.metaBlockLength--;
|
||||
if ((state.pos++ & ringBufferMask) == ringBufferMask) {
|
||||
// Could happen if block ends at ring buffer end.
|
||||
if (state.metaBlockLength <= 0) {
|
||||
BitReader.reload(br);
|
||||
state.runningState = BLOCK_START;
|
||||
return;
|
||||
}
|
||||
|
||||
int chunkLength = Math.min(state.ringBufferSize - state.pos, state.metaBlockLength);
|
||||
BitReader.copyBytes(br, ringBuffer, state.pos, chunkLength);
|
||||
state.metaBlockLength -= chunkLength;
|
||||
state.pos += chunkLength;
|
||||
if (state.pos == state.ringBufferSize) {
|
||||
state.nextRunningState = COPY_UNCOMPRESSED;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
state.runningState = WRITE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BitReader.reload(br);
|
||||
state.runningState = BLOCK_START;
|
||||
}
|
||||
|
||||
@ -610,8 +623,6 @@ final class Decode {
|
||||
|
||||
case MAIN_LOOP:
|
||||
if (state.metaBlockLength <= 0) {
|
||||
// Protect pos from overflow, wrap it around at every GB of input data.
|
||||
state.pos &= 0x3fffffff;
|
||||
state.runningState = BLOCK_START;
|
||||
continue;
|
||||
}
|
||||
@ -620,6 +631,7 @@ final class Decode {
|
||||
decodeCommandBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[1]--;
|
||||
BitReader.fillBitWindow(br);
|
||||
int cmdCode = readSymbol(state.hGroup1.codes, state.treeCommandOffset, br);
|
||||
int rangeIdx = cmdCode >>> 6;
|
||||
state.distanceCode = 0;
|
||||
@ -646,10 +658,11 @@ final class Decode {
|
||||
decodeLiteralBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[0]--;
|
||||
ringBuffer[state.pos & ringBufferMask] = (byte) readSymbol(
|
||||
state.hGroup0.codes, state.literalTree, br);
|
||||
BitReader.fillBitWindow(br);
|
||||
ringBuffer[state.pos] =
|
||||
(byte) readSymbol(state.hGroup0.codes, state.literalTree, br);
|
||||
state.j++;
|
||||
if ((state.pos++ & ringBufferMask) == ringBufferMask) {
|
||||
if (state.pos++ == ringBufferMask) {
|
||||
state.nextRunningState = INSERT_LOOP;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
@ -670,11 +683,12 @@ final class Decode {
|
||||
| Context.LOOKUP[state.contextLookupOffset2 + prevByte2])] & 0xFF;
|
||||
state.blockLength[0]--;
|
||||
prevByte2 = prevByte1;
|
||||
BitReader.fillBitWindow(br);
|
||||
prevByte1 = readSymbol(
|
||||
state.hGroup0.codes, state.hGroup0.trees[literalTreeIndex], br);
|
||||
ringBuffer[state.pos & ringBufferMask] = (byte) prevByte1;
|
||||
ringBuffer[state.pos] = (byte) prevByte1;
|
||||
state.j++;
|
||||
if ((state.pos++ & ringBufferMask) == ringBufferMask) {
|
||||
if (state.pos++ == ringBufferMask) {
|
||||
state.nextRunningState = INSERT_LOOP;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
@ -697,6 +711,7 @@ final class Decode {
|
||||
decodeDistanceBlockSwitch(state);
|
||||
}
|
||||
state.blockLength[2]--;
|
||||
BitReader.fillBitWindow(br);
|
||||
state.distanceCode = readSymbol(state.hGroup2.codes, state.hGroup2.trees[
|
||||
state.distContextMap[state.distContextMapSlice
|
||||
+ (state.copyLength > 4 ? 3 : state.copyLength - 2)] & 0xFF], br);
|
||||
@ -718,14 +733,14 @@ final class Decode {
|
||||
throw new BrotliRuntimeException("Negative distance"); // COV_NF_LINE
|
||||
}
|
||||
|
||||
if (state.pos < state.maxBackwardDistance
|
||||
&& state.maxDistance != state.maxBackwardDistance) {
|
||||
if (state.maxDistance != state.maxBackwardDistance
|
||||
&& state.pos < state.maxBackwardDistance) {
|
||||
state.maxDistance = state.pos;
|
||||
} else {
|
||||
state.maxDistance = state.maxBackwardDistance;
|
||||
}
|
||||
|
||||
state.copyDst = state.pos & ringBufferMask;
|
||||
state.copyDst = state.pos;
|
||||
if (state.distance > state.maxDistance) {
|
||||
state.runningState = TRANSFORM;
|
||||
continue;
|
||||
@ -744,12 +759,12 @@ final class Decode {
|
||||
// fall through
|
||||
case COPY_LOOP:
|
||||
for (; state.j < state.copyLength;) {
|
||||
ringBuffer[state.pos & ringBufferMask] =
|
||||
ringBuffer[state.pos] =
|
||||
ringBuffer[(state.pos - state.distance) & ringBufferMask];
|
||||
// TODO: condense
|
||||
state.metaBlockLength--;
|
||||
state.j++;
|
||||
if ((state.pos++ & ringBufferMask) == ringBufferMask) {
|
||||
if (state.pos++ == ringBufferMask) {
|
||||
state.nextRunningState = COPY_LOOP;
|
||||
state.bytesToWrite = state.ringBufferSize;
|
||||
state.bytesWritten = 0;
|
||||
@ -821,6 +836,10 @@ final class Decode {
|
||||
// Output buffer is full.
|
||||
return;
|
||||
}
|
||||
if (state.pos >= state.maxBackwardDistance) {
|
||||
state.maxDistance = state.maxBackwardDistance;
|
||||
}
|
||||
state.pos &= ringBufferMask;
|
||||
state.runningState = state.nextRunningState;
|
||||
continue;
|
||||
|
||||
@ -833,7 +852,7 @@ final class Decode {
|
||||
throw new BrotliRuntimeException("Invalid metablock length");
|
||||
}
|
||||
BitReader.jumpToByteBoundary(br);
|
||||
BitReader.checkHealth(state.br);
|
||||
BitReader.checkHealth(state.br, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -6,43 +6,31 @@
|
||||
|
||||
package org.brotli.dec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
/**
|
||||
* Byte-to-int conversion magic.
|
||||
*/
|
||||
final class IntReader {
|
||||
|
||||
static final int CAPACITY = 1024 + 16;
|
||||
private byte[] byteBuffer;
|
||||
private int[] intBuffer;
|
||||
|
||||
private final ByteBuffer byteBuffer =
|
||||
ByteBuffer.allocateDirect(CAPACITY << 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||
private final IntBuffer intBuffer = byteBuffer.asIntBuffer();
|
||||
static void init(IntReader ir, byte[] byteBuffer, int[] intBuffer) {
|
||||
ir.byteBuffer = byteBuffer;
|
||||
ir.intBuffer = intBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize reader with new data chunk.
|
||||
* Translates bytes to ints.
|
||||
*
|
||||
* NB: intLen == 4 * byteSize!
|
||||
* NB: intLen should be less or equal to {@link CAPACITY}
|
||||
* NB: intLen should be less or equal to intBuffer length.
|
||||
*/
|
||||
static void reload(IntReader ir, byte[] data, int offset, int intLen) {
|
||||
ir.byteBuffer.clear();
|
||||
ir.byteBuffer.put(data, offset, intLen << 2);
|
||||
ir.intBuffer.rewind();
|
||||
}
|
||||
|
||||
static int position(IntReader ir) {
|
||||
return ir.intBuffer.position();
|
||||
}
|
||||
|
||||
static void setPosition(IntReader ir, int position) {
|
||||
ir.intBuffer.position(position);
|
||||
}
|
||||
|
||||
static int read(IntReader ir) {
|
||||
// Advances position by 1.
|
||||
return ir.intBuffer.get();
|
||||
static void convert(IntReader ir, int intLen) {
|
||||
for (int i = 0; i < intLen; ++i) {
|
||||
ir.intBuffer[i] = ((ir.byteBuffer[i * 4] & 0xFF))
|
||||
| ((ir.byteBuffer[(i * 4) + 1] & 0xFF) << 8)
|
||||
| ((ir.byteBuffer[(i * 4) + 2] & 0xFF) << 16)
|
||||
| ((ir.byteBuffer[(i * 4) + 3] & 0xFF) << 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user