* slimmer stack frames in encoder
 * fix MSAN problem in hasher_composite
   (not dangerous, only in large_window mode)
 * fix JNI decoder wrapper - power-of-two payloads fail to decode sometimes
 * reformat polyfil.js and decode_test.js
This commit is contained in:
Eugene Kliuchnikov 2021-07-29 22:29:43 +02:00 committed by GitHub
parent ce222e317e
commit 630b5084ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 658 additions and 516 deletions

View File

@ -73,6 +73,14 @@ static BROTLI_INLINE uint32_t ZopfliNodeCommandLength(const ZopfliNode* self) {
return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF); return ZopfliNodeCopyLength(self) + (self->dcode_insert_length & 0x7FFFFFF);
} }
/* Temporary data for ZopfliCostModelSetFromCommands. */
typedef struct ZopfliCostModelArena {
uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS];
uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
} ZopfliCostModelArena;
/* Histogram based cost model for zopflification. */ /* Histogram based cost model for zopflification. */
typedef struct ZopfliCostModel { typedef struct ZopfliCostModel {
/* The insert and copy length symbols. */ /* The insert and copy length symbols. */
@ -83,6 +91,12 @@ typedef struct ZopfliCostModel {
float* literal_costs_; float* literal_costs_;
float min_cost_cmd_; float min_cost_cmd_;
size_t num_bytes_; size_t num_bytes_;
/* Temporary data. */
union {
size_t literal_histograms[3 * 256];
ZopfliCostModelArena arena;
};
} ZopfliCostModel; } ZopfliCostModel;
static void InitZopfliCostModel( static void InitZopfliCostModel(
@ -139,18 +153,15 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
const Command* commands, const Command* commands,
size_t num_commands, size_t num_commands,
size_t last_insert_len) { size_t last_insert_len) {
uint32_t histogram_literal[BROTLI_NUM_LITERAL_SYMBOLS]; ZopfliCostModelArena* arena = &self->arena;
uint32_t histogram_cmd[BROTLI_NUM_COMMAND_SYMBOLS];
uint32_t histogram_dist[BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE];
float cost_literal[BROTLI_NUM_LITERAL_SYMBOLS];
size_t pos = position - last_insert_len; size_t pos = position - last_insert_len;
float min_cost_cmd = kInfinity; float min_cost_cmd = kInfinity;
size_t i; size_t i;
float* cost_cmd = self->cost_cmd_; float* cost_cmd = self->cost_cmd_;
memset(histogram_literal, 0, sizeof(histogram_literal)); memset(arena->histogram_literal, 0, sizeof(arena->histogram_literal));
memset(histogram_cmd, 0, sizeof(histogram_cmd)); memset(arena->histogram_cmd, 0, sizeof(arena->histogram_cmd));
memset(histogram_dist, 0, sizeof(histogram_dist)); memset(arena->histogram_dist, 0, sizeof(arena->histogram_dist));
for (i = 0; i < num_commands; i++) { for (i = 0; i < num_commands; i++) {
size_t inslength = commands[i].insert_len_; size_t inslength = commands[i].insert_len_;
@ -159,21 +170,21 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
size_t cmdcode = commands[i].cmd_prefix_; size_t cmdcode = commands[i].cmd_prefix_;
size_t j; size_t j;
histogram_cmd[cmdcode]++; arena->histogram_cmd[cmdcode]++;
if (cmdcode >= 128) histogram_dist[distcode]++; if (cmdcode >= 128) arena->histogram_dist[distcode]++;
for (j = 0; j < inslength; j++) { for (j = 0; j < inslength; j++) {
histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++; arena->histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
} }
pos += inslength + copylength; pos += inslength + copylength;
} }
SetCost(histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE, SetCost(arena->histogram_literal, BROTLI_NUM_LITERAL_SYMBOLS, BROTLI_TRUE,
cost_literal); arena->cost_literal);
SetCost(histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE, SetCost(arena->histogram_cmd, BROTLI_NUM_COMMAND_SYMBOLS, BROTLI_FALSE,
cost_cmd); cost_cmd);
SetCost(histogram_dist, self->distance_histogram_size, BROTLI_FALSE, SetCost(arena->histogram_dist, self->distance_histogram_size, BROTLI_FALSE,
self->cost_dist_); self->cost_dist_);
for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) { for (i = 0; i < BROTLI_NUM_COMMAND_SYMBOLS; ++i) {
@ -188,7 +199,7 @@ static void ZopfliCostModelSetFromCommands(ZopfliCostModel* self,
literal_costs[0] = 0.0; literal_costs[0] = 0.0;
for (i = 0; i < num_bytes; ++i) { for (i = 0; i < num_bytes; ++i) {
literal_carry += literal_carry +=
cost_literal[ringbuffer[(position + i) & ringbuffer_mask]]; arena->cost_literal[ringbuffer[(position + i) & ringbuffer_mask]];
literal_costs[i + 1] = literal_costs[i] + literal_carry; literal_costs[i + 1] = literal_costs[i] + literal_carry;
literal_carry -= literal_costs[i + 1] - literal_costs[i]; literal_carry -= literal_costs[i + 1] - literal_costs[i];
} }
@ -206,7 +217,8 @@ static void ZopfliCostModelSetFromLiteralCosts(ZopfliCostModel* self,
size_t num_bytes = self->num_bytes_; size_t num_bytes = self->num_bytes_;
size_t i; size_t i;
BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask, BrotliEstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
ringbuffer, &literal_costs[1]); ringbuffer, self->literal_histograms,
&literal_costs[1]);
literal_costs[0] = 0.0; literal_costs[0] = 0.0;
for (i = 0; i < num_bytes; ++i) { for (i = 0; i < num_bytes; ++i) {
literal_carry += literal_costs[i + 1]; literal_carry += literal_costs[i + 1];
@ -661,21 +673,25 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes,
const size_t stream_offset = params->stream_offset; const size_t stream_offset = params->stream_offset;
const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin); const size_t max_backward_limit = BROTLI_MAX_BACKWARD_LIMIT(params->lgwin);
const size_t max_zopfli_len = MaxZopfliLen(params); const size_t max_zopfli_len = MaxZopfliLen(params);
ZopfliCostModel model;
StartPosQueue queue; StartPosQueue queue;
BackwardMatch matches[2 * (MAX_NUM_MATCHES_H10 + 64)]; BackwardMatch* BROTLI_RESTRICT matches =
BROTLI_ALLOC(m, BackwardMatch, 2 * (MAX_NUM_MATCHES_H10 + 64));
const size_t store_end = num_bytes >= StoreLookaheadH10() ? const size_t store_end = num_bytes >= StoreLookaheadH10() ?
position + num_bytes - StoreLookaheadH10() + 1 : position; position + num_bytes - StoreLookaheadH10() + 1 : position;
size_t i; size_t i;
size_t gap = 0; size_t gap = 0;
size_t lz_matches_offset = 0; size_t lz_matches_offset = 0;
ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1);
BROTLI_UNUSED(literal_context_lut); BROTLI_UNUSED(literal_context_lut);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) || BROTLI_IS_NULL(matches)) {
return 0;
}
nodes[0].length = 0; nodes[0].length = 0;
nodes[0].u.cost = 0; nodes[0].u.cost = 0;
InitZopfliCostModel(m, &model, &params->dist, num_bytes); InitZopfliCostModel(m, model, &params->dist, num_bytes);
if (BROTLI_IS_OOM(m)) return 0; if (BROTLI_IS_OOM(m)) return 0;
ZopfliCostModelSetFromLiteralCosts( ZopfliCostModelSetFromLiteralCosts(
&model, position, ringbuffer, ringbuffer_mask); model, position, ringbuffer, ringbuffer_mask);
InitStartPosQueue(&queue); InitStartPosQueue(&queue);
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) { for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; i++) {
const size_t pos = position + i; const size_t pos = position + i;
@ -694,7 +710,7 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes,
num_matches = 1; num_matches = 1;
} }
skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask, skip = UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
params, max_backward_limit, dist_cache, num_matches, matches, &model, params, max_backward_limit, dist_cache, num_matches, matches, model,
&queue, nodes); &queue, nodes);
if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0; if (skip < BROTLI_LONG_COPY_QUICK_STEP) skip = 0;
if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) { if (num_matches == 1 && BackwardMatchLength(&matches[0]) > max_zopfli_len) {
@ -710,12 +726,14 @@ size_t BrotliZopfliComputeShortestPath(MemoryManager* m, size_t num_bytes,
i++; i++;
if (i + HashTypeLengthH10() - 1 >= num_bytes) break; if (i + HashTypeLengthH10() - 1 >= num_bytes) break;
EvaluateNode(position + stream_offset, i, max_backward_limit, gap, EvaluateNode(position + stream_offset, i, max_backward_limit, gap,
dist_cache, &model, &queue, nodes); dist_cache, model, &queue, nodes);
skip--; skip--;
} }
} }
} }
CleanupZopfliCostModel(m, &model); CleanupZopfliCostModel(m, model);
BROTLI_FREE(m, model);
BROTLI_FREE(m, matches);
return ComputeShortestPathFromNodes(num_bytes, nodes); return ComputeShortestPathFromNodes(num_bytes, nodes);
} }
@ -753,14 +771,14 @@ void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes,
size_t orig_last_insert_len; size_t orig_last_insert_len;
int orig_dist_cache[4]; int orig_dist_cache[4];
size_t orig_num_commands; size_t orig_num_commands;
ZopfliCostModel model; ZopfliCostModel* model = BROTLI_ALLOC(m, ZopfliCostModel, 1);
ZopfliNode* nodes; ZopfliNode* nodes;
BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size); BackwardMatch* matches = BROTLI_ALLOC(m, BackwardMatch, matches_size);
size_t gap = 0; size_t gap = 0;
size_t shadow_matches = 0; size_t shadow_matches = 0;
BROTLI_UNUSED(literal_context_lut); BROTLI_UNUSED(literal_context_lut);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(num_matches) || if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(model) ||
BROTLI_IS_NULL(matches)) { BROTLI_IS_NULL(num_matches) || BROTLI_IS_NULL(matches)) {
return; return;
} }
for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) { for (i = 0; i + HashTypeLengthH10() - 1 < num_bytes; ++i) {
@ -810,15 +828,15 @@ void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes,
orig_num_commands = *num_commands; orig_num_commands = *num_commands;
nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1); nodes = BROTLI_ALLOC(m, ZopfliNode, num_bytes + 1);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return; if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) return;
InitZopfliCostModel(m, &model, &params->dist, num_bytes); InitZopfliCostModel(m, model, &params->dist, num_bytes);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
BrotliInitZopfliNodes(nodes, num_bytes + 1); BrotliInitZopfliNodes(nodes, num_bytes + 1);
if (i == 0) { if (i == 0) {
ZopfliCostModelSetFromLiteralCosts( ZopfliCostModelSetFromLiteralCosts(
&model, position, ringbuffer, ringbuffer_mask); model, position, ringbuffer, ringbuffer_mask);
} else { } else {
ZopfliCostModelSetFromCommands(&model, position, ringbuffer, ZopfliCostModelSetFromCommands(model, position, ringbuffer,
ringbuffer_mask, commands, *num_commands - orig_num_commands, ringbuffer_mask, commands, *num_commands - orig_num_commands,
orig_last_insert_len); orig_last_insert_len);
} }
@ -827,12 +845,13 @@ void BrotliCreateHqZopfliBackwardReferences(MemoryManager* m, size_t num_bytes,
*last_insert_len = orig_last_insert_len; *last_insert_len = orig_last_insert_len;
memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0])); memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
*num_commands += ZopfliIterate(num_bytes, position, ringbuffer, *num_commands += ZopfliIterate(num_bytes, position, ringbuffer,
ringbuffer_mask, params, gap, dist_cache, &model, num_matches, matches, ringbuffer_mask, params, gap, dist_cache, model, num_matches, matches,
nodes); nodes);
BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache, BrotliZopfliCreateCommands(num_bytes, position, nodes, dist_cache,
last_insert_len, params, commands, num_literals); last_insert_len, params, commands, num_literals);
} }
CleanupZopfliCostModel(m, &model); CleanupZopfliCostModel(m, model);
BROTLI_FREE(m, model);
BROTLI_FREE(m, nodes); BROTLI_FREE(m, nodes);
BROTLI_FREE(m, matches); BROTLI_FREE(m, matches);
BROTLI_FREE(m, num_matches); BROTLI_FREE(m, num_matches);

View File

