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:
Eugene Kliuchnikov 2017-04-05 18:50:01 +02:00 committed by GitHub
parent e12a7a2d28
commit 21c118ba77
8 changed files with 337 additions and 250 deletions

View File

@ -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) {

View File

@ -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);
}
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}
}

View File

@ -18,8 +18,4 @@ class BrotliRuntimeException extends RuntimeException {
BrotliRuntimeException(String message, Throwable cause) {
super(message, cause);
}
BrotliRuntimeException(Throwable cause) {
super(cause);
}
}

View File

@ -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

View File

@ -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);
}
}
}