xref: /aosp_15_r20/external/brotli/c/enc/cluster_inc.h (revision f4ee7fba7774faf2a30f13154332c0a06550dbc4)
1*f4ee7fbaSAndroid Build Coastguard Worker /* NOLINT(build/header_guard) */
2*f4ee7fbaSAndroid Build Coastguard Worker /* Copyright 2013 Google Inc. All Rights Reserved.
3*f4ee7fbaSAndroid Build Coastguard Worker 
4*f4ee7fbaSAndroid Build Coastguard Worker    Distributed under MIT license.
5*f4ee7fbaSAndroid Build Coastguard Worker    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
6*f4ee7fbaSAndroid Build Coastguard Worker */
7*f4ee7fbaSAndroid Build Coastguard Worker 
8*f4ee7fbaSAndroid Build Coastguard Worker /* template parameters: FN, CODE */
9*f4ee7fbaSAndroid Build Coastguard Worker 
10*f4ee7fbaSAndroid Build Coastguard Worker #define HistogramType FN(Histogram)
11*f4ee7fbaSAndroid Build Coastguard Worker 
12*f4ee7fbaSAndroid Build Coastguard Worker /* Computes the bit cost reduction by combining out[idx1] and out[idx2] and if
13*f4ee7fbaSAndroid Build Coastguard Worker    it is below a threshold, stores the pair (idx1, idx2) in the *pairs queue. */
14*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL void FN(BrotliCompareAndPushToQueue)(
15*f4ee7fbaSAndroid Build Coastguard Worker     const HistogramType* out, const uint32_t* cluster_size, uint32_t idx1,
16*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t idx2, size_t max_num_pairs, HistogramPair* pairs,
17*f4ee7fbaSAndroid Build Coastguard Worker     size_t* num_pairs) CODE({
18*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_BOOL is_good_pair = BROTLI_FALSE;
19*f4ee7fbaSAndroid Build Coastguard Worker   HistogramPair p;
20*f4ee7fbaSAndroid Build Coastguard Worker   p.idx1 = p.idx2 = 0;
21*f4ee7fbaSAndroid Build Coastguard Worker   p.cost_diff = p.cost_combo = 0;
22*f4ee7fbaSAndroid Build Coastguard Worker   if (idx1 == idx2) {
23*f4ee7fbaSAndroid Build Coastguard Worker     return;
24*f4ee7fbaSAndroid Build Coastguard Worker   }
25*f4ee7fbaSAndroid Build Coastguard Worker   if (idx2 < idx1) {
26*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t t = idx2;
27*f4ee7fbaSAndroid Build Coastguard Worker     idx2 = idx1;
28*f4ee7fbaSAndroid Build Coastguard Worker     idx1 = t;
29*f4ee7fbaSAndroid Build Coastguard Worker   }
30*f4ee7fbaSAndroid Build Coastguard Worker   p.idx1 = idx1;
31*f4ee7fbaSAndroid Build Coastguard Worker   p.idx2 = idx2;
32*f4ee7fbaSAndroid Build Coastguard Worker   p.cost_diff = 0.5 * ClusterCostDiff(cluster_size[idx1], cluster_size[idx2]);
33*f4ee7fbaSAndroid Build Coastguard Worker   p.cost_diff -= out[idx1].bit_cost_;
34*f4ee7fbaSAndroid Build Coastguard Worker   p.cost_diff -= out[idx2].bit_cost_;
35*f4ee7fbaSAndroid Build Coastguard Worker 
36*f4ee7fbaSAndroid Build Coastguard Worker   if (out[idx1].total_count_ == 0) {
37*f4ee7fbaSAndroid Build Coastguard Worker     p.cost_combo = out[idx2].bit_cost_;
38*f4ee7fbaSAndroid Build Coastguard Worker     is_good_pair = BROTLI_TRUE;
39*f4ee7fbaSAndroid Build Coastguard Worker   } else if (out[idx2].total_count_ == 0) {
40*f4ee7fbaSAndroid Build Coastguard Worker     p.cost_combo = out[idx1].bit_cost_;
41*f4ee7fbaSAndroid Build Coastguard Worker     is_good_pair = BROTLI_TRUE;
42*f4ee7fbaSAndroid Build Coastguard Worker   } else {
43*f4ee7fbaSAndroid Build Coastguard Worker     double threshold = *num_pairs == 0 ? 1e99 :
44*f4ee7fbaSAndroid Build Coastguard Worker         BROTLI_MAX(double, 0.0, pairs[0].cost_diff);
45*f4ee7fbaSAndroid Build Coastguard Worker     HistogramType combo = out[idx1];
46*f4ee7fbaSAndroid Build Coastguard Worker     double cost_combo;
47*f4ee7fbaSAndroid Build Coastguard Worker     FN(HistogramAddHistogram)(&combo, &out[idx2]);
48*f4ee7fbaSAndroid Build Coastguard Worker     cost_combo = FN(BrotliPopulationCost)(&combo);
49*f4ee7fbaSAndroid Build Coastguard Worker     if (cost_combo < threshold - p.cost_diff) {
50*f4ee7fbaSAndroid Build Coastguard Worker       p.cost_combo = cost_combo;
51*f4ee7fbaSAndroid Build Coastguard Worker       is_good_pair = BROTLI_TRUE;
52*f4ee7fbaSAndroid Build Coastguard Worker     }
53*f4ee7fbaSAndroid Build Coastguard Worker   }
54*f4ee7fbaSAndroid Build Coastguard Worker   if (is_good_pair) {
55*f4ee7fbaSAndroid Build Coastguard Worker     p.cost_diff += p.cost_combo;
56*f4ee7fbaSAndroid Build Coastguard Worker     if (*num_pairs > 0 && HistogramPairIsLess(&pairs[0], &p)) {
57*f4ee7fbaSAndroid Build Coastguard Worker       /* Replace the top of the queue if needed. */
58*f4ee7fbaSAndroid Build Coastguard Worker       if (*num_pairs < max_num_pairs) {
59*f4ee7fbaSAndroid Build Coastguard Worker         pairs[*num_pairs] = pairs[0];
60*f4ee7fbaSAndroid Build Coastguard Worker         ++(*num_pairs);
61*f4ee7fbaSAndroid Build Coastguard Worker       }
62*f4ee7fbaSAndroid Build Coastguard Worker       pairs[0] = p;
63*f4ee7fbaSAndroid Build Coastguard Worker     } else if (*num_pairs < max_num_pairs) {
64*f4ee7fbaSAndroid Build Coastguard Worker       pairs[*num_pairs] = p;
65*f4ee7fbaSAndroid Build Coastguard Worker       ++(*num_pairs);
66*f4ee7fbaSAndroid Build Coastguard Worker     }
67*f4ee7fbaSAndroid Build Coastguard Worker   }
68*f4ee7fbaSAndroid Build Coastguard Worker })
69*f4ee7fbaSAndroid Build Coastguard Worker 
70*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL size_t FN(BrotliHistogramCombine)(HistogramType* out,
71*f4ee7fbaSAndroid Build Coastguard Worker                                                   uint32_t* cluster_size,
72*f4ee7fbaSAndroid Build Coastguard Worker                                                   uint32_t* symbols,
73*f4ee7fbaSAndroid Build Coastguard Worker                                                   uint32_t* clusters,
74*f4ee7fbaSAndroid Build Coastguard Worker                                                   HistogramPair* pairs,
75*f4ee7fbaSAndroid Build Coastguard Worker                                                   size_t num_clusters,
76*f4ee7fbaSAndroid Build Coastguard Worker                                                   size_t symbols_size,
77*f4ee7fbaSAndroid Build Coastguard Worker                                                   size_t max_clusters,
78*f4ee7fbaSAndroid Build Coastguard Worker                                                   size_t max_num_pairs) CODE({
79*f4ee7fbaSAndroid Build Coastguard Worker   double cost_diff_threshold = 0.0;
80*f4ee7fbaSAndroid Build Coastguard Worker   size_t min_cluster_size = 1;
81*f4ee7fbaSAndroid Build Coastguard Worker   size_t num_pairs = 0;
82*f4ee7fbaSAndroid Build Coastguard Worker 
83*f4ee7fbaSAndroid Build Coastguard Worker   {
84*f4ee7fbaSAndroid Build Coastguard Worker     /* We maintain a vector of histogram pairs, with the property that the pair
85*f4ee7fbaSAndroid Build Coastguard Worker        with the maximum bit cost reduction is the first. */
86*f4ee7fbaSAndroid Build Coastguard Worker     size_t idx1;
87*f4ee7fbaSAndroid Build Coastguard Worker     for (idx1 = 0; idx1 < num_clusters; ++idx1) {
88*f4ee7fbaSAndroid Build Coastguard Worker       size_t idx2;
89*f4ee7fbaSAndroid Build Coastguard Worker       for (idx2 = idx1 + 1; idx2 < num_clusters; ++idx2) {
90*f4ee7fbaSAndroid Build Coastguard Worker         FN(BrotliCompareAndPushToQueue)(out, cluster_size, clusters[idx1],
91*f4ee7fbaSAndroid Build Coastguard Worker             clusters[idx2], max_num_pairs, &pairs[0], &num_pairs);
92*f4ee7fbaSAndroid Build Coastguard Worker       }
93*f4ee7fbaSAndroid Build Coastguard Worker     }
94*f4ee7fbaSAndroid Build Coastguard Worker   }
95*f4ee7fbaSAndroid Build Coastguard Worker 
96*f4ee7fbaSAndroid Build Coastguard Worker   while (num_clusters > min_cluster_size) {
97*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t best_idx1;
98*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t best_idx2;
99*f4ee7fbaSAndroid Build Coastguard Worker     size_t i;
100*f4ee7fbaSAndroid Build Coastguard Worker     if (pairs[0].cost_diff >= cost_diff_threshold) {
101*f4ee7fbaSAndroid Build Coastguard Worker       cost_diff_threshold = 1e99;
102*f4ee7fbaSAndroid Build Coastguard Worker       min_cluster_size = max_clusters;
103*f4ee7fbaSAndroid Build Coastguard Worker       continue;
104*f4ee7fbaSAndroid Build Coastguard Worker     }
105*f4ee7fbaSAndroid Build Coastguard Worker     /* Take the best pair from the top of heap. */
106*f4ee7fbaSAndroid Build Coastguard Worker     best_idx1 = pairs[0].idx1;
107*f4ee7fbaSAndroid Build Coastguard Worker     best_idx2 = pairs[0].idx2;
108*f4ee7fbaSAndroid Build Coastguard Worker     FN(HistogramAddHistogram)(&out[best_idx1], &out[best_idx2]);
109*f4ee7fbaSAndroid Build Coastguard Worker     out[best_idx1].bit_cost_ = pairs[0].cost_combo;
110*f4ee7fbaSAndroid Build Coastguard Worker     cluster_size[best_idx1] += cluster_size[best_idx2];
111*f4ee7fbaSAndroid Build Coastguard Worker     for (i = 0; i < symbols_size; ++i) {
112*f4ee7fbaSAndroid Build Coastguard Worker       if (symbols[i] == best_idx2) {
113*f4ee7fbaSAndroid Build Coastguard Worker         symbols[i] = best_idx1;
114*f4ee7fbaSAndroid Build Coastguard Worker       }
115*f4ee7fbaSAndroid Build Coastguard Worker     }
116*f4ee7fbaSAndroid Build Coastguard Worker     for (i = 0; i < num_clusters; ++i) {
117*f4ee7fbaSAndroid Build Coastguard Worker       if (clusters[i] == best_idx2) {
118*f4ee7fbaSAndroid Build Coastguard Worker         memmove(&clusters[i], &clusters[i + 1],
119*f4ee7fbaSAndroid Build Coastguard Worker                 (num_clusters - i - 1) * sizeof(clusters[0]));
120*f4ee7fbaSAndroid Build Coastguard Worker         break;
121*f4ee7fbaSAndroid Build Coastguard Worker       }
122*f4ee7fbaSAndroid Build Coastguard Worker     }
123*f4ee7fbaSAndroid Build Coastguard Worker     --num_clusters;
124*f4ee7fbaSAndroid Build Coastguard Worker     {
125*f4ee7fbaSAndroid Build Coastguard Worker       /* Remove pairs intersecting the just combined best pair. */
126*f4ee7fbaSAndroid Build Coastguard Worker       size_t copy_to_idx = 0;
127*f4ee7fbaSAndroid Build Coastguard Worker       for (i = 0; i < num_pairs; ++i) {
128*f4ee7fbaSAndroid Build Coastguard Worker         HistogramPair* p = &pairs[i];
129*f4ee7fbaSAndroid Build Coastguard Worker         if (p->idx1 == best_idx1 || p->idx2 == best_idx1 ||
130*f4ee7fbaSAndroid Build Coastguard Worker             p->idx1 == best_idx2 || p->idx2 == best_idx2) {
131*f4ee7fbaSAndroid Build Coastguard Worker           /* Remove invalid pair from the queue. */
132*f4ee7fbaSAndroid Build Coastguard Worker           continue;
133*f4ee7fbaSAndroid Build Coastguard Worker         }
134*f4ee7fbaSAndroid Build Coastguard Worker         if (HistogramPairIsLess(&pairs[0], p)) {
135*f4ee7fbaSAndroid Build Coastguard Worker           /* Replace the top of the queue if needed. */
136*f4ee7fbaSAndroid Build Coastguard Worker           HistogramPair front = pairs[0];
137*f4ee7fbaSAndroid Build Coastguard Worker           pairs[0] = *p;
138*f4ee7fbaSAndroid Build Coastguard Worker           pairs[copy_to_idx] = front;
139*f4ee7fbaSAndroid Build Coastguard Worker         } else {
140*f4ee7fbaSAndroid Build Coastguard Worker           pairs[copy_to_idx] = *p;
141*f4ee7fbaSAndroid Build Coastguard Worker         }
142*f4ee7fbaSAndroid Build Coastguard Worker         ++copy_to_idx;
143*f4ee7fbaSAndroid Build Coastguard Worker       }
144*f4ee7fbaSAndroid Build Coastguard Worker       num_pairs = copy_to_idx;
145*f4ee7fbaSAndroid Build Coastguard Worker     }
146*f4ee7fbaSAndroid Build Coastguard Worker 
147*f4ee7fbaSAndroid Build Coastguard Worker     /* Push new pairs formed with the combined histogram to the heap. */
148*f4ee7fbaSAndroid Build Coastguard Worker     for (i = 0; i < num_clusters; ++i) {
149*f4ee7fbaSAndroid Build Coastguard Worker       FN(BrotliCompareAndPushToQueue)(out, cluster_size, best_idx1, clusters[i],
150*f4ee7fbaSAndroid Build Coastguard Worker                                       max_num_pairs, &pairs[0], &num_pairs);
151*f4ee7fbaSAndroid Build Coastguard Worker     }
152*f4ee7fbaSAndroid Build Coastguard Worker   }
153*f4ee7fbaSAndroid Build Coastguard Worker   return num_clusters;
154*f4ee7fbaSAndroid Build Coastguard Worker })
155*f4ee7fbaSAndroid Build Coastguard Worker 
156*f4ee7fbaSAndroid Build Coastguard Worker /* What is the bit cost of moving histogram from cur_symbol to candidate. */
157*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL double FN(BrotliHistogramBitCostDistance)(
158*f4ee7fbaSAndroid Build Coastguard Worker     const HistogramType* histogram, const HistogramType* candidate) CODE({
159*f4ee7fbaSAndroid Build Coastguard Worker   if (histogram->total_count_ == 0) {
160*f4ee7fbaSAndroid Build Coastguard Worker     return 0.0;
161*f4ee7fbaSAndroid Build Coastguard Worker   } else {
162*f4ee7fbaSAndroid Build Coastguard Worker     HistogramType tmp = *histogram;
163*f4ee7fbaSAndroid Build Coastguard Worker     FN(HistogramAddHistogram)(&tmp, candidate);
164*f4ee7fbaSAndroid Build Coastguard Worker     return FN(BrotliPopulationCost)(&tmp) - candidate->bit_cost_;
165*f4ee7fbaSAndroid Build Coastguard Worker   }
166*f4ee7fbaSAndroid Build Coastguard Worker })
167*f4ee7fbaSAndroid Build Coastguard Worker 
168*f4ee7fbaSAndroid Build Coastguard Worker /* Find the best 'out' histogram for each of the 'in' histograms.
169*f4ee7fbaSAndroid Build Coastguard Worker    When called, clusters[0..num_clusters) contains the unique values from
170*f4ee7fbaSAndroid Build Coastguard Worker    symbols[0..in_size), but this property is not preserved in this function.
171*f4ee7fbaSAndroid Build Coastguard Worker    Note: we assume that out[]->bit_cost_ is already up-to-date. */
172*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL void FN(BrotliHistogramRemap)(const HistogramType* in,
173*f4ee7fbaSAndroid Build Coastguard Worker     size_t in_size, const uint32_t* clusters, size_t num_clusters,
174*f4ee7fbaSAndroid Build Coastguard Worker     HistogramType* out, uint32_t* symbols) CODE({
175*f4ee7fbaSAndroid Build Coastguard Worker   size_t i;
176*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < in_size; ++i) {
177*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t best_out = i == 0 ? symbols[0] : symbols[i - 1];
178*f4ee7fbaSAndroid Build Coastguard Worker     double best_bits =
179*f4ee7fbaSAndroid Build Coastguard Worker         FN(BrotliHistogramBitCostDistance)(&in[i], &out[best_out]);
180*f4ee7fbaSAndroid Build Coastguard Worker     size_t j;
181*f4ee7fbaSAndroid Build Coastguard Worker     for (j = 0; j < num_clusters; ++j) {
182*f4ee7fbaSAndroid Build Coastguard Worker       const double cur_bits =
183*f4ee7fbaSAndroid Build Coastguard Worker           FN(BrotliHistogramBitCostDistance)(&in[i], &out[clusters[j]]);
184*f4ee7fbaSAndroid Build Coastguard Worker       if (cur_bits < best_bits) {
185*f4ee7fbaSAndroid Build Coastguard Worker         best_bits = cur_bits;
186*f4ee7fbaSAndroid Build Coastguard Worker         best_out = clusters[j];
187*f4ee7fbaSAndroid Build Coastguard Worker       }
188*f4ee7fbaSAndroid Build Coastguard Worker     }
189*f4ee7fbaSAndroid Build Coastguard Worker     symbols[i] = best_out;
190*f4ee7fbaSAndroid Build Coastguard Worker   }
191*f4ee7fbaSAndroid Build Coastguard Worker 
192*f4ee7fbaSAndroid Build Coastguard Worker   /* Recompute each out based on raw and symbols. */
193*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < num_clusters; ++i) {
194*f4ee7fbaSAndroid Build Coastguard Worker     FN(HistogramClear)(&out[clusters[i]]);
195*f4ee7fbaSAndroid Build Coastguard Worker   }
196*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < in_size; ++i) {
197*f4ee7fbaSAndroid Build Coastguard Worker     FN(HistogramAddHistogram)(&out[symbols[i]], &in[i]);
198*f4ee7fbaSAndroid Build Coastguard Worker   }
199*f4ee7fbaSAndroid Build Coastguard Worker })
200*f4ee7fbaSAndroid Build Coastguard Worker 
201*f4ee7fbaSAndroid Build Coastguard Worker /* Reorders elements of the out[0..length) array and changes values in
202*f4ee7fbaSAndroid Build Coastguard Worker    symbols[0..length) array in the following way:
203*f4ee7fbaSAndroid Build Coastguard Worker      * when called, symbols[] contains indexes into out[], and has N unique
204*f4ee7fbaSAndroid Build Coastguard Worker        values (possibly N < length)
205*f4ee7fbaSAndroid Build Coastguard Worker      * on return, symbols'[i] = f(symbols[i]) and
206*f4ee7fbaSAndroid Build Coastguard Worker                   out'[symbols'[i]] = out[symbols[i]], for each 0 <= i < length,
207*f4ee7fbaSAndroid Build Coastguard Worker        where f is a bijection between the range of symbols[] and [0..N), and
208*f4ee7fbaSAndroid Build Coastguard Worker        the first occurrences of values in symbols'[i] come in consecutive
209*f4ee7fbaSAndroid Build Coastguard Worker        increasing order.
210*f4ee7fbaSAndroid Build Coastguard Worker    Returns N, the number of unique values in symbols[]. */
211*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL size_t FN(BrotliHistogramReindex)(MemoryManager* m,
212*f4ee7fbaSAndroid Build Coastguard Worker     HistogramType* out, uint32_t* symbols, size_t length) CODE({
213*f4ee7fbaSAndroid Build Coastguard Worker   static const uint32_t kInvalidIndex = BROTLI_UINT32_MAX;
214*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t* new_index = BROTLI_ALLOC(m, uint32_t, length);
215*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t next_index;
216*f4ee7fbaSAndroid Build Coastguard Worker   HistogramType* tmp;
217*f4ee7fbaSAndroid Build Coastguard Worker   size_t i;
218*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(new_index)) return 0;
219*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < length; ++i) {
220*f4ee7fbaSAndroid Build Coastguard Worker       new_index[i] = kInvalidIndex;
221*f4ee7fbaSAndroid Build Coastguard Worker   }
222*f4ee7fbaSAndroid Build Coastguard Worker   next_index = 0;
223*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < length; ++i) {
224*f4ee7fbaSAndroid Build Coastguard Worker     if (new_index[symbols[i]] == kInvalidIndex) {
225*f4ee7fbaSAndroid Build Coastguard Worker       new_index[symbols[i]] = next_index;
226*f4ee7fbaSAndroid Build Coastguard Worker       ++next_index;
227*f4ee7fbaSAndroid Build Coastguard Worker     }
228*f4ee7fbaSAndroid Build Coastguard Worker   }
229*f4ee7fbaSAndroid Build Coastguard Worker   /* TODO: by using idea of "cycle-sort" we can avoid allocation of
230*f4ee7fbaSAndroid Build Coastguard Worker      tmp and reduce the number of copying by the factor of 2. */
231*f4ee7fbaSAndroid Build Coastguard Worker   tmp = BROTLI_ALLOC(m, HistogramType, next_index);
232*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(tmp)) return 0;
233*f4ee7fbaSAndroid Build Coastguard Worker   next_index = 0;
234*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < length; ++i) {
235*f4ee7fbaSAndroid Build Coastguard Worker     if (new_index[symbols[i]] == next_index) {
236*f4ee7fbaSAndroid Build Coastguard Worker       tmp[next_index] = out[symbols[i]];
237*f4ee7fbaSAndroid Build Coastguard Worker       ++next_index;
238*f4ee7fbaSAndroid Build Coastguard Worker     }
239*f4ee7fbaSAndroid Build Coastguard Worker     symbols[i] = new_index[symbols[i]];
240*f4ee7fbaSAndroid Build Coastguard Worker   }
241*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, new_index);
242*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < next_index; ++i) {
243*f4ee7fbaSAndroid Build Coastguard Worker     out[i] = tmp[i];
244*f4ee7fbaSAndroid Build Coastguard Worker   }
245*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, tmp);
246*f4ee7fbaSAndroid Build Coastguard Worker   return next_index;
247*f4ee7fbaSAndroid Build Coastguard Worker })
248*f4ee7fbaSAndroid Build Coastguard Worker 
249*f4ee7fbaSAndroid Build Coastguard Worker BROTLI_INTERNAL void FN(BrotliClusterHistograms)(
250*f4ee7fbaSAndroid Build Coastguard Worker     MemoryManager* m, const HistogramType* in, const size_t in_size,
251*f4ee7fbaSAndroid Build Coastguard Worker     size_t max_histograms, HistogramType* out, size_t* out_size,
252*f4ee7fbaSAndroid Build Coastguard Worker     uint32_t* histogram_symbols) CODE({
253*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t* cluster_size = BROTLI_ALLOC(m, uint32_t, in_size);
254*f4ee7fbaSAndroid Build Coastguard Worker   uint32_t* clusters = BROTLI_ALLOC(m, uint32_t, in_size);
255*f4ee7fbaSAndroid Build Coastguard Worker   size_t num_clusters = 0;
256*f4ee7fbaSAndroid Build Coastguard Worker   const size_t max_input_histograms = 64;
257*f4ee7fbaSAndroid Build Coastguard Worker   size_t pairs_capacity = max_input_histograms * max_input_histograms / 2;
258*f4ee7fbaSAndroid Build Coastguard Worker   /* For the first pass of clustering, we allow all pairs. */
259*f4ee7fbaSAndroid Build Coastguard Worker   HistogramPair* pairs = BROTLI_ALLOC(m, HistogramPair, pairs_capacity + 1);
260*f4ee7fbaSAndroid Build Coastguard Worker   size_t i;
261*f4ee7fbaSAndroid Build Coastguard Worker 
262*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_IS_OOM(m) || BROTLI_IS_NULL(cluster_size) ||
263*f4ee7fbaSAndroid Build Coastguard Worker       BROTLI_IS_NULL(clusters) || BROTLI_IS_NULL(pairs)) {
264*f4ee7fbaSAndroid Build Coastguard Worker     return;
265*f4ee7fbaSAndroid Build Coastguard Worker   }
266*f4ee7fbaSAndroid Build Coastguard Worker 
267*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < in_size; ++i) {
268*f4ee7fbaSAndroid Build Coastguard Worker     cluster_size[i] = 1;
269*f4ee7fbaSAndroid Build Coastguard Worker   }
270*f4ee7fbaSAndroid Build Coastguard Worker 
271*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < in_size; ++i) {
272*f4ee7fbaSAndroid Build Coastguard Worker     out[i] = in[i];
273*f4ee7fbaSAndroid Build Coastguard Worker     out[i].bit_cost_ = FN(BrotliPopulationCost)(&in[i]);
274*f4ee7fbaSAndroid Build Coastguard Worker     histogram_symbols[i] = (uint32_t)i;
275*f4ee7fbaSAndroid Build Coastguard Worker   }
276*f4ee7fbaSAndroid Build Coastguard Worker 
277*f4ee7fbaSAndroid Build Coastguard Worker   for (i = 0; i < in_size; i += max_input_histograms) {
278*f4ee7fbaSAndroid Build Coastguard Worker     size_t num_to_combine =
279*f4ee7fbaSAndroid Build Coastguard Worker         BROTLI_MIN(size_t, in_size - i, max_input_histograms);
280*f4ee7fbaSAndroid Build Coastguard Worker     size_t num_new_clusters;
281*f4ee7fbaSAndroid Build Coastguard Worker     size_t j;
282*f4ee7fbaSAndroid Build Coastguard Worker     for (j = 0; j < num_to_combine; ++j) {
283*f4ee7fbaSAndroid Build Coastguard Worker       clusters[num_clusters + j] = (uint32_t)(i + j);
284*f4ee7fbaSAndroid Build Coastguard Worker     }
285*f4ee7fbaSAndroid Build Coastguard Worker     num_new_clusters =
286*f4ee7fbaSAndroid Build Coastguard Worker         FN(BrotliHistogramCombine)(out, cluster_size,
287*f4ee7fbaSAndroid Build Coastguard Worker                                    &histogram_symbols[i],
288*f4ee7fbaSAndroid Build Coastguard Worker                                    &clusters[num_clusters], pairs,
289*f4ee7fbaSAndroid Build Coastguard Worker                                    num_to_combine, num_to_combine,
290*f4ee7fbaSAndroid Build Coastguard Worker                                    max_histograms, pairs_capacity);
291*f4ee7fbaSAndroid Build Coastguard Worker     num_clusters += num_new_clusters;
292*f4ee7fbaSAndroid Build Coastguard Worker   }
293*f4ee7fbaSAndroid Build Coastguard Worker 
294*f4ee7fbaSAndroid Build Coastguard Worker   {
295*f4ee7fbaSAndroid Build Coastguard Worker     /* For the second pass, we limit the total number of histogram pairs.
296*f4ee7fbaSAndroid Build Coastguard Worker        After this limit is reached, we only keep searching for the best pair. */
297*f4ee7fbaSAndroid Build Coastguard Worker     size_t max_num_pairs = BROTLI_MIN(size_t,
298*f4ee7fbaSAndroid Build Coastguard Worker         64 * num_clusters, (num_clusters / 2) * num_clusters);
299*f4ee7fbaSAndroid Build Coastguard Worker     BROTLI_ENSURE_CAPACITY(
300*f4ee7fbaSAndroid Build Coastguard Worker         m, HistogramPair, pairs, pairs_capacity, max_num_pairs + 1);
301*f4ee7fbaSAndroid Build Coastguard Worker     if (BROTLI_IS_OOM(m)) return;
302*f4ee7fbaSAndroid Build Coastguard Worker 
303*f4ee7fbaSAndroid Build Coastguard Worker     /* Collapse similar histograms. */
304*f4ee7fbaSAndroid Build Coastguard Worker     num_clusters = FN(BrotliHistogramCombine)(out, cluster_size,
305*f4ee7fbaSAndroid Build Coastguard Worker                                               histogram_symbols, clusters,
306*f4ee7fbaSAndroid Build Coastguard Worker                                               pairs, num_clusters, in_size,
307*f4ee7fbaSAndroid Build Coastguard Worker                                               max_histograms, max_num_pairs);
308*f4ee7fbaSAndroid Build Coastguard Worker   }
309*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, pairs);
310*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, cluster_size);
311*f4ee7fbaSAndroid Build Coastguard Worker   /* Find the optimal map from original histograms to the final ones. */
312*f4ee7fbaSAndroid Build Coastguard Worker   FN(BrotliHistogramRemap)(in, in_size, clusters, num_clusters,
313*f4ee7fbaSAndroid Build Coastguard Worker                            out, histogram_symbols);
314*f4ee7fbaSAndroid Build Coastguard Worker   BROTLI_FREE(m, clusters);
315*f4ee7fbaSAndroid Build Coastguard Worker   /* Convert the context map to a canonical form. */
316*f4ee7fbaSAndroid Build Coastguard Worker   *out_size = FN(BrotliHistogramReindex)(m, out, histogram_symbols, in_size);
317*f4ee7fbaSAndroid Build Coastguard Worker   if (BROTLI_IS_OOM(m)) return;
318*f4ee7fbaSAndroid Build Coastguard Worker })
319*f4ee7fbaSAndroid Build Coastguard Worker 
320*f4ee7fbaSAndroid Build Coastguard Worker #undef HistogramType
321