Update decoder

* More discreet trivial literal context detection
 * Make total_out parameter nullable
 * More strict stream validity check
 * Added BrotliErrorString
This commit is contained in:
Eugene Kliuchnikov 2016-05-31 15:40:59 +02:00
parent 3e528595e0
commit 755b909424
4 changed files with 111 additions and 83 deletions

View File

@ -1066,22 +1066,45 @@ static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe,
return 1;
}
static BROTLI_INLINE void DetectTrivialLiteralBlockTypes(BrotliState* s) {
size_t i;
for (i = 0; i < 8; ++i) s->trivial_literal_contexts[i] = 0;
for (i = 0; i < s->num_block_types[0]; i++) {
size_t offset = i << kLiteralContextBits;
size_t error = 0;
size_t sample = s->context_map[offset];
size_t j;
for (j = 0; j < (1u << kLiteralContextBits);) {
BROTLI_REPEAT(4, error |= s->context_map[offset + j++] ^ sample;)
}
if (error == 0) {
s->trivial_literal_contexts[i >> 5] |= 1u << (i & 31);
}
}
}
static BROTLI_INLINE void PrepareLiteralDecoding(BrotliState* s) {
uint8_t context_mode;
size_t trivial;
uint32_t block_type = s->block_type_rb[1];
uint32_t context_offset = block_type << kLiteralContextBits;
s->context_map_slice = s->context_map + context_offset;
trivial = s->trivial_literal_contexts[block_type >> 5];
s->trivial_literal_context = (trivial >> (block_type & 31)) & 1;
s->literal_htree = s->literal_hgroup.htrees[s->context_map_slice[0]];
context_mode = s->context_modes[block_type];
s->context_lookup1 = &kContextLookup[kContextLookupOffsets[context_mode]];
s->context_lookup2 = &kContextLookup[kContextLookupOffsets[context_mode + 1]];
}
/* Decodes the block type and updates the state for literal context.
Reads 3..54 bits. */
static BROTLI_INLINE int DecodeLiteralBlockSwitchInternal(int safe,
BrotliState* s) {
uint8_t context_mode;
uint32_t context_offset;
if (!DecodeBlockTypeAndLength(safe, s, 0)) {
return 0;
}
context_offset = s->block_type_rb[1] << kLiteralContextBits;
s->context_map_slice = s->context_map + context_offset;
s->literal_htree_index = s->context_map_slice[0];
s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index];
context_mode = s->context_modes[s->block_type_rb[1]];
s->context_lookup1 = &kContextLookup[kContextLookupOffsets[context_mode]];
s->context_lookup2 = &kContextLookup[kContextLookupOffsets[context_mode + 1]];
PrepareLiteralDecoding(s);
return 1;
}
@ -1153,7 +1176,7 @@ static BrotliErrorCode BROTLI_NOINLINE WriteRingBuffer(size_t* available_out,
BROTLI_LOG_UINT(to_write);
BROTLI_LOG_UINT(num_written);
s->partial_pos_out += num_written;
*total_out = s->partial_pos_out;
if (total_out) *total_out = s->partial_pos_out;
if (num_written < to_write) {
return BROTLI_NEEDS_MORE_OUTPUT;
}
@ -1566,6 +1589,7 @@ CommandInner:
if (PREDICT_FALSE(s->block_length[0] == 0)) {
BROTLI_SAFE(DecodeLiteralBlockSwitch(s));
PreloadSymbol(safe, s->literal_htree, br, &bits, &value);
if (!s->trivial_literal_context) goto CommandInner;
}
if (!safe) {
s->ringbuffer[pos] =
@ -1579,7 +1603,6 @@ CommandInner:
s->ringbuffer[pos] = (uint8_t)literal;
}
--s->block_length[0];
BROTLI_LOG_UINT(s->literal_htree_index);
BROTLI_LOG_ARRAY_INDEX(s->ringbuffer, pos);
++pos;
if (PREDICT_FALSE(pos == s->ringbuffer_size)) {
@ -1601,6 +1624,7 @@ CommandInner:
}
if (PREDICT_FALSE(s->block_length[0] == 0)) {
BROTLI_SAFE(DecodeLiteralBlockSwitch(s));
if (s->trivial_literal_context) goto CommandInner;
}
context = s->context_lookup1[p1] | s->context_lookup2[p2];
BROTLI_LOG_UINT(context);
@ -1629,7 +1653,7 @@ CommandInner:
} while (--i != 0);
}
BROTLI_LOG_UINT(s->meta_block_remaining_len);
if (s->meta_block_remaining_len <= 0) {
if (PREDICT_FALSE(s->meta_block_remaining_len <= 0)) {
s->state = BROTLI_STATE_METABLOCK_DONE;
goto saveStateAndReturn;
}
@ -1709,12 +1733,6 @@ postReadDistance:
s->dist_rb[s->dist_rb_idx & 3] = s->distance_code;
++s->dist_rb_idx;
s->meta_block_remaining_len -= i;
if (PREDICT_FALSE(s->meta_block_remaining_len < 0)) {
BROTLI_LOG(("Invalid backward reference. pos: %d distance: %d "
"len: %d bytes left: %d\n",
pos, s->distance_code, i, s->meta_block_remaining_len));
return BROTLI_FAILURE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_2);
}
/* There are 32+ bytes of slack in the ringbuffer allocation.
Also, we have 16 short codes, that make these 16 bytes irrelevant
in the ringbuffer. Let's copy over them as a first guess.
@ -2071,23 +2089,16 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
}
s->state = BROTLI_STATE_CONTEXT_MAP_1;
/* No break, continue to next state */
case BROTLI_STATE_CONTEXT_MAP_1: {
uint32_t j;
result = DecodeContextMap(s->num_block_types[0] << kLiteralContextBits,
case BROTLI_STATE_CONTEXT_MAP_1:
result = DecodeContextMap(
s->num_block_types[0] << kLiteralContextBits,
&s->num_literal_htrees, &s->context_map, s);
if (result != BROTLI_SUCCESS) {
break;
}
s->trivial_literal_context = 1;
for (j = 0; j < s->num_block_types[0] << kLiteralContextBits; j++) {
if (s->context_map[j] != j >> kLiteralContextBits) {
s->trivial_literal_context = 0;
break;
}
}
DetectTrivialLiteralBlockTypes(s);
s->state = BROTLI_STATE_CONTEXT_MAP_2;
/* No break, continue to next state */
}
case BROTLI_STATE_CONTEXT_MAP_2:
{
uint32_t num_distance_codes =
@ -2137,15 +2148,9 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
if (result != BROTLI_SUCCESS) break;
s->loop_counter++;
if (s->loop_counter >= 3) {
uint8_t context_mode = s->context_modes[s->block_type_rb[1]];
s->context_map_slice = s->context_map;
PrepareLiteralDecoding(s);
s->dist_context_map_slice = s->dist_context_map;
s->context_lookup1 =
&kContextLookup[kContextLookupOffsets[context_mode]];
s->context_lookup2 =
&kContextLookup[kContextLookupOffsets[context_mode + 1]];
s->htree_command = s->insert_copy_hgroup.htrees[0];
s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index];
if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) {
result = BROTLI_FAILURE(BROTLI_ERROR_ALLOC_RING_BUFFER_2);
break;
@ -2194,6 +2199,10 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
}
break;
case BROTLI_STATE_METABLOCK_DONE:
if (s->meta_block_remaining_len < 0) {
result = BROTLI_FAILURE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_2);
break;
}
BrotliStateCleanupAfterMetablock(s);
if (!s->is_last_metablock) {
s->state = BROTLI_STATE_METABLOCK_BEGIN;

View File

@ -28,49 +28,57 @@ typedef enum {
BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3
} BrotliResult;
#define BROTLI_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
BROTLI_ERROR_CODE(BROTLI_NO_ERROR, 0) SEPARATOR \
/* Same as BrotliResult values */ \
BROTLI_ERROR_CODE(BROTLI_SUCCESS, 1) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_NEEDS_MORE_INPUT, 2) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_NEEDS_MORE_OUTPUT, 3) SEPARATOR \
\
/* Errors caused by invalid input */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_EXUBERANT_NIBBLE, -1) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_RESERVED, -2) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_EXUBERANT_META_NIBBLE, -3) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_CL_SPACE, -6) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_HUFFMAN_SPACE, -7) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_CONTEXT_MAP_REPEAT, -8) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_1, -9) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_BLOCK_LENGTH_2, -10) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_TRANSFORM, -11) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_DICTIONARY, -12) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_WINDOW_BITS, -13) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_PADDING_1, -14) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_FORMAT_PADDING_2, -15) SEPARATOR \
\
/* -16..-20 codes are reserved */ \
\
/* Memory allocation problems */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_CONTEXT_MODES, -21) SEPARATOR \
/* Literal, insert and distance trees together */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_TREE_GROUPS, -22) SEPARATOR \
/* -23..-24 codes are reserved for distinct tree groups */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_CONTEXT_MAP, -25) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_RING_BUFFER_1, -26) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_RING_BUFFER_2, -27) SEPARATOR \
/* -28..-29 codes are reserved for dynamic ringbuffer allocation */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_ALLOC_BLOCK_TYPE_TREES, -30) SEPARATOR \
\
/* "Impossible" states */ \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_1, -31) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_2, -32) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_3, -33) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_4, -34) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_5, -35) SEPARATOR \
BROTLI_ERROR_CODE(BROTLI_ERROR_UNREACHABLE_6, -36)
typedef enum {
BROTLI_NO_ERROR = 0,
/* Same as BrotliResult values */
BROTLI_SUCCESS = 1,
BROTLI_NEEDS_MORE_INPUT = 2,
BROTLI_NEEDS_MORE_OUTPUT = 3,
/* Errors caused by invalid input */
BROTLI_ERROR_FORMAT_EXUBERANT_NIBBLE = -1,
BROTLI_ERROR_FORMAT_RESERVED = -2,
BROTLI_ERROR_FORMAT_EXUBERANT_META_NIBBLE = -3,
BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET = -4,
BROTLI_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME = -5,
BROTLI_ERROR_FORMAT_CL_SPACE = -6,
BROTLI_ERROR_FORMAT_HUFFMAN_SPACE = -7,
BROTLI_ERROR_FORMAT_CONTEXT_MAP_REPEAT = -8,
BROTLI_ERROR_FORMAT_BLOCK_LENGTH_1 = -9,
BROTLI_ERROR_FORMAT_BLOCK_LENGTH_2 = -10,
BROTLI_ERROR_FORMAT_TRANSFORM = -11,
BROTLI_ERROR_FORMAT_DICTIONARY = -12,
BROTLI_ERROR_FORMAT_WINDOW_BITS = -13,
BROTLI_ERROR_FORMAT_PADDING_1 = -14,
BROTLI_ERROR_FORMAT_PADDING_2 = -15,
/* -16..-20 codes are reserved */
/* Memory allocation problems */
BROTLI_ERROR_ALLOC_CONTEXT_MODES = -21,
BROTLI_ERROR_ALLOC_TREE_GROUPS = -22, /* Literal, insert, distance */
/* -23..-24 codes are reserved for distinct tree groups */
BROTLI_ERROR_ALLOC_CONTEXT_MAP = -25,
BROTLI_ERROR_ALLOC_RING_BUFFER_1 = -26,
BROTLI_ERROR_ALLOC_RING_BUFFER_2 = -27,
/* -28..-29 codes are reserved for dynamic ringbuffer allocation */
BROTLI_ERROR_ALLOC_BLOCK_TYPE_TREES = -30,
/* "Impossible" states */
BROTLI_ERROR_UNREACHABLE_1 = -31,
BROTLI_ERROR_UNREACHABLE_2 = -32,
BROTLI_ERROR_UNREACHABLE_3 = -33,
BROTLI_ERROR_UNREACHABLE_4 = -34,
BROTLI_ERROR_UNREACHABLE_5 = -35,
BROTLI_ERROR_UNREACHABLE_6 = -36
#define _BROTLI_COMMA /**/,
#define _BROTLI_ERROR_CODE_ENUM_ITEM(NAME, CODE) NAME = CODE
BROTLI_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_ENUM_ITEM, _BROTLI_COMMA)
#undef _BROTLI_ERROR_CODE_ENUM_ITEM
#undef _BROTLI_COMMA
} BrotliErrorCode;
#define BROTLI_LAST_ERROR_CODE BROTLI_ERROR_UNREACHABLE_6
@ -111,8 +119,8 @@ BrotliResult BrotliDecompressBuffer(size_t encoded_size,
bytes consumed, and the |*next_in| pointer will be incremented by that
amount. Similarly, |*available_out| will be decremented by the amount of
output bytes written, and the |*next_out| pointer will be incremented by that
amount. |total_out| will be set to the number of bytes decompressed since
last state initialization.
amount. |total_out|, if it is not a null-pointer, will be set to the number
of bytes decompressed since the last state initialization.
Input is never overconsumed, so |next_in| and |available_in| could be passed
to the next consumer after decoding is complete. */
@ -149,6 +157,17 @@ int BrotliStateIsStreamEnd(const BrotliState* s);
BROTLI_RESULT_ERROR. */
BrotliErrorCode BrotliGetErrorCode(const BrotliState* s);
static inline const char* BrotliErrorString(BrotliErrorCode c) {
switch (c) {
#define _BROTLI_ERROR_CODE_CASE(NAME, CODE) case NAME: return #NAME;
#define _BROTLI_NOTHING
BROTLI_ERROR_CODES_LIST(_BROTLI_ERROR_CODE_CASE, _BROTLI_NOTHING)
#undef _BROTLI_ERROR_CODE_CASE
#undef _BROTLI_NOTHING
default: return "INVALID BrotliErrorCode";
}
}
#if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */
#endif

View File

@ -118,7 +118,6 @@ void BrotliStateMetablockBegin(BrotliState* s) {
s->context_modes = NULL;
s->dist_context_map = NULL;
s->context_map_slice = NULL;
s->literal_htree_index = 0;
s->literal_htree = NULL;
s->dist_context_map_slice = NULL;
s->dist_htree_index = 0;

View File

@ -152,7 +152,6 @@ struct BrotliStateStruct {
uint32_t num_dist_htrees;
uint8_t* dist_context_map;
HuffmanCode* literal_htree;
uint8_t literal_htree_index;
uint8_t dist_htree_index;
uint32_t repeat_code_len;
uint32_t prev_code_len;
@ -218,6 +217,8 @@ struct BrotliStateStruct {
uint32_t num_literal_htrees;
uint8_t* context_map;
uint8_t* context_modes;
uint32_t trivial_literal_contexts[8]; /* 256 bits */
};
typedef struct BrotliStateStruct BrotliStateInternal;