From 92e3023914f10c411575194d24d13aff63d64ecc Mon Sep 17 00:00:00 2001 From: Eugene Kliuchnikov Date: Fri, 22 Jan 2016 10:19:41 +0100 Subject: [PATCH] Update decoder. * Reduce memory usage * Update API documentation * Remove deprecated API * Move non-API declatarions from decode.h * Remove streams * Add more debug logging * Fix shift in BrotliBitReaderUnload * Allocate ringbuffer at later stages * Sort / fix includes * Fix whitespaces * Eliminate dead code * Drive-by code simplifications --- dec/Makefile | 2 +- dec/bit_reader.c | 4 +- dec/bit_reader.h | 14 ++- dec/decode.c | 284 ++++++++++++++++++----------------------------- dec/decode.h | 132 ++++++---------------- dec/dictionary.h | 2 +- dec/huffman.c | 9 +- dec/huffman.h | 11 +- dec/prefix.h | 2 + dec/state.c | 22 ++-- dec/state.h | 16 +-- dec/transform.h | 19 ++-- 12 files changed, 185 insertions(+), 332 deletions(-) diff --git a/dec/Makefile b/dec/Makefile index fa2fc3d..4d11ed1 100644 --- a/dec/Makefile +++ b/dec/Makefile @@ -4,7 +4,7 @@ include ../shared.mk CFLAGS += -Wall -OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o streams.o +OBJS = bit_reader.o decode.o dictionary.o huffman.o state.o all : $(OBJS) diff --git a/dec/bit_reader.c b/dec/bit_reader.c index fc814d0..72509a2 100644 --- a/dec/bit_reader.c +++ b/dec/bit_reader.c @@ -6,10 +6,10 @@ /* Bit reading helpers */ -#include - #include "./bit_reader.h" + #include "./port.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { diff --git a/dec/bit_reader.h b/dec/bit_reader.h index 0301a48..58a3f84 100644 --- a/dec/bit_reader.h +++ b/dec/bit_reader.h @@ -9,8 +9,8 @@ #ifndef BROTLI_DEC_BIT_READER_H_ #define BROTLI_DEC_BIT_READER_H_ -#include -#include +#include /* memcpy */ + #include "./port.h" #include "./types.h" @@ -283,7 +283,11 @@ static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) { uint32_t unused_bits = unused_bytes << 3; br->avail_in += unused_bytes; br->next_in -= unused_bytes; - br->val_ <<= unused_bits; + if (unused_bits == sizeof(br->val_) << 3) { + br->val_ = 0; + } else { + br->val_ <<= unused_bits; + } br->bit_pos_ += unused_bits; } @@ -349,9 +353,7 @@ static BROTLI_INLINE int BrotliJumpToByteBoundary(BrotliBitReader* br) { static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, size_t offset) { uint32_t available_bits = BrotliGetAvailableBits(br); size_t bytes_left = available_bits >> 3; - if (available_bits & 7) { - return -1; - } + BROTLI_DCHECK((available_bits & 7) == 0); if (offset < bytes_left) { return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF; } diff --git a/dec/decode.c b/dec/decode.c index 6e3f9e1..5cff603 100644 --- a/dec/decode.c +++ b/dec/decode.c @@ -4,26 +4,42 @@ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ -#include -#include -#include -#include "./bit_reader.h" -#include "./context.h" #include "./decode.h" -#include "./dictionary.h" -#include "./port.h" -#include "./transform.h" -#include "./huffman.h" -#include "./prefix.h" #ifdef __ARM_NEON__ #include #endif +#include /* printf (debug output) */ +#include /* free, malloc */ +#include /* memcpy, memset */ + +#include "./bit_reader.h" +#include "./context.h" +#include "./dictionary.h" +#include "./huffman.h" +#include "./port.h" +#include "./prefix.h" +#include "./transform.h" + #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif +/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */ +/* In debug build it dumps file name, line and pretty function name. */ +#if defined(_MSC_VER) || !defined(BROTLI_DEBUG) +#define BROTLI_FAILURE() BROTLI_RESULT_ERROR +#else +#define BROTLI_FAILURE() \ + BrotliFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__) +static inline BrotliResult BrotliFailure(const char *f, int l, const char *fn) { + fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); + fflush(stderr); + return BROTLI_RESULT_ERROR; +} +#endif + #ifdef BROTLI_DECODE_DEBUG #define BROTLI_LOG_UINT(name) \ printf("[%s] %s = %lu\n", __func__, #name, (unsigned long)(name)) @@ -239,7 +255,7 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s, /* No break, transit to the next state. */ case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED: - if (!s->is_last_metablock && !s->is_metadata) { + if (!s->is_last_metablock) { if (!BrotliSafeReadBits(br, 1, &bits)) { return BROTLI_RESULT_NEEDS_MORE_INPUT; } @@ -283,9 +299,9 @@ static BrotliResult BROTLI_NOINLINE DecodeMetaBlockLength(BrotliState* s, } s->meta_block_remaining_len |= (int)(bits << (i * 8)); } - s->substate_metablock_header = - BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED; - break; + ++s->meta_block_remaining_len; + s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE; + return BROTLI_RESULT_SUCCESS; default: return BROTLI_FAILURE(); @@ -473,6 +489,8 @@ static BROTLI_INLINE void ProcessSingleCodeLength(uint32_t code_len, *prev_code_len = code_len; *space -= 32768U >> code_len; code_length_histo[code_len]++; + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n", + *symbol, code_len)); } (*symbol)++; } @@ -513,6 +531,8 @@ static BROTLI_INLINE void ProcessRepeatedCodeLength(uint32_t code_len, *space = 0xFFFFF; return; } + BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n", + *symbol, *symbol + repeat_delta - 1, *repeat_code_len)); if (*repeat_code_len != 0) { unsigned last = *symbol + repeat_delta; int next = next_symbol[*repeat_code_len]; @@ -1018,9 +1038,9 @@ rleCode: s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE; return BROTLI_RESULT_SUCCESS; } + default: + return BROTLI_FAILURE(); } - - return BROTLI_FAILURE(); } /* Decodes a command or literal and updates block type ringbuffer. @@ -1028,9 +1048,10 @@ rleCode: static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe, BrotliState* s, int tree_type) { uint32_t max_block_type = s->num_block_types[tree_type]; - int tree_offset = tree_type * BROTLI_HUFFMAN_MAX_TABLE_SIZE; - const HuffmanCode* type_tree = &s->block_type_trees[tree_offset]; - const HuffmanCode* len_tree = &s->block_len_trees[tree_offset]; + const HuffmanCode* type_tree = &s->block_type_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_258]; + const HuffmanCode* len_tree = &s->block_len_trees[ + tree_type * BROTLI_HUFFMAN_MAX_SIZE_26]; BrotliBitReader* br = &s->br; uint32_t* ringbuffer = &s->block_type_rb[tree_type * 2]; uint32_t block_type; @@ -1152,7 +1173,7 @@ static BrotliResult WriteRingBuffer(size_t* available_out, uint8_t** next_out, *available_out -= num_written; BROTLI_LOG_UINT(to_write); BROTLI_LOG_UINT(num_written); - s->partial_pos_out += (size_t)num_written; + s->partial_pos_out += num_written; *total_out = s->partial_pos_out; if (num_written < to_write) { return BROTLI_RESULT_NEEDS_MORE_OUTPUT; @@ -1160,9 +1181,48 @@ static BrotliResult WriteRingBuffer(size_t* available_out, uint8_t** next_out, return BROTLI_RESULT_SUCCESS; } +/* Allocates ringbuffer. + + s->ringbuffer_size MUST be updated by BrotliCalculateRingBufferSize before + this function is called. + + Last two bytes of ringbuffer are initialized to 0, so context calculation + could be done uniformly for the first two and all other positions. + + Custom dictionary, if any, is copied to the end of ringbuffer. +*/ +static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s) { + /* We need the slack region for the following reasons: + - doing up to two 16-byte copies for fast backward copying + - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */ + static const int kRingBufferWriteAheadSlack = 42; + s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size + + kRingBufferWriteAheadSlack)); + if (s->ringbuffer == 0) { + return 0; + } + + s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; + + s->ringbuffer[s->ringbuffer_size - 2] = 0; + s->ringbuffer[s->ringbuffer_size - 1] = 0; + + if (s->custom_dict) { + memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask], + s->custom_dict, (size_t)s->custom_dict_size); + } + + return 1; +} + static BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput( size_t* available_out, uint8_t** next_out, size_t* total_out, BrotliState* s) { + /* TODO: avoid allocation for single uncompressed block. */ + if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) { + return BROTLI_FAILURE(); + } + /* State machine */ for (;;) { switch (s->substate_uncompressed) { @@ -1185,7 +1245,6 @@ static BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput( return BROTLI_RESULT_NEEDS_MORE_INPUT; } s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE; - /*s->partial_pos_rb += (size_t)s->ringbuffer_size;*/ /* No break, continue to next state */ } case BROTLI_STATE_UNCOMPRESSED_WRITE: { @@ -1231,21 +1290,15 @@ int BrotliDecompressedSize(size_t encoded_size, return (next_block_header != -1) && ((next_block_header & 3) == 3); } -/* Allocates the smallest feasible ring buffer. +/* Calculates the smallest feasible ring buffer. If we know the data size is small, do not allocate more ringbuffer size than needed to reduce memory usage. - This method is called before the first non-empty non-metadata block is - processed. When this method is called, metablock size and flags MUST be - decoded. + When this method is called, metablock size and flags MUST be decoded. */ -static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s, +static void BROTLI_NOINLINE BrotliCalculateRingBufferSize(BrotliState* s, BrotliBitReader* br) { - /* We need the slack region for the following reasons: - - doing up to two 16-byte copies for fast backward copying - - inserting transformed dictionary word (5 prefix + 24 base + 8 suffix) */ - static const int kRingBufferWriteAheadSlack = 42; int is_last = s->is_last_metablock; s->ringbuffer_size = 1 << s->window_bits; @@ -1274,20 +1327,6 @@ static int BROTLI_NOINLINE BrotliAllocateRingBuffer(BrotliState* s, } s->ringbuffer_mask = s->ringbuffer_size - 1; - s->ringbuffer = (uint8_t*)BROTLI_ALLOC(s, (size_t)(s->ringbuffer_size + - kRingBufferWriteAheadSlack + kBrotliMaxDictionaryWordLength)); - if (s->ringbuffer == 0) { - return 0; - } - s->ringbuffer_end = s->ringbuffer + s->ringbuffer_size; - s->ringbuffer[s->ringbuffer_size - 2] = 0; - s->ringbuffer[s->ringbuffer_size - 1] = 0; - if (s->custom_dict) { - memcpy(&s->ringbuffer[(-s->custom_dict_size) & s->ringbuffer_mask], - s->custom_dict, (size_t)s->custom_dict_size); - } - - return 1; } /* Reads 1..256 2-bit context modes. */ @@ -1461,13 +1500,6 @@ static BROTLI_INLINE int SafeReadCommand(BrotliState* s, BrotliBitReader* br, return ReadCommandInternal(1, s, br, insert_length); } -static BROTLI_INLINE int WarmupBitReader(int safe, BrotliBitReader* const br) { - if (safe) { - return 1; - } - return BrotliWarmupBitReader(br); -} - static BROTLI_INLINE int CheckInputAmount(int safe, BrotliBitReader* const br, size_t num) { if (safe) { @@ -1494,10 +1526,13 @@ static BROTLI_INLINE BrotliResult ProcessCommandsInternal(int safe, BrotliResult result = BROTLI_RESULT_SUCCESS; BrotliBitReader* br = &s->br; - if (!CheckInputAmount(safe, br, 28) || !WarmupBitReader(safe, br)) { + if (!CheckInputAmount(safe, br, 28)) { result = BROTLI_RESULT_NEEDS_MORE_INPUT; goto saveStateAndReturn; } + if (!safe) { + BROTLI_UNUSED(BrotliWarmupBitReader(br)); + } /* Jump into state machine. */ if (s->state == BROTLI_STATE_COMMAND_BEGIN) { @@ -1527,9 +1562,8 @@ CommandBegin: } /* Read the insert/copy length in the command */ BROTLI_SAFE(ReadCommand(s, br, &i)); - BROTLI_LOG_UINT(i); - BROTLI_LOG_UINT(s->copy_length); - BROTLI_LOG_UINT(s->distance_code); + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n", + pos, i, s->copy_length)); if (i == 0) { goto CommandPostDecodeLiterals; } @@ -1615,6 +1649,7 @@ CommandInner: } } while (--i != 0); } + BROTLI_LOG_UINT(s->meta_block_remaining_len); if (s->meta_block_remaining_len <= 0) { s->state = BROTLI_STATE_METABLOCK_DONE; goto saveStateAndReturn; @@ -1635,7 +1670,8 @@ CommandPostDecodeLiterals: } BROTLI_SAFE(ReadDistance(s, br)); postReadDistance: - BROTLI_LOG_UINT(s->distance_code); + BROTLI_LOG(("[ProcessCommandsInternal] pos = %d distance = %d\n", + pos, s->distance_code)); if (s->max_distance != s->max_backward_distance) { if (pos < s->max_backward_distance_minus_custom_dict_size) { s->max_distance = pos + s->custom_dict_size; @@ -1649,7 +1685,7 @@ postReadDistance: if (s->distance_code > s->max_distance) { if (i >= kBrotliMinDictionaryWordLength && i <= kBrotliMaxDictionaryWordLength) { - int offset = kBrotliDictionaryOffsetsByLength[i]; + int offset = (int)kBrotliDictionaryOffsetsByLength[i]; int word_id = s->distance_code - s->max_distance - 1; uint32_t shift = kBrotliDictionarySizeBitsByLength[i]; int mask = (int)BitMask(shift); @@ -1734,6 +1770,7 @@ postReadDistance: } } } + BROTLI_LOG_UINT(s->meta_block_remaining_len); if (s->meta_block_remaining_len <= 0) { /* Next metablock, if any */ s->state = BROTLI_STATE_METABLOCK_DONE; @@ -1813,113 +1850,6 @@ BrotliResult BrotliDecompressBuffer(size_t encoded_size, return result; } -BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output) { - BrotliState s; - BrotliResult result; - BrotliStateInit(&s); - result = BrotliDecompressStreaming(input, output, 1, &s); - if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - /* Not ok: it didn't finish even though this is a non-streaming function. */ - result = BROTLI_FAILURE(); - } - BrotliStateCleanup(&s); - return result; -} - -BrotliResult BrotliDecompressBufferStreaming(size_t* available_in, - const uint8_t** next_in, - int finish, - size_t* available_out, - uint8_t** next_out, - size_t* total_out, - BrotliState* s) { - BrotliResult result = BrotliDecompressStream(available_in, next_in, - available_out, next_out, total_out, s); - if (finish && result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - result = BROTLI_FAILURE(); - } - return result; -} - -BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, - int finish, BrotliState* s) { - const size_t kBufferSize = 65536; - BrotliResult result; - uint8_t* input_buffer; - uint8_t* output_buffer; - size_t avail_in; - const uint8_t* next_in; - size_t total_out; - - if (s->legacy_input_buffer == 0) { - s->legacy_input_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); - } - if (s->legacy_output_buffer == 0) { - s->legacy_output_buffer = (uint8_t*)BROTLI_ALLOC(s, kBufferSize); - } - if (s->legacy_input_buffer == 0 || s->legacy_output_buffer == 0) { - return BROTLI_FAILURE(); - } - input_buffer = s->legacy_input_buffer; - output_buffer = s->legacy_output_buffer; - - /* Push remaining output. */ - if (s->legacy_output_len > s->legacy_output_pos) { - size_t to_write = s->legacy_output_len - s->legacy_output_pos; - int num_written = BrotliWrite( - output, output_buffer + s->legacy_output_pos, to_write); - if (num_written < 0) { - return BROTLI_FAILURE(); - } - s->legacy_output_pos += (size_t)num_written; - if ((size_t)num_written < to_write) { - return BROTLI_RESULT_NEEDS_MORE_OUTPUT; - } - } - s->legacy_output_pos = 0; - - avail_in = s->legacy_input_len - s->legacy_input_pos; - next_in = input_buffer + s->legacy_input_pos; - - while (1) { - size_t to_write; - int num_written; - size_t avail_out = kBufferSize; - uint8_t* next_out = output_buffer; - result = BrotliDecompressStream(&avail_in, &next_in, - &avail_out, &next_out, &total_out, s); - s->legacy_input_pos = (size_t)(next_out - input_buffer); - to_write = (size_t)(next_out - output_buffer); - num_written = BrotliWrite(output, output_buffer, to_write); - if (num_written < 0) { - return BROTLI_FAILURE(); - } - if ((size_t)num_written < to_write) { - s->legacy_output_len = to_write; - s->legacy_output_pos = (size_t)num_written; - return BROTLI_RESULT_NEEDS_MORE_OUTPUT; - } - if (result == BROTLI_RESULT_NEEDS_MORE_INPUT) { - int num_read = BrotliRead(input, input_buffer, kBufferSize); - if (num_read < 0 || (num_read == 0 && finish)) { - return BROTLI_FAILURE(); - } - if (num_read == 0) { - s->legacy_input_len = 0; - s->legacy_input_pos = 0; - return BROTLI_RESULT_NEEDS_MORE_INPUT; - } - avail_in = (size_t)num_read; - next_in = input_buffer; - s->legacy_input_len = avail_in; - s->legacy_input_pos = 0; - } else if (result != BROTLI_RESULT_NEEDS_MORE_OUTPUT) { - /* Success or failure. */ - return result; - } - } -} - /* Invariant: input stream is never overconsumed: * invalid input implies that the whole stream is invalid -> any amount of input could be read and discarded @@ -2030,13 +1960,14 @@ BrotliResult BrotliDecompressStream(size_t* available_in, /* Allocate memory for both block_type_trees and block_len_trees. */ s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s, - 6 * BROTLI_HUFFMAN_MAX_TABLE_SIZE * sizeof(HuffmanCode)); + sizeof(HuffmanCode) * 3 * + (BROTLI_HUFFMAN_MAX_SIZE_258 + BROTLI_HUFFMAN_MAX_SIZE_26)); if (s->block_type_trees == 0) { result = BROTLI_FAILURE(); break; } s->block_len_trees = s->block_type_trees + - 3 * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + 3 * BROTLI_HUFFMAN_MAX_SIZE_258; s->state = BROTLI_STATE_METABLOCK_BEGIN; /* No break, continue to next state */ @@ -2069,10 +2000,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, break; } if (!s->ringbuffer) { - if (!BrotliAllocateRingBuffer(s, br)) { - result = BROTLI_FAILURE(); - break; - } + BrotliCalculateRingBufferSize(s, br); } if (s->is_uncompressed) { s->state = BROTLI_STATE_UNCOMPRESSED; @@ -2124,7 +2052,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, s->state = BROTLI_STATE_HUFFMAN_CODE_1; /* No break, continue to next state */ case BROTLI_STATE_HUFFMAN_CODE_1: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_258; result = ReadHuffmanCode(s->num_block_types[s->loop_counter] + 2, &s->block_type_trees[tree_offset], NULL, s); if (result != BROTLI_RESULT_SUCCESS) break; @@ -2132,7 +2060,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, /* No break, continue to next state */ } case BROTLI_STATE_HUFFMAN_CODE_2: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; result = ReadHuffmanCode(kNumBlockLengthCodes, &s->block_len_trees[tree_offset], NULL, s); if (result != BROTLI_RESULT_SUCCESS) break; @@ -2140,7 +2068,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, /* No break, continue to next state */ } case BROTLI_STATE_HUFFMAN_CODE_3: { - int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_TABLE_SIZE; + int tree_offset = s->loop_counter * BROTLI_HUFFMAN_MAX_SIZE_26; if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter], &s->block_len_trees[tree_offset], br)) { result = BROTLI_RESULT_NEEDS_MORE_INPUT; @@ -2212,7 +2140,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, s->num_literal_htrees); BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup, kNumInsertAndCopyCodes, - s->num_block_types[1]); + s->num_block_types[1]); BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes, s->num_dist_htrees); if (s->literal_hgroup.codes == 0 || @@ -2252,6 +2180,10 @@ BrotliResult BrotliDecompressStream(size_t* available_in, &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(); + break; + } s->state = BROTLI_STATE_COMMAND_BEGIN; } break; @@ -2276,7 +2208,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, s->max_distance = s->max_backward_distance; if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) { memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos); - if (s->meta_block_remaining_len <= 0) { + if (s->meta_block_remaining_len == 0) { /* Next metablock, if any */ s->state = BROTLI_STATE_METABLOCK_DONE; } else { @@ -2287,7 +2219,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in, s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY; } else { /* BROTLI_STATE_COMMAND_INNER_WRITE */ if (s->loop_counter == 0) { - if (s->meta_block_remaining_len <= 0) { + if (s->meta_block_remaining_len == 0) { s->state = BROTLI_STATE_METABLOCK_DONE; } else { s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS; diff --git a/dec/decode.h b/dec/decode.h index af3c0b5..074b2f5 100644 --- a/dec/decode.h +++ b/dec/decode.h @@ -10,7 +10,6 @@ #define BROTLI_DEC_DECODE_H_ #include "./state.h" -#include "./streams.h" #include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) @@ -18,126 +17,57 @@ extern "C" { #endif typedef enum { - /* Decoding error, e.g. corrupt input or no memory */ + /* Decoding error, e.g. corrupt input or memory allocation problem */ BROTLI_RESULT_ERROR = 0, - /* Successfully completely done */ + /* Decoding successfully completed */ BROTLI_RESULT_SUCCESS = 1, - /* Partially done, but must be called again with more input */ + /* Partially done; should be called again with more input */ BROTLI_RESULT_NEEDS_MORE_INPUT = 2, - /* Partially done, but must be called again with more output */ + /* Partially done; should be called again with more output */ BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3 } BrotliResult; -/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */ -/* In debug build it dumps file name, line and pretty function name. */ -#if defined(_MSC_VER) || !defined(BROTLI_DEBUG) -#define BROTLI_FAILURE() BROTLI_RESULT_ERROR -#else -#define BROTLI_FAILURE() \ - BrotliFailure(__FILE__, __LINE__, __PRETTY_FUNCTION__) -static inline BrotliResult BrotliFailure(const char *f, int l, const char *fn) { - fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); - fflush(stderr); - return BROTLI_RESULT_ERROR; -} -#endif - -/* Creates the instance of BrotliState and initializes it. alloc_func and - free_func MUST be both zero or both non-zero. In the case they are both zero, - default memory allocators are used. opaque parameter is passed to alloc_func - and free_func when they are called. */ +/* Creates the instance of BrotliState and initializes it. |alloc_func| and + |free_func| MUST be both zero or both non-zero. In the case they are both + zero, default memory allocators are used. |opaque| is passed to |alloc_func| + and |free_func| when they are called. */ BrotliState* BrotliCreateState( brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque); /* Deinitializes and frees BrotliState instance. */ void BrotliDestroyState(BrotliState* state); -/* Sets *decoded_size to the decompressed size of the given encoded stream. */ -/* This function only works if the encoded buffer has a single meta block, */ -/* or if it has two meta-blocks, where the first is uncompressed and the */ -/* second is empty. */ -/* Returns 1 on success, 0 on failure. */ +/* Sets |*decoded_size| to the decompressed size of the given encoded stream. + This function only works if the encoded buffer has a single meta block, + or if it has two meta-blocks, where the first is uncompressed and the + second is empty. + Returns 1 on success, 0 on failure. */ int BrotliDecompressedSize(size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size); -/* Decompresses the data in encoded_buffer into decoded_buffer, and sets */ -/* *decoded_size to the decompressed length. */ +/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets + |*decoded_size| to the decompressed length. */ BrotliResult BrotliDecompressBuffer(size_t encoded_size, const uint8_t* encoded_buffer, size_t* decoded_size, uint8_t* decoded_buffer); -/* Same as above, but uses the specified input and output callbacks instead */ -/* of reading from and writing to pre-allocated memory buffers. */ -/* DEPRECATED */ -BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output); +/* Decompresses the data. Supports partial input and output. -/* Same as above, but supports the caller to call the decoder repeatedly with - partial data to support streaming. The state must be initialized with - BrotliStateInit and reused with every call for the same stream. - Return values: - 0: failure. - 1: success, and done. - 2: success so far, end not reached so should call again with more input. - The finish parameter is used as follows, for a series of calls with the - same state: - 0: Every call except the last one must be called with finish set to 0. The - last call may have finish set to either 0 or 1. Only if finish is 0, can - the function return 2. It may also return 0 or 1, in that case no more - calls (even with finish 1) may be made. - 1: Only the last call may have finish set to 1. It's ok to give empty input - if all input was already given to previous calls. It is also ok to have - only one single call in total, with finish 1, and with all input - available immediately. That matches the non-streaming case. If finish is - 1, the function can only return 0 or 1, never 2. After a finish, no more - calls may be done. - After everything is done, the state must be cleaned with BrotliStateCleanup - to free allocated resources. - The given BrotliOutput must always accept all output and make enough space, - it returning a smaller value than the amount of bytes to write always results - in an error. -*/ -/* DEPRECATED */ -BrotliResult BrotliDecompressStreaming(BrotliInput input, BrotliOutput output, - int finish, BrotliState* s); + Must be called with an allocated input buffer in |*next_in| and an allocated + output buffer in |*next_out|. The values |*available_in| and |*available_out| + must specify the allocated size in |*next_in| and |*next_out| respectively. -/* Same as above, but with memory buffers. - Must be called with an allocated input buffer in *next_in and an allocated - output buffer in *next_out. The values *available_in and *available_out - must specify the allocated size in *next_in and *next_out respectively. - The value *total_out must be 0 initially, and will be summed with the - amount of output bytes written after each call, so that at the end it - gives the complete decoded size. - After each call, *available_in will be decremented by the amount of input - 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. - - The input may be partial. With each next function call, *next_in and - *available_in must be updated to point to a next part of the compressed - input. The current implementation will always consume all input unless - an error occurs, so normally *available_in will always be 0 after - calling this function and the next adjacent part of input is desired. - - In the current implementation, the function requires that there is enough - output buffer size to write all currently processed input, so - *available_out must be large enough. Since the function updates *next_out - each time, as long as the output buffer is large enough you can keep - reusing this variable. It is also possible to update *next_out and - *available_out yourself before a next call, e.g. to point to a new larger - buffer. -*/ -/* DEPRECATED */ -BrotliResult BrotliDecompressBufferStreaming(size_t* available_in, - const uint8_t** next_in, - int finish, - size_t* available_out, - uint8_t** next_out, - size_t* total_out, - BrotliState* s); + After each call, |*available_in| will be decremented by the amount of input + 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. + Input is never overconsumed, so |next_in| and |available_in| could be passed + to the next consumer after decoding is complete. */ BrotliResult BrotliDecompressStream(size_t* available_in, const uint8_t** next_in, size_t* available_out, @@ -150,10 +80,10 @@ BrotliResult BrotliDecompressStream(size_t* available_in, Not to be confused with the built-in transformable dictionary of Brotli. The dictionary must exist in memory until decoding is done and is owned by the caller. To use: - -initialize state with BrotliStateInit - -use BrotliSetCustomDictionary - -use BrotliDecompressBufferStreaming - -clean up with BrotliStateCleanup + 1) initialize state with BrotliStateInit + 2) use BrotliSetCustomDictionary + 3) use BrotliDecompressStream + 4) clean up with BrotliStateCleanup */ void BrotliSetCustomDictionary( size_t size, const uint8_t* dict, BrotliState* s); diff --git a/dec/dictionary.h b/dec/dictionary.h index b327fd6..ee3a469 100644 --- a/dec/dictionary.h +++ b/dec/dictionary.h @@ -17,7 +17,7 @@ extern "C" { extern const uint8_t kBrotliDictionary[122784]; -static const int kBrotliDictionaryOffsetsByLength[] = { +static const uint32_t kBrotliDictionaryOffsetsByLength[] = { 0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032, 53248, 63488, 74752, 87040, 93696, 100864, 104704, 106752, 108928, 113536, 115968, 118528, 119872, 121280, 122016, diff --git a/dec/huffman.c b/dec/huffman.c index 291f0a7..c980854 100644 --- a/dec/huffman.c +++ b/dec/huffman.c @@ -6,11 +6,12 @@ /* Utilities for building Huffman decoding tables. */ -#include -#include -#include #include "./huffman.h" + +#include /* memcpy, memset */ + #include "./port.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -237,7 +238,7 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table, for (len = root_bits + 1, step = 2; len <= max_length; ++len) { symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1); for (; count[len] != 0; --count[len]) { - if (sub_key == (uint32_t)(BROTLI_REVERSE_BITS_LOWEST << 1)) { + if (sub_key == (BROTLI_REVERSE_BITS_LOWEST << 1U)) { table += table_size; table_bits = NextTableBitSize(count, len, root_bits); table_size = 1 << table_bits; diff --git a/dec/huffman.h b/dec/huffman.h index e8481f0..010baed 100644 --- a/dec/huffman.h +++ b/dec/huffman.h @@ -20,9 +20,14 @@ extern "C" { /* For current format this constant equals to kNumInsertAndCopyCodes */ #define BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE 704 -/* Maximum possible Huffman table size for an alphabet size of 704, max code - * length 15 and root table bits 8. */ -#define BROTLI_HUFFMAN_MAX_TABLE_SIZE 1080 +/* Maximum possible Huffman table size for an alphabet size of (index * 32), + * max code length 15 and root table bits 8. */ +static const uint16_t kMaxHuffmanTableSize[] = { + 256, 402, 436, 468, 500, 534, 566, 598, 630, 662, 694, 726, 758, 790, 822, + 854, 886, 920, 952, 984, 1016, 1048, 1080}; +#define BROTLI_HUFFMAN_MAX_SIZE_26 396 +#define BROTLI_HUFFMAN_MAX_SIZE_258 632 +#define BROTLI_HUFFMAN_MAX_SIZE_272 646 #define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5 diff --git a/dec/prefix.h b/dec/prefix.h index 41263fd..eaae37f 100644 --- a/dec/prefix.h +++ b/dec/prefix.h @@ -11,6 +11,8 @@ #ifndef BROTLI_DEC_PREFIX_H_ #define BROTLI_DEC_PREFIX_H_ +#include "./types.h" + /* Represents the range of values belonging to a prefix code: */ /* [offset, offset + 2^nbits) */ struct PrefixCodeRange { diff --git a/dec/state.c b/dec/state.c index d5815e0..d48e55d 100644 --- a/dec/state.c +++ b/dec/state.c @@ -4,11 +4,12 @@ See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ -#include "./huffman.h" #include "./state.h" -#include -#include +#include /* free, malloc */ + +#include "./huffman.h" +#include "./types.h" #if defined(__cplusplus) || defined(c_plusplus) extern "C" { @@ -94,13 +95,6 @@ void BrotliStateInitWithCustomAllocators(BrotliState* s, s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1]; s->mtf_upper_bound = 255; - - s->legacy_input_buffer = 0; - s->legacy_output_buffer = 0; - s->legacy_input_len = 0; - s->legacy_output_len = 0; - s->legacy_input_pos = 0; - s->legacy_output_pos = 0; } void BrotliStateMetablockBegin(BrotliState* s) { @@ -150,8 +144,6 @@ void BrotliStateCleanup(BrotliState* s) { BROTLI_FREE(s, s->ringbuffer); BROTLI_FREE(s, s->block_type_trees); - BROTLI_FREE(s, s->legacy_input_buffer); - BROTLI_FREE(s, s->legacy_output_buffer); } int BrotliStateIsStreamStart(const BrotliState* s) { @@ -166,9 +158,9 @@ int BrotliStateIsStreamEnd(const BrotliState* s) { void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group, uint32_t alphabet_size, uint32_t ntrees) { /* Pack two allocations into one */ - const size_t code_size = - sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE); - const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees; + const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5]; + const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size; + const size_t htree_size = sizeof(HuffmanCode*) * ntrees; char *p = (char*)BROTLI_ALLOC(s, code_size + htree_size); group->alphabet_size = (uint16_t)alphabet_size; group->num_htrees = (uint16_t)ntrees; diff --git a/dec/state.h b/dec/state.h index f569f58..026fc4f 100644 --- a/dec/state.h +++ b/dec/state.h @@ -9,7 +9,6 @@ #ifndef BROTLI_DEC_STATE_H_ #define BROTLI_DEC_STATE_H_ -#include #include "./bit_reader.h" #include "./huffman.h" #include "./types.h" @@ -95,6 +94,10 @@ typedef enum { struct BrotliStateStruct { BrotliRunningState state; + + /* This counter is reused for several disjoint loops. */ + int loop_counter; + BrotliBitReader br; brotli_alloc_func alloc_func; @@ -108,8 +111,6 @@ struct BrotliStateStruct { } buffer; uint32_t buffer_length; - /* This counter is reused for several disjoint loops. */ - int loop_counter; int pos; int max_backward_distance; int max_backward_distance_minus_custom_dict_size; @@ -188,7 +189,7 @@ struct BrotliStateStruct { uint32_t context_index; uint32_t max_run_length_prefix; uint32_t code; - HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_TABLE_SIZE]; + HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272]; /* For InverseMoveToFrontTransform */ uint32_t mtf_upper_bound; @@ -217,13 +218,6 @@ struct BrotliStateStruct { uint32_t num_literal_htrees; uint8_t* context_map; uint8_t* context_modes; - - uint8_t* legacy_input_buffer; - uint8_t* legacy_output_buffer; - size_t legacy_input_len; - size_t legacy_output_len; - size_t legacy_input_pos; - size_t legacy_output_pos; }; typedef struct BrotliStateStruct BrotliState; diff --git a/dec/transform.h b/dec/transform.h index f47eeb4..03aa2b9 100644 --- a/dec/transform.h +++ b/dec/transform.h @@ -9,8 +9,6 @@ #ifndef BROTLI_DEC_TRANSFORM_H_ #define BROTLI_DEC_TRANSFORM_H_ -#include -#include #include "./port.h" #include "./types.h" @@ -269,22 +267,19 @@ static BROTLI_NOINLINE int TransformDictionaryWord( } { const int t = kTransforms[transform].transform; - int skip = t < kOmitFirst1 ? 0 : t - (kOmitFirst1 - 1); int i = 0; - uint8_t* uppercase; - if (skip > len) { - skip = len; - } - word += skip; - len -= skip; - if (t <= kOmitLast9) { + int skip = t - (kOmitFirst1 - 1); + if (skip > 0) { + word += skip; + len -= skip; + } else if (t <= kOmitLast9) { len -= t; } while (i < len) { dst[idx++] = word[i++]; } - uppercase = &dst[idx - len]; if (t == kUppercaseFirst) { - ToUpperCase(uppercase); + ToUpperCase(&dst[idx - len]); } else if (t == kUppercaseAll) { + uint8_t* uppercase = &dst[idx - len]; while (len > 0) { int step = ToUpperCase(uppercase); uppercase += step;