Skip to content

Commit 6a25ea1

Browse files
authored
Merge 60e1726 into fcda9db
2 parents fcda9db + 60e1726 commit 6a25ea1

18 files changed

Lines changed: 774 additions & 404 deletions

c/enc/block_splitter.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ static const double kCommandBlockSwitchCost = 13.5;
3030
static const double kDistanceBlockSwitchCost = 14.6;
3131
static const size_t kLiteralStrideLength = 70;
3232
static const size_t kCommandStrideLength = 40;
33+
static const size_t kDistanceStrideLength = 40;
3334
static const size_t kSymbolsPerLiteralHistogram = 544;
3435
static const size_t kSymbolsPerCommandHistogram = 530;
3536
static const size_t kSymbolsPerDistanceHistogram = 544;
@@ -119,6 +120,8 @@ void BrotliDestroyBlockSplit(MemoryManager* m, BlockSplit* self) {
119120
BROTLI_FREE(m, self->lengths);
120121
}
121122

123+
/* Extracts literals, command distance and prefix codes, then applies
124+
* SplitByteVector to create partitioning. */
122125
void BrotliSplitBlock(MemoryManager* m,
123126
const Command* cmds,
124127
const size_t num_commands,
@@ -136,14 +139,20 @@ void BrotliSplitBlock(MemoryManager* m,
136139
/* Create a continuous array of literals. */
137140
CopyLiteralsToByteArray(cmds, num_commands, data, pos, mask, literals);
138141
/* Create the block split on the array of literals.
139-
Literal histograms have alphabet size 256. */
142+
* Literal histograms can have alphabet size up to 256.
143+
* Though, to accomodate context modeling, less than half of maximum size
144+
* is allowed. */
140145
SplitByteVectorLiteral(
141146
m, literals, literals_count,
142147
kSymbolsPerLiteralHistogram, kMaxLiteralHistograms,
143148
kLiteralStrideLength, kLiteralBlockSwitchCost, params,
144149
literal_split);
145150
if (BROTLI_IS_OOM(m)) return;
146151
BROTLI_FREE(m, literals);
152+
/* NB: this might be a good place for injecting extra splitting without
153+
* increasing encoder complexity; however, output parition would be less
154+
* optimal than one produced with forced splitting inside
155+
* SplitByteVector (FindBlocks / ClusterBlocks). */
147156
}
148157

149158
{
@@ -181,7 +190,7 @@ void BrotliSplitBlock(MemoryManager* m,
181190
SplitByteVectorDistance(
182191
m, distance_prefixes, j,
183192
kSymbolsPerDistanceHistogram, kMaxCommandHistograms,
184-
kCommandStrideLength, kDistanceBlockSwitchCost, params,
193+
kDistanceStrideLength, kDistanceBlockSwitchCost, params,
185194
dist_split);
186195
if (BROTLI_IS_OOM(m)) return;
187196
BROTLI_FREE(m, distance_prefixes);

c/enc/block_splitter_inc.h

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,46 +71,56 @@ static size_t FN(FindBlocks)(const DataType* data, const size_t length,
7171
double* cost,
7272
uint8_t* switch_signal,
7373
uint8_t* block_id) {
74-
const size_t data_size = FN(HistogramDataSize)();
75-
const size_t bitmaplen = (num_histograms + 7) >> 3;
74+
const size_t alphabet_size = FN(HistogramDataSize)();
75+
const size_t bitmap_len = (num_histograms + 7) >> 3;
7676
size_t num_blocks = 1;
77+
size_t byte_ix;
7778
size_t i;
7879
size_t j;
7980
BROTLI_DCHECK(num_histograms <= 256);
81+
82+
/* Trivial case: single historgram -> single block type. */
8083
if (num_histograms <= 1) {
8184
for (i = 0; i < length; ++i) {
8285
block_id[i] = 0;
8386
}
8487
return 1;
8588
}
86-
memset(insert_cost, 0, sizeof(insert_cost[0]) * data_size * num_histograms);
89+
90+
/* Fill bitcost for each symbol of all histograms.
91+
* Non-existing symbol cost: 2 + log2(total_count).
92+
* Regular symbol cost: -log2(symbol_count / total_count). */
93+
memset(insert_cost, 0,
94+
sizeof(insert_cost[0]) * alphabet_size * num_histograms);
8795
for (i = 0; i < num_histograms; ++i) {
8896
insert_cost[i] = FastLog2((uint32_t)histograms[i].total_count_);
8997
}
90-
for (i = data_size; i != 0;) {
98+
for (i = alphabet_size; i != 0;) {
99+
/* Reverse order to use the 0-th row as a temporary storage. */
91100
--i;
92101
for (j = 0; j < num_histograms; ++j) {
93102
insert_cost[i * num_histograms + j] =
94103
insert_cost[j] - BitCost(histograms[j].data_[i]);
95104
}
96105
}
97-
memset(cost, 0, sizeof(cost[0]) * num_histograms);
98-
memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmaplen);
106+
99107
/* After each iteration of this loop, cost[k] will contain the difference
100108
between the minimum cost of arriving at the current byte position using
101109
entropy code k, and the minimum cost of arriving at the current byte
102110
position. This difference is capped at the block switch cost, and if it
103111
reaches block switch cost, it means that when we trace back from the last
104112
position, we need to switch here. */
105-
for (i = 0; i < length; ++i) {
106-
const size_t byte_ix = i;
107-
size_t ix = byte_ix * bitmaplen;
108-
size_t insert_cost_ix = data[byte_ix] * num_histograms;
113+
memset(cost, 0, sizeof(cost[0]) * num_histograms);
114+
memset(switch_signal, 0, sizeof(switch_signal[0]) * length * bitmap_len);
115+
for (byte_ix = 0; byte_ix < length; ++byte_ix) {
116+
size_t ix = byte_ix * bitmap_len;
117+
size_t symbol = data[byte_ix];
118+
size_t insert_cost_ix = symbol * num_histograms;
109119
double min_cost = 1e99;
110120
double block_switch_cost = block_switch_bitcost;
111121
size_t k;
112122
for (k = 0; k < num_histograms; ++k) {
113-
/* We are coding the symbol in data[byte_ix] with entropy code k. */
123+
/* We are coding the symbol with entropy code k. */
114124
cost[k] += insert_cost[insert_cost_ix + k];
115125
if (cost[k] < min_cost) {
116126
min_cost = cost[k];
@@ -126,20 +136,21 @@ static size_t FN(FindBlocks)(const DataType* data, const size_t length,
126136
if (cost[k] >= block_switch_cost) {
127137
const uint8_t mask = (uint8_t)(1u << (k & 7));
128138
cost[k] = block_switch_cost;
129-
BROTLI_DCHECK((k >> 3) < bitmaplen);
139+
BROTLI_DCHECK((k >> 3) < bitmap_len);
130140
switch_signal[ix + (k >> 3)] |= mask;
131141
}
132142
}
133143
}
144+
145+
byte_ix = length - 1;
134146
{ /* Trace back from the last position and switch at the marked places. */
135-
size_t byte_ix = length - 1;
136-
size_t ix = byte_ix * bitmaplen;
147+
size_t ix = byte_ix * bitmap_len;
137148
uint8_t cur_id = block_id[byte_ix];
138149
while (byte_ix > 0) {
139150
const uint8_t mask = (uint8_t)(1u << (cur_id & 7));
140-
BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmaplen);
151+
BROTLI_DCHECK(((size_t)cur_id >> 3) < bitmap_len);
141152
--byte_ix;
142-
ix -= bitmaplen;
153+
ix -= bitmap_len;
143154
if (switch_signal[ix + (cur_id >> 3)] & mask) {
144155
if (cur_id != block_id[byte_ix]) {
145156
cur_id = block_id[byte_ix];
@@ -185,6 +196,8 @@ static void FN(BuildBlockHistograms)(const DataType* data, const size_t length,
185196
}
186197
}
187198

199+
/* Given the initial partitioning build partitioning with limited number
200+
* of histograms (and block types). */
188201
static void FN(ClusterBlocks)(MemoryManager* m,
189202
const DataType* data, const size_t length,
190203
const size_t num_blocks,
@@ -228,6 +241,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
228241

229242
memset(block_lengths, 0, num_blocks * sizeof(uint32_t));
230243

244+
/* Calculate block lengths (convert repeating values -> series length). */
231245
{
232246
size_t block_idx = 0;
233247
for (i = 0; i < length; ++i) {
@@ -240,15 +254,17 @@ static void FN(ClusterBlocks)(MemoryManager* m,
240254
BROTLI_DCHECK(block_idx == num_blocks);
241255
}
242256

257+
/* Pre-cluster blocks (cluster batches). */
243258
for (i = 0; i < num_blocks; i += HISTOGRAMS_PER_BATCH) {
244259
const size_t num_to_combine =
245260
BROTLI_MIN(size_t, num_blocks - i, HISTOGRAMS_PER_BATCH);
246261
size_t num_new_clusters;
247262
size_t j;
248263
for (j = 0; j < num_to_combine; ++j) {
249264
size_t k;
265+
size_t block_length = block_lengths[i + j];
250266
FN(HistogramClear)(&histograms[j]);
251-
for (k = 0; k < block_lengths[i + j]; ++k) {
267+
for (k = 0; k < block_length; ++k) {
252268
FN(HistogramAdd)(&histograms[j], data[pos++]);
253269
}
254270
histograms[j].bit_cost_ = FN(BrotliPopulationCost)(&histograms[j]);
@@ -278,14 +294,14 @@ static void FN(ClusterBlocks)(MemoryManager* m,
278294
}
279295
BROTLI_FREE(m, histograms);
280296

297+
/* Final clustering. */
281298
max_num_pairs =
282299
BROTLI_MIN(size_t, 64 * num_clusters, (num_clusters / 2) * num_clusters);
283300
if (pairs_capacity < max_num_pairs + 1) {
284301
BROTLI_FREE(m, pairs);
285302
pairs = BROTLI_ALLOC(m, HistogramPair, max_num_pairs + 1);
286303
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(pairs)) return;
287304
}
288-
289305
clusters = BROTLI_ALLOC(m, uint32_t, num_clusters);
290306
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(clusters)) return;
291307
for (i = 0; i < num_clusters; ++i) {
@@ -298,6 +314,7 @@ static void FN(ClusterBlocks)(MemoryManager* m,
298314
BROTLI_FREE(m, pairs);
299315
BROTLI_FREE(m, cluster_size);
300316

317+
/* Assign blocks to final histograms. */
301318
new_index = BROTLI_ALLOC(m, uint32_t, num_clusters);
302319
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return;
303320
for (i = 0; i < num_clusters; ++i) new_index[i] = kInvalidIndex;
@@ -313,6 +330,8 @@ static void FN(ClusterBlocks)(MemoryManager* m,
313330
for (j = 0; j < block_lengths[i]; ++j) {
314331
FN(HistogramAdd)(&histo, data[pos++]);
315332
}
333+
/* Among equally good histograms prefer last used. */
334+
/* TODO: should we give a block-switch discount here? */
316335
best_out = (i == 0) ? histogram_symbols[0] : histogram_symbols[i - 1];
317336
best_bits =
318337
FN(BrotliHistogramBitCostDistance)(&histo, &all_histograms[best_out]);
@@ -337,6 +356,9 @@ static void FN(ClusterBlocks)(MemoryManager* m,
337356
BROTLI_ENSURE_CAPACITY(
338357
m, uint32_t, split->lengths, split->lengths_alloc_size, num_blocks);
339358
if (BROTLI_IS_OOM(m)) return;
359+
360+
/* Rewrite final assignment to block-split. There might be less blocks
361+
* than |num_blocks| due to clustering. */
340362
{
341363
uint32_t cur_length = 0;
342364
size_t block_idx = 0;
@@ -361,24 +383,36 @@ static void FN(ClusterBlocks)(MemoryManager* m,
361383
BROTLI_FREE(m, histogram_symbols);
362384
}
363385

386+
/* Create BlockSplit (partitioning) given the limits, estimates and "effort"
387+
* parameters.
388+
*
389+
* NB: max_histograms is often less than number of histograms allowed by format;
390+
* this is done intentionally, to save some "space" for context-aware
391+
* clustering (here entropy is estimated for context-free symbols). */
364392
static void FN(SplitByteVector)(MemoryManager* m,
365393
const DataType* data, const size_t length,
366-
const size_t literals_per_histogram,
394+
const size_t symbols_per_histogram,
367395
const size_t max_histograms,
368396
const size_t sampling_stride_length,
369397
const double block_switch_cost,
370398
const BrotliEncoderParams* params,
371399
BlockSplit* split) {
372400
const size_t data_size = FN(HistogramDataSize)();
373-
size_t num_histograms = length / literals_per_histogram + 1;
374401
HistogramType* histograms;
402+
/* Calculate number of histograms; initial estimate is one histogram per
403+
* specified amount of symbols; however, this value is capped. */
404+
size_t num_histograms = length / symbols_per_histogram + 1;
375405
if (num_histograms > max_histograms) {
376406
num_histograms = max_histograms;
377407
}
408+
409+
/* Corner case: no input. */
378410
if (length == 0) {
379411
split->num_types = 1;
380412
return;
381-
} else if (length < kMinLengthForBlockSplitting) {
413+
}
414+
415+
if (length < kMinLengthForBlockSplitting) {
382416
BROTLI_ENSURE_CAPACITY(m, uint8_t,
383417
split->types, split->types_alloc_size, split->num_blocks + 1);
384418
BROTLI_ENSURE_CAPACITY(m, uint32_t,

c/enc/hash.h

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef BROTLI_ENC_HASH_H_
1111
#define BROTLI_ENC_HASH_H_
1212

13+
#include <stdlib.h> /* exit */
1314
#include <string.h> /* memcmp, memset */
1415

1516
#include "../common/constants.h"
@@ -28,15 +29,28 @@ extern "C" {
2829
#endif
2930

3031
typedef struct {
31-
/* Dynamically allocated area; first member for quickest access. */
32-
void* extra;
32+
/**
33+
* Dynamically allocated areas; regular hasher uses one or two allocations;
34+
* "composite" hasher uses up to 4 allocations.
35+
*/
36+
void* extra[4];
37+
38+
/**
39+
* False before the fisrt invocation of HasherSetup (where "extra" memory)
40+
* is allocated.
41+
*/
42+
BROTLI_BOOL is_setup_;
3343

3444
size_t dict_num_lookups;
3545
size_t dict_num_matches;
3646

3747
BrotliHasherParams params;
3848

39-
/* False if hasher needs to be "prepared" before use. */
49+
/**
50+
* False if hasher needs to be "prepared" before use (before the first
51+
* invocation of HasherSetup or after HasherReset). "preparation" is hasher
52+
* data initialization (using input ringbuffer).
53+
*/
4054
BROTLI_BOOL is_prepared_;
4155
} HasherCommon;
4256

@@ -391,42 +405,52 @@ typedef struct {
391405

392406
/* MUST be invoked before any other method. */
393407
static BROTLI_INLINE void HasherInit(Hasher* hasher) {
394-
hasher->common.extra = NULL;
408+
hasher->common.is_setup_ = BROTLI_FALSE;
409+
hasher->common.extra[0] = NULL;
410+
hasher->common.extra[1] = NULL;
411+
hasher->common.extra[2] = NULL;
412+
hasher->common.extra[3] = NULL;
395413
}
396414

397415
static BROTLI_INLINE void DestroyHasher(MemoryManager* m, Hasher* hasher) {
398-
if (hasher->common.extra == NULL) return;
399-
BROTLI_FREE(m, hasher->common.extra);
416+
if (hasher->common.extra[0] != NULL) BROTLI_FREE(m, hasher->common.extra[0]);
417+
if (hasher->common.extra[1] != NULL) BROTLI_FREE(m, hasher->common.extra[1]);
418+
if (hasher->common.extra[2] != NULL) BROTLI_FREE(m, hasher->common.extra[2]);
419+
if (hasher->common.extra[3] != NULL) BROTLI_FREE(m, hasher->common.extra[3]);
400420
}
401421

402422
static BROTLI_INLINE void HasherReset(Hasher* hasher) {
403423
hasher->common.is_prepared_ = BROTLI_FALSE;
404424
}
405425

406-
static BROTLI_INLINE size_t HasherSize(const BrotliEncoderParams* params,
407-
BROTLI_BOOL one_shot, const size_t input_size) {
426+
static BROTLI_INLINE void HasherSize(const BrotliEncoderParams* params,
427+
BROTLI_BOOL one_shot, const size_t input_size, size_t* alloc_size) {
408428
switch (params->hasher.type) {
409-
#define SIZE_(N) \
410-
case N: \
411-
return HashMemAllocInBytesH ## N(params, one_shot, input_size);
429+
#define SIZE_(N) \
430+
case N: \
431+
HashMemAllocInBytesH ## N(params, one_shot, input_size, alloc_size); \
432+
break;
412433
FOR_ALL_HASHERS(SIZE_)
413434
#undef SIZE_
414435
default:
415436
break;
416437
}
417-
return 0; /* Default case. */
418438
}
419439

420440
static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher,
421441
BrotliEncoderParams* params, const uint8_t* data, size_t position,
422442
size_t input_size, BROTLI_BOOL is_last) {
423443
BROTLI_BOOL one_shot = (position == 0 && is_last);
424-
if (hasher->common.extra == NULL) {
425-
size_t alloc_size;
444+
if (!hasher->common.is_setup_) {
445+
size_t alloc_size[4] = {0};
446+
size_t i;
426447
ChooseHasher(params, &params->hasher);
427-
alloc_size = HasherSize(params, one_shot, input_size);
428-
hasher->common.extra = BROTLI_ALLOC(m, uint8_t, alloc_size);
429-
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra)) return;
448+
HasherSize(params, one_shot, input_size, alloc_size);
449+
for (i = 0; i < 4; ++i) {
450+
if (alloc_size[i] == 0) continue;
451+
hasher->common.extra[i] = BROTLI_ALLOC(m, uint8_t, alloc_size[i]);
452+
if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(hasher->common.extra[i])) return;
453+
}
430454
hasher->common.params = params->hasher;
431455
switch (hasher->common.params.type) {
432456
#define INITIALIZE_(N) \
@@ -440,6 +464,7 @@ static BROTLI_INLINE void HasherSetup(MemoryManager* m, Hasher* hasher,
440464
break;
441465
}
442466
HasherReset(hasher);
467+
hasher->common.is_setup_ = BROTLI_TRUE;
443468
}
444469

445470
if (!hasher->common.is_prepared_) {

0 commit comments

Comments
 (0)