@ -46,17 +46,17 @@ static void FN(RandomSample)(uint32_t* seed,
static void FN(RefineEntropyCodes)(const DataType* data, size_t length, static void FN(RefineEntropyCodes)(const DataType* data, size_t length,
size_t stride, size_t stride,
size_t num_histograms, size_t num_histograms,
HistogramType* histograms) { HistogramType* histograms,
HistogramType* tmp) {
size_t iters = size_t iters =
kIterMulForRefining * length / stride + kMinItersForRefining; kIterMulForRefining * length / stride + kMinItersForRefining;
uint32_t seed = 7; uint32_t seed = 7;
size_t iter; size_t iter;
iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms; iters = ((iters + num_histograms - 1) / num_histograms) * num_histograms;
for (iter = 0; iter < iters; ++iter) { for (iter = 0; iter < iters; ++iter) {
HistogramType sample; FN(HistogramClear)(tmp);
FN(HistogramClear)(&sample); FN(RandomSample)(&seed, data, length, stride, tmp);
FN(RandomSample)(&seed, data, length, stride, &sample); FN(HistogramAddHistogram)(&histograms[iter % num_histograms], tmp);
FN(HistogramAddHistogram)(&histograms[iter % num_histograms], &sample);
} }
} }
@ -204,7 +204,8 @@ static void FN(ClusterBlocks)(MemoryManager* m,
uint8_t* block_ids, uint8_t* block_ids,
BlockSplit* split) { BlockSplit* split) {
uint32_t* histogram_symbols = BROTLI_ALLOC(m, uint32_t, num_blocks); uint32_t* histogram_symbols = BROTLI_ALLOC(m, uint32_t, num_blocks);
uint32_t* block_lengths = BROTLI_ALLOC(m, uint32_t, num_blocks); uint32_t* u32 =
BROTLI_ALLOC(m, uint32_t, num_blocks + 4 * HISTOGRAMS_PER_BATCH);
const size_t expected_num_clusters = CLUSTERS_PER_BATCH * const size_t expected_num_clusters = CLUSTERS_PER_BATCH *
(num_blocks + HISTOGRAMS_PER_BATCH - 1) / HISTOGRAMS_PER_BATCH; (num_blocks + HISTOGRAMS_PER_BATCH - 1) / HISTOGRAMS_PER_BATCH;
size_t all_histograms_size = 0; size_t all_histograms_size = 0;
@ -227,19 +228,23 @@ static void FN(ClusterBlocks)(MemoryManager* m,
static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX; static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX;
uint32_t* new_index; uint32_t* new_index;
size_t i; size_t i;
uint32_t sizes[HISTOGRAMS_PER_BATCH] = { 0 }; uint32_t* BROTLI_RESTRICT const sizes = u32 + 0 * HISTOGRAMS_PER_BATCH;
uint32_t new_clusters[HISTOGRAMS_PER_BATCH] = { 0 }; uint32_t* BROTLI_RESTRICT const new_clusters = u32 + 1 * HISTOGRAMS_PER_BATCH;
uint32_t symbols[HISTOGRAMS_PER_BATCH] = { 0 }; uint32_t* BROTLI_RESTRICT const symbols = u32 + 2 * HISTOGRAMS_PER_BATCH;
uint32_t remap[HISTOGRAMS_PER_BATCH] = { 0 }; uint32_t* BROTLI_RESTRICT const remap = u32 + 3 * HISTOGRAMS_PER_BATCH;
uint32_t* BROTLI_RESTRICT const block_lengths =
u32 + 4 * HISTOGRAMS_PER_BATCH;
/* TODO: move to arena? */
HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 2);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histogram_symbols) || if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histogram_symbols) ||
BROTLI_IS_NULL(block_lengths) || BROTLI_IS_NULL(all_histograms) || BROTLI_IS_NULL(u32) || BROTLI_IS_NULL(all_histograms) ||
BROTLI_IS_NULL(cluster_size) || BROTLI_IS_NULL(histograms) || BROTLI_IS_NULL(cluster_size) || BROTLI_IS_NULL(histograms) ||
BROTLI_IS_NULL(pairs)) { BROTLI_IS_NULL(pairs) || BROTLI_IS_NULL(tmp)) {
return; return;
} }
memset(block_lengths, 0, num_blocks * sizeof(uint32_t)); memset(u32, 0, (num_blocks + 4 * HISTOGRAMS_PER_BATCH) * sizeof(uint32_t));
/* Calculate block lengths (convert repeating values -> series length). */ /* Calculate block lengths (convert repeating values -> series length). */
{ {
@ -273,7 +278,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
sizes[j] = 1; sizes[j] = 1;
} }
num_new_clusters = FN(BrotliHistogramCombine)( num_new_clusters = FN(BrotliHistogramCombine)(
histograms, sizes, symbols, new_clusters, pairs, num_to_combine, histograms, tmp, sizes, symbols, new_clusters, pairs, num_to_combine,
num_to_combine, HISTOGRAMS_PER_BATCH, max_num_pairs); num_to_combine, HISTOGRAMS_PER_BATCH, max_num_pairs);
BROTLI_ENSURE_CAPACITY(m, HistogramType, all_histograms, BROTLI_ENSURE_CAPACITY(m, HistogramType, all_histograms,
all_histograms_capacity, all_histograms_size + num_new_clusters); all_histograms_capacity, all_histograms_size + num_new_clusters);
@ -308,7 +313,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
clusters[i] = (uint32_t)i; clusters[i] = (uint32_t)i;
} }
num_final_clusters = FN(BrotliHistogramCombine)( num_final_clusters = FN(BrotliHistogramCombine)(
all_histograms, cluster_size, histogram_symbols, clusters, pairs, all_histograms, tmp, cluster_size, histogram_symbols, clusters, pairs,
num_clusters, num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES, num_clusters, num_blocks, BROTLI_MAX_NUMBER_OF_BLOCK_TYPES,
max_num_pairs); max_num_pairs);
BROTLI_FREE(m, pairs); BROTLI_FREE(m, pairs);
@ -322,22 +327,21 @@ static void FN(ClusterBlocks)(MemoryManager* m,
{ {
uint32_t next_index = 0; uint32_t next_index = 0;
for (i = 0; i < num_blocks; ++i) { for (i = 0; i < num_blocks; ++i) {
HistogramType histo;
size_t j; size_t j;
uint32_t best_out; uint32_t best_out;
double best_bits; double best_bits;
FN(HistogramClear)(&histo); FN(HistogramClear)(tmp);
for (j = 0; j < block_lengths[i]; ++j) { for (j = 0; j < block_lengths[i]; ++j) {
FN(HistogramAdd)(&histo, data[pos++]); FN(HistogramAdd)(tmp, data[pos++]);
} }
/* Among equally good histograms prefer last used. */ /* Among equally good histograms prefer last used. */
/* TODO: should we give a block-switch discount here? */ /* TODO: should we give a block-switch discount here? */
best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1]; best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1];
best_bits = best_bits = FN(BrotliHistogramBitCostDistance)(
FN(BrotliHistogramBitCostDistance)(&histo, &all_histograms[best_out]); tmp, &all_histograms[best_out], tmp + 1);
for (j = 0; j < num_final_clusters; ++j) { for (j = 0; j < num_final_clusters; ++j) {
const double cur_bits = FN(BrotliHistogramBitCostDistance)( const double cur_bits = FN(BrotliHistogramBitCostDistance)(
&histo, &all_histograms[clusters[j]]); tmp, &all_histograms[clusters[j]], tmp + 1);
if (cur_bits < best_bits) { if (cur_bits < best_bits) {
best_bits = cur_bits; best_bits = cur_bits;
best_out = clusters[j]; best_out = clusters[j];
@ -349,6 +353,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
} }
} }
} }
BROTLI_FREE(m, tmp);
BROTLI_FREE(m, clusters); BROTLI_FREE(m, clusters);
BROTLI_FREE(m, all_histograms); BROTLI_FREE(m, all_histograms);
BROTLI_ENSURE_CAPACITY( BROTLI_ENSURE_CAPACITY(
@ -379,7 +384,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
split->num_types = (size_t)max_type + 1; split->num_types = (size_t)max_type + 1;
} }
BROTLI_FREE(m, new_index); BROTLI_FREE(m, new_index);
BROTLI_FREE(m, block_lengths); BROTLI_FREE(m, u32);
BROTLI_FREE(m, histogram_symbols); BROTLI_FREE(m, histogram_symbols);
} }
@ -399,6 +404,7 @@ static void FN(SplitByteVector)(MemoryManager* m,
BlockSplit* split) { BlockSplit* split) {
const size_t data_size = FN(HistogramDataSize)(); const size_t data_size = FN(HistogramDataSize)();
HistogramType* histograms; HistogramType* histograms;
HistogramType* tmp;
/* Calculate number of histograms; initial estimate is one histogram per /* Calculate number of histograms; initial estimate is one histogram per
* specified amount of symbols; however, this value is capped. */ * specified amount of symbols; however, this value is capped. */
size_t num_histograms = length / symbols_per_histogram + 1; size_t num_histograms = length / symbols_per_histogram + 1;
@ -424,7 +430,8 @@ static void FN(SplitByteVector)(MemoryManager* m,
split->num_blocks++; split->num_blocks++;
return; return;
} }
histograms = BROTLI_ALLOC(m, HistogramType, num_histograms); histograms = BROTLI_ALLOC(m, HistogramType, num_histograms + 1);
tmp = histograms + num_histograms;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histograms)) return; if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(histograms)) return;
/* Find good entropy codes. */ /* Find good entropy codes. */
FN(InitialEntropyCodes)(data, length, FN(InitialEntropyCodes)(data, length,
@ -432,7 +439,7 @@ static void FN(SplitByteVector)(MemoryManager* m,
num_histograms, histograms); num_histograms, histograms);
FN(RefineEntropyCodes)(data, length, FN(RefineEntropyCodes)(data, length,
sampling_stride_length, sampling_stride_length,
num_histograms, histograms); num_histograms, histograms, tmp);
{ {
/* Find a good path through literals with the good entropy codes. */ /* Find a good path through literals with the good entropy codes. */
uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length); uint8_t* block_ids = BROTLI_ALLOC(m, uint8_t, length);

View File

@ -286,6 +286,7 @@ void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
/* Write the Huffman tree into the brotli-representation. /* Write the Huffman tree into the brotli-representation.
The command alphabet is the largest, so this allocation will fit all The command alphabet is the largest, so this allocation will fit all
alphabets. */ alphabets. */
/* TODO: fix me */
uint8_t huffman_tree[BROTLI_NUM_COMMAND_SYMBOLS]; uint8_t huffman_tree[BROTLI_NUM_COMMAND_SYMBOLS];
uint8_t huffman_tree_extra_bits[BROTLI_NUM_COMMAND_SYMBOLS]; uint8_t huffman_tree_extra_bits[BROTLI_NUM_COMMAND_SYMBOLS];
size_t huffman_tree_size = 0; size_t huffman_tree_size = 0;
@ -400,7 +401,7 @@ static BROTLI_INLINE BROTLI_BOOL SortHuffmanTree(
return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_); return TO_BROTLI_BOOL(v0->total_count_ < v1->total_count_);
} }
void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m, void BrotliBuildAndStoreHuffmanTreeFast(HuffmanTree* tree,
const uint32_t* histogram, const uint32_t* histogram,
const size_t histogram_total, const size_t histogram_total,
const size_t max_bits, const size_t max_bits,
@ -432,10 +433,7 @@ void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m,
memset(depth, 0, length * sizeof(depth[0])); memset(depth, 0, length * sizeof(depth[0]));
{ {
const size_t max_tree_size = 2 * length + 1;
HuffmanTree* tree = BROTLI_ALLOC(m, HuffmanTree, max_tree_size);
uint32_t count_limit; uint32_t count_limit;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return;
for (count_limit = 1; ; count_limit *= 2) { for (count_limit = 1; ; count_limit *= 2) {
HuffmanTree* node = tree; HuffmanTree* node = tree;
size_t l; size_t l;
@ -500,7 +498,6 @@ void BrotliBuildAndStoreHuffmanTreeFast(MemoryManager* m,
} }
} }
} }
BROTLI_FREE(m, tree);
} }
BrotliConvertBitDepthsToSymbols(depth, length, bits); BrotliConvertBitDepthsToSymbols(depth, length, bits);
if (count <= 4) { if (count <= 4) {
@ -677,7 +674,14 @@ static void RunLengthCodeZeros(const size_t in_size,
#define SYMBOL_BITS 9 #define SYMBOL_BITS 9
typedef struct EncodeContextMapArena {
uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS];
} EncodeContextMapArena;
static void EncodeContextMap(MemoryManager* m, static void EncodeContextMap(MemoryManager* m,
EncodeContextMapArena* arena,
const uint32_t* context_map, const uint32_t* context_map,
size_t context_map_size, size_t context_map_size,
size_t num_clusters, size_t num_clusters,
@ -687,10 +691,10 @@ static void EncodeContextMap(MemoryManager* m,
uint32_t* rle_symbols; uint32_t* rle_symbols;
uint32_t max_run_length_prefix = 6; uint32_t max_run_length_prefix = 6;
size_t num_rle_symbols = 0; size_t num_rle_symbols = 0;
uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint32_t* BROTLI_RESTRICT const histogram = arena->histogram;
static const uint32_t kSymbolMask = (1u << SYMBOL_BITS) - 1u; static const uint32_t kSymbolMask = (1u << SYMBOL_BITS) - 1u;
uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint8_t* BROTLI_RESTRICT const depths = arena->depths;
uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint16_t* BROTLI_RESTRICT const bits = arena->bits;
StoreVarLenUint8(num_clusters - 1, storage_ix, storage); StoreVarLenUint8(num_clusters - 1, storage_ix, storage);
@ -703,7 +707,7 @@ static void EncodeContextMap(MemoryManager* m,
MoveToFrontTransform(context_map, context_map_size, rle_symbols); MoveToFrontTransform(context_map, context_map_size, rle_symbols);
RunLengthCodeZeros(context_map_size, rle_symbols, RunLengthCodeZeros(context_map_size, rle_symbols,
&num_rle_symbols, &max_run_length_prefix); &num_rle_symbols, &max_run_length_prefix);
memset(histogram, 0, sizeof(histogram)); memset(histogram, 0, sizeof(arena->histogram));
for (i = 0; i < num_rle_symbols; ++i) { for (i = 0; i < num_rle_symbols; ++i) {
++histogram[rle_symbols[i] & kSymbolMask]; ++histogram[rle_symbols[i] & kSymbolMask];
} }
@ -787,7 +791,8 @@ static void BuildAndStoreBlockSplitCode(const uint8_t* types,
} }
/* Stores a context map where the histogram type is always the block type. */ /* Stores a context map where the histogram type is always the block type. */
static void StoreTrivialContextMap(size_t num_types, static void StoreTrivialContextMap(EncodeContextMapArena* arena,
size_t num_types,
size_t context_bits, size_t context_bits,
HuffmanTree* tree, HuffmanTree* tree,
size_t* storage_ix, size_t* storage_ix,
@ -797,9 +802,9 @@ static void StoreTrivialContextMap(size_t num_types,
size_t repeat_code = context_bits - 1u; size_t repeat_code = context_bits - 1u;
size_t repeat_bits = (1u << repeat_code) - 1u; size_t repeat_bits = (1u << repeat_code) - 1u;
size_t alphabet_size = num_types + repeat_code; size_t alphabet_size = num_types + repeat_code;
uint32_t histogram[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint32_t* BROTLI_RESTRICT const histogram = arena->histogram;
uint8_t depths[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint8_t* BROTLI_RESTRICT const depths = arena->depths;
uint16_t bits[BROTLI_MAX_CONTEXT_MAP_SYMBOLS]; uint16_t* BROTLI_RESTRICT const bits = arena->bits;
size_t i; size_t i;
memset(histogram, 0, alphabet_size * sizeof(histogram[0])); memset(histogram, 0, alphabet_size * sizeof(histogram[0]));
/* Write RLEMAX. */ /* Write RLEMAX. */
@ -932,6 +937,13 @@ static void JumpToByteBoundary(size_t* storage_ix, uint8_t* storage) {
storage[*storage_ix >> 3] = 0; storage[*storage_ix >> 3] = 0;
} }
typedef struct StoreMetablockArena {
BlockEncoder literal_enc;
BlockEncoder command_enc;
BlockEncoder distance_enc;
EncodeContextMapArena context_map_arena;
} StoreMetablockArena;
void BrotliStoreMetaBlock(MemoryManager* m, void BrotliStoreMetaBlock(MemoryManager* m,
const uint8_t* input, size_t start_pos, size_t length, size_t mask, const uint8_t* input, size_t start_pos, size_t length, size_t mask,
uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last, uint8_t prev_byte, uint8_t prev_byte2, BROTLI_BOOL is_last,
@ -945,9 +957,10 @@ void BrotliStoreMetaBlock(MemoryManager* m,
uint32_t num_effective_distance_symbols = params->dist.alphabet_size_limit; uint32_t num_effective_distance_symbols = params->dist.alphabet_size_limit;
HuffmanTree* tree; HuffmanTree* tree;
ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
BlockEncoder literal_enc; StoreMetablockArena* arena = NULL;
BlockEncoder command_enc; BlockEncoder* literal_enc = NULL;
BlockEncoder distance_enc; BlockEncoder* command_enc = NULL;
BlockEncoder* distance_enc = NULL;
const BrotliDistanceParams* dist = &params->dist; const BrotliDistanceParams* dist = &params->dist;
BROTLI_DCHECK( BROTLI_DCHECK(
num_effective_distance_symbols <= BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS); num_effective_distance_symbols <= BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS);
@ -955,21 +968,24 @@ void BrotliStoreMetaBlock(MemoryManager* m,
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE); tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return; arena = BROTLI_ALLOC(m, StoreMetablockArena, 1);
InitBlockEncoder(&literal_enc, BROTLI_NUM_LITERAL_SYMBOLS, if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree) || BROTLI_IS_NULL(arena)) return;
literal_enc = &arena->literal_enc;
command_enc = &arena->command_enc;
distance_enc = &arena->distance_enc;
InitBlockEncoder(literal_enc, BROTLI_NUM_LITERAL_SYMBOLS,
mb->literal_split.num_types, mb->literal_split.types, mb->literal_split.num_types, mb->literal_split.types,
mb->literal_split.lengths, mb->literal_split.num_blocks); mb->literal_split.lengths, mb->literal_split.num_blocks);
InitBlockEncoder(&command_enc, BROTLI_NUM_COMMAND_SYMBOLS, InitBlockEncoder(command_enc, BROTLI_NUM_COMMAND_SYMBOLS,
mb->command_split.num_types, mb->command_split.types, mb->command_split.num_types, mb->command_split.types,
mb->command_split.lengths, mb->command_split.num_blocks); mb->command_split.lengths, mb->command_split.num_blocks);
InitBlockEncoder(&distance_enc, num_effective_distance_symbols, InitBlockEncoder(distance_enc, num_effective_distance_symbols,
mb->distance_split.num_types, mb->distance_split.types, mb->distance_split.num_types, mb->distance_split.types,
mb->distance_split.lengths, mb->distance_split.num_blocks); mb->distance_split.lengths, mb->distance_split.num_blocks);
BuildAndStoreBlockSwitchEntropyCodes(&literal_enc, tree, storage_ix, storage); BuildAndStoreBlockSwitchEntropyCodes(literal_enc, tree, storage_ix, storage);
BuildAndStoreBlockSwitchEntropyCodes(&command_enc, tree, storage_ix, storage); BuildAndStoreBlockSwitchEntropyCodes(command_enc, tree, storage_ix, storage);
BuildAndStoreBlockSwitchEntropyCodes( BuildAndStoreBlockSwitchEntropyCodes(distance_enc, tree, storage_ix, storage);
&distance_enc, tree, storage_ix, storage);
BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage); BrotliWriteBits(2, dist->distance_postfix_bits, storage_ix, storage);
BrotliWriteBits( BrotliWriteBits(
@ -980,34 +996,36 @@ void BrotliStoreMetaBlock(MemoryManager* m,
} }
if (mb->literal_context_map_size == 0) { if (mb->literal_context_map_size == 0) {
StoreTrivialContextMap(mb->literal_histograms_size, StoreTrivialContextMap(
&arena->context_map_arena, mb->literal_histograms_size,
BROTLI_LITERAL_CONTEXT_BITS, tree, storage_ix, storage); BROTLI_LITERAL_CONTEXT_BITS, tree, storage_ix, storage);
} else { } else {
EncodeContextMap(m, EncodeContextMap(m, &arena->context_map_arena,
mb->literal_context_map, mb->literal_context_map_size, mb->literal_context_map, mb->literal_context_map_size,
mb->literal_histograms_size, tree, storage_ix, storage); mb->literal_histograms_size, tree, storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
} }
if (mb->distance_context_map_size == 0) { if (mb->distance_context_map_size == 0) {
StoreTrivialContextMap(mb->distance_histograms_size, StoreTrivialContextMap(
&arena->context_map_arena, mb->distance_histograms_size,
BROTLI_DISTANCE_CONTEXT_BITS, tree, storage_ix, storage); BROTLI_DISTANCE_CONTEXT_BITS, tree, storage_ix, storage);
} else { } else {
EncodeContextMap(m, EncodeContextMap(m, &arena->context_map_arena,
mb->distance_context_map, mb->distance_context_map_size, mb->distance_context_map, mb->distance_context_map_size,
mb->distance_histograms_size, tree, storage_ix, storage); mb->distance_histograms_size, tree, storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
} }
BuildAndStoreEntropyCodesLiteral(m, &literal_enc, mb->literal_histograms, BuildAndStoreEntropyCodesLiteral(m, literal_enc, mb->literal_histograms,
mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree, mb->literal_histograms_size, BROTLI_NUM_LITERAL_SYMBOLS, tree,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
BuildAndStoreEntropyCodesCommand(m, &command_enc, mb->command_histograms, BuildAndStoreEntropyCodesCommand(m, command_enc, mb->command_histograms,
mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree, mb->command_histograms_size, BROTLI_NUM_COMMAND_SYMBOLS, tree,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
BuildAndStoreEntropyCodesDistance(m, &distance_enc, mb->distance_histograms, BuildAndStoreEntropyCodesDistance(m, distance_enc, mb->distance_histograms,
mb->distance_histograms_size, num_distance_symbols, tree, mb->distance_histograms_size, num_distance_symbols, tree,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
@ -1016,12 +1034,12 @@ void BrotliStoreMetaBlock(MemoryManager* m,
for (i = 0; i < n_commands; ++i) { for (i = 0; i < n_commands; ++i) {
const Command cmd = commands[i]; const Command cmd = commands[i];
size_t cmd_code = cmd.cmd_prefix_; size_t cmd_code = cmd.cmd_prefix_;
StoreSymbol(&command_enc, cmd_code, storage_ix, storage); StoreSymbol(command_enc, cmd_code, storage_ix, storage);
StoreCommandExtra(&cmd, storage_ix, storage); StoreCommandExtra(&cmd, storage_ix, storage);
if (mb->literal_context_map_size == 0) { if (mb->literal_context_map_size == 0) {
size_t j; size_t j;
for (j = cmd.insert_len_; j != 0; --j) { for (j = cmd.insert_len_; j != 0; --j) {
StoreSymbol(&literal_enc, input[pos & mask], storage_ix, storage); StoreSymbol(literal_enc, input[pos & mask], storage_ix, storage);
++pos; ++pos;
} }
} else { } else {
@ -1030,7 +1048,7 @@ void BrotliStoreMetaBlock(MemoryManager* m,
size_t context = size_t context =
BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
uint8_t literal = input[pos & mask]; uint8_t literal = input[pos & mask];
StoreSymbolWithContext(&literal_enc, literal, context, StoreSymbolWithContext(literal_enc, literal, context,
mb->literal_context_map, storage_ix, storage, mb->literal_context_map, storage_ix, storage,
BROTLI_LITERAL_CONTEXT_BITS); BROTLI_LITERAL_CONTEXT_BITS);
prev_byte2 = prev_byte; prev_byte2 = prev_byte;
@ -1047,10 +1065,10 @@ void BrotliStoreMetaBlock(MemoryManager* m,
uint32_t distnumextra = cmd.dist_prefix_ >> 10; uint32_t distnumextra = cmd.dist_prefix_ >> 10;
uint64_t distextra = cmd.dist_extra_; uint64_t distextra = cmd.dist_extra_;
if (mb->distance_context_map_size == 0) { if (mb->distance_context_map_size == 0) {
StoreSymbol(&distance_enc, dist_code, storage_ix, storage); StoreSymbol(distance_enc, dist_code, storage_ix, storage);
} else { } else {
size_t context = CommandDistanceContext(&cmd); size_t context = CommandDistanceContext(&cmd);
StoreSymbolWithContext(&distance_enc, dist_code, context, StoreSymbolWithContext(distance_enc, dist_code, context,
mb->distance_context_map, storage_ix, storage, mb->distance_context_map, storage_ix, storage,
BROTLI_DISTANCE_CONTEXT_BITS); BROTLI_DISTANCE_CONTEXT_BITS);
} }
@ -1058,9 +1076,10 @@ void BrotliStoreMetaBlock(MemoryManager* m,
} }
} }
} }
CleanupBlockEncoder(m, &distance_enc); CleanupBlockEncoder(m, distance_enc);
CleanupBlockEncoder(m, &command_enc); CleanupBlockEncoder(m, command_enc);
CleanupBlockEncoder(m, &literal_enc); CleanupBlockEncoder(m, literal_enc);
BROTLI_FREE(m, arena);
if (is_last) { if (is_last) {
JumpToByteBoundary(storage_ix, storage); JumpToByteBoundary(storage_ix, storage);
} }
@ -1131,54 +1150,60 @@ static void StoreDataWithHuffmanCodes(const uint8_t* input,
} }
} }
void BrotliStoreMetaBlockTrivial(MemoryManager* m, /* TODO: pull alloc/dealloc to caller? */
const uint8_t* input, size_t start_pos, size_t length, size_t mask, typedef struct MetablockArena {
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
const Command* commands, size_t n_commands,
size_t* storage_ix, uint8_t* storage) {
HistogramLiteral lit_histo; HistogramLiteral lit_histo;
HistogramCommand cmd_histo; HistogramCommand cmd_histo;
HistogramDistance dist_histo; HistogramDistance dist_histo;
/* TODO: merge bits and depth? */
uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS]; uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS]; uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS]; uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS]; uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE]; uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
HuffmanTree* tree; HuffmanTree tree[MAX_HUFFMAN_TREE_SIZE];
} MetablockArena;
void BrotliStoreMetaBlockTrivial(MemoryManager* m,
const uint8_t* input, size_t start_pos, size_t length, size_t mask,
BROTLI_BOOL is_last, const BrotliEncoderParams* params,
const Command* commands, size_t n_commands,
size_t* storage_ix, uint8_t* storage) {
MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1);
uint32_t num_distance_symbols = params->dist.alphabet_size_max; uint32_t num_distance_symbols = params->dist.alphabet_size_max;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
HistogramClearLiteral(&lit_histo); HistogramClearLiteral(&arena->lit_histo);
HistogramClearCommand(&cmd_histo); HistogramClearCommand(&arena->cmd_histo);
HistogramClearDistance(&dist_histo); HistogramClearDistance(&arena->dist_histo);
BuildHistograms(input, start_pos, mask, commands, n_commands, BuildHistograms(input, start_pos, mask, commands, n_commands,
&lit_histo, &cmd_histo, &dist_histo); &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo);
BrotliWriteBits(13, 0, storage_ix, storage); BrotliWriteBits(13, 0, storage_ix, storage);
tree = BROTLI_ALLOC(m, HuffmanTree, MAX_HUFFMAN_TREE_SIZE); BuildAndStoreHuffmanTree(arena->lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS,
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tree)) return; BROTLI_NUM_LITERAL_SYMBOLS, arena->tree,
BuildAndStoreHuffmanTree(lit_histo.data_, BROTLI_NUM_LITERAL_SYMBOLS, arena->lit_depth, arena->lit_bits,
BROTLI_NUM_LITERAL_SYMBOLS, tree,
lit_depth, lit_bits,
storage_ix, storage); storage_ix, storage);
BuildAndStoreHuffmanTree(cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS, BuildAndStoreHuffmanTree(arena->cmd_histo.data_, BROTLI_NUM_COMMAND_SYMBOLS,
BROTLI_NUM_COMMAND_SYMBOLS, tree, BROTLI_NUM_COMMAND_SYMBOLS, arena->tree,
cmd_depth, cmd_bits, arena->cmd_depth, arena->cmd_bits,
storage_ix, storage); storage_ix, storage);
BuildAndStoreHuffmanTree(dist_histo.data_, MAX_SIMPLE_DISTANCE_ALPHABET_SIZE, BuildAndStoreHuffmanTree(arena->dist_histo.data_,
num_distance_symbols, tree, MAX_SIMPLE_DISTANCE_ALPHABET_SIZE,
dist_depth, dist_bits, num_distance_symbols, arena->tree,
arena->dist_depth, arena->dist_bits,
storage_ix, storage); storage_ix, storage);
BROTLI_FREE(m, tree);
StoreDataWithHuffmanCodes(input, start_pos, mask, commands, StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
n_commands, lit_depth, lit_bits, n_commands, arena->lit_depth, arena->lit_bits,
cmd_depth, cmd_bits, arena->cmd_depth, arena->cmd_bits,
dist_depth, dist_bits, arena->dist_depth, arena->dist_bits,
storage_ix, storage); storage_ix, storage);
BROTLI_FREE(m, arena);
if (is_last) { if (is_last) {
JumpToByteBoundary(storage_ix, storage); JumpToByteBoundary(storage_ix, storage);
} }
@ -1189,9 +1214,11 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
BROTLI_BOOL is_last, const BrotliEncoderParams* params, BROTLI_BOOL is_last, const BrotliEncoderParams* params,
const Command* commands, size_t n_commands, const Command* commands, size_t n_commands,
size_t* storage_ix, uint8_t* storage) { size_t* storage_ix, uint8_t* storage) {
MetablockArena* arena = BROTLI_ALLOC(m, MetablockArena, 1);
uint32_t num_distance_symbols = params->dist.alphabet_size_max; uint32_t num_distance_symbols = params->dist.alphabet_size_max;
uint32_t distance_alphabet_bits = uint32_t distance_alphabet_bits =
Log2FloorNonZero(num_distance_symbols - 1) + 1; Log2FloorNonZero(num_distance_symbols - 1) + 1;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage); StoreCompressedMetaBlockHeader(is_last, length, storage_ix, storage);
@ -1202,8 +1229,6 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
size_t pos = start_pos; size_t pos = start_pos;
size_t num_literals = 0; size_t num_literals = 0;
size_t i; size_t i;
uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
for (i = 0; i < n_commands; ++i) { for (i = 0; i < n_commands; ++i) {
const Command cmd = commands[i]; const Command cmd = commands[i];
size_t j; size_t j;
@ -1214,61 +1239,50 @@ void BrotliStoreMetaBlockFast(MemoryManager* m,
num_literals += cmd.insert_len_; num_literals += cmd.insert_len_;
pos += CommandCopyLen(&cmd); pos += CommandCopyLen(&cmd);
} }
BrotliBuildAndStoreHuffmanTreeFast(m, histogram, num_literals, BrotliBuildAndStoreHuffmanTreeFast(arena->tree, histogram, num_literals,
/* max_bits = */ 8, /* max_bits = */ 8,
lit_depth, lit_bits, arena->lit_depth, arena->lit_bits,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return;
StoreStaticCommandHuffmanTree(storage_ix, storage); StoreStaticCommandHuffmanTree(storage_ix, storage);
StoreStaticDistanceHuffmanTree(storage_ix, storage); StoreStaticDistanceHuffmanTree(storage_ix, storage);
StoreDataWithHuffmanCodes(input, start_pos, mask, commands, StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
n_commands, lit_depth, lit_bits, n_commands, arena->lit_depth, arena->lit_bits,
kStaticCommandCodeDepth, kStaticCommandCodeDepth,
kStaticCommandCodeBits, kStaticCommandCodeBits,
kStaticDistanceCodeDepth, kStaticDistanceCodeDepth,
kStaticDistanceCodeBits, kStaticDistanceCodeBits,
storage_ix, storage); storage_ix, storage);
} else { } else {
HistogramLiteral lit_histo; HistogramClearLiteral(&arena->lit_histo);
HistogramCommand cmd_histo; HistogramClearCommand(&arena->cmd_histo);
HistogramDistance dist_histo; HistogramClearDistance(&arena->dist_histo);
uint8_t lit_depth[BROTLI_NUM_LITERAL_SYMBOLS];
uint16_t lit_bits[BROTLI_NUM_LITERAL_SYMBOLS];
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS];
uint16_t cmd_bits[BROTLI_NUM_COMMAND_SYMBOLS];
uint8_t dist_depth[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
uint16_t dist_bits[MAX_SIMPLE_DISTANCE_ALPHABET_SIZE];
HistogramClearLiteral(&lit_histo);
HistogramClearCommand(&cmd_histo);
HistogramClearDistance(&dist_histo);
BuildHistograms(input, start_pos, mask, commands, n_commands, BuildHistograms(input, start_pos, mask, commands, n_commands,
&lit_histo, &cmd_histo, &dist_histo); &arena->lit_histo, &arena->cmd_histo, &arena->dist_histo);
BrotliBuildAndStoreHuffmanTreeFast(m, lit_histo.data_, BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->lit_histo.data_,
lit_histo.total_count_, arena->lit_histo.total_count_,
/* max_bits = */ 8, /* max_bits = */ 8,
lit_depth, lit_bits, arena->lit_depth, arena->lit_bits,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->cmd_histo.data_,
BrotliBuildAndStoreHuffmanTreeFast(m, cmd_histo.data_, arena->cmd_histo.total_count_,
cmd_histo.total_count_,
/* max_bits = */ 10, /* max_bits = */ 10,
cmd_depth, cmd_bits, arena->cmd_depth, arena->cmd_bits,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; BrotliBuildAndStoreHuffmanTreeFast(arena->tree, arena->dist_histo.data_,
BrotliBuildAndStoreHuffmanTreeFast(m, dist_histo.data_, arena->dist_histo.total_count_,
dist_histo.total_count_,
/* max_bits = */ /* max_bits = */
distance_alphabet_bits, distance_alphabet_bits,
dist_depth, dist_bits, arena->dist_depth, arena->dist_bits,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return;
StoreDataWithHuffmanCodes(input, start_pos, mask, commands, StoreDataWithHuffmanCodes(input, start_pos, mask, commands,
n_commands, lit_depth, lit_bits, n_commands, arena->lit_depth, arena->lit_bits,
cmd_depth, cmd_bits, arena->cmd_depth, arena->cmd_bits,
dist_depth, dist_bits, arena->dist_depth, arena->dist_bits,
storage_ix, storage); storage_ix, storage);
} }
BROTLI_FREE(m, arena);
if (is_last) { if (is_last) {
JumpToByteBoundary(storage_ix, storage); JumpToByteBoundary(storage_ix, storage);
} }

View File

@ -35,7 +35,7 @@ BROTLI_INTERNAL void BrotliStoreHuffmanTree(const uint8_t* depths, size_t num,
HuffmanTree* tree, size_t* storage_ix, uint8_t* storage); HuffmanTree* tree, size_t* storage_ix, uint8_t* storage);
BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast( BROTLI_INTERNAL void BrotliBuildAndStoreHuffmanTreeFast(
MemoryManager* m, const uint32_t* histogram, const size_t histogram_total, HuffmanTree* tree, const uint32_t* histogram, const size_t histogram_total,
const size_t max_bits, uint8_t* depth, uint16_t* bits, size_t* storage_ix, const size_t max_bits, uint8_t* depth, uint16_t* bits, size_t* storage_ix,
uint8_t* storage); uint8_t* storage);

View File

@ -12,8 +12,8 @@
/* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if /* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if
it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */ it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */
BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)( BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
const HistogramType* out, const uint32_t* cluster_size, uint32_t idx1, const HistogramType* out, HistogramType* tmp, const uint32_t* cluster_size,
uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs, uint32_t idx1, uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs,
size_t* num_pairs) CODE({ size_t* num_pairs) CODE({
BROTLI_BOOL is_good_pair = BROTLI_FALSE; BROTLI_BOOL is_good_pair = BROTLI_FALSE;
HistogramPair p; HistogramPair p;
@ -42,10 +42,10 @@ BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
} else { } else {
double threshold = *num_pairs == 0 ? 1e99 : double threshold = *num_pairs == 0 ? 1e99 :
BROTLI_MAX(double, 0.0, pairs[0].cost_diff); BROTLI_MAX(double, 0.0, pairs[0].cost_diff);
HistogramType combo = out[idx1];
double cost_combo; double cost_combo;
FN(HistogramAddHistogram)(&combo, &out[idx2]); *tmp = out[idx1];
cost_combo = FN(BrotliPopulationCost)(&combo); FN(HistogramAddHistogram)(tmp, &out[idx2]);
cost_combo = FN(BrotliPopulationCost)(tmp);
if (cost_combo < threshold - p.cost_diff) { if (cost_combo < threshold - p.cost_diff) {
p.cost_combo = cost_combo; p.cost_combo = cost_combo;
is_good_pair = BROTLI_TRUE; is_good_pair = BROTLI_TRUE;
@ -68,6 +68,7 @@ BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
}) })
BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out, BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
HistogramType* tmp,
uint32_t* cluster_size, uint32_t* cluster_size,
uint32_t* symbols, uint32_t* symbols,
uint32_t* clusters, uint32_t* clusters,
@ -87,7 +88,7 @@ BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
for (idx1 = 0; idx1 < num_clusters; ++idx1) { for (idx1 = 0; idx1 < num_clusters; ++idx1) {
size_t idx2; size_t idx2;
for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) { for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) {
FN(BrotliCompareAndPushToQueue)(out, cluster_size, clusters[idx1], FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, clusters[idx1],
clusters[idx2], max_num_pairs, &pairs[0], &num_pairs); clusters[idx2], max_num_pairs, &pairs[0], &num_pairs);
} }
} }
@ -146,8 +147,8 @@ BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
/* Push new pairs formed with the combined histogram to the heap. */ /* Push new pairs formed with the combined histogram to the heap. */
for (i = 0; i < num_clusters; ++i) { for (i = 0; i < num_clusters; ++i) {
FN(BrotliCompareAndPushToQueue)(out, cluster_size, best_idx1, clusters[i], FN(BrotliCompareAndPushToQueue)(out, tmp, cluster_size, best_idx1,
max_num_pairs, &pairs[0], &num_pairs); clusters[i], max_num_pairs, &pairs[0], &num_pairs);
} }
} }
return num_clusters; return num_clusters;
@ -155,13 +156,14 @@ BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
/* What is the bit cost of moving histogram from cur_symbol to candidate. */ /* What is the bit cost of moving histogram from cur_symbol to candidate. */
BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)( BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)(
const HistogramType* histogram, const HistogramType* candidate) CODE({ const HistogramType* histogram, const HistogramType* candidate,
HistogramType* tmp) CODE({
if (histogram->total_count_ == 0) { if (histogram->total_count_ == 0) {
return 0.0; return 0.0;
} else { } else {
HistogramType tmp = *histogram; *tmp = *histogram;
FN(HistogramAddHistogram)(&tmp, candidate); FN(HistogramAddHistogram)(tmp, candidate);
return FN(BrotliPopulationCost)(&tmp) - candidate->bit_cost_; return FN(BrotliPopulationCost)(tmp) - candidate->bit_cost_;
} }
}) })
@ -171,16 +173,16 @@ BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)(
Note: we assume that out[]->bit_cost_ is already up-to-date. */ Note: we assume that out[]->bit_cost_ is already up-to-date. */
BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in, BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in,
size_t in_size, const uint32_t* clusters, size_t num_clusters, size_t in_size, const uint32_t* clusters, size_t num_clusters,
HistogramType* out, uint32_t* symbols) CODE({ HistogramType* out, HistogramType* tmp, uint32_t* symbols) CODE({
size_t i; size_t i;
for (i = 0; i < in_size; ++i) { for (i = 0; i < in_size; ++i) {
uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1]; uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1];
double best_bits = double best_bits =
FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out]); FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out], tmp);
size_t j; size_t j;
for (j = 0; j < num_clusters; ++j) { for (j = 0; j < num_clusters; ++j) {
const double cur_bits = const double cur_bits =
FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]]); FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]], tmp);
if (cur_bits < best_bits) { if (cur_bits < best_bits) {
best_bits = cur_bits; best_bits = cur_bits;
best_out = clusters[j]; best_out = clusters[j];
@ -257,10 +259,12 @@ BROTLI_INTERNAL void FN(BrotliClusterHistograms)(
size_t pairs_capacity = max_input_histograms * max_input_histograms / 2; size_t pairs_capacity = max_input_histograms * max_input_histograms / 2;
/* For the first pass of clustering, we allow all pairs. */ /* For the first pass of clustering, we allow all pairs. */
HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1); HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1);
/* TODO: move to "persistent" arena? */
HistogramType* tmp = BROTLI_ALLOC(m, HistogramType, 1);
size_t i; size_t i;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) || if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) ||
BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)) { BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)|| BROTLI_IS_NULL(tmp)) {
return; return;
} }
@ -283,7 +287,7 @@ BROTLI_INTERNAL void FN(BrotliClusterHistograms)(
clusters[num_clusters + j] = (uint32_t)(i + j); clusters[num_clusters + j] = (uint32_t)(i + j);
} }
num_new_clusters = num_new_clusters =
FN(BrotliHistogramCombine)(out, cluster_size, FN(BrotliHistogramCombine)(out, tmp, cluster_size,
&histogram_symbols[i], &histogram_symbols[i],
&clusters[num_clusters], pairs, &clusters[num_clusters], pairs,
num_to_combine, num_to_combine, num_to_combine, num_to_combine,
@ -301,7 +305,7 @@ BROTLI_INTERNAL void FN(BrotliClusterHistograms)(
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
/* Collapse similar histograms. */ /* Collapse similar histograms. */
num_clusters = FN(BrotliHistogramCombine)(out, cluster_size, num_clusters = FN(BrotliHistogramCombine)(out, tmp, cluster_size,
histogram_symbols, clusters, histogram_symbols, clusters,
pairs, num_clusters, in_size, pairs, num_clusters, in_size,
max_histograms, max_num_pairs); max_histograms, max_num_pairs);
@ -310,7 +314,8 @@ BROTLI_INTERNAL void FN(BrotliClusterHistograms)(
BROTLI_FREE(m, cluster_size); BROTLI_FREE(m, cluster_size);
/* Find the optimal map from original histograms to the final ones. */ /* Find the optimal map from original histograms to the final ones. */
FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters, FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters,
out, histogram_symbols); out, tmp, histogram_symbols);
BROTLI_FREE(m, tmp);
BROTLI_FREE(m, clusters); BROTLI_FREE(m, clusters);
/* Convert the context map to a canonical form. */ /* Convert the context map to a canonical form. */
*out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size); *out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size);

