Esempio n. 1
0
 /* 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_);
     }
 }
Esempio n. 2
0
            /* 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);
            }
Esempio n. 3
0
            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);
                    }
                }
            }
Esempio n. 4
0
            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);
            }
Esempio n. 5
0
            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);
                    }
                }
            }
Esempio n. 6
0
            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);
            }
Esempio n. 7
0
        /* 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_;
            }
        }
Esempio n. 8
0
 public static void HistogramAdd(HistogramLiteral *self, size_t val)
 {
     ++self->data_[val];
     ++self->total_count_;
 }
Esempio n. 9
0
 public static void HistogramClear(HistogramLiteral *self)
 {
     memset(self->data_, 0, DATA_SIZE * sizeof(uint));
     self->total_count_ = 0;
     self->bit_cost_    = double.MaxValue;
 }
Esempio n. 10
0
            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);
            }
Esempio n. 11
0
            /* 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_;
                }
            }