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
This commit is contained in:
Eugene Kliuchnikov 2016-01-22 10:19:41 +01:00
parent c788a55927
commit 92e3023914
12 changed files with 185 additions and 332 deletions

View File

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

View File

@ -6,10 +6,10 @@
/* Bit reading helpers */
#include <stdlib.h>
#include "./bit_reader.h"
#include "./port.h"
#include "./types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {

View File

@ -9,8 +9,8 @@
#ifndef BROTLI_DEC_BIT_READER_H_
#define BROTLI_DEC_BIT_READER_H_
#include <stdio.h>
#include <string.h>
#include <string.h> /* 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;
}

View File

@ -4,26 +4,42 @@
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 <arm_neon.h>
#endif
#include <stdio.h> /* printf (debug output) */
#include <stdlib.h> /* free, malloc */
#include <string.h> /* 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;

View File

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

View File

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

View File

@ -6,11 +6,12 @@
/* Utilities for building Huffman decoding tables. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "./huffman.h"
#include <string.h> /* 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;

View File

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

View File

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

View File

@ -4,11 +4,12 @@
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
*/
#include "./huffman.h"
#include "./state.h"
#include <stdlib.h>
#include <string.h>
#include <stdlib.h> /* 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;

View File

@ -9,7 +9,6 @@
#ifndef BROTLI_DEC_STATE_H_
#define BROTLI_DEC_STATE_H_
#include <stdio.h>
#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;

View File

@ -9,8 +9,6 @@
#ifndef BROTLI_DEC_TRANSFORM_H_
#define BROTLI_DEC_TRANSFORM_H_
#include <stdio.h>
#include <ctype.h>
#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;