/* What is the bit cost of moving histogram from cur_symbol to candidate. */ public static double BrotliHistogramBitCostDistance( HistogramLiteral *histogram, HistogramLiteral *candidate) { if (histogram->total_count_ == 0) { return(0.0); } else { HistogramLiteral tmp = *histogram; HistogramLiteral.HistogramAddHistogram(&tmp, candidate); return(BitCostLiteral.BrotliPopulationCost(&tmp) - candidate->bit_cost_); } }
/* Reorders elements of the out_[0..length) array and changes values in * symbols[0..length) array in the following way: * when called, symbols[] contains indexes into out_[], and has N unique * values (possibly N < length) * on return, symbols'[i] = f(symbols[i]) and * out_'[symbols'[i]] = out_[symbols[i]], for each 0 <= i < length, * where f is a bijection between the range of symbols[] and [0..N), and * the first occurrences of values in symbols'[i] come in consecutive * increasing order. * Returns N, the number of unique values in symbols[]. */ public static size_t BrotliHistogramReindex(ref MemoryManager m, HistogramLiteral *out_, uint *symbols, size_t length) { const uint kInvalidIndex = uint.MaxValue; uint * new_index = (uint *)BrotliAllocate(ref m, length * sizeof(uint)); uint next_index; HistogramLiteral *tmp; size_t i; for (i = 0; i < length; ++i) { new_index[i] = kInvalidIndex; } next_index = 0; for (i = 0; i < length; ++i) { if (new_index[symbols[i]] == kInvalidIndex) { new_index[symbols[i]] = next_index; ++next_index; } } /* TODO: by using idea of "cycle-sort" we can avoid allocation of * tmp and reduce the number of copying by the factor of 2. */ tmp = (HistogramLiteral *)BrotliAllocate(ref m, next_index * sizeof(HistogramLiteral)); next_index = 0; for (i = 0; i < length; ++i) { if (new_index[symbols[i]] == next_index) { tmp[next_index] = out_[symbols[i]]; ++next_index; } symbols[i] = new_index[symbols[i]]; } BrotliFree(ref m, new_index); for (i = 0; i < next_index; ++i) { out_[i] = tmp[i]; } BrotliFree(ref m, tmp); return(next_index); }
public static unsafe void BuildAndStoreEntropyCodes(ref MemoryManager m, BlockEncoder *self, HistogramLiteral *histograms, size_t histograms_size, HuffmanTree *tree, size_t *storage_ix, byte *storage) { size_t alphabet_size = self->alphabet_size_; size_t table_size = histograms_size * alphabet_size; self->depths_ = (byte *)BrotliAllocate(ref m, table_size * sizeof(byte)); self->bits_ = (ushort *)BrotliAllocate(ref m, table_size * sizeof(ushort)); { size_t i; for (i = 0; i < histograms_size; ++i) { size_t ix = i * alphabet_size; BuildAndStoreHuffmanTree(&histograms[i].data_[0], alphabet_size, tree, &self->depths_[ix], &self->bits_[ix], storage_ix, storage); } } }
public static size_t BrotliHistogramCombine(HistogramLiteral *out_, uint *cluster_size, uint *symbols, uint *clusters, HistogramPair *pairs, size_t num_clusters, size_t symbols_size, size_t max_clusters, size_t max_num_pairs) { double cost_diff_threshold = 0.0; size_t min_cluster_size = 1; size_t num_pairs = 0; { /* We maintain a vector of histogram pairs, with the property that the pair * with the maximum bit cost reduction is the first. */ size_t idx1; for (idx1 = 0; idx1 < num_clusters; ++idx1) { size_t idx2; for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) { BrotliCompareAndPushToQueue(out_, cluster_size, clusters[idx1], clusters[idx2], max_num_pairs, &pairs[0], &num_pairs); } } } while (num_clusters > min_cluster_size) { uint best_idx1; uint best_idx2; size_t i; if (pairs[0].cost_diff >= cost_diff_threshold) { cost_diff_threshold = 1e99; min_cluster_size = max_clusters; continue; } /* Take the best pair from the top of heap. */ best_idx1 = pairs[0].idx1; best_idx2 = pairs[0].idx2; HistogramLiteral.HistogramAddHistogram(&out_[best_idx1], &out_[best_idx2]); out_[best_idx1].bit_cost_ = pairs[0].cost_combo; cluster_size[best_idx1] += cluster_size[best_idx2]; for (i = 0; i < symbols_size; ++i) { if (symbols[i] == best_idx2) { symbols[i] = best_idx1; } } for (i = 0; i < num_clusters; ++i) { if (clusters[i] == best_idx2) { memmove(&clusters[i], &clusters[i + 1], (num_clusters - i - 1) * sizeof(uint)); break; } } --num_clusters; { /* Remove pairs intersecting the just combined best pair. */ size_t copy_to_idx = 0; for (i = 0; i < num_pairs; ++i) { HistogramPair *p = &pairs[i]; if (p->idx1 == best_idx1 || p->idx2 == best_idx1 || p->idx1 == best_idx2 || p->idx2 == best_idx2) { /* Remove invalid pair from the queue. */ continue; } if (HistogramPairIsLess(&pairs[0], p)) { /* Replace the top of the queue if needed. */ HistogramPair front = pairs[0]; pairs[0] = *p; pairs[copy_to_idx] = front; } else { pairs[copy_to_idx] = *p; } ++copy_to_idx; } num_pairs = copy_to_idx; } /* Push new pairs formed with the combined histogram to the heap. */ for (i = 0; i < num_clusters; ++i) { BrotliCompareAndPushToQueue(out_, cluster_size, best_idx1, clusters[i], max_num_pairs, &pairs[0], &num_pairs); } } return(num_clusters); }
public static void BrotliCompareAndPushToQueue( HistogramLiteral *out_, uint *cluster_size, uint idx1, uint idx2, size_t max_num_pairs, HistogramPair *pairs, size_t *num_pairs) { bool is_good_pair = false; HistogramPair p = new HistogramPair(); if (idx1 == idx2) { return; } if (idx2 < idx1) { uint t = idx2; idx2 = idx1; idx1 = t; } p.idx1 = idx1; p.idx2 = idx2; p.cost_diff = 0.5 * ClusterCostDiff(cluster_size[idx1], cluster_size[idx2]); p.cost_diff -= out_[idx1].bit_cost_; p.cost_diff -= out_[idx2].bit_cost_; if (out_[idx1].total_count_ == 0) { p.cost_combo = out_[idx2].bit_cost_; is_good_pair = true; } else if (out_[idx2].total_count_ == 0) { p.cost_combo = out_[idx1].bit_cost_; is_good_pair = true; } else { double threshold = *num_pairs == 0 ? 1e99 : Math.Max(0.0, pairs[0].cost_diff); HistogramLiteral combo = out_[idx1]; double cost_combo; HistogramLiteral.HistogramAddHistogram(&combo, &out_[idx2]); cost_combo = BitCostLiteral.BrotliPopulationCost(&combo); if (cost_combo < threshold - p.cost_diff) { p.cost_combo = cost_combo; is_good_pair = true; } } if (is_good_pair) { p.cost_diff += p.cost_combo; if (*num_pairs > 0 && HistogramPairIsLess(&pairs[0], &p)) { /* Replace the top of the queue if needed. */ if (*num_pairs < max_num_pairs) { pairs[*num_pairs] = pairs[0]; ++(*num_pairs); } pairs[0] = p; } else if (*num_pairs < max_num_pairs) { pairs[*num_pairs] = p; ++(*num_pairs); } } }
public static void BrotliClusterHistograms( ref MemoryManager m, HistogramLiteral *in_, size_t in_size, size_t max_histograms, HistogramLiteral *out_, size_t *out_size, uint *histogram_symbols) { uint * cluster_size = (uint *)BrotliAllocate(ref m, in_size * sizeof(uint)); uint * clusters = (uint *)BrotliAllocate(ref m, in_size * sizeof(uint)); size_t num_clusters = 0; size_t max_input_histograms = 64; size_t pairs_capacity = max_input_histograms * max_input_histograms / 2; /* For the first pass of clustering, we allow all pairs. */ HistogramPair *pairs = (HistogramPair *)BrotliAllocate(ref m, (pairs_capacity + 1) * sizeof(HistogramPair)); size_t i; for (i = 0; i < in_size; ++i) { cluster_size[i] = 1; } for (i = 0; i < in_size; ++i) { out_[i] = in_[i]; out_[i].bit_cost_ = BitCostLiteral.BrotliPopulationCost(&in_[i]); histogram_symbols[i] = (uint)i; } for (i = 0; i < in_size; i += max_input_histograms) { size_t num_to_combine = Math.Min(in_size - i, max_input_histograms); size_t num_new_clusters; size_t j; for (j = 0; j < num_to_combine; ++j) { clusters[num_clusters + j] = (uint)(i + j); } num_new_clusters = BrotliHistogramCombine(out_, cluster_size, &histogram_symbols[i], &clusters[num_clusters], pairs, num_to_combine, num_to_combine, max_histograms, pairs_capacity); num_clusters += num_new_clusters; } { /* For the second pass, we limit the total number of histogram pairs. * After this limit is reached, we only keep searching for the best pair. */ size_t max_num_pairs = Math.Min( 64 * num_clusters, (num_clusters / 2) * num_clusters); BrotliEnsureCapacity(ref m, sizeof(HistogramPair), (void **)&pairs, &pairs_capacity, max_num_pairs + 1); /* Collapse similar histograms. */ num_clusters = BrotliHistogramCombine(out_, cluster_size, histogram_symbols, clusters, pairs, num_clusters, in_size, max_histograms, max_num_pairs); } BrotliFree(ref m, pairs); BrotliFree(ref m, cluster_size); /* Find the optimal map from original histograms to the final ones. */ BrotliHistogramRemap(in_, in_size, clusters, num_clusters, out_, histogram_symbols); BrotliFree(ref m, clusters); /* Convert the context map to a canonical form. */ *out_size = BrotliHistogramReindex(ref m, out_, histogram_symbols, in_size); }
/* Does either of three things: * (1) emits the current block with a new block type; * (2) emits the current block with the type of the second last block; * (3) merges the current block with the last block. */ private static unsafe void ContextBlockSplitterFinishBlock( ContextBlockSplitter *self, ref MemoryManager m, bool is_final) { BlockSplit * split = self->split_; size_t num_contexts = self->num_contexts_; double * last_entropy = self->last_entropy_; HistogramLiteral *histograms = self->histograms_; if (self->block_size_ < self->min_block_size_) { self->block_size_ = self->min_block_size_; } if (self->num_blocks_ == 0) { size_t i; /* Create first block. */ split->lengths[0] = (uint)self->block_size_; split->types[0] = 0; for (i = 0; i < num_contexts; ++i) { last_entropy[i] = BitsEntropy(histograms[i].data_, self->alphabet_size_); last_entropy[num_contexts + i] = last_entropy[i]; } ++self->num_blocks_; ++split->num_types; self->curr_histogram_ix_ += num_contexts; if (self->curr_histogram_ix_ < *self->histograms_size_) { HistogramLiteral.ClearHistograms( &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); } self->block_size_ = 0; } else if (self->block_size_ > 0) { /* Try merging the set of histograms for the current block type with the * respective set of histograms for the last and second last block types. * Decide over the split based on the total reduction of entropy across * all contexts. */ double * entropy = stackalloc double[BROTLI_MAX_STATIC_CONTEXTS]; HistogramLiteral *combined_histo = (HistogramLiteral *)BrotliAllocate(ref m, 2 * num_contexts * sizeof(HistogramLiteral)); double[] combined_entropy = new double[2 * BROTLI_MAX_STATIC_CONTEXTS]; double[] diff = { 0.0, 0.0 }; size_t i; for (i = 0; i < num_contexts; ++i) { size_t curr_histo_ix = self->curr_histogram_ix_ + i; size_t j; entropy[i] = BitsEntropy(histograms[curr_histo_ix].data_, self->alphabet_size_); for (j = 0; j < 2; ++j) { size_t jx = j * num_contexts + i; size_t last_histogram_ix = (j == 0 ? self->last_histogram_ix_0 : self->last_histogram_ix_1) + i; combined_histo[jx] = histograms[curr_histo_ix]; HistogramLiteral.HistogramAddHistogram(&combined_histo[jx], &histograms[last_histogram_ix]); combined_entropy[jx] = BitsEntropy( &combined_histo[jx].data_[0], self->alphabet_size_); diff[j] += combined_entropy[jx] - entropy[i] - last_entropy[jx]; } } if (split->num_types < self->max_block_types_ && diff[0] > self->split_threshold_ && diff[1] > self->split_threshold_) { /* Create new block. */ split->lengths[self->num_blocks_] = (uint)self->block_size_; split->types[self->num_blocks_] = (byte)split->num_types; self->last_histogram_ix_1 = self->last_histogram_ix_0; self->last_histogram_ix_0 = split->num_types * num_contexts; for (i = 0; i < num_contexts; ++i) { last_entropy[num_contexts + i] = last_entropy[i]; last_entropy[i] = entropy[i]; } ++self->num_blocks_; ++split->num_types; self->curr_histogram_ix_ += num_contexts; if (self->curr_histogram_ix_ < *self->histograms_size_) { HistogramLiteral.ClearHistograms( &self->histograms_[self->curr_histogram_ix_], self->num_contexts_); } self->block_size_ = 0; self->merge_last_count_ = 0; self->target_block_size_ = self->min_block_size_; } else if (diff[1] < diff[0] - 20.0) { /* Combine this block with second last block. */ split->lengths[self->num_blocks_] = (uint)self->block_size_; split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; size_t tmp = self->last_histogram_ix_0; self->last_histogram_ix_0 = self->last_histogram_ix_1; self->last_histogram_ix_1 = tmp; for (i = 0; i < num_contexts; ++i) { histograms[self->last_histogram_ix_0 + i] = combined_histo[num_contexts + i]; last_entropy[num_contexts + i] = last_entropy[i]; last_entropy[i] = combined_entropy[num_contexts + i]; HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_ + i]); } ++self->num_blocks_; self->block_size_ = 0; self->merge_last_count_ = 0; self->target_block_size_ = self->min_block_size_; } else { /* Combine this block with last block. */ split->lengths[self->num_blocks_ - 1] += (uint)self->block_size_; for (i = 0; i < num_contexts; ++i) { histograms[self->last_histogram_ix_0 + i] = combined_histo[i]; last_entropy[i] = combined_entropy[i]; if (split->num_types == 1) { last_entropy[num_contexts + i] = last_entropy[i]; } HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_ + i]); } self->block_size_ = 0; if (++self->merge_last_count_ > 1) { self->target_block_size_ += self->min_block_size_; } } BrotliFree(ref m, combined_histo); } if (is_final) { *self->histograms_size_ = split->num_types * num_contexts; split->num_blocks = self->num_blocks_; } }
public static void HistogramAdd(HistogramLiteral *self, size_t val) { ++self->data_[val]; ++self->total_count_; }
public static void HistogramClear(HistogramLiteral *self) { memset(self->data_, 0, DATA_SIZE * sizeof(uint)); self->total_count_ = 0; self->bit_cost_ = double.MaxValue; }
public static double BrotliPopulationCost(HistogramLiteral *histogram) { const double kOneSymbolHistogramCost = 12; const double kTwoSymbolHistogramCost = 20; const double kThreeSymbolHistogramCost = 28; const double kFourSymbolHistogramCost = 37; size_t data_size = HistogramLiteral.HistogramDataSize(); int count = 0; size_t *s = stackalloc size_t[5]; double bits = 0.0; size_t i; if (histogram->total_count_ == 0) { return(kOneSymbolHistogramCost); } for (i = 0; i < data_size; ++i) { if (histogram->data_[i] > 0) { s[count] = i; ++count; if (count > 4) { break; } } } if (count == 1) { return(kOneSymbolHistogramCost); } if (count == 2) { return(kTwoSymbolHistogramCost + (double)histogram->total_count_); } if (count == 3) { uint histo0 = histogram->data_[s[0]]; uint histo1 = histogram->data_[s[1]]; uint histo2 = histogram->data_[s[2]]; uint histomax = Math.Max(histo0, Math.Max(histo1, histo2)); return(kThreeSymbolHistogramCost + 2 * (histo0 + histo1 + histo2) - histomax); } if (count == 4) { uint *histo = stackalloc uint[4]; uint h23; uint histomax; for (i = 0; i < 4; ++i) { histo[i] = histogram->data_[s[i]]; } /* Sort */ for (i = 0; i < 4; ++i) { size_t j; for (j = i + 1; j < 4; ++j) { if (histo[j] > histo[i]) { uint tmp = histo[j]; histo[j] = histo[i]; histo[i] = tmp; } } } h23 = histo[2] + histo[3]; histomax = Math.Max(h23, histo[0]); return(kFourSymbolHistogramCost + 3 * h23 + 2 * (histo[0] + histo[1]) - histomax); } { /* In this loop we compute the entropy of the histogram and simultaneously * build a simplified histogram of the code length codes where we use the * zero repeat code 17, but we don't use the non-zero repeat code 16. */ size_t max_depth = 1; uint * depth_histo = stackalloc uint[BROTLI_CODE_LENGTH_CODES]; memset(depth_histo, 0, BROTLI_CODE_LENGTH_CODES * sizeof(uint)); double log2total = FastLog2(histogram->total_count_); for (i = 0; i < data_size;) { if (histogram->data_[i] > 0) { /* Compute -log2(P(symbol)) = -log2(count(symbol)/total_count) = * = log2(total_count) - log2(count(symbol)) */ double log2p = log2total - FastLog2(histogram->data_[i]); /* Approximate the bit depth by round(-log2(P(symbol))) */ size_t depth = (size_t)(log2p + 0.5); bits += histogram->data_[i] * log2p; if (depth > 15) { depth = 15; } if (depth > max_depth) { max_depth = depth; } ++depth_histo[depth]; ++i; } else { /* Compute the run length of zeros and add the appropriate number of 0 * and 17 code length codes to the code length code histogram. */ uint reps = 1; size_t k; for (k = i + 1; k < data_size && histogram->data_[k] == 0; ++k) { ++reps; } i += reps; if (i == data_size) { /* Don't add any cost for the last zero run, since these are encoded * only implicitly. */ break; } if (reps < 3) { depth_histo[0] += reps; } else { reps -= 2; while (reps > 0) { ++depth_histo[BROTLI_REPEAT_ZERO_CODE_LENGTH]; /* Add the 3 extra bits for the 17 code length code. */ bits += 3; reps >>= 3; } } } } /* Add the estimated encoding cost of the code length code histogram. */ bits += (double)(18 + 2 * max_depth); /* Add the entropy of the code length code histogram. */ bits += BitsEntropy(depth_histo, BROTLI_CODE_LENGTH_CODES); } return(bits); }
/* Does either of three things: * (1) emits the current block with a new block type; * (2) emits the current block with the type of the second last block; * (3) merges the current block with the last block. */ public static unsafe void BlockSplitterFinishBlock( BlockSplitterLiteral *self, bool is_final) { BlockSplit * split = self->split_; double * last_entropy = self->last_entropy_; HistogramLiteral *histograms = self->histograms_; self->block_size_ = Math.Max(self->block_size_, self->min_block_size_); if (self->num_blocks_ == 0) { /* Create first block. */ split->lengths[0] = (uint)self->block_size_; split->types[0] = 0; last_entropy[0] = BitsEntropy(histograms[0].data_, self->alphabet_size_); last_entropy[1] = last_entropy[0]; ++self->num_blocks_; ++split->num_types; ++self->curr_histogram_ix_; if (self->curr_histogram_ix_ < *self->histograms_size_) { HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_]); } self->block_size_ = 0; } else if (self->block_size_ > 0) { double entropy = BitsEntropy(histograms[self->curr_histogram_ix_].data_, self->alphabet_size_); HistogramLiteral *combined_histo = stackalloc HistogramLiteral[2]; double * combined_entropy = stackalloc double[2]; double * diff = stackalloc double[2]; size_t j; for (j = 0; j < 2; ++j) { size_t last_histogram_ix = j == 0 ? self->last_histogram_ix_0 : self->last_histogram_ix_1; combined_histo[j] = histograms[self->curr_histogram_ix_]; HistogramLiteral.HistogramAddHistogram(&combined_histo[j], &histograms[last_histogram_ix]); combined_entropy[j] = BitsEntropy( &combined_histo[j].data_[0], self->alphabet_size_); diff[j] = combined_entropy[j] - entropy - last_entropy[j]; } if (split->num_types < BROTLI_MAX_NUMBER_OF_BLOCK_TYPES && diff[0] > self->split_threshold_ && diff[1] > self->split_threshold_) { /* Create new block. */ split->lengths[self->num_blocks_] = (uint)self->block_size_; split->types[self->num_blocks_] = (byte)split->num_types; self->last_histogram_ix_1 = self->last_histogram_ix_0; self->last_histogram_ix_0 = (byte)split->num_types; last_entropy[1] = last_entropy[0]; last_entropy[0] = entropy; ++self->num_blocks_; ++split->num_types; ++self->curr_histogram_ix_; if (self->curr_histogram_ix_ < *self->histograms_size_) { HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_]); } self->block_size_ = 0; self->merge_last_count_ = 0; self->target_block_size_ = self->min_block_size_; } else if (diff[1] < diff[0] - 20.0) { /* Combine this block with second last block. */ split->lengths[self->num_blocks_] = (uint)self->block_size_; split->types[self->num_blocks_] = split->types[self->num_blocks_ - 2]; size_t tmp = self->last_histogram_ix_0; self->last_histogram_ix_0 = self->last_histogram_ix_1; self->last_histogram_ix_1 = tmp; histograms[self->last_histogram_ix_0] = combined_histo[1]; last_entropy[1] = last_entropy[0]; last_entropy[0] = combined_entropy[1]; ++self->num_blocks_; self->block_size_ = 0; HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_]); self->merge_last_count_ = 0; self->target_block_size_ = self->min_block_size_; } else { /* Combine this block with last block. */ split->lengths[self->num_blocks_ - 1] += (uint)self->block_size_; histograms[self->last_histogram_ix_0] = combined_histo[0]; last_entropy[0] = combined_entropy[0]; if (split->num_types == 1) { last_entropy[1] = last_entropy[0]; } self->block_size_ = 0; HistogramLiteral.HistogramClear(&histograms[self->curr_histogram_ix_]); if (++self->merge_last_count_ > 1) { self->target_block_size_ += self->min_block_size_; } } } if (is_final) { *self->histograms_size_ = split->num_types; split->num_blocks = self->num_blocks_; } }