View File

@ -16,14 +16,12 @@
#include <string.h> /* memcmp, memcpy, memset */ #include <string.h> /* memcmp, memcpy, memset */
#include "../common/constants.h"
#include "../common/platform.h" #include "../common/platform.h"
#include <brotli/types.h> #include <brotli/types.h>
#include "./brotli_bit_stream.h" #include "./brotli_bit_stream.h"
#include "./entropy_encode.h" #include "./entropy_encode.h"
#include "./fast_log.h" #include "./fast_log.h"
#include "./find_match_length.h" #include "./find_match_length.h"
#include "./memory.h"
#include "./write_bits.h" #include "./write_bits.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
@ -69,16 +67,18 @@ static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2) {
and thus have to assign a non-zero depth for each literal. and thus have to assign a non-zero depth for each literal.
Returns estimated compression ratio millibytes/char for encoding given input Returns estimated compression ratio millibytes/char for encoding given input
with generated code. */ with generated code. */
static size_t BuildAndStoreLiteralPrefixCode(MemoryManager* m, static size_t BuildAndStoreLiteralPrefixCode(BrotliOnePassArena* s,
const uint8_t* input, const uint8_t* input,
const size_t input_size, const size_t input_size,
uint8_t depths[256], uint8_t depths[256],
uint16_t bits[256], uint16_t bits[256],
size_t* storage_ix, size_t* storage_ix,
uint8_t* storage) { uint8_t* storage) {
uint32_t histogram[256] = { 0 }; uint32_t* BROTLI_RESTRICT const histogram = s->histogram;
size_t histogram_total; size_t histogram_total;
size_t i; size_t i;
memset(histogram, 0, sizeof(s->histogram));
if (input_size < (1 << 15)) { if (input_size < (1 << 15)) {
for (i = 0; i < input_size; ++i) { for (i = 0; i < input_size; ++i) {
++histogram[input[i]]; ++histogram[input[i]];
@ -108,10 +108,9 @@ static size_t BuildAndStoreLiteralPrefixCode(MemoryManager* m,
histogram_total += adjust; histogram_total += adjust;
} }
} }
BrotliBuildAndStoreHuffmanTreeFast(m, histogram, histogram_total, BrotliBuildAndStoreHuffmanTreeFast(s->tree, histogram, histogram_total,
/* max_bits = */ 8, /* max_bits = */ 8,
depths, bits, storage_ix, storage); depths, bits, storage_ix, storage);
if (BROTLI_IS_OOM(m)) return 0;
{ {
size_t literal_ratio = 0; size_t literal_ratio = 0;
for (i = 0; i < 256; ++i) { for (i = 0; i < 256; ++i) {
@ -124,53 +123,56 @@ static size_t BuildAndStoreLiteralPrefixCode(MemoryManager* m,
/* Builds a command and distance prefix code (each 64 symbols) into "depth" and /* Builds a command and distance prefix code (each 64 symbols) into "depth" and
"bits" based on "histogram" and stores it into the bit stream. */ "bits" based on "histogram" and stores it into the bit stream. */
static void BuildAndStoreCommandPrefixCode(const uint32_t histogram[128], static void BuildAndStoreCommandPrefixCode(BrotliOnePassArena* s,
uint8_t depth[128], uint16_t bits[128], size_t* storage_ix, size_t* storage_ix, uint8_t* storage) {
uint8_t* storage) { const uint32_t* const histogram = s->cmd_histo;
/* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ uint8_t* const depth = s->cmd_depth;
HuffmanTree tree[129]; uint16_t* const bits = s->cmd_bits;
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS] = { 0 }; uint8_t* BROTLI_RESTRICT const tmp_depth = s->tmp_depth;
uint16_t cmd_bits[64]; uint16_t* BROTLI_RESTRICT const tmp_bits = s->tmp_bits;
/* TODO: do only once on initialization. */
memset(tmp_depth, 0, BROTLI_NUM_COMMAND_SYMBOLS);
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth); BrotliCreateHuffmanTree(histogram, 64, 15, s->tree, depth);
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]); BrotliCreateHuffmanTree(&histogram[64], 64, 14, s->tree, &depth[64]);
/* We have to jump through a few hoops here in order to compute /* We have to jump through a few hoops here in order to compute
the command bits because the symbols are in a different order than in the command bits because the symbols are in a different order than in
the full alphabet. This looks complicated, but having the symbols the full alphabet. This looks complicated, but having the symbols
in this order in the command bits saves a few branches in the Emit* in this order in the command bits saves a few branches in the Emit*
functions. */ functions. */
memcpy(cmd_depth, depth, 24); memcpy(tmp_depth, depth, 24);
memcpy(cmd_depth + 24, depth + 40, 8); memcpy(tmp_depth + 24, depth + 40, 8);
memcpy(cmd_depth + 32, depth + 24, 8); memcpy(tmp_depth + 32, depth + 24, 8);
memcpy(cmd_depth + 40, depth + 48, 8); memcpy(tmp_depth + 40, depth + 48, 8);
memcpy(cmd_depth + 48, depth + 32, 8); memcpy(tmp_depth + 48, depth + 32, 8);
memcpy(cmd_depth + 56, depth + 56, 8); memcpy(tmp_depth + 56, depth + 56, 8);
BrotliConvertBitDepthsToSymbols(cmd_depth, 64, cmd_bits); BrotliConvertBitDepthsToSymbols(tmp_depth, 64, tmp_bits);
memcpy(bits, cmd_bits, 48); memcpy(bits, tmp_bits, 48);
memcpy(bits + 24, cmd_bits + 32, 16); memcpy(bits + 24, tmp_bits + 32, 16);
memcpy(bits + 32, cmd_bits + 48, 16); memcpy(bits + 32, tmp_bits + 48, 16);
memcpy(bits + 40, cmd_bits + 24, 16); memcpy(bits + 40, tmp_bits + 24, 16);
memcpy(bits + 48, cmd_bits + 40, 16); memcpy(bits + 48, tmp_bits + 40, 16);
memcpy(bits + 56, cmd_bits + 56, 16); memcpy(bits + 56, tmp_bits + 56, 16);
BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]); BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]);
{ {
/* Create the bit length array for the full command alphabet. */ /* Create the bit length array for the full command alphabet. */
size_t i; size_t i;
memset(cmd_depth, 0, 64); /* only 64 first values were used */ memset(tmp_depth, 0, 64); /* only 64 first values were used */
memcpy(cmd_depth, depth, 8); memcpy(tmp_depth, depth, 8);
memcpy(cmd_depth + 64, depth + 8, 8); memcpy(tmp_depth + 64, depth + 8, 8);
memcpy(cmd_depth + 128, depth + 16, 8); memcpy(tmp_depth + 128, depth + 16, 8);
memcpy(cmd_depth + 192, depth + 24, 8); memcpy(tmp_depth + 192, depth + 24, 8);
memcpy(cmd_depth + 384, depth + 32, 8); memcpy(tmp_depth + 384, depth + 32, 8);
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
cmd_depth[128 + 8 * i] = depth[40 + i]; tmp_depth[128 + 8 * i] = depth[40 + i];
cmd_depth[256 + 8 * i] = depth[48 + i]; tmp_depth[256 + 8 * i] = depth[48 + i];
cmd_depth[448 + 8 * i] = depth[56 + i]; tmp_depth[448 + 8 * i] = depth[56 + i];
} }
/* TODO: could/should full-length machinery be avoided? */
BrotliStoreHuffmanTree( BrotliStoreHuffmanTree(
cmd_depth, BROTLI_NUM_COMMAND_SYMBOLS, tree, storage_ix, storage); tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS, s->tree, storage_ix, storage);
} }
BrotliStoreHuffmanTree(&depth[64], 64, tree, storage_ix, storage); BrotliStoreHuffmanTree(&depth[64], 64, s->tree, storage_ix, storage);
} }
/* REQUIRES: insertlen < 6210 */ /* REQUIRES: insertlen < 6210 */
@ -369,11 +371,12 @@ static void RewindBitPosition(const size_t new_storage_ix,
*storage_ix = new_storage_ix; *storage_ix = new_storage_ix;
} }
static BROTLI_BOOL ShouldMergeBlock( static BROTLI_BOOL ShouldMergeBlock(BrotliOnePassArena* s,
const uint8_t* data, size_t len, const uint8_t* depths) { const uint8_t* data, size_t len, const uint8_t* depths) {
size_t histo[256] = { 0 }; uint32_t* BROTLI_RESTRICT const histo = s->histogram;
static const size_t kSampleRate = 43; static const size_t kSampleRate = 43;
size_t i; size_t i;
memset(histo, 0, sizeof(s->histogram));
for (i = 0; i < len; i += kSampleRate) { for (i = 0; i < len; i += kSampleRate) {
++histo[data[i]]; ++histo[data[i]];
} }
@ -423,11 +426,14 @@ static uint32_t kCmdHistoSeed[128] = {
}; };
static BROTLI_INLINE void BrotliCompressFragmentFastImpl( static BROTLI_INLINE void BrotliCompressFragmentFastImpl(
MemoryManager* m, const uint8_t* input, size_t input_size, BrotliOnePassArena* s, const uint8_t* input, size_t input_size,
BROTLI_BOOL is_last, int* table, size_t table_bits, uint8_t cmd_depth[128], BROTLI_BOOL is_last, int* table, size_t table_bits,
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
size_t* storage_ix, uint8_t* storage) { size_t* storage_ix, uint8_t* storage) {
uint32_t cmd_histo[128]; uint8_t* BROTLI_RESTRICT const cmd_depth = s->cmd_depth;
uint16_t* BROTLI_RESTRICT const cmd_bits = s->cmd_bits;
uint32_t* BROTLI_RESTRICT const cmd_histo = s->cmd_histo;
uint8_t* BROTLI_RESTRICT const lit_depth = s->lit_depth;
uint16_t* BROTLI_RESTRICT const lit_bits = s->lit_bits;
const uint8_t* ip_end; const uint8_t* ip_end;
/* "next_emit" is a pointer to the first byte that is not covered by a /* "next_emit" is a pointer to the first byte that is not covered by a
@ -451,9 +457,6 @@ static BROTLI_INLINE void BrotliCompressFragmentFastImpl(
we can update it later if we decide to extend this meta-block. */ we can update it later if we decide to extend this meta-block. */
size_t mlen_storage_ix = *storage_ix + 3; size_t mlen_storage_ix = *storage_ix + 3;
uint8_t lit_depth[256];
uint16_t lit_bits[256];
size_t literal_ratio; size_t literal_ratio;
const uint8_t* ip; const uint8_t* ip;
@ -466,25 +469,24 @@ static BROTLI_INLINE void BrotliCompressFragmentFastImpl(
BrotliWriteBits(13, 0, storage_ix, storage); BrotliWriteBits(13, 0, storage_ix, storage);
literal_ratio = BuildAndStoreLiteralPrefixCode( literal_ratio = BuildAndStoreLiteralPrefixCode(
m, input, block_size, lit_depth, lit_bits, storage_ix, storage); s, input, block_size, s->lit_depth, s->lit_bits, storage_ix, storage);
if (BROTLI_IS_OOM(m)) return;
{ {
/* Store the pre-compressed command and distance prefix codes. */ /* Store the pre-compressed command and distance prefix codes. */
size_t i; size_t i;
for (i = 0; i + 7 < *cmd_code_numbits; i += 8) { for (i = 0; i + 7 < s->cmd_code_numbits; i += 8) {
BrotliWriteBits(8, cmd_code[i >> 3], storage_ix, storage); BrotliWriteBits(8, s->cmd_code[i >> 3], storage_ix, storage);
} }
} }
BrotliWriteBits(*cmd_code_numbits & 7, cmd_code[*cmd_code_numbits >> 3], BrotliWriteBits(s->cmd_code_numbits & 7,
storage_ix, storage); s->cmd_code[s->cmd_code_numbits >> 3], storage_ix, storage);
emit_commands: emit_commands:
/* Initialize the command and distance histograms. We will gather /* Initialize the command and distance histograms. We will gather
statistics of command and distance codes during the processing statistics of command and distance codes during the processing
of this block and use it to update the command and distance of this block and use it to update the command and distance
prefix codes for the next block. */ prefix codes for the next block. */
memcpy(cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed)); memcpy(s->cmd_histo, kCmdHistoSeed, sizeof(kCmdHistoSeed));
/* "ip" is the input pointer. */ /* "ip" is the input pointer. */
ip = input; ip = input;
@ -667,7 +669,7 @@ trawl:
last insert-only command. */ last insert-only command. */
if (input_size > 0 && if (input_size > 0 &&
total_block_size + block_size <= (1 << 20) && total_block_size + block_size <= (1 << 20) &&
ShouldMergeBlock(input, block_size, lit_depth)) { ShouldMergeBlock(s, input, block_size, lit_depth)) {
BROTLI_DCHECK(total_block_size > (1 << 16)); BROTLI_DCHECK(total_block_size > (1 << 16));
/* Update the size of the current meta-block and continue emitting commands. /* Update the size of the current meta-block and continue emitting commands.
We can do this because the current size and the new size both have 5 We can do this because the current size and the new size both have 5
@ -711,20 +713,17 @@ next_block:
/* No block splits, no contexts. */ /* No block splits, no contexts. */
BrotliWriteBits(13, 0, storage_ix, storage); BrotliWriteBits(13, 0, storage_ix, storage);
literal_ratio = BuildAndStoreLiteralPrefixCode( literal_ratio = BuildAndStoreLiteralPrefixCode(
m, input, block_size, lit_depth, lit_bits, storage_ix, storage); s, input, block_size, lit_depth, lit_bits, storage_ix, storage);
if (BROTLI_IS_OOM(m)) return; BuildAndStoreCommandPrefixCode(s, storage_ix, storage);
BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depth, cmd_bits,
storage_ix, storage);
goto emit_commands; goto emit_commands;
} }
if (!is_last) { if (!is_last) {
/* If this is not the last block, update the command and distance prefix /* If this is not the last block, update the command and distance prefix
codes for the next block and store the compressed forms. */ codes for the next block and store the compressed forms. */
cmd_code[0] = 0; s->cmd_code[0] = 0;
*cmd_code_numbits = 0; s->cmd_code_numbits = 0;
BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depth, cmd_bits, BuildAndStoreCommandPrefixCode(s, &s->cmd_code_numbits, s->cmd_code);
cmd_code_numbits, cmd_code);
} }
} }
@ -732,20 +731,17 @@ next_block:
#define BAKE_METHOD_PARAM_(B) \ #define BAKE_METHOD_PARAM_(B) \
static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B( \ static BROTLI_NOINLINE void BrotliCompressFragmentFastImpl ## B( \
MemoryManager* m, const uint8_t* input, size_t input_size, \ BrotliOnePassArena* s, const uint8_t* input, size_t input_size, \
BROTLI_BOOL is_last, int* table, uint8_t cmd_depth[128], \ BROTLI_BOOL is_last, int* table, size_t* storage_ix, uint8_t* storage) { \
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code, \ BrotliCompressFragmentFastImpl(s, input, input_size, is_last, table, B, \
size_t* storage_ix, uint8_t* storage) { \ storage_ix, storage); \
BrotliCompressFragmentFastImpl(m, input, input_size, is_last, table, B, \
cmd_depth, cmd_bits, cmd_code_numbits, cmd_code, storage_ix, storage); \
} }
FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
#undef BAKE_METHOD_PARAM_ #undef BAKE_METHOD_PARAM_
void BrotliCompressFragmentFast( void BrotliCompressFragmentFast(
MemoryManager* m, const uint8_t* input, size_t input_size, BrotliOnePassArena* s, const uint8_t* input, size_t input_size,
BROTLI_BOOL is_last, int* table, size_t table_size, uint8_t cmd_depth[128], BROTLI_BOOL is_last, int* table, size_t table_size,
uint16_t cmd_bits[128], size_t* cmd_code_numbits, uint8_t* cmd_code,
size_t* storage_ix, uint8_t* storage) { size_t* storage_ix, uint8_t* storage) {
const size_t initial_storage_ix = *storage_ix; const size_t initial_storage_ix = *storage_ix;
const size_t table_bits = Log2FloorNonZero(table_size); const size_t table_bits = Log2FloorNonZero(table_size);
@ -762,8 +758,7 @@ void BrotliCompressFragmentFast(
#define CASE_(B) \ #define CASE_(B) \
case B: \ case B: \
BrotliCompressFragmentFastImpl ## B( \ BrotliCompressFragmentFastImpl ## B( \
m, input, input_size, is_last, table, cmd_depth, cmd_bits, \ s, input, input_size, is_last, table, storage_ix, storage);\
cmd_code_numbits, cmd_code, storage_ix, storage); \
break; break;
FOR_TABLE_BITS_(CASE_) FOR_TABLE_BITS_(CASE_)
#undef CASE_ #undef CASE_

View File

@ -12,14 +12,42 @@
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_ #ifndef BROTLI_ENC_COMPRESS_FRAGMENT_H_
#define BROTLI_ENC_COMPRESS_FRAGMENT_H_ #define BROTLI_ENC_COMPRESS_FRAGMENT_H_
#include "../common/constants.h"
#include "../common/platform.h" #include "../common/platform.h"
#include <brotli/types.h> #include <brotli/types.h>
#include "./memory.h" #include "./entropy_encode.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
typedef struct BrotliOnePassArena {
uint8_t lit_depth[256];
uint16_t lit_bits[256];
/* Command and distance prefix codes (each 64 symbols, stored back-to-back)
used for the next block. The command prefix code is over a smaller alphabet
with the following 64 symbols:
0 - 15: insert length code 0, copy length code 0 - 15, same distance
16 - 39: insert length code 0, copy length code 0 - 23
40 - 63: insert length code 0 - 23, copy length code 0
Note that symbols 16 and 40 represent the same code in the full alphabet,
but we do not use either of them. */
uint8_t cmd_depth[128];
uint16_t cmd_bits[128];
uint32_t cmd_histo[128];
/* The compressed form of the command and distance prefix codes for the next
block. */
uint8_t cmd_code[512];
size_t cmd_code_numbits;
HuffmanTree tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1];
uint32_t histogram[256];
uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS];
uint16_t tmp_bits[64];
} BrotliOnePassArena;
/* Compresses "input" string to the "*storage" buffer as one or more complete /* Compresses "input" string to the "*storage" buffer as one or more complete
meta-blocks, and updates the "*storage_ix" bit position. meta-blocks, and updates the "*storage_ix" bit position.
@ -42,15 +70,11 @@ extern "C" {
REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two REQUIRES: "table_size" is an odd (9, 11, 13, 15) power of two
OUTPUT: maximal copy distance <= |input_size| OUTPUT: maximal copy distance <= |input_size|
OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
BROTLI_INTERNAL void BrotliCompressFragmentFast(MemoryManager* m, BROTLI_INTERNAL void BrotliCompressFragmentFast(BrotliOnePassArena* s,
const uint8_t* input, const uint8_t* input,
size_t input_size, size_t input_size,
BROTLI_BOOL is_last, BROTLI_BOOL is_last,
int* table, size_t table_size, int* table, size_t table_size,
uint8_t cmd_depth[128],
uint16_t cmd_bits[128],
size_t* cmd_code_numbits,
uint8_t* cmd_code,
size_t* storage_ix, size_t* storage_ix,
uint8_t* storage); uint8_t* storage);

View File

@ -22,7 +22,6 @@
#include "./entropy_encode.h" #include "./entropy_encode.h"
#include "./fast_log.h" #include "./fast_log.h"
#include "./find_match_length.h" #include "./find_match_length.h"
#include "./memory.h"
#include "./write_bits.h" #include "./write_bits.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
@ -66,53 +65,53 @@ static BROTLI_INLINE BROTLI_BOOL IsMatch(const uint8_t* p1, const uint8_t* p2,
/* Builds a command and distance prefix code (each 64 symbols) into "depth" and /* Builds a command and distance prefix code (each 64 symbols) into "depth" and
"bits" based on "histogram" and stores it into the bit stream. */ "bits" based on "histogram" and stores it into the bit stream. */
static void BuildAndStoreCommandPrefixCode( static void BuildAndStoreCommandPrefixCode(BrotliTwoPassArena* s,
const uint32_t histogram[128], size_t* storage_ix,
uint8_t depth[128], uint16_t bits[128], uint8_t* storage) {
size_t* storage_ix, uint8_t* storage) {
/* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */ /* Tree size for building a tree over 64 symbols is 2 * 64 + 1. */
HuffmanTree tree[129]; /* TODO: initialize once. */
uint8_t cmd_depth[BROTLI_NUM_COMMAND_SYMBOLS] = { 0 }; memset(s->tmp_depth, 0, sizeof(s->tmp_depth));
uint16_t cmd_bits[64]; BrotliCreateHuffmanTree(s->cmd_histo, 64, 15, s->tmp_tree, s->cmd_depth);
BrotliCreateHuffmanTree(histogram, 64, 15, tree, depth); BrotliCreateHuffmanTree(&s->cmd_histo[64], 64, 14, s->tmp_tree,
BrotliCreateHuffmanTree(&histogram[64], 64, 14, tree, &depth[64]); &s->cmd_depth[64]);
/* We have to jump through a few hoops here in order to compute /* We have to jump through a few hoops here in order to compute
the command bits because the symbols are in a different order than in the command bits because the symbols are in a different order than in
the full alphabet. This looks complicated, but having the symbols the full alphabet. This looks complicated, but having the symbols
in this order in the command bits saves a few branches in the Emit* in this order in the command bits saves a few branches in the Emit*
functions. */ functions. */
memcpy(cmd_depth, depth + 24, 24); memcpy(s->tmp_depth, s->cmd_depth + 24, 24);
memcpy(cmd_depth + 24, depth, 8); memcpy(s->tmp_depth + 24, s->cmd_depth, 8);
memcpy(cmd_depth + 32, depth + 48, 8); memcpy(s->tmp_depth + 32, s->cmd_depth + 48, 8);
memcpy(cmd_depth + 40, depth + 8, 8); memcpy(s->tmp_depth + 40, s->cmd_depth + 8, 8);
memcpy(cmd_depth + 48, depth + 56, 8); memcpy(s->tmp_depth + 48, s->cmd_depth + 56, 8);
memcpy(cmd_depth + 56, depth + 16, 8); memcpy(s->tmp_depth + 56, s->cmd_depth + 16, 8);
BrotliConvertBitDepthsToSymbols(cmd_depth, 64, cmd_bits); BrotliConvertBitDepthsToSymbols(s->tmp_depth, 64, s->tmp_bits);
memcpy(bits, cmd_bits + 24, 16); memcpy(s->cmd_bits, s->tmp_bits + 24, 16);
memcpy(bits + 8, cmd_bits + 40, 16); memcpy(s->cmd_bits + 8, s->tmp_bits + 40, 16);
memcpy(bits + 16, cmd_bits + 56, 16); memcpy(s->cmd_bits + 16, s->tmp_bits + 56, 16);
memcpy(bits + 24, cmd_bits, 48); memcpy(s->cmd_bits + 24, s->tmp_bits, 48);
memcpy(bits + 48, cmd_bits + 32, 16); memcpy(s->cmd_bits + 48, s->tmp_bits + 32, 16);
memcpy(bits + 56, cmd_bits + 48, 16); memcpy(s->cmd_bits + 56, s->tmp_bits + 48, 16);
BrotliConvertBitDepthsToSymbols(&depth[64], 64, &bits[64]); BrotliConvertBitDepthsToSymbols(&s->cmd_depth[64], 64, &s->cmd_bits[64]);
{ {
/* Create the bit length array for the full command alphabet. */ /* Create the bit length array for the full command alphabet. */
size_t i; size_t i;
memset(cmd_depth, 0, 64); /* only 64 first values were used */ memset(s->tmp_depth, 0, 64); /* only 64 first values were used */
memcpy(cmd_depth, depth + 24, 8); memcpy(s->tmp_depth, s->cmd_depth + 24, 8);
memcpy(cmd_depth + 64, depth + 32, 8); memcpy(s->tmp_depth + 64, s->cmd_depth + 32, 8);
memcpy(cmd_depth + 128, depth + 40, 8); memcpy(s->tmp_depth + 128, s->cmd_depth + 40, 8);
memcpy(cmd_depth + 192, depth + 48, 8); memcpy(s->tmp_depth + 192, s->cmd_depth + 48, 8);
memcpy(cmd_depth + 384, depth + 56, 8); memcpy(s->tmp_depth + 384, s->cmd_depth + 56, 8);
for (i = 0; i < 8; ++i) { for (i = 0; i < 8; ++i) {
cmd_depth[128 + 8 * i] = depth[i]; s->tmp_depth[128 + 8 * i] = s->cmd_depth[i];
cmd_depth[256 + 8 * i] = depth[8 + i]; s->tmp_depth[256 + 8 * i] = s->cmd_depth[8 + i];
cmd_depth[448 + 8 * i] = depth[16 + i]; s->tmp_depth[448 + 8 * i] = s->cmd_depth[16 + i];
} }
BrotliStoreHuffmanTree( BrotliStoreHuffmanTree(s->tmp_depth, BROTLI_NUM_COMMAND_SYMBOLS,
cmd_depth, BROTLI_NUM_COMMAND_SYMBOLS, tree, storage_ix, storage); s->tmp_tree, storage_ix, storage);
} }
BrotliStoreHuffmanTree(&depth[64], 64, tree, storage_ix, storage); BrotliStoreHuffmanTree(&s->cmd_depth[64], 64, s->tmp_tree, storage_ix,
storage);
} }
static BROTLI_INLINE void EmitInsertLen( static BROTLI_INLINE void EmitInsertLen(
@ -452,65 +451,64 @@ emit_remainder:
} }
} }
static void StoreCommands(MemoryManager* m, static void StoreCommands(BrotliTwoPassArena* s,
const uint8_t* literals, const size_t num_literals, const uint8_t* literals, const size_t num_literals,
const uint32_t* commands, const size_t num_commands, const uint32_t* commands, const size_t num_commands,
size_t* storage_ix, uint8_t* storage) { size_t* storage_ix, uint8_t* storage) {
static const uint32_t kNumExtraBits[128] = { static const uint32_t kNumExtraBits[128] = {
0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 12, 14, 24, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 6, 7, 8, 9, 10, 12, 14, 24, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24, 1, 1, 2, 2, 3, 3, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10, 24,
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24,
}; };
static const uint32_t kInsertOffset[24] = { static const uint32_t kInsertOffset[24] = {
0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26, 34, 50, 66, 98, 130, 194, 322, 578, 0, 1, 2, 3, 4, 5, 6, 8, 10, 14, 18, 26,
1090, 2114, 6210, 22594, 34, 50, 66, 98, 130, 194, 322, 578, 1090, 2114, 6210, 22594,
}; };
uint8_t lit_depths[256];
uint16_t lit_bits[256];
uint32_t lit_histo[256] = { 0 };
uint8_t cmd_depths[128] = { 0 };
uint16_t cmd_bits[128] = { 0 };
uint32_t cmd_histo[128] = { 0 };
size_t i; size_t i;
memset(s->lit_histo, 0, sizeof(s->lit_histo));
/* TODO: is that necessary? */
memset(s->cmd_depth, 0, sizeof(s->cmd_depth));
/* TODO: is that necessary? */
memset(s->cmd_bits, 0, sizeof(s->cmd_bits));
memset(s->cmd_histo, 0, sizeof(s->cmd_histo));
for (i = 0; i < num_literals; ++i) { for (i = 0; i < num_literals; ++i) {
++lit_histo[literals[i]]; ++s->lit_histo[literals[i]];
} }
BrotliBuildAndStoreHuffmanTreeFast(m, lit_histo, num_literals, BrotliBuildAndStoreHuffmanTreeFast(s->tmp_tree, s->lit_histo, num_literals,
/* max_bits = */ 8, /* max_bits = */ 8, s->lit_depth,
lit_depths, lit_bits, s->lit_bits, storage_ix, storage);
storage_ix, storage);
if (BROTLI_IS_OOM(m)) return;
for (i = 0; i < num_commands; ++i) { for (i = 0; i < num_commands; ++i) {
const uint32_t code = commands[i] & 0xFF; const uint32_t code = commands[i] & 0xFF;
BROTLI_DCHECK(code < 128); BROTLI_DCHECK(code < 128);
++cmd_histo[code]; ++s->cmd_histo[code];
} }
cmd_histo[1] += 1; s->cmd_histo[1] += 1;
cmd_histo[2] += 1; s->cmd_histo[2] += 1;
cmd_histo[64] += 1; s->cmd_histo[64] += 1;
cmd_histo[84] += 1; s->cmd_histo[84] += 1;
BuildAndStoreCommandPrefixCode(cmd_histo, cmd_depths, cmd_bits, BuildAndStoreCommandPrefixCode(s, storage_ix, storage);
storage_ix, storage);
for (i = 0; i < num_commands; ++i) { for (i = 0; i < num_commands; ++i) {
const uint32_t cmd = commands[i]; const uint32_t cmd = commands[i];
const uint32_t code = cmd & 0xFF; const uint32_t code = cmd & 0xFF;
const uint32_t extra = cmd >> 8; const uint32_t extra = cmd >> 8;
BROTLI_DCHECK(code < 128); BROTLI_DCHECK(code < 128);
BrotliWriteBits(cmd_depths[code], cmd_bits[code], storage_ix, storage); BrotliWriteBits(s->cmd_depth[code], s->cmd_bits[code], storage_ix, storage);
BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage); BrotliWriteBits(kNumExtraBits[code], extra, storage_ix, storage);
if (code < 24) { if (code < 24) {
const uint32_t insert = kInsertOffset[code] + extra; const uint32_t insert = kInsertOffset[code] + extra;
uint32_t j; uint32_t j;
for (j = 0; j < insert; ++j) { for (j = 0; j < insert; ++j) {
const uint8_t lit = *literals; const uint8_t lit = *literals;
BrotliWriteBits(lit_depths[lit], lit_bits[lit], storage_ix, storage); BrotliWriteBits(s->lit_depth[lit], s->lit_bits[lit], storage_ix,
storage);
++literals; ++literals;
} }
} }
@ -521,19 +519,19 @@ static void StoreCommands(MemoryManager* m,
#define MIN_RATIO 0.98 #define MIN_RATIO 0.98
#define SAMPLE_RATE 43 #define SAMPLE_RATE 43
static BROTLI_BOOL ShouldCompress( static BROTLI_BOOL ShouldCompress(BrotliTwoPassArena* s,
const uint8_t* input, size_t input_size, size_t num_literals) { const uint8_t* input, size_t input_size, size_t num_literals) {
double corpus_size = (double)input_size; double corpus_size = (double)input_size;
if ((double)num_literals < MIN_RATIO * corpus_size) { if ((double)num_literals < MIN_RATIO * corpus_size) {
return BROTLI_TRUE; return BROTLI_TRUE;
} else { } else {
uint32_t literal_histo[256] = { 0 };
const double max_total_bit_cost = corpus_size * 8 * MIN_RATIO / SAMPLE_RATE; const double max_total_bit_cost = corpus_size * 8 * MIN_RATIO / SAMPLE_RATE;
size_t i; size_t i;
memset(s->lit_histo, 0, sizeof(s->lit_histo));
for (i = 0; i < input_size; i += SAMPLE_RATE) { for (i = 0; i < input_size; i += SAMPLE_RATE) {
++literal_histo[input[i]]; ++s->lit_histo[input[i]];
} }
return TO_BROTLI_BOOL(BitsEntropy(literal_histo, 256) < max_total_bit_cost); return TO_BROTLI_BOOL(BitsEntropy(s->lit_histo, 256) < max_total_bit_cost);
} }
} }
@ -555,7 +553,7 @@ static void EmitUncompressedMetaBlock(const uint8_t* input, size_t input_size,
} }
static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl( static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl(
MemoryManager* m, const uint8_t* input, size_t input_size, BrotliTwoPassArena* s, const uint8_t* input, size_t input_size,
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
int* table, size_t table_bits, size_t min_match, int* table, size_t table_bits, size_t min_match,
size_t* storage_ix, uint8_t* storage) { size_t* storage_ix, uint8_t* storage) {
@ -573,14 +571,13 @@ static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl(
CreateCommands(input, block_size, input_size, base_ip, table, CreateCommands(input, block_size, input_size, base_ip, table,
table_bits, min_match, &literals, &commands); table_bits, min_match, &literals, &commands);
num_literals = (size_t)(literals - literal_buf); num_literals = (size_t)(literals - literal_buf);
if (ShouldCompress(input, block_size, num_literals)) { if (ShouldCompress(s, input, block_size, num_literals)) {
const size_t num_commands = (size_t)(commands - command_buf); const size_t num_commands = (size_t)(commands - command_buf);
BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage); BrotliStoreMetaBlockHeader(block_size, 0, storage_ix, storage);
/* No block splits, no contexts. */ /* No block splits, no contexts. */
BrotliWriteBits(13, 0, storage_ix, storage); BrotliWriteBits(13, 0, storage_ix, storage);
StoreCommands(m, literal_buf, num_literals, command_buf, num_commands, StoreCommands(s, literal_buf, num_literals, command_buf, num_commands,
storage_ix, storage); storage_ix, storage);
if (BROTLI_IS_OOM(m)) return;
} else { } else {
/* Since we did not find many backward references and the entropy of /* Since we did not find many backward references and the entropy of
the data is close to 8 bits, we can simply emit an uncompressed block. the data is close to 8 bits, we can simply emit an uncompressed block.
@ -597,18 +594,18 @@ static BROTLI_INLINE void BrotliCompressFragmentTwoPassImpl(
#define BAKE_METHOD_PARAM_(B) \ #define BAKE_METHOD_PARAM_(B) \
static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B( \ static BROTLI_NOINLINE void BrotliCompressFragmentTwoPassImpl ## B( \
MemoryManager* m, const uint8_t* input, size_t input_size, \ BrotliTwoPassArena* s, const uint8_t* input, size_t input_size, \
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, \ BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, \
int* table, size_t* storage_ix, uint8_t* storage) { \ int* table, size_t* storage_ix, uint8_t* storage) { \
size_t min_match = (B <= 15) ? 4 : 6; \ size_t min_match = (B <= 15) ? 4 : 6; \
BrotliCompressFragmentTwoPassImpl(m, input, input_size, is_last, command_buf,\ BrotliCompressFragmentTwoPassImpl(s, input, input_size, is_last, command_buf,\
literal_buf, table, B, min_match, storage_ix, storage); \ literal_buf, table, B, min_match, storage_ix, storage); \
} }
FOR_TABLE_BITS_(BAKE_METHOD_PARAM_) FOR_TABLE_BITS_(BAKE_METHOD_PARAM_)
#undef BAKE_METHOD_PARAM_ #undef BAKE_METHOD_PARAM_
void BrotliCompressFragmentTwoPass( void BrotliCompressFragmentTwoPass(
MemoryManager* m, const uint8_t* input, size_t input_size, BrotliTwoPassArena* s, const uint8_t* input, size_t input_size,
BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf, BROTLI_BOOL is_last, uint32_t* command_buf, uint8_t* literal_buf,
int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) { int* table, size_t table_size, size_t* storage_ix, uint8_t* storage) {
const size_t initial_storage_ix = *storage_ix; const size_t initial_storage_ix = *storage_ix;
@ -617,7 +614,7 @@ void BrotliCompressFragmentTwoPass(
#define CASE_(B) \ #define CASE_(B) \
case B: \ case B: \
BrotliCompressFragmentTwoPassImpl ## B( \ BrotliCompressFragmentTwoPassImpl ## B( \
m, input, input_size, is_last, command_buf, \ s, input, input_size, is_last, command_buf, \
literal_buf, table, storage_ix, storage); \ literal_buf, table, storage_ix, storage); \
break; break;
FOR_TABLE_BITS_(CASE_) FOR_TABLE_BITS_(CASE_)

View File

@ -13,16 +13,33 @@
#ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ #ifndef BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
#define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_ #define BROTLI_ENC_COMPRESS_FRAGMENT_TWO_PASS_H_
#include "../common/constants.h"
#include "../common/platform.h" #include "../common/platform.h"
#include <brotli/types.h> #include <brotli/types.h>
#include "./memory.h" #include "./entropy_encode.h"
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
extern "C" { extern "C" {
#endif #endif
/* TODO: turn to macro. */
static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17; static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17;
typedef struct BrotliTwoPassArena {
uint32_t lit_histo[256];
uint8_t lit_depth[256];
uint16_t lit_bits[256];
uint32_t cmd_histo[128];
uint8_t cmd_depth[128];
uint16_t cmd_bits[128];
/* BuildAndStoreCommandPrefixCode */
HuffmanTree tmp_tree[2 * BROTLI_NUM_LITERAL_SYMBOLS + 1];
uint8_t tmp_depth[BROTLI_NUM_COMMAND_SYMBOLS];
uint16_t tmp_bits[64];
} BrotliTwoPassArena;
/* Compresses "input" string to the "*storage" buffer as one or more complete /* Compresses "input" string to the "*storage" buffer as one or more complete
meta-blocks, and updates the "*storage_ix" bit position. meta-blocks, and updates the "*storage_ix" bit position.
@ -36,7 +53,7 @@ static const size_t kCompressFragmentTwoPassBlockSize = 1 << 17;
REQUIRES: "table_size" is a power of two REQUIRES: "table_size" is a power of two
OUTPUT: maximal copy distance <= |input_size| OUTPUT: maximal copy distance <= |input_size|
OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */ OUTPUT: maximal copy distance <= BROTLI_MAX_BACKWARD_LIMIT(18) */
BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(MemoryManager* m, BROTLI_INTERNAL void BrotliCompressFragmentTwoPass(BrotliTwoPassArena* s,
const uint8_t* input, const uint8_t* input,
size_t input_size, size_t input_size,
BROTLI_BOOL is_last, BROTLI_BOOL is_last,

View File

@ -95,20 +95,10 @@ typedef struct BrotliEncoderStateStruct {
int small_table_[1 << 10]; /* 4KiB */ int small_table_[1 << 10]; /* 4KiB */
int* large_table_; /* Allocated only when needed */ int* large_table_; /* Allocated only when needed */
size_t large_table_size_; size_t large_table_size_;
/* Command and distance prefix codes (each 64 symbols, stored back-to-back)
used for the next block in FAST_ONE_PASS_COMPRESSION_QUALITY. The command BrotliOnePassArena* one_pass_arena_;
prefix code is over a smaller alphabet with the following 64 symbols: BrotliTwoPassArena* two_pass_arena_;
0 - 15: insert length code 0, copy length code 0 - 15, same distance
16 - 39: insert length code 0, copy length code 0 - 23
40 - 63: insert length code 0 - 23, copy length code 0
Note that symbols 16 and 40 represent the same code in the full alphabet,
but we do not use either of them in FAST_ONE_PASS_COMPRESSION_QUALITY. */
uint8_t cmd_depths_[128];
uint16_t cmd_bits_[128];
/* The compressed form of the command and distance prefix codes for the next
block in FAST_ONE_PASS_COMPRESSION_QUALITY. */
uint8_t cmd_code_[512];
size_t cmd_code_numbits_;
/* Command and literal buffers for FAST_TWO_PASS_COMPRESSION_QUALITY. */ /* Command and literal buffers for FAST_TWO_PASS_COMPRESSION_QUALITY. */
uint32_t* command_buf_; uint32_t* command_buf_;
uint8_t* literal_buf_; uint8_t* literal_buf_;
@ -283,11 +273,9 @@ static void EncodeWindowBits(int lgwin, BROTLI_BOOL large_window,
} }
} }
/* TODO: move to compress_fragment.c? */
/* Initializes the command and distance prefix codes for the first block. */ /* Initializes the command and distance prefix codes for the first block. */
static void InitCommandPrefixCodes(uint8_t cmd_depths[128], static void InitCommandPrefixCodes(BrotliOnePassArena* s) {
uint16_t cmd_bits[128],
uint8_t cmd_code[512],
size_t* cmd_code_numbits) {
static const uint8_t kDefaultCommandDepths[128] = { static const uint8_t kDefaultCommandDepths[128] = {
0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7,
@ -320,13 +308,13 @@ static void InitCommandPrefixCodes(uint8_t cmd_depths[128],
0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00, 0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x04, 0x00,
}; };
static const size_t kDefaultCommandCodeNumBits = 448; static const size_t kDefaultCommandCodeNumBits = 448;
COPY_ARRAY(cmd_depths, kDefaultCommandDepths); COPY_ARRAY(s->cmd_depth, kDefaultCommandDepths);
COPY_ARRAY(cmd_bits, kDefaultCommandBits); COPY_ARRAY(s->cmd_bits, kDefaultCommandBits);
/* Initialize the pre-compressed form of the command and distance prefix /* Initialize the pre-compressed form of the command and distance prefix
codes. */ codes. */
COPY_ARRAY(cmd_code, kDefaultCommandCode); COPY_ARRAY(s->cmd_code, kDefaultCommandCode);
*cmd_code_numbits = kDefaultCommandCodeNumBits; s->cmd_code_numbits = kDefaultCommandCodeNumBits;
} }
/* Decide about the context map based on the ability of the prediction /* Decide about the context map based on the ability of the prediction
@ -401,7 +389,8 @@ static void ChooseContextMap(int quality,
first 5 bits of literals. */ first 5 bits of literals. */
static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input, static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
size_t* num_literal_contexts, const uint32_t** literal_context_map) { size_t* num_literal_contexts, const uint32_t** literal_context_map,
uint32_t* arena) {
static const uint32_t kStaticContextMapComplexUTF8[64] = { static const uint32_t kStaticContextMapComplexUTF8[64] = {
11, 11, 12, 12, /* 0 special */ 11, 11, 12, 12, /* 0 special */
0, 0, 0, 0, /* 4 lf */ 0, 0, 0, 0, /* 4 lf */
@ -426,16 +415,17 @@ static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
return BROTLI_FALSE; return BROTLI_FALSE;
} else { } else {
const size_t end_pos = start_pos + length; const size_t end_pos = start_pos + length;
/* To make entropy calculations faster and to fit on the stack, we collect /* To make entropy calculations faster, we collect histograms
histograms over the 5 most significant bits of literals. One histogram over the 5 most significant bits of literals. One histogram
without context and 13 additional histograms for each context value. */ without context and 13 additional histograms for each context value. */
uint32_t combined_histo[32] = { 0 }; uint32_t* BROTLI_RESTRICT const combined_histo = arena;
uint32_t context_histo[13][32] = { { 0 } }; uint32_t* BROTLI_RESTRICT const context_histo = arena + 32;
uint32_t total = 0; uint32_t total = 0;
double entropy[3]; double entropy[3];
size_t dummy; size_t dummy;
size_t i; size_t i;
ContextLut utf8_lut = BROTLI_CONTEXT_LUT(CONTEXT_UTF8); ContextLut utf8_lut = BROTLI_CONTEXT_LUT(CONTEXT_UTF8);
memset(arena, 0, sizeof(arena[0]) * 32 * 14);
for (; start_pos + 64 <= end_pos; start_pos += 4096) { for (; start_pos + 64 <= end_pos; start_pos += 4096) {
const size_t stride_end_pos = start_pos + 64; const size_t stride_end_pos = start_pos + 64;
uint8_t prev2 = input[start_pos & mask]; uint8_t prev2 = input[start_pos & mask];
@ -449,7 +439,7 @@ static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
BROTLI_CONTEXT(prev1, prev2, utf8_lut)]; BROTLI_CONTEXT(prev1, prev2, utf8_lut)];
++total; ++total;
++combined_histo[literal >> 3]; ++combined_histo[literal >> 3];
++context_histo[context][literal >> 3]; ++context_histo[(context << 5) + (literal >> 3)];
prev2 = prev1; prev2 = prev1;
prev1 = literal; prev1 = literal;
} }
@ -457,7 +447,7 @@ static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
entropy[1] = ShannonEntropy(combined_histo, 32, &dummy); entropy[1] = ShannonEntropy(combined_histo, 32, &dummy);
entropy[2] = 0; entropy[2] = 0;
for (i = 0; i < 13; ++i) { for (i = 0; i < 13; ++i) {
entropy[2] += ShannonEntropy(&context_histo[i][0], 32, &dummy); entropy[2] += ShannonEntropy(context_histo + (i << 5), 32, &dummy);
} }
entropy[0] = 1.0 / (double)total; entropy[0] = 1.0 / (double)total;
entropy[1] *= entropy[0]; entropy[1] *= entropy[0];
@ -481,19 +471,21 @@ static BROTLI_BOOL ShouldUseComplexStaticContextMap(const uint8_t* input,
static void DecideOverLiteralContextModeling(const uint8_t* input, static void DecideOverLiteralContextModeling(const uint8_t* input,
size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint, size_t start_pos, size_t length, size_t mask, int quality, size_t size_hint,
size_t* num_literal_contexts, const uint32_t** literal_context_map) { size_t* num_literal_contexts, const uint32_t** literal_context_map,
uint32_t* arena) {
if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) { if (quality < MIN_QUALITY_FOR_CONTEXT_MODELING || length < 64) {
return; return;
} else if (ShouldUseComplexStaticContextMap( } else if (ShouldUseComplexStaticContextMap(
input, start_pos, length, mask, quality, size_hint, input, start_pos, length, mask, quality, size_hint,
num_literal_contexts, literal_context_map)) { num_literal_contexts, literal_context_map, arena)) {
/* Context map was already set, nothing else to do. */ /* Context map was already set, nothing else to do. */
} else { } else {
/* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of /* Gather bi-gram data of the UTF8 byte prefixes. To make the analysis of
UTF8 data faster we only examine 64 byte long strides at every 4kB UTF8 data faster we only examine 64 byte long strides at every 4kB
intervals. */ intervals. */
const size_t end_pos = start_pos + length; const size_t end_pos = start_pos + length;
uint32_t bigram_prefix_histo[9] = { 0 }; uint32_t* BROTLI_RESTRICT const bigram_prefix_histo = arena;
memset(bigram_prefix_histo, 0, sizeof(arena[0]) * 9);
for (; start_pos + 64 <= end_pos; start_pos += 4096) { for (; start_pos + 64 <= end_pos; start_pos += 4096) {
static const int lut[4] = { 0, 0, 1, 2 }; static const int lut[4] = { 0, 0, 1, 2 };
const size_t stride_end_pos = start_pos + 64; const size_t stride_end_pos = start_pos + 64;
@ -613,10 +605,14 @@ static void WriteMetaBlockInternal(MemoryManager* m,
size_t num_literal_contexts = 1; size_t num_literal_contexts = 1;
const uint32_t* literal_context_map = NULL; const uint32_t* literal_context_map = NULL;
if (!params->disable_literal_context_modeling) { if (!params->disable_literal_context_modeling) {
/* TODO: pull to higher level and reuse. */
uint32_t* arena = BROTLI_ALLOC(m, uint32_t, 14 * 32);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
DecideOverLiteralContextModeling( DecideOverLiteralContextModeling(
data, wrapped_last_flush_pos, bytes, mask, params->quality, data, wrapped_last_flush_pos, bytes, mask, params->quality,
params->size_hint, &num_literal_contexts, params->size_hint, &num_literal_contexts,
&literal_context_map); &literal_context_map, arena);
BROTLI_FREE(m, arena);
} }
BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask, BrotliBuildMetaBlockGreedy(m, data, wrapped_last_flush_pos, mask,
prev_byte, prev_byte2, literal_context_lut, num_literal_contexts, prev_byte, prev_byte2, literal_context_lut, num_literal_contexts,
@ -681,12 +677,13 @@ static void ChooseDistanceParams(BrotliEncoderParams* params) {
} }
} }
BrotliInitDistanceParams( BrotliInitDistanceParams(&params->dist, distance_postfix_bits,
params, distance_postfix_bits, num_direct_distance_codes); num_direct_distance_codes, params->large_window);
} }
static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) { static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) {
if (BROTLI_IS_OOM(&s->memory_manager_)) return BROTLI_FALSE; MemoryManager* m = &s->memory_manager_;
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
if (s->is_initialized_) return BROTLI_TRUE; if (s->is_initialized_) return BROTLI_TRUE;
s->last_bytes_bits_ = 0; s->last_bytes_bits_ = 0;
@ -728,8 +725,12 @@ static BROTLI_BOOL EnsureInitialized(BrotliEncoderState* s) {
} }
if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
InitCommandPrefixCodes(s->cmd_depths_, s->cmd_bits_, s->one_pass_arena_ = BROTLI_ALLOC(m, BrotliOnePassArena, 1);
s->cmd_code_, &s->cmd_code_numbits_); if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
InitCommandPrefixCodes(s->one_pass_arena_);
} else if (s->params.quality == FAST_TWO_PASS_COMPRESSION_QUALITY) {
s->two_pass_arena_ = BROTLI_ALLOC(m, BrotliTwoPassArena, 1);
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
} }
s->is_initialized_ = BROTLI_TRUE; s->is_initialized_ = BROTLI_TRUE;
@ -769,7 +770,8 @@ static void BrotliEncoderInitState(BrotliEncoderState* s) {
HasherInit(&s->hasher_); HasherInit(&s->hasher_);
s->large_table_ = NULL; s->large_table_ = NULL;
s->large_table_size_ = 0; s->large_table_size_ = 0;
s->cmd_code_numbits_ = 0; s->one_pass_arena_ = NULL;
s->two_pass_arena_ = NULL;
s->command_buf_ = NULL; s->command_buf_ = NULL;
s->literal_buf_ = NULL; s->literal_buf_ = NULL;
s->next_out_ = NULL; s->next_out_ = NULL;
@ -796,13 +798,9 @@ static void BrotliEncoderInitState(BrotliEncoderState* s) {
BrotliEncoderState* BrotliEncoderCreateInstance( BrotliEncoderState* BrotliEncoderCreateInstance(
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) { brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
BrotliEncoderState* state = 0; BrotliEncoderState* state = (BrotliEncoderState*)BrotliBootstrapAlloc(
if (!alloc_func && !free_func) { sizeof(BrotliEncoderState), alloc_func, free_func, opaque);
state = (BrotliEncoderState*)malloc(sizeof(BrotliEncoderState)); if (state == NULL) {
} else if (alloc_func && free_func) {
state = (BrotliEncoderState*)alloc_func(opaque, sizeof(BrotliEncoderState));
}
if (state == 0) {
/* BROTLI_DUMP(); */ /* BROTLI_DUMP(); */
return 0; return 0;
} }
@ -823,6 +821,8 @@ static void BrotliEncoderCleanupState(BrotliEncoderState* s) {
RingBufferFree(m, &s->ringbuffer_); RingBufferFree(m, &s->ringbuffer_);
DestroyHasher(m, &s->hasher_); DestroyHasher(m, &s->hasher_);
BROTLI_FREE(m, s->large_table_); BROTLI_FREE(m, s->large_table_);
BROTLI_FREE(m, s->one_pass_arena_);
BROTLI_FREE(m, s->two_pass_arena_);
BROTLI_FREE(m, s->command_buf_); BROTLI_FREE(m, s->command_buf_);
BROTLI_FREE(m, s->literal_buf_); BROTLI_FREE(m, s->literal_buf_);
} }
@ -832,11 +832,8 @@ void BrotliEncoderDestroyInstance(BrotliEncoderState* state) {
if (!state) { if (!state) {
return; return;
} else { } else {
MemoryManager* m = &state->memory_manager_;
brotli_free_func free_func = m->free_func;
void* opaque = m->opaque;
BrotliEncoderCleanupState(state); BrotliEncoderCleanupState(state);
free_func(opaque, state); BrotliBootstrapFree(state, &state->memory_manager_);
} }
} }
@ -1012,16 +1009,14 @@ static BROTLI_BOOL EncodeData(
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
BrotliCompressFragmentFast( BrotliCompressFragmentFast(
m, &data[wrapped_last_processed_pos & mask], s->one_pass_arena_, &data[wrapped_last_processed_pos & mask],
bytes, is_last, bytes, is_last,
table, table_size, table, table_size,
s->cmd_depths_, s->cmd_bits_,
&s->cmd_code_numbits_, s->cmd_code_,
&storage_ix, storage); &storage_ix, storage);
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
} else { } else {
BrotliCompressFragmentTwoPass( BrotliCompressFragmentTwoPass(
m, &data[wrapped_last_processed_pos & mask], s->two_pass_arena_, &data[wrapped_last_processed_pos & mask],
bytes, is_last, bytes, is_last,
s->command_buf_, s->literal_buf_, s->command_buf_, s->literal_buf_,
table, table_size, table, table_size,
@ -1204,8 +1199,8 @@ static size_t WriteMetadataHeader(
static BROTLI_BOOL BrotliCompressBufferQuality10( static BROTLI_BOOL BrotliCompressBufferQuality10(
int lgwin, size_t input_size, const uint8_t* input_buffer, int lgwin, size_t input_size, const uint8_t* input_buffer,
size_t* encoded_size, uint8_t* encoded_buffer) { size_t* encoded_size, uint8_t* encoded_buffer) {
MemoryManager memory_manager; MemoryManager* m =
MemoryManager* m = &memory_manager; (MemoryManager*)BrotliBootstrapAlloc(sizeof(MemoryManager), 0, 0, 0);
const size_t mask = BROTLI_SIZE_MAX >> 1; const size_t mask = BROTLI_SIZE_MAX >> 1;
int dist_cache[4] = { 4, 11, 15, 16 }; int dist_cache[4] = { 4, 11, 15, 16 };
@ -1219,8 +1214,6 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
const size_t hasher_eff_size = BROTLI_MIN(size_t, const size_t hasher_eff_size = BROTLI_MIN(size_t,
input_size, BROTLI_MAX_BACKWARD_LIMIT(lgwin) + BROTLI_WINDOW_GAP); input_size, BROTLI_MAX_BACKWARD_LIMIT(lgwin) + BROTLI_WINDOW_GAP);
BrotliEncoderParams params;
const int lgmetablock = BROTLI_MIN(int, 24, lgwin + 1); const int lgmetablock = BROTLI_MIN(int, 24, lgwin + 1);
size_t max_block_size; size_t max_block_size;
const size_t max_metablock_size = (size_t)1 << lgmetablock; const size_t max_metablock_size = (size_t)1 << lgmetablock;
@ -1230,25 +1223,35 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
uint8_t prev_byte = 0; uint8_t prev_byte = 0;
uint8_t prev_byte2 = 0; uint8_t prev_byte2 = 0;
Hasher hasher; BrotliEncoderParams* params = NULL;
HasherInit(&hasher); Hasher* hasher = NULL;
BrotliEncoderInitParams(&params);
params.quality = 10;
params.lgwin = lgwin;
if (lgwin > BROTLI_MAX_WINDOW_BITS) {
params.large_window = BROTLI_TRUE;
}
SanitizeParams(&params);
params.lgblock = ComputeLgBlock(&params);
ChooseDistanceParams(&params);
max_block_size = (size_t)1 << params.lgblock;
if (m == NULL) return BROTLI_FALSE;
BrotliInitMemoryManager(m, 0, 0, 0); BrotliInitMemoryManager(m, 0, 0, 0);
params = BROTLI_ALLOC(m, BrotliEncoderParams, 2);
hasher = BROTLI_ALLOC(m, Hasher, 1);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(params) || BROTLI_IS_NULL(hasher)) {
goto oom;
}
BrotliEncoderInitParams(params);
HasherInit(hasher);
params->quality = 10;
params->lgwin = lgwin;
if (lgwin > BROTLI_MAX_WINDOW_BITS) {
params->large_window = BROTLI_TRUE;
}
SanitizeParams(params);
params->lgblock = ComputeLgBlock(params);
ChooseDistanceParams(params);
max_block_size = (size_t)1 << params->lgblock;
/* Since default static dictionary is used we assume that
* params->quality < params->dictionary.max_quality. */
BROTLI_DCHECK(input_size <= mask + 1); BROTLI_DCHECK(input_size <= mask + 1);
EncodeWindowBits(lgwin, params.large_window, &last_bytes, &last_bytes_bits); EncodeWindowBits(lgwin, params->large_window, &last_bytes, &last_bytes_bits);
InitOrStitchToPreviousBlock(m, &hasher, input_buffer, mask, &params, InitOrStitchToPreviousBlock(m, hasher, input_buffer, mask, params,
0, hasher_eff_size, BROTLI_TRUE); 0, hasher_eff_size, BROTLI_TRUE);
if (BROTLI_IS_OOM(m)) goto oom; if (BROTLI_IS_OOM(m)) goto oom;
@ -1267,7 +1270,7 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
uint8_t* storage; uint8_t* storage;
size_t storage_ix; size_t storage_ix;
ContextType literal_context_mode = ChooseContextMode(&params, ContextType literal_context_mode = ChooseContextMode(params,
input_buffer, metablock_start, mask, metablock_end - metablock_start); input_buffer, metablock_start, mask, metablock_end - metablock_start);
ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode); ContextLut literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
@ -1280,10 +1283,10 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
size_t new_cmd_alloc_size; size_t new_cmd_alloc_size;
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) goto oom; if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(nodes)) goto oom;
BrotliInitZopfliNodes(nodes, block_size + 1); BrotliInitZopfliNodes(nodes, block_size + 1);
StitchToPreviousBlockH10(&hasher.privat._H10, block_size, block_start, StitchToPreviousBlockH10(&hasher->privat._H10, block_size, block_start,
input_buffer, mask); input_buffer, mask);
path_size = BrotliZopfliComputeShortestPath(m, block_size, block_start, path_size = BrotliZopfliComputeShortestPath(m, block_size, block_start,
input_buffer, mask, literal_context_lut, &params, dist_cache, &hasher, input_buffer, mask, literal_context_lut, params, dist_cache, hasher,
nodes); nodes);
if (BROTLI_IS_OOM(m)) goto oom; if (BROTLI_IS_OOM(m)) goto oom;
/* We allocate a command buffer in the first iteration of this loop that /* We allocate a command buffer in the first iteration of this loop that
@ -1307,7 +1310,7 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
commands = new_commands; commands = new_commands;
} }
BrotliZopfliCreateCommands(block_size, block_start, &nodes[0], dist_cache, BrotliZopfliCreateCommands(block_size, block_start, &nodes[0], dist_cache,
&last_insert_len, &params, &commands[num_commands], &num_literals); &last_insert_len, params, &commands[num_commands], &num_literals);
num_commands += path_size; num_commands += path_size;
block_start += block_size; block_start += block_size;
metablock_size += block_size; metablock_size += block_size;
@ -1349,10 +1352,11 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
&storage_ix, storage); &storage_ix, storage);
} else { } else {
MetaBlockSplit mb; MetaBlockSplit mb;
BrotliEncoderParams block_params = params; BrotliEncoderParams* block_params = params + 1;
*block_params = *params; /* shallow copy */
InitMetaBlockSplit(&mb); InitMetaBlockSplit(&mb);
BrotliBuildMetaBlock(m, input_buffer, metablock_start, mask, BrotliBuildMetaBlock(m, input_buffer, metablock_start, mask,
&block_params, block_params,
prev_byte, prev_byte2, prev_byte, prev_byte2,
commands, num_commands, commands, num_commands,
literal_context_mode, literal_context_mode,
@ -1362,7 +1366,7 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
/* The number of distance symbols effectively used for distance /* The number of distance symbols effectively used for distance
histograms. It might be less than distance alphabet size histograms. It might be less than distance alphabet size
for "Large Window Brotli" (32-bit). */ for "Large Window Brotli" (32-bit). */
BrotliOptimizeHistograms(block_params.dist.alphabet_size_limit, &mb); BrotliOptimizeHistograms(block_params->dist.alphabet_size_limit, &mb);
} }
storage = BROTLI_ALLOC(m, uint8_t, 2 * metablock_size + 503); storage = BROTLI_ALLOC(m, uint8_t, 2 * metablock_size + 503);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(storage)) goto oom; if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(storage)) goto oom;
@ -1371,7 +1375,7 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
BrotliStoreMetaBlock(m, input_buffer, metablock_start, metablock_size, BrotliStoreMetaBlock(m, input_buffer, metablock_start, metablock_size,
mask, prev_byte, prev_byte2, mask, prev_byte, prev_byte2,
is_last, is_last,
&block_params, block_params,
literal_context_mode, literal_context_mode,
commands, num_commands, commands, num_commands,
&mb, &mb,
@ -1415,11 +1419,15 @@ static BROTLI_BOOL BrotliCompressBufferQuality10(
} }
*encoded_size = total_out_size; *encoded_size = total_out_size;
DestroyHasher(m, &hasher); DestroyHasher(m, hasher);
BROTLI_FREE(m, hasher);
BROTLI_FREE(m, params);
BrotliBootstrapFree(m, m);
return ok; return ok;
oom: oom:
BrotliWipeOutMemoryManager(m); BrotliWipeOutMemoryManager(m);
BrotliBootstrapFree(m, m);
return BROTLI_FALSE; return BROTLI_FALSE;
} }
@ -1677,13 +1685,12 @@ static BROTLI_BOOL BrotliEncoderCompressStreamFast(
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) { if (s->params.quality == FAST_ONE_PASS_COMPRESSION_QUALITY) {
BrotliCompressFragmentFast(m, *next_in, block_size, is_last, table, BrotliCompressFragmentFast(s->one_pass_arena_, *next_in, block_size,
table_size, s->cmd_depths_, s->cmd_bits_, &s->cmd_code_numbits_, is_last, table, table_size, &storage_ix, storage);
s->cmd_code_, &storage_ix, storage);
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
} else { } else {
BrotliCompressFragmentTwoPass(m, *next_in, block_size, is_last, BrotliCompressFragmentTwoPass(s->two_pass_arena_, *next_in, block_size,
command_buf, literal_buf, table, table_size, is_last, command_buf, literal_buf, table, table_size,
&storage_ix, storage); &storage_ix, storage);
if (BROTLI_IS_OOM(m)) return BROTLI_FALSE; if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
} }

View File

@ -445,13 +445,15 @@ static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher,
size_t alloc_size[4] = {0}; size_t alloc_size[4] = {0};
size_t i; size_t i;
ChooseHasher(params, &params->hasher); ChooseHasher(params, &params->hasher);
hasher->common.params = params->hasher;
hasher->common.dict_num_lookups = 0;
hasher->common.dict_num_matches = 0;
HasherSize(params, one_shot, input_size, alloc_size); HasherSize(params, one_shot, input_size, alloc_size);
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
if (alloc_size[i] == 0) continue; if (alloc_size[i] == 0) continue;
hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]); hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return; if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return;
} }
hasher->common.params = params->hasher;
switch (hasher->common.params.type) { switch (hasher->common.params.type) {
#define INITIALIZE_(N) \ #define INITIALIZE_(N) \
case N: \ case N: \
@ -479,10 +481,6 @@ static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher,
#undef PREPARE_ #undef PREPARE_
default: break; default: break;
} }
if (position == 0) {
hasher->common.dict_num_lookups = 0;
hasher->common.dict_num_matches = 0;
}
hasher->common.is_prepared_ = BROTLI_TRUE; hasher->common.is_prepared_ = BROTLI_TRUE;
} }
} }

View File

@ -82,7 +82,7 @@ static BROTLI_INLINE void FN(HashMemAllocInBytes)(
size_t alloc_size_b[4] = {0}; size_t alloc_size_b[4] = {0};
FN_A(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_a); FN_A(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_a);
FN_B(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_b); FN_B(HashMemAllocInBytes)(params, one_shot, input_size, alloc_size_b);
// Should never happen. /* Should never happen. */
if (alloc_size_a[2] != 0 || alloc_size_a[3] != 0) exit(EXIT_FAILURE); if (alloc_size_a[2] != 0 || alloc_size_a[3] != 0) exit(EXIT_FAILURE);
if (alloc_size_b[2] != 0 || alloc_size_b[3] != 0) exit(EXIT_FAILURE); if (alloc_size_b[2] != 0 || alloc_size_b[3] != 0) exit(EXIT_FAILURE);
alloc_size[0] = alloc_size_a[0]; alloc_size[0] = alloc_size_a[0];

View File

@ -9,6 +9,8 @@
#include "./literal_cost.h" #include "./literal_cost.h"
#include <string.h> /* memset */
#include "../common/platform.h" #include "../common/platform.h"
#include <brotli/types.h> #include <brotli/types.h>
#include "./fast_log.h" #include "./fast_log.h"
@ -54,22 +56,23 @@ static size_t DecideMultiByteStatsLevel(size_t pos, size_t len, size_t mask,
} }
static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask, static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
const uint8_t* data, float* cost) { const uint8_t* data,
size_t* histogram, float* cost) {
/* max_utf8 is 0 (normal ASCII single byte modeling), /* max_utf8 is 0 (normal ASCII single byte modeling),
1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */ 1 (for 2-byte UTF-8 modeling), or 2 (for 3-byte UTF-8 modeling). */
const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data); const size_t max_utf8 = DecideMultiByteStatsLevel(pos, len, mask, data);
size_t histogram[3][256] = { { 0 } };
size_t window_half = 495; size_t window_half = 495;
size_t in_window = BROTLI_MIN(size_t, window_half, len); size_t in_window = BROTLI_MIN(size_t, window_half, len);
size_t in_window_utf8[3] = { 0 }; size_t in_window_utf8[3] = { 0 };
size_t i; size_t i;
memset(histogram, 0, 3 * 256 * sizeof(histogram[0]));
{ /* Bootstrap histograms. */ { /* Bootstrap histograms. */
size_t last_c = 0; size_t last_c = 0;
size_t utf8_pos = 0; size_t utf8_pos = 0;
for (i = 0; i < in_window; ++i) { for (i = 0; i < in_window; ++i) {
size_t c = data[(pos + i) & mask]; size_t c = data[(pos + i) & mask];
++histogram[utf8_pos][c]; ++histogram[256 * utf8_pos + c];
++in_window_utf8[utf8_pos]; ++in_window_utf8[utf8_pos];
utf8_pos = UTF8Position(last_c, c, max_utf8); utf8_pos = UTF8Position(last_c, c, max_utf8);
last_c = c; last_c = c;
@ -85,7 +88,7 @@ static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
size_t last_c = size_t last_c =
i < window_half + 2 ? 0 : data[(pos + i - window_half - 2) & mask]; i < window_half + 2 ? 0 : data[(pos + i - window_half - 2) & mask];
size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8);
--histogram[utf8_pos2][data[(pos + i - window_half) & mask]]; --histogram[256 * utf8_pos2 + data[(pos + i - window_half) & mask]];
--in_window_utf8[utf8_pos2]; --in_window_utf8[utf8_pos2];
} }
if (i + window_half < len) { if (i + window_half < len) {
@ -93,7 +96,7 @@ static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
size_t c = data[(pos + i + window_half - 1) & mask]; size_t c = data[(pos + i + window_half - 1) & mask];
size_t last_c = data[(pos + i + window_half - 2) & mask]; size_t last_c = data[(pos + i + window_half - 2) & mask];
size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8); size_t utf8_pos2 = UTF8Position(last_c, c, max_utf8);
++histogram[utf8_pos2][data[(pos + i + window_half) & mask]]; ++histogram[256 * utf8_pos2 + data[(pos + i + window_half) & mask]];
++in_window_utf8[utf8_pos2]; ++in_window_utf8[utf8_pos2];
} }
{ {
@ -101,7 +104,7 @@ static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
size_t last_c = i < 2 ? 0 : data[(pos + i - 2) & mask]; size_t last_c = i < 2 ? 0 : data[(pos + i - 2) & mask];
size_t utf8_pos = UTF8Position(last_c, c, max_utf8); size_t utf8_pos = UTF8Position(last_c, c, max_utf8);
size_t masked_pos = (pos + i) & mask; size_t masked_pos = (pos + i) & mask;
size_t histo = histogram[utf8_pos][data[masked_pos]]; size_t histo = histogram[256 * utf8_pos + data[masked_pos]];
double lit_cost; double lit_cost;
if (histo == 0) { if (histo == 0) {
histo = 1; histo = 1;
@ -125,17 +128,18 @@ static void EstimateBitCostsForLiteralsUTF8(size_t pos, size_t len, size_t mask,
} }
void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask, void BrotliEstimateBitCostsForLiterals(size_t pos, size_t len, size_t mask,
const uint8_t* data, float* cost) { const uint8_t* data,
size_t* histogram, float* cost) {
if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) { if (BrotliIsMostlyUTF8(data, pos, mask, len, kMinUTF8Ratio)) {
EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, cost); EstimateBitCostsForLiteralsUTF8(pos, len, mask, data, histogram, cost);
return; return;
} else { } else {
size_t histogram[256] = { 0 };
size_t window_half = 2000; size_t window_half = 2000;
size_t in_window = BROTLI_MIN(size_t, window_half, len); size_t in_window = BROTLI_MIN(size_t, window_half, len);
size_t i;
memset(histogram, 0, 256 * sizeof(histogram[0]));
/* Bootstrap histogram. */ /* Bootstrap histogram. */
size_t i;
for (i = 0; i < in_window; ++i) { for (i = 0; i < in_window; ++i) {
++histogram[data[(pos + i) & mask]]; ++histogram[data[(pos + i) & mask]];
} }

View File

@ -21,7 +21,8 @@ extern "C" {
ring-buffer (data, mask) will take entropy coded and writes these estimates ring-buffer (data, mask) will take entropy coded and writes these estimates
to the cost[0..len) array. */ to the cost[0..len) array. */
BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals( BROTLI_INTERNAL void BrotliEstimateBitCostsForLiterals(
size_t pos, size_t len, size_t mask, const uint8_t* data, float* cost); size_t pos, size_t len, size_t mask, const uint8_t* data, size_t* histogram,
float* cost);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */ } /* extern "C" */

View File

@ -165,6 +165,28 @@ void BrotliWipeOutMemoryManager(MemoryManager* m) {
#endif /* BROTLI_ENCODER_EXIT_ON_OOM */ #endif /* BROTLI_ENCODER_EXIT_ON_OOM */
void* BrotliBootstrapAlloc(size_t size,
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
if (!alloc_func && !free_func) {
return malloc(size);
} else if (alloc_func && free_func) {
return alloc_func(opaque, size);
}
return NULL;
}
void BrotliBootstrapFree(void* address, MemoryManager* m) {
if (!address) {
/* Should not happen! */
return;
} else {
/* Copy values, as those would be freed. */
brotli_free_func free_func = m->free_func;
void* opaque = m->opaque;
free_func(opaque, address);
}
}
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */ } /* extern "C" */
#endif #endif

View File

@ -107,6 +107,12 @@ V: value to append
A[(S) - 1] = (V); \ A[(S) - 1] = (V); \
} }
/* "Bootstrap" allocations are not tracked by memory manager; should be used
only to allocate MemoryManager itself (or structure containing it). */
BROTLI_INTERNAL void* BrotliBootstrapAlloc(size_t size,
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
BROTLI_INTERNAL void BrotliBootstrapFree(void* address, MemoryManager* m);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */ } /* extern "C" */
#endif #endif

View File

@ -25,9 +25,8 @@
extern "C" { extern "C" {
#endif #endif
void BrotliInitDistanceParams(BrotliEncoderParams* params, void BrotliInitDistanceParams(BrotliDistanceParams* dist_params,
uint32_t npostfix, uint32_t ndirect) { uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window) {
BrotliDistanceParams* dist_params = &params->dist;
uint32_t alphabet_size_max; uint32_t alphabet_size_max;
uint32_t alphabet_size_limit; uint32_t alphabet_size_limit;
uint32_t max_distance; uint32_t max_distance;
@ -41,7 +40,7 @@ void BrotliInitDistanceParams(BrotliEncoderParams* params,
max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) - max_distance = ndirect + (1U << (BROTLI_MAX_DISTANCE_BITS + npostfix + 2)) -
(1U << (npostfix + 2)); (1U << (npostfix + 2));
if (params->large_window) { if (large_window) {
BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit( BrotliDistanceCodeLimit limit = BrotliCalculateDistanceCodeLimit(
BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect); BROTLI_MAX_ALLOWED_DISTANCE, npostfix, ndirect);
alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE( alphabet_size_max = BROTLI_DISTANCE_ALPHABET_SIZE(
@ -83,14 +82,14 @@ static BROTLI_BOOL ComputeDistanceCost(const Command* cmds,
size_t num_commands, size_t num_commands,
const BrotliDistanceParams* orig_params, const BrotliDistanceParams* orig_params,
const BrotliDistanceParams* new_params, const BrotliDistanceParams* new_params,
double* cost) { double* cost,
HistogramDistance* tmp) {
size_t i; size_t i;
BROTLI_BOOL equal_params = BROTLI_FALSE; BROTLI_BOOL equal_params = BROTLI_FALSE;
uint16_t dist_prefix; uint16_t dist_prefix;
uint32_t dist_extra; uint32_t dist_extra;
double extra_bits = 0.0; double extra_bits = 0.0;
HistogramDistance histo; HistogramClearDistance(tmp);
HistogramClearDistance(&histo);
if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits && if (orig_params->distance_postfix_bits == new_params->distance_postfix_bits &&
orig_params->num_direct_distance_codes == orig_params->num_direct_distance_codes ==
@ -114,12 +113,12 @@ static BROTLI_BOOL ComputeDistanceCost(const Command* cmds,
&dist_prefix, &dist_prefix,
&dist_extra); &dist_extra);
} }
HistogramAddDistance(&histo, dist_prefix & 0x3FF); HistogramAddDistance(tmp, dist_prefix & 0x3FF);
extra_bits += dist_prefix >> 10; extra_bits += dist_prefix >> 10;
} }
} }
*cost = BrotliPopulationCostDistance(&histo) + extra_bits; *cost = BrotliPopulationCostDistance(tmp) + extra_bits;
return BROTLI_TRUE; return BROTLI_TRUE;
} }
@ -147,43 +146,46 @@ void BrotliBuildMetaBlock(MemoryManager* m,
uint32_t ndirect_msb = 0; uint32_t ndirect_msb = 0;
BROTLI_BOOL check_orig = BROTLI_TRUE; BROTLI_BOOL check_orig = BROTLI_TRUE;
double best_dist_cost = 1e99; double best_dist_cost = 1e99;
BrotliEncoderParams orig_params = *params; BrotliDistanceParams orig_params = params->dist;
BrotliEncoderParams new_params = *params; BrotliDistanceParams new_params = params->dist;
HistogramDistance* tmp = BROTLI_ALLOC(m, HistogramDistance, 1);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return;
for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) { for (npostfix = 0; npostfix <= BROTLI_MAX_NPOSTFIX; npostfix++) {
for (; ndirect_msb < 16; ndirect_msb++) { for (; ndirect_msb < 16; ndirect_msb++) {
uint32_t ndirect = ndirect_msb << npostfix; uint32_t ndirect = ndirect_msb << npostfix;
BROTLI_BOOL skip; BROTLI_BOOL skip;
double dist_cost; double dist_cost;
BrotliInitDistanceParams(&new_params, npostfix, ndirect); BrotliInitDistanceParams(&new_params, npostfix, ndirect,
if (npostfix == orig_params.dist.distance_postfix_bits && params->large_window);
ndirect == orig_params.dist.num_direct_distance_codes) { if (npostfix == orig_params.distance_postfix_bits &&
ndirect == orig_params.num_direct_distance_codes) {
check_orig = BROTLI_FALSE; check_orig = BROTLI_FALSE;
} }
skip = !ComputeDistanceCost( skip = !ComputeDistanceCost(
cmds, num_commands, cmds, num_commands, &orig_params, &new_params, &dist_cost, tmp);
&orig_params.dist, &new_params.dist, &dist_cost);
if (skip || (dist_cost > best_dist_cost)) { if (skip || (dist_cost > best_dist_cost)) {
break; break;
} }
best_dist_cost = dist_cost; best_dist_cost = dist_cost;
params->dist = new_params.dist; params->dist = new_params;
} }
if (ndirect_msb > 0) ndirect_msb--; if (ndirect_msb > 0) ndirect_msb--;
ndirect_msb /= 2; ndirect_msb /= 2;
} }
if (check_orig) { if (check_orig) {
double dist_cost; double dist_cost;
ComputeDistanceCost(cmds, num_commands, ComputeDistanceCost(cmds, num_commands, &orig_params, &orig_params,
&orig_params.dist, &orig_params.dist, &dist_cost); &dist_cost, tmp);
if (dist_cost < best_dist_cost) { if (dist_cost < best_dist_cost) {
/* NB: currently unused; uncomment when more param tuning is added. */ /* NB: currently unused; uncomment when more param tuning is added. */
/* best_dist_cost = dist_cost; */ /* best_dist_cost = dist_cost; */
params->dist = orig_params.dist; params->dist = orig_params;
} }
} }
RecomputeDistancePrefixes(cmds, num_commands, BROTLI_FREE(m, tmp);
&orig_params.dist, &params->dist); RecomputeDistancePrefixes(cmds, num_commands, &orig_params, &params->dist);
BrotliSplitBlock(m, cmds, num_commands, BrotliSplitBlock(m, cmds, num_commands,
ringbuffer, pos, mask, params, ringbuffer, pos, mask, params,
@ -535,17 +537,21 @@ static void MapStaticContexts(MemoryManager* m,
} }
} }
static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal( typedef struct GreedyMetablockArena {
MemoryManager* m, const uint8_t* ringbuffer, size_t pos, size_t mask,
uint8_t prev_byte, uint8_t prev_byte2, ContextLut literal_context_lut,
const size_t num_contexts, const uint32_t* static_context_map,
const Command* commands, size_t n_commands, MetaBlockSplit* mb) {
union { union {
BlockSplitterLiteral plain; BlockSplitterLiteral plain;
ContextBlockSplitter ctx; ContextBlockSplitter ctx;
} lit_blocks; } lit_blocks;
BlockSplitterCommand cmd_blocks; BlockSplitterCommand cmd_blocks;
BlockSplitterDistance dist_blocks; BlockSplitterDistance dist_blocks;
} GreedyMetablockArena;
static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
MemoryManager* m, GreedyMetablockArena* arena, const uint8_t* ringbuffer,
size_t pos, size_t mask, uint8_t prev_byte, uint8_t prev_byte2,
ContextLut literal_context_lut, const size_t num_contexts,
const uint32_t* static_context_map, const Command* commands,
size_t n_commands, MetaBlockSplit* mb) {
size_t num_literals = 0; size_t num_literals = 0;
size_t i; size_t i;
for (i = 0; i < n_commands; ++i) { for (i = 0; i < n_commands; ++i) {
@ -553,20 +559,20 @@ static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
} }
if (num_contexts == 1) { if (num_contexts == 1) {
InitBlockSplitterLiteral(m, &lit_blocks.plain, 256, 512, 400.0, InitBlockSplitterLiteral(m, &arena->lit_blocks.plain, 256, 512, 400.0,
num_literals, &mb->literal_split, &mb->literal_histograms, num_literals, &mb->literal_split, &mb->literal_histograms,
&mb->literal_histograms_size); &mb->literal_histograms_size);
} else { } else {
InitContextBlockSplitter(m, &lit_blocks.ctx, 256, num_contexts, 512, 400.0, InitContextBlockSplitter(m, &arena->lit_blocks.ctx, 256, num_contexts, 512,
num_literals, &mb->literal_split, &mb->literal_histograms, 400.0, num_literals, &mb->literal_split, &mb->literal_histograms,
&mb->literal_histograms_size); &mb->literal_histograms_size);
} }
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
InitBlockSplitterCommand(m, &cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS, 1024, InitBlockSplitterCommand(m, &arena->cmd_blocks, BROTLI_NUM_COMMAND_SYMBOLS,
500.0, n_commands, &mb->command_split, &mb->command_histograms, 1024, 500.0, n_commands, &mb->command_split, &mb->command_histograms,
&mb->command_histograms_size); &mb->command_histograms_size);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
InitBlockSplitterDistance(m, &dist_blocks, 64, 512, 100.0, n_commands, InitBlockSplitterDistance(m, &arena->dist_blocks, 64, 512, 100.0, n_commands,
&mb->distance_split, &mb->distance_histograms, &mb->distance_split, &mb->distance_histograms,
&mb->distance_histograms_size); &mb->distance_histograms_size);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
@ -574,15 +580,15 @@ static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
for (i = 0; i < n_commands; ++i) { for (i = 0; i < n_commands; ++i) {
const Command cmd = commands[i]; const Command cmd = commands[i];
size_t j; size_t j;
BlockSplitterAddSymbolCommand(&cmd_blocks, cmd.cmd_prefix_); BlockSplitterAddSymbolCommand(&arena->cmd_blocks, cmd.cmd_prefix_);
for (j = cmd.insert_len_; j != 0; --j) { for (j = cmd.insert_len_; j != 0; --j) {
uint8_t literal = ringbuffer[pos & mask]; uint8_t literal = ringbuffer[pos & mask];
if (num_contexts == 1) { if (num_contexts == 1) {
BlockSplitterAddSymbolLiteral(&lit_blocks.plain, literal); BlockSplitterAddSymbolLiteral(&arena->lit_blocks.plain, literal);
} else { } else {
size_t context = size_t context =
BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut); BROTLI_CONTEXT(prev_byte, prev_byte2, literal_context_lut);
ContextBlockSplitterAddSymbol(&lit_blocks.ctx, m, literal, ContextBlockSplitterAddSymbol(&arena->lit_blocks.ctx, m, literal,
static_context_map[context]); static_context_map[context]);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
} }
@ -595,21 +601,24 @@ static BROTLI_INLINE void BrotliBuildMetaBlockGreedyInternal(
prev_byte2 = ringbuffer[(pos - 2) & mask]; prev_byte2 = ringbuffer[(pos - 2) & mask];
prev_byte = ringbuffer[(pos - 1) & mask]; prev_byte = ringbuffer[(pos - 1) & mask];
if (cmd.cmd_prefix_ >= 128) { if (cmd.cmd_prefix_ >= 128) {
BlockSplitterAddSymbolDistance(&dist_blocks, cmd.dist_prefix_ & 0x3FF); BlockSplitterAddSymbolDistance(
&arena->dist_blocks, cmd.dist_prefix_ & 0x3FF);
} }
} }
} }
if (num_contexts == 1) { if (num_contexts == 1) {
BlockSplitterFinishBlockLiteral( BlockSplitterFinishBlockLiteral(
&lit_blocks.plain, /* is_final = */ BROTLI_TRUE); &arena->lit_blocks.plain, /* is_final = */ BROTLI_TRUE);
} else { } else {
ContextBlockSplitterFinishBlock( ContextBlockSplitterFinishBlock(
&lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE); &arena->lit_blocks.ctx, m, /* is_final = */ BROTLI_TRUE);
if (BROTLI_IS_OOM(m)) return; if (BROTLI_IS_OOM(m)) return;
} }
BlockSplitterFinishBlockCommand(&cmd_blocks, /* is_final = */ BROTLI_TRUE); BlockSplitterFinishBlockCommand(
BlockSplitterFinishBlockDistance(&dist_blocks, /* is_final = */ BROTLI_TRUE); &arena->cmd_blocks, /* is_final = */ BROTLI_TRUE);
BlockSplitterFinishBlockDistance(
&arena->dist_blocks, /* is_final = */ BROTLI_TRUE);
if (num_contexts > 1) { if (num_contexts > 1) {
MapStaticContexts(m, num_contexts, static_context_map, mb); MapStaticContexts(m, num_contexts, static_context_map, mb);
@ -628,14 +637,18 @@ void BrotliBuildMetaBlockGreedy(MemoryManager* m,
const Command* commands, const Command* commands,
size_t n_commands, size_t n_commands,
MetaBlockSplit* mb) { MetaBlockSplit* mb) {
GreedyMetablockArena* arena = BROTLI_ALLOC(m, GreedyMetablockArena, 1);
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(arena)) return;
if (num_contexts == 1) { if (num_contexts == 1) {
BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte, BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask,
prev_byte2, literal_context_lut, 1, NULL, commands, n_commands, mb); prev_byte, prev_byte2, literal_context_lut, 1, NULL, commands,
n_commands, mb);
} else { } else {
BrotliBuildMetaBlockGreedyInternal(m, ringbuffer, pos, mask, prev_byte, BrotliBuildMetaBlockGreedyInternal(m, arena, ringbuffer, pos, mask,
prev_byte2, literal_context_lut, num_contexts, static_context_map, prev_byte, prev_byte2, literal_context_lut, num_contexts,
commands, n_commands, mb); static_context_map, commands, n_commands, mb);
} }
BROTLI_FREE(m, arena);
} }
void BrotliOptimizeHistograms(uint32_t num_distance_codes, void BrotliOptimizeHistograms(uint32_t num_distance_codes,

View File

@ -95,8 +95,8 @@ BROTLI_INTERNAL void BrotliBuildMetaBlockGreedy(
BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes, BROTLI_INTERNAL void BrotliOptimizeHistograms(uint32_t num_distance_codes,
MetaBlockSplit* mb); MetaBlockSplit* mb);
BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliEncoderParams* params, BROTLI_INTERNAL void BrotliInitDistanceParams(BrotliDistanceParams* params,
uint32_t npostfix, uint32_t ndirect); uint32_t npostfix, uint32_t ndirect, BROTLI_BOOL large_window);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)
} /* extern "C" */ } /* extern "C" */

