mirror of
https://github.com/google/brotli.git
synced 2024-12-28 18:51:08 +00:00
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:
parent
c788a55927
commit
92e3023914
@ -4,7 +4,7 @@ include ../shared.mk
|
|||||||
|
|
||||||
CFLAGS += -Wall
|
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)
|
all : $(OBJS)
|
||||||
|
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
/* Bit reading helpers */
|
/* Bit reading helpers */
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "./bit_reader.h"
|
#include "./bit_reader.h"
|
||||||
|
|
||||||
#include "./port.h"
|
#include "./port.h"
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
#ifndef BROTLI_DEC_BIT_READER_H_
|
#ifndef BROTLI_DEC_BIT_READER_H_
|
||||||
#define BROTLI_DEC_BIT_READER_H_
|
#define BROTLI_DEC_BIT_READER_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <string.h> /* memcpy */
|
||||||
#include <string.h>
|
|
||||||
#include "./port.h"
|
#include "./port.h"
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
|
||||||
@ -283,7 +283,11 @@ static BROTLI_INLINE void BrotliBitReaderUnload(BrotliBitReader* br) {
|
|||||||
uint32_t unused_bits = unused_bytes << 3;
|
uint32_t unused_bits = unused_bytes << 3;
|
||||||
br->avail_in += unused_bytes;
|
br->avail_in += unused_bytes;
|
||||||
br->next_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;
|
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) {
|
static BROTLI_INLINE int BrotliPeekByte(BrotliBitReader* br, size_t offset) {
|
||||||
uint32_t available_bits = BrotliGetAvailableBits(br);
|
uint32_t available_bits = BrotliGetAvailableBits(br);
|
||||||
size_t bytes_left = available_bits >> 3;
|
size_t bytes_left = available_bits >> 3;
|
||||||
if (available_bits & 7) {
|
BROTLI_DCHECK((available_bits & 7) == 0);
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (offset < bytes_left) {
|
if (offset < bytes_left) {
|
||||||
return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF;
|
return (BrotliGetBitsUnmasked(br) >> (unsigned)(offset << 3)) & 0xFF;
|
||||||
}
|
}
|
||||||
|
284
dec/decode.c
284
dec/decode.c
@ -4,26 +4,42 @@
|
|||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
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 "./decode.h"
|
||||||
#include "./dictionary.h"
|
|
||||||
#include "./port.h"
|
|
||||||
#include "./transform.h"
|
|
||||||
#include "./huffman.h"
|
|
||||||
#include "./prefix.h"
|
|
||||||
|
|
||||||
#ifdef __ARM_NEON__
|
#ifdef __ARM_NEON__
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#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)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
#ifdef BROTLI_DECODE_DEBUG
|
||||||
#define BROTLI_LOG_UINT(name) \
|
#define BROTLI_LOG_UINT(name) \
|
||||||
printf("[%s] %s = %lu\n", __func__, #name, (unsigned long)(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. */
|
/* No break, transit to the next state. */
|
||||||
|
|
||||||
case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED:
|
case BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED:
|
||||||
if (!s->is_last_metablock && !s->is_metadata) {
|
if (!s->is_last_metablock) {
|
||||||
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
if (!BrotliSafeReadBits(br, 1, &bits)) {
|
||||||
return BROTLI_RESULT_NEEDS_MORE_INPUT;
|
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->meta_block_remaining_len |= (int)(bits << (i * 8));
|
||||||
}
|
}
|
||||||
s->substate_metablock_header =
|
++s->meta_block_remaining_len;
|
||||||
BROTLI_STATE_METABLOCK_HEADER_UNCOMPRESSED;
|
s->substate_metablock_header = BROTLI_STATE_METABLOCK_HEADER_NONE;
|
||||||
break;
|
return BROTLI_RESULT_SUCCESS;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return BROTLI_FAILURE();
|
return BROTLI_FAILURE();
|
||||||
@ -473,6 +489,8 @@ static BROTLI_INLINE void ProcessSingleCodeLength(uint32_t code_len,
|
|||||||
*prev_code_len = code_len;
|
*prev_code_len = code_len;
|
||||||
*space -= 32768U >> code_len;
|
*space -= 32768U >> code_len;
|
||||||
code_length_histo[code_len]++;
|
code_length_histo[code_len]++;
|
||||||
|
BROTLI_LOG(("[ReadHuffmanCode] code_length[%d] = %d\n",
|
||||||
|
*symbol, code_len));
|
||||||
}
|
}
|
||||||
(*symbol)++;
|
(*symbol)++;
|
||||||
}
|
}
|
||||||
@ -513,6 +531,8 @@ static BROTLI_INLINE void ProcessRepeatedCodeLength(uint32_t code_len,
|
|||||||
*space = 0xFFFFF;
|
*space = 0xFFFFF;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
BROTLI_LOG(("[ReadHuffmanCode] code_length[%d..%d] = %d\n",
|
||||||
|
*symbol, *symbol + repeat_delta - 1, *repeat_code_len));
|
||||||
if (*repeat_code_len != 0) {
|
if (*repeat_code_len != 0) {
|
||||||
unsigned last = *symbol + repeat_delta;
|
unsigned last = *symbol + repeat_delta;
|
||||||
int next = next_symbol[*repeat_code_len];
|
int next = next_symbol[*repeat_code_len];
|
||||||
@ -1018,9 +1038,9 @@ rleCode:
|
|||||||
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
s->substate_context_map = BROTLI_STATE_CONTEXT_MAP_NONE;
|
||||||
return BROTLI_RESULT_SUCCESS;
|
return BROTLI_RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
return BROTLI_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
return BROTLI_FAILURE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decodes a command or literal and updates block type ringbuffer.
|
/* Decodes a command or literal and updates block type ringbuffer.
|
||||||
@ -1028,9 +1048,10 @@ rleCode:
|
|||||||
static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe,
|
static BROTLI_INLINE int DecodeBlockTypeAndLength(int safe,
|
||||||
BrotliState* s, int tree_type) {
|
BrotliState* s, int tree_type) {
|
||||||
uint32_t max_block_type = s->num_block_types[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[
|
||||||
const HuffmanCode* type_tree = &s->block_type_trees[tree_offset];
|
tree_type * BROTLI_HUFFMAN_MAX_SIZE_258];
|
||||||
const HuffmanCode* len_tree = &s->block_len_trees[tree_offset];
|
const HuffmanCode* len_tree = &s->block_len_trees[
|
||||||
|
tree_type * BROTLI_HUFFMAN_MAX_SIZE_26];
|
||||||
BrotliBitReader* br = &s->br;
|
BrotliBitReader* br = &s->br;
|
||||||
uint32_t* ringbuffer = &s->block_type_rb[tree_type * 2];
|
uint32_t* ringbuffer = &s->block_type_rb[tree_type * 2];
|
||||||
uint32_t block_type;
|
uint32_t block_type;
|
||||||
@ -1152,7 +1173,7 @@ static BrotliResult WriteRingBuffer(size_t* available_out, uint8_t** next_out,
|
|||||||
*available_out -= num_written;
|
*available_out -= num_written;
|
||||||
BROTLI_LOG_UINT(to_write);
|
BROTLI_LOG_UINT(to_write);
|
||||||
BROTLI_LOG_UINT(num_written);
|
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;
|
*total_out = s->partial_pos_out;
|
||||||
if (num_written < to_write) {
|
if (num_written < to_write) {
|
||||||
return BROTLI_RESULT_NEEDS_MORE_OUTPUT;
|
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;
|
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(
|
static BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput(
|
||||||
size_t* available_out, uint8_t** next_out, size_t* total_out,
|
size_t* available_out, uint8_t** next_out, size_t* total_out,
|
||||||
BrotliState* s) {
|
BrotliState* s) {
|
||||||
|
/* TODO: avoid allocation for single uncompressed block. */
|
||||||
|
if (!s->ringbuffer && !BrotliAllocateRingBuffer(s)) {
|
||||||
|
return BROTLI_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
/* State machine */
|
/* State machine */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (s->substate_uncompressed) {
|
switch (s->substate_uncompressed) {
|
||||||
@ -1185,7 +1245,6 @@ static BrotliResult BROTLI_NOINLINE CopyUncompressedBlockToOutput(
|
|||||||
return BROTLI_RESULT_NEEDS_MORE_INPUT;
|
return BROTLI_RESULT_NEEDS_MORE_INPUT;
|
||||||
}
|
}
|
||||||
s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
|
s->substate_uncompressed = BROTLI_STATE_UNCOMPRESSED_WRITE;
|
||||||
/*s->partial_pos_rb += (size_t)s->ringbuffer_size;*/
|
|
||||||
/* No break, continue to next state */
|
/* No break, continue to next state */
|
||||||
}
|
}
|
||||||
case BROTLI_STATE_UNCOMPRESSED_WRITE: {
|
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);
|
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
|
If we know the data size is small, do not allocate more ringbuffer
|
||||||
size than needed to reduce memory usage.
|
size than needed to reduce memory usage.
|
||||||
|
|
||||||
This method is called before the first non-empty non-metadata block is
|
When this method is called, metablock size and flags MUST be decoded.
|
||||||
processed. 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) {
|
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;
|
int is_last = s->is_last_metablock;
|
||||||
s->ringbuffer_size = 1 << s->window_bits;
|
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_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. */
|
/* 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);
|
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,
|
static BROTLI_INLINE int CheckInputAmount(int safe,
|
||||||
BrotliBitReader* const br, size_t num) {
|
BrotliBitReader* const br, size_t num) {
|
||||||
if (safe) {
|
if (safe) {
|
||||||
@ -1494,10 +1526,13 @@ static BROTLI_INLINE BrotliResult ProcessCommandsInternal(int safe,
|
|||||||
BrotliResult result = BROTLI_RESULT_SUCCESS;
|
BrotliResult result = BROTLI_RESULT_SUCCESS;
|
||||||
BrotliBitReader* br = &s->br;
|
BrotliBitReader* br = &s->br;
|
||||||
|
|
||||||
if (!CheckInputAmount(safe, br, 28) || !WarmupBitReader(safe, br)) {
|
if (!CheckInputAmount(safe, br, 28)) {
|
||||||
result = BROTLI_RESULT_NEEDS_MORE_INPUT;
|
result = BROTLI_RESULT_NEEDS_MORE_INPUT;
|
||||||
goto saveStateAndReturn;
|
goto saveStateAndReturn;
|
||||||
}
|
}
|
||||||
|
if (!safe) {
|
||||||
|
BROTLI_UNUSED(BrotliWarmupBitReader(br));
|
||||||
|
}
|
||||||
|
|
||||||
/* Jump into state machine. */
|
/* Jump into state machine. */
|
||||||
if (s->state == BROTLI_STATE_COMMAND_BEGIN) {
|
if (s->state == BROTLI_STATE_COMMAND_BEGIN) {
|
||||||
@ -1527,9 +1562,8 @@ CommandBegin:
|
|||||||
}
|
}
|
||||||
/* Read the insert/copy length in the command */
|
/* Read the insert/copy length in the command */
|
||||||
BROTLI_SAFE(ReadCommand(s, br, &i));
|
BROTLI_SAFE(ReadCommand(s, br, &i));
|
||||||
BROTLI_LOG_UINT(i);
|
BROTLI_LOG(("[ProcessCommandsInternal] pos = %d insert = %d copy = %d\n",
|
||||||
BROTLI_LOG_UINT(s->copy_length);
|
pos, i, s->copy_length));
|
||||||
BROTLI_LOG_UINT(s->distance_code);
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
goto CommandPostDecodeLiterals;
|
goto CommandPostDecodeLiterals;
|
||||||
}
|
}
|
||||||
@ -1615,6 +1649,7 @@ CommandInner:
|
|||||||
}
|
}
|
||||||
} while (--i != 0);
|
} while (--i != 0);
|
||||||
}
|
}
|
||||||
|
BROTLI_LOG_UINT(s->meta_block_remaining_len);
|
||||||
if (s->meta_block_remaining_len <= 0) {
|
if (s->meta_block_remaining_len <= 0) {
|
||||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||||
goto saveStateAndReturn;
|
goto saveStateAndReturn;
|
||||||
@ -1635,7 +1670,8 @@ CommandPostDecodeLiterals:
|
|||||||
}
|
}
|
||||||
BROTLI_SAFE(ReadDistance(s, br));
|
BROTLI_SAFE(ReadDistance(s, br));
|
||||||
postReadDistance:
|
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 (s->max_distance != s->max_backward_distance) {
|
||||||
if (pos < s->max_backward_distance_minus_custom_dict_size) {
|
if (pos < s->max_backward_distance_minus_custom_dict_size) {
|
||||||
s->max_distance = pos + s->custom_dict_size;
|
s->max_distance = pos + s->custom_dict_size;
|
||||||
@ -1649,7 +1685,7 @@ postReadDistance:
|
|||||||
if (s->distance_code > s->max_distance) {
|
if (s->distance_code > s->max_distance) {
|
||||||
if (i >= kBrotliMinDictionaryWordLength &&
|
if (i >= kBrotliMinDictionaryWordLength &&
|
||||||
i <= kBrotliMaxDictionaryWordLength) {
|
i <= kBrotliMaxDictionaryWordLength) {
|
||||||
int offset = kBrotliDictionaryOffsetsByLength[i];
|
int offset = (int)kBrotliDictionaryOffsetsByLength[i];
|
||||||
int word_id = s->distance_code - s->max_distance - 1;
|
int word_id = s->distance_code - s->max_distance - 1;
|
||||||
uint32_t shift = kBrotliDictionarySizeBitsByLength[i];
|
uint32_t shift = kBrotliDictionarySizeBitsByLength[i];
|
||||||
int mask = (int)BitMask(shift);
|
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) {
|
if (s->meta_block_remaining_len <= 0) {
|
||||||
/* Next metablock, if any */
|
/* Next metablock, if any */
|
||||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||||
@ -1813,113 +1850,6 @@ BrotliResult BrotliDecompressBuffer(size_t encoded_size,
|
|||||||
return result;
|
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:
|
/* Invariant: input stream is never overconsumed:
|
||||||
* invalid input implies that the whole stream is invalid -> any amount of
|
* invalid input implies that the whole stream is invalid -> any amount of
|
||||||
input could be read and discarded
|
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. */
|
/* Allocate memory for both block_type_trees and block_len_trees. */
|
||||||
s->block_type_trees = (HuffmanCode*)BROTLI_ALLOC(s,
|
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) {
|
if (s->block_type_trees == 0) {
|
||||||
result = BROTLI_FAILURE();
|
result = BROTLI_FAILURE();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
s->block_len_trees = s->block_type_trees +
|
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;
|
s->state = BROTLI_STATE_METABLOCK_BEGIN;
|
||||||
/* No break, continue to next state */
|
/* No break, continue to next state */
|
||||||
@ -2069,10 +2000,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!s->ringbuffer) {
|
if (!s->ringbuffer) {
|
||||||
if (!BrotliAllocateRingBuffer(s, br)) {
|
BrotliCalculateRingBufferSize(s, br);
|
||||||
result = BROTLI_FAILURE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (s->is_uncompressed) {
|
if (s->is_uncompressed) {
|
||||||
s->state = BROTLI_STATE_UNCOMPRESSED;
|
s->state = BROTLI_STATE_UNCOMPRESSED;
|
||||||
@ -2124,7 +2052,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
s->state = BROTLI_STATE_HUFFMAN_CODE_1;
|
s->state = BROTLI_STATE_HUFFMAN_CODE_1;
|
||||||
/* No break, continue to next state */
|
/* No break, continue to next state */
|
||||||
case BROTLI_STATE_HUFFMAN_CODE_1: {
|
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,
|
result = ReadHuffmanCode(s->num_block_types[s->loop_counter] + 2,
|
||||||
&s->block_type_trees[tree_offset], NULL, s);
|
&s->block_type_trees[tree_offset], NULL, s);
|
||||||
if (result != BROTLI_RESULT_SUCCESS) break;
|
if (result != BROTLI_RESULT_SUCCESS) break;
|
||||||
@ -2132,7 +2060,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
/* No break, continue to next state */
|
/* No break, continue to next state */
|
||||||
}
|
}
|
||||||
case BROTLI_STATE_HUFFMAN_CODE_2: {
|
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,
|
result = ReadHuffmanCode(kNumBlockLengthCodes,
|
||||||
&s->block_len_trees[tree_offset], NULL, s);
|
&s->block_len_trees[tree_offset], NULL, s);
|
||||||
if (result != BROTLI_RESULT_SUCCESS) break;
|
if (result != BROTLI_RESULT_SUCCESS) break;
|
||||||
@ -2140,7 +2068,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
/* No break, continue to next state */
|
/* No break, continue to next state */
|
||||||
}
|
}
|
||||||
case BROTLI_STATE_HUFFMAN_CODE_3: {
|
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],
|
if (!SafeReadBlockLength(s, &s->block_length[s->loop_counter],
|
||||||
&s->block_len_trees[tree_offset], br)) {
|
&s->block_len_trees[tree_offset], br)) {
|
||||||
result = BROTLI_RESULT_NEEDS_MORE_INPUT;
|
result = BROTLI_RESULT_NEEDS_MORE_INPUT;
|
||||||
@ -2212,7 +2140,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
s->num_literal_htrees);
|
s->num_literal_htrees);
|
||||||
BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup,
|
BrotliHuffmanTreeGroupInit(s, &s->insert_copy_hgroup,
|
||||||
kNumInsertAndCopyCodes,
|
kNumInsertAndCopyCodes,
|
||||||
s->num_block_types[1]);
|
s->num_block_types[1]);
|
||||||
BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes,
|
BrotliHuffmanTreeGroupInit(s, &s->distance_hgroup, num_distance_codes,
|
||||||
s->num_dist_htrees);
|
s->num_dist_htrees);
|
||||||
if (s->literal_hgroup.codes == 0 ||
|
if (s->literal_hgroup.codes == 0 ||
|
||||||
@ -2252,6 +2180,10 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
&kContextLookup[kContextLookupOffsets[context_mode + 1]];
|
&kContextLookup[kContextLookupOffsets[context_mode + 1]];
|
||||||
s->htree_command = s->insert_copy_hgroup.htrees[0];
|
s->htree_command = s->insert_copy_hgroup.htrees[0];
|
||||||
s->literal_htree = s->literal_hgroup.htrees[s->literal_htree_index];
|
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;
|
s->state = BROTLI_STATE_COMMAND_BEGIN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2276,7 +2208,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
s->max_distance = s->max_backward_distance;
|
s->max_distance = s->max_backward_distance;
|
||||||
if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) {
|
if (s->state == BROTLI_STATE_COMMAND_POST_WRITE_1) {
|
||||||
memcpy(s->ringbuffer, s->ringbuffer_end, (size_t)s->pos);
|
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 */
|
/* Next metablock, if any */
|
||||||
s->state = BROTLI_STATE_METABLOCK_DONE;
|
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||||
} else {
|
} else {
|
||||||
@ -2287,7 +2219,7 @@ BrotliResult BrotliDecompressStream(size_t* available_in,
|
|||||||
s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY;
|
s->state = BROTLI_STATE_COMMAND_POST_WRAP_COPY;
|
||||||
} else { /* BROTLI_STATE_COMMAND_INNER_WRITE */
|
} else { /* BROTLI_STATE_COMMAND_INNER_WRITE */
|
||||||
if (s->loop_counter == 0) {
|
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;
|
s->state = BROTLI_STATE_METABLOCK_DONE;
|
||||||
} else {
|
} else {
|
||||||
s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS;
|
s->state = BROTLI_STATE_COMMAND_POST_DECODE_LITERALS;
|
||||||
|
132
dec/decode.h
132
dec/decode.h
@ -10,7 +10,6 @@
|
|||||||
#define BROTLI_DEC_DECODE_H_
|
#define BROTLI_DEC_DECODE_H_
|
||||||
|
|
||||||
#include "./state.h"
|
#include "./state.h"
|
||||||
#include "./streams.h"
|
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
@ -18,126 +17,57 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
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,
|
BROTLI_RESULT_ERROR = 0,
|
||||||
/* Successfully completely done */
|
/* Decoding successfully completed */
|
||||||
BROTLI_RESULT_SUCCESS = 1,
|
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,
|
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
|
BROTLI_RESULT_NEEDS_MORE_OUTPUT = 3
|
||||||
} BrotliResult;
|
} BrotliResult;
|
||||||
|
|
||||||
/* BROTLI_FAILURE macro unwraps to BROTLI_RESULT_ERROR in non-debug build. */
|
/* Creates the instance of BrotliState and initializes it. |alloc_func| and
|
||||||
/* In debug build it dumps file name, line and pretty function name. */
|
|free_func| MUST be both zero or both non-zero. In the case they are both
|
||||||
#if defined(_MSC_VER) || !defined(BROTLI_DEBUG)
|
zero, default memory allocators are used. |opaque| is passed to |alloc_func|
|
||||||
#define BROTLI_FAILURE() BROTLI_RESULT_ERROR
|
and |free_func| when they are called. */
|
||||||
#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. */
|
|
||||||
BrotliState* BrotliCreateState(
|
BrotliState* BrotliCreateState(
|
||||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||||
|
|
||||||
/* Deinitializes and frees BrotliState instance. */
|
/* Deinitializes and frees BrotliState instance. */
|
||||||
void BrotliDestroyState(BrotliState* state);
|
void BrotliDestroyState(BrotliState* state);
|
||||||
|
|
||||||
/* Sets *decoded_size to the decompressed size of the given encoded stream. */
|
/* 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, */
|
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 */
|
or if it has two meta-blocks, where the first is uncompressed and the
|
||||||
/* second is empty. */
|
second is empty.
|
||||||
/* Returns 1 on success, 0 on failure. */
|
Returns 1 on success, 0 on failure. */
|
||||||
int BrotliDecompressedSize(size_t encoded_size,
|
int BrotliDecompressedSize(size_t encoded_size,
|
||||||
const uint8_t* encoded_buffer,
|
const uint8_t* encoded_buffer,
|
||||||
size_t* decoded_size);
|
size_t* decoded_size);
|
||||||
|
|
||||||
/* Decompresses the data in encoded_buffer into decoded_buffer, and sets */
|
/* Decompresses the data in |encoded_buffer| into |decoded_buffer|, and sets
|
||||||
/* *decoded_size to the decompressed length. */
|
|*decoded_size| to the decompressed length. */
|
||||||
BrotliResult BrotliDecompressBuffer(size_t encoded_size,
|
BrotliResult BrotliDecompressBuffer(size_t encoded_size,
|
||||||
const uint8_t* encoded_buffer,
|
const uint8_t* encoded_buffer,
|
||||||
size_t* decoded_size,
|
size_t* decoded_size,
|
||||||
uint8_t* decoded_buffer);
|
uint8_t* decoded_buffer);
|
||||||
|
|
||||||
/* Same as above, but uses the specified input and output callbacks instead */
|
/* Decompresses the data. Supports partial input and output.
|
||||||
/* of reading from and writing to pre-allocated memory buffers. */
|
|
||||||
/* DEPRECATED */
|
|
||||||
BrotliResult BrotliDecompress(BrotliInput input, BrotliOutput output);
|
|
||||||
|
|
||||||
/* Same as above, but supports the caller to call the decoder repeatedly with
|
Must be called with an allocated input buffer in |*next_in| and an allocated
|
||||||
partial data to support streaming. The state must be initialized with
|
output buffer in |*next_out|. The values |*available_in| and |*available_out|
|
||||||
BrotliStateInit and reused with every call for the same stream.
|
must specify the allocated size in |*next_in| and |*next_out| respectively.
|
||||||
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);
|
|
||||||
|
|
||||||
/* Same as above, but with memory buffers.
|
After each call, |*available_in| will be decremented by the amount of input
|
||||||
Must be called with an allocated input buffer in *next_in and an allocated
|
bytes consumed, and the |*next_in| pointer will be incremented by that
|
||||||
output buffer in *next_out. The values *available_in and *available_out
|
amount. Similarly, |*available_out| will be decremented by the amount of
|
||||||
must specify the allocated size in *next_in and *next_out respectively.
|
output bytes written, and the |*next_out| pointer will be incremented by that
|
||||||
The value *total_out must be 0 initially, and will be summed with the
|
amount. |total_out| will be set to the number of bytes decompressed since
|
||||||
amount of output bytes written after each call, so that at the end it
|
last state initialization.
|
||||||
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);
|
|
||||||
|
|
||||||
|
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,
|
BrotliResult BrotliDecompressStream(size_t* available_in,
|
||||||
const uint8_t** next_in,
|
const uint8_t** next_in,
|
||||||
size_t* available_out,
|
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.
|
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 dictionary must exist in memory until decoding is done and is owned by
|
||||||
the caller. To use:
|
the caller. To use:
|
||||||
-initialize state with BrotliStateInit
|
1) initialize state with BrotliStateInit
|
||||||
-use BrotliSetCustomDictionary
|
2) use BrotliSetCustomDictionary
|
||||||
-use BrotliDecompressBufferStreaming
|
3) use BrotliDecompressStream
|
||||||
-clean up with BrotliStateCleanup
|
4) clean up with BrotliStateCleanup
|
||||||
*/
|
*/
|
||||||
void BrotliSetCustomDictionary(
|
void BrotliSetCustomDictionary(
|
||||||
size_t size, const uint8_t* dict, BrotliState* s);
|
size_t size, const uint8_t* dict, BrotliState* s);
|
||||||
|
@ -17,7 +17,7 @@ extern "C" {
|
|||||||
|
|
||||||
extern const uint8_t kBrotliDictionary[122784];
|
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,
|
0, 0, 0, 0, 0, 4096, 9216, 21504, 35840, 44032,
|
||||||
53248, 63488, 74752, 87040, 93696, 100864, 104704, 106752, 108928, 113536,
|
53248, 63488, 74752, 87040, 93696, 100864, 104704, 106752, 108928, 113536,
|
||||||
115968, 118528, 119872, 121280, 122016,
|
115968, 118528, 119872, 121280, 122016,
|
||||||
|
@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
/* Utilities for building Huffman decoding tables. */
|
/* Utilities for building Huffman decoding tables. */
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "./huffman.h"
|
#include "./huffman.h"
|
||||||
|
|
||||||
|
#include <string.h> /* memcpy, memset */
|
||||||
|
|
||||||
#include "./port.h"
|
#include "./port.h"
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -237,7 +238,7 @@ uint32_t BrotliBuildHuffmanTable(HuffmanCode* root_table,
|
|||||||
for (len = root_bits + 1, step = 2; len <= max_length; ++len) {
|
for (len = root_bits + 1, step = 2; len <= max_length; ++len) {
|
||||||
symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
symbol = len - (BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1);
|
||||||
for (; count[len] != 0; --count[len]) {
|
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 += table_size;
|
||||||
table_bits = NextTableBitSize(count, len, root_bits);
|
table_bits = NextTableBitSize(count, len, root_bits);
|
||||||
table_size = 1 << table_bits;
|
table_size = 1 << table_bits;
|
||||||
|
@ -20,9 +20,14 @@ extern "C" {
|
|||||||
/* For current format this constant equals to kNumInsertAndCopyCodes */
|
/* For current format this constant equals to kNumInsertAndCopyCodes */
|
||||||
#define BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE 704
|
#define BROTLI_HUFFMAN_MAX_CODE_LENGTHS_SIZE 704
|
||||||
|
|
||||||
/* Maximum possible Huffman table size for an alphabet size of 704, max code
|
/* Maximum possible Huffman table size for an alphabet size of (index * 32),
|
||||||
* length 15 and root table bits 8. */
|
* max code length 15 and root table bits 8. */
|
||||||
#define BROTLI_HUFFMAN_MAX_TABLE_SIZE 1080
|
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
|
#define BROTLI_HUFFMAN_MAX_CODE_LENGTH_CODE_LENGTH 5
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#ifndef BROTLI_DEC_PREFIX_H_
|
#ifndef BROTLI_DEC_PREFIX_H_
|
||||||
#define BROTLI_DEC_PREFIX_H_
|
#define BROTLI_DEC_PREFIX_H_
|
||||||
|
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
/* Represents the range of values belonging to a prefix code: */
|
/* Represents the range of values belonging to a prefix code: */
|
||||||
/* [offset, offset + 2^nbits) */
|
/* [offset, offset + 2^nbits) */
|
||||||
struct PrefixCodeRange {
|
struct PrefixCodeRange {
|
||||||
|
22
dec/state.c
22
dec/state.c
@ -4,11 +4,12 @@
|
|||||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "./huffman.h"
|
|
||||||
#include "./state.h"
|
#include "./state.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h> /* free, malloc */
|
||||||
#include <string.h>
|
|
||||||
|
#include "./huffman.h"
|
||||||
|
#include "./types.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -94,13 +95,6 @@ void BrotliStateInitWithCustomAllocators(BrotliState* s,
|
|||||||
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
s->symbol_lists = &s->symbols_lists_array[BROTLI_HUFFMAN_MAX_CODE_LENGTH + 1];
|
||||||
|
|
||||||
s->mtf_upper_bound = 255;
|
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) {
|
void BrotliStateMetablockBegin(BrotliState* s) {
|
||||||
@ -150,8 +144,6 @@ void BrotliStateCleanup(BrotliState* s) {
|
|||||||
|
|
||||||
BROTLI_FREE(s, s->ringbuffer);
|
BROTLI_FREE(s, s->ringbuffer);
|
||||||
BROTLI_FREE(s, s->block_type_trees);
|
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) {
|
int BrotliStateIsStreamStart(const BrotliState* s) {
|
||||||
@ -166,9 +158,9 @@ int BrotliStateIsStreamEnd(const BrotliState* s) {
|
|||||||
void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group,
|
void BrotliHuffmanTreeGroupInit(BrotliState* s, HuffmanTreeGroup* group,
|
||||||
uint32_t alphabet_size, uint32_t ntrees) {
|
uint32_t alphabet_size, uint32_t ntrees) {
|
||||||
/* Pack two allocations into one */
|
/* Pack two allocations into one */
|
||||||
const size_t code_size =
|
const size_t max_table_size = kMaxHuffmanTableSize[(alphabet_size + 31) >> 5];
|
||||||
sizeof(HuffmanCode) * (size_t)(ntrees * BROTLI_HUFFMAN_MAX_TABLE_SIZE);
|
const size_t code_size = sizeof(HuffmanCode) * ntrees * max_table_size;
|
||||||
const size_t htree_size = sizeof(HuffmanCode*) * (size_t)ntrees;
|
const size_t htree_size = sizeof(HuffmanCode*) * ntrees;
|
||||||
char *p = (char*)BROTLI_ALLOC(s, code_size + htree_size);
|
char *p = (char*)BROTLI_ALLOC(s, code_size + htree_size);
|
||||||
group->alphabet_size = (uint16_t)alphabet_size;
|
group->alphabet_size = (uint16_t)alphabet_size;
|
||||||
group->num_htrees = (uint16_t)ntrees;
|
group->num_htrees = (uint16_t)ntrees;
|
||||||
|
16
dec/state.h
16
dec/state.h
@ -9,7 +9,6 @@
|
|||||||
#ifndef BROTLI_DEC_STATE_H_
|
#ifndef BROTLI_DEC_STATE_H_
|
||||||
#define BROTLI_DEC_STATE_H_
|
#define BROTLI_DEC_STATE_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "./bit_reader.h"
|
#include "./bit_reader.h"
|
||||||
#include "./huffman.h"
|
#include "./huffman.h"
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
@ -95,6 +94,10 @@ typedef enum {
|
|||||||
|
|
||||||
struct BrotliStateStruct {
|
struct BrotliStateStruct {
|
||||||
BrotliRunningState state;
|
BrotliRunningState state;
|
||||||
|
|
||||||
|
/* This counter is reused for several disjoint loops. */
|
||||||
|
int loop_counter;
|
||||||
|
|
||||||
BrotliBitReader br;
|
BrotliBitReader br;
|
||||||
|
|
||||||
brotli_alloc_func alloc_func;
|
brotli_alloc_func alloc_func;
|
||||||
@ -108,8 +111,6 @@ struct BrotliStateStruct {
|
|||||||
} buffer;
|
} buffer;
|
||||||
uint32_t buffer_length;
|
uint32_t buffer_length;
|
||||||
|
|
||||||
/* This counter is reused for several disjoint loops. */
|
|
||||||
int loop_counter;
|
|
||||||
int pos;
|
int pos;
|
||||||
int max_backward_distance;
|
int max_backward_distance;
|
||||||
int max_backward_distance_minus_custom_dict_size;
|
int max_backward_distance_minus_custom_dict_size;
|
||||||
@ -188,7 +189,7 @@ struct BrotliStateStruct {
|
|||||||
uint32_t context_index;
|
uint32_t context_index;
|
||||||
uint32_t max_run_length_prefix;
|
uint32_t max_run_length_prefix;
|
||||||
uint32_t code;
|
uint32_t code;
|
||||||
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_TABLE_SIZE];
|
HuffmanCode context_map_table[BROTLI_HUFFMAN_MAX_SIZE_272];
|
||||||
|
|
||||||
/* For InverseMoveToFrontTransform */
|
/* For InverseMoveToFrontTransform */
|
||||||
uint32_t mtf_upper_bound;
|
uint32_t mtf_upper_bound;
|
||||||
@ -217,13 +218,6 @@ struct BrotliStateStruct {
|
|||||||
uint32_t num_literal_htrees;
|
uint32_t num_literal_htrees;
|
||||||
uint8_t* context_map;
|
uint8_t* context_map;
|
||||||
uint8_t* context_modes;
|
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;
|
typedef struct BrotliStateStruct BrotliState;
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
#ifndef BROTLI_DEC_TRANSFORM_H_
|
#ifndef BROTLI_DEC_TRANSFORM_H_
|
||||||
#define BROTLI_DEC_TRANSFORM_H_
|
#define BROTLI_DEC_TRANSFORM_H_
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include "./port.h"
|
#include "./port.h"
|
||||||
#include "./types.h"
|
#include "./types.h"
|
||||||
|
|
||||||
@ -269,22 +267,19 @@ static BROTLI_NOINLINE int TransformDictionaryWord(
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
const int t = kTransforms[transform].transform;
|
const int t = kTransforms[transform].transform;
|
||||||
int skip = t < kOmitFirst1 ? 0 : t - (kOmitFirst1 - 1);
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
uint8_t* uppercase;
|
int skip = t - (kOmitFirst1 - 1);
|
||||||
if (skip > len) {
|
if (skip > 0) {
|
||||||
skip = len;
|
word += skip;
|
||||||
}
|
len -= skip;
|
||||||
word += skip;
|
} else if (t <= kOmitLast9) {
|
||||||
len -= skip;
|
|
||||||
if (t <= kOmitLast9) {
|
|
||||||
len -= t;
|
len -= t;
|
||||||
}
|
}
|
||||||
while (i < len) { dst[idx++] = word[i++]; }
|
while (i < len) { dst[idx++] = word[i++]; }
|
||||||
uppercase = &dst[idx - len];
|
|
||||||
if (t == kUppercaseFirst) {
|
if (t == kUppercaseFirst) {
|
||||||
ToUpperCase(uppercase);
|
ToUpperCase(&dst[idx - len]);
|
||||||
} else if (t == kUppercaseAll) {
|
} else if (t == kUppercaseAll) {
|
||||||
|
uint8_t* uppercase = &dst[idx - len];
|
||||||
while (len > 0) {
|
while (len > 0) {
|
||||||
int step = ToUpperCase(uppercase);
|
int step = ToUpperCase(uppercase);
|
||||||
uppercase += step;
|
uppercase += step;
|
||||||
|
Loading…
Reference in New Issue
Block a user