View File

@ -27,6 +27,9 @@ typedef struct FN(BlockSplitter) {
HistogramType* histograms_; /* not owned */ HistogramType* histograms_; /* not owned */
size_t* histograms_size_; /* not owned */ size_t* histograms_size_; /* not owned */
/* Temporary storage for BlockSplitterFinishBlock. */
HistogramType combined_histo[2];
/* The number of symbols that we want to collect before deciding on whether /* The number of symbols that we want to collect before deciding on whether
or not to merge the block with a previous one or emit a new block. */ or not to merge the block with a previous one or emit a new block. */
size_t target_block_size_; size_t target_block_size_;
@ -104,17 +107,16 @@ static void FN(BlockSplitterFinishBlock)(
} else if (self->block_size_ > 0) { } else if (self->block_size_ > 0) {
double entropy = BitsEntropy(histograms[self->curr_histogram_ix_].data_, double entropy = BitsEntropy(histograms[self->curr_histogram_ix_].data_,
self->alphabet_size_); self->alphabet_size_);
HistogramType combined_histo[2];
double combined_entropy[2]; double combined_entropy[2];
double diff[2]; double diff[2];
size_t j; size_t j;
for (j = 0; j < 2; ++j) { for (j = 0; j < 2; ++j) {
size_t last_histogram_ix = self->last_histogram_ix_[j]; size_t last_histogram_ix = self->last_histogram_ix_[j];
combined_histo[j] = histograms[self->curr_histogram_ix_]; self->combined_histo[j] = histograms[self->curr_histogram_ix_];
FN(HistogramAddHistogram)(&combined_histo[j], FN(HistogramAddHistogram)(&self->combined_histo[j],
&histograms[last_histogram_ix]); &histograms[last_histogram_ix]);
combined_entropy[j] = BitsEntropy( combined_entropy[j] = BitsEntropy(
&combined_histo[j].data_[0], self->alphabet_size_); &self->combined_histo[j].data_[0], self->alphabet_size_);
diff[j] = combined_entropy[j] - entropy - last_entropy[j]; diff[j] = combined_entropy[j] - entropy - last_entropy[j];
} }
@ -141,7 +143,7 @@ static void FN(BlockSplitterFinishBlock)(
split->lengths[self->num_blocks_] = (uint32_t)self->block_size_; split->lengths[self->num_blocks_] = (uint32_t)self->block_size_;
split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2];
BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1); BROTLI_SWAP(size_t, self->last_histogram_ix_, 0, 1);
histograms[self->last_histogram_ix_[0]] = combined_histo[1]; histograms[self->last_histogram_ix_[0]] = self->combined_histo[1];
last_entropy[1] = last_entropy[0]; last_entropy[1] = last_entropy[0];
last_entropy[0] = combined_entropy[1]; last_entropy[0] = combined_entropy[1];
++self->num_blocks_; ++self->num_blocks_;
@ -152,7 +154,7 @@ static void FN(BlockSplitterFinishBlock)(
} else { } else {
/* Combine this block with last block. */ /* Combine this block with last block. */
split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_; split->lengths[self->num_blocks_ - 1] += (uint32_t)self->block_size_;
histograms[self->last_histogram_ix_[0]] = combined_histo[0]; histograms[self->last_histogram_ix_[0]] = self->combined_histo[0];
last_entropy[0] = combined_entropy[0]; last_entropy[0] = combined_entropy[0];
if (split->num_types == 1) { if (split->num_types == 1) {
last_entropy[1] = last_entropy[0]; last_entropy[1] = last_entropy[0];

View File

@ -156,6 +156,15 @@ public class Decoder {
totalOutputSize += chunk.length; totalOutputSize += chunk.length;
break; break;
case NEEDS_MORE_INPUT:
// Give decoder a chance to process the remaining of the buffered byte.
decoder.push(0);
// If decoder still needs input, this means that stream is truncated.
if (decoder.getStatus() == DecoderJNI.Status.NEEDS_MORE_INPUT) {
throw new IOException("corrupted input");
}
break;
default: default:
throw new IOException("corrupted input"); throw new IOException("corrupted input");
} }

View File

@ -20,22 +20,25 @@ function bytesToString(bytes) {
* @return {!Int8Array} * @return {!Int8Array}
*/ */
function stringToBytes(str) { function stringToBytes(str) {
var out = new Int8Array(str.length); let out = new Int8Array(str.length);
for (var i = 0; i < str.length; ++i) out[i] = str.charCodeAt(i); for (let i = 0; i < str.length; ++i) out[i] = str.charCodeAt(i);
return out; return out;
} }
testSuite({ testSuite({
testMetadata() { testMetadata() {
assertEquals("", bytesToString(BrotliDecode(Int8Array.from([1, 11, 0, 42, 3])))); assertEquals(
'', bytesToString(BrotliDecode(Int8Array.from([1, 11, 0, 42, 3]))));
}, },
testCompoundDictionary() { testCompoundDictionary() {
var txt = "kot lomom kolol slona\n"; const txt = 'kot lomom kolol slona\n';
var dictionary = stringToBytes(txt); const dictionary = stringToBytes(txt);
var compressed = [0xa1, 0xa8, 0x00, 0xc0, 0x2f, 0x01, 0x10, 0xc4, 0x44, 0x09, 0x00]; const compressed =
[0xa1, 0xa8, 0x00, 0xc0, 0x2f, 0x01, 0x10, 0xc4, 0x44, 0x09, 0x00];
assertEquals(txt.length, compressed.length * 2); assertEquals(txt.length, compressed.length * 2);
var options = {"customDictionary": dictionary}; const options = {'customDictionary': dictionary};
assertEquals(txt, bytesToString(BrotliDecode(Int8Array.from(compressed), options))); assertEquals(
txt, bytesToString(BrotliDecode(Int8Array.from(compressed), options)));
} }
}); });

View File

@ -5,8 +5,8 @@ if (!Int32Array.__proto__.from) {
if (!obj['length']) { if (!obj['length']) {
return new this(0); return new this(0);
} }
var typed_array = new this(obj.length); let typed_array = new this(obj.length);
for(var i = 0; i < typed_array.length; i++) { for (let i = 0; i < typed_array.length; i++) {
typed_array[i] = obj[i]; typed_array[i] = obj[i];
} }
return typed_array; return typed_array;
@ -16,12 +16,12 @@ if (!Int32Array.__proto__.from) {
if (!Array.prototype.copyWithin) { if (!Array.prototype.copyWithin) {
Array.prototype.copyWithin = function(target, start, end) { Array.prototype.copyWithin = function(target, start, end) {
var O = Object(this); let O = Object(this);
var len = O.length >>> 0; let len = O.length >>> 0;
var to = target | 0; let to = target | 0;
var from = start | 0; let from = start | 0;
var count = Math.min(Math.min(end | 0, len) - from, len - to); let count = Math.min(Math.min(end | 0, len) - from, len - to);
var direction = 1; let direction = 1;
if (from < to && to < (from + count)) { if (from < to && to < (from + count)) {
direction = -1; direction = -1;
from += count - 1; from += count - 1;
@ -41,8 +41,8 @@ if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', { Object.defineProperty(Array.prototype, 'fill', {
value: function(value, start, end) { value: function(value, start, end) {
end = end | 0; end = end | 0;
var O = Object(this); let O = Object(this);
var k = start | 0; let k = start | 0;
while (k < end) { while (k < end) {
O[k] = value; O[k] = value;
k++; k++;
@ -66,9 +66,8 @@ if (!Int32Array.prototype.fill) {
if (!Int8Array.prototype.slice) { if (!Int8Array.prototype.slice) {
Object.defineProperty(Int8Array.prototype, 'slice', { Object.defineProperty(Int8Array.prototype, 'slice', {
value: function (begin, end) value: function(begin, end) {
{ return new Int8Array(Array.prototype.slice.call(this, begin, end));
return new Int8Array(Array.prototype.slice.call(this, begin, end)); }
}
}); });
} }