xref: /aosp_15_r20/external/cronet/third_party/brotli/enc/encoder_dict.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /* Copyright 2017 Google Inc. All Rights Reserved.
2 
3    Distributed under MIT license.
4    See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
5 */
6 
7 #include "encoder_dict.h"
8 
9 #include <stdlib.h>  /* malloc, free */
10 
11 #include "../common/dictionary.h"
12 #include "../common/platform.h"
13 #include "../common/shared_dictionary_internal.h"
14 #include "../common/transform.h"
15 #include "compound_dictionary.h"
16 #include "dictionary_hash.h"
17 #include "memory.h"
18 #include "quality.h"
19 #include "hash.h"
20 
21 #if defined(__cplusplus) || defined(c_plusplus)
22 extern "C" {
23 #endif
24 
25 #define NUM_HASH_BITS 15u
26 #define NUM_HASH_BUCKETS (1u << NUM_HASH_BITS)
27 
BrotliTrieInit(BrotliTrie * trie)28 static void BrotliTrieInit(BrotliTrie* trie) {
29   trie->pool_capacity = 0;
30   trie->pool_size = 0;
31   trie->pool = 0;
32 
33   /* Set up the root node */
34   trie->root.single = 0;
35   trie->root.len_ = 0;
36   trie->root.idx_ = 0;
37   trie->root.sub = 0;
38 }
39 
BrotliTrieFree(MemoryManager * m,BrotliTrie * trie)40 static void BrotliTrieFree(MemoryManager* m, BrotliTrie* trie) {
41   BrotliFree(m, trie->pool);
42 }
43 
44 /* Initializes to RFC 7932 static dictionary / transforms. */
InitEncoderDictionary(BrotliEncoderDictionary * dict)45 static void InitEncoderDictionary(BrotliEncoderDictionary* dict) {
46   dict->words = BrotliGetDictionary();
47   dict->num_transforms = (uint32_t)BrotliGetTransforms()->num_transforms;
48 
49   dict->hash_table_words = kStaticDictionaryHashWords;
50   dict->hash_table_lengths = kStaticDictionaryHashLengths;
51   dict->buckets = kStaticDictionaryBuckets;
52   dict->dict_words = kStaticDictionaryWords;
53 
54   dict->cutoffTransformsCount = kCutoffTransformsCount;
55   dict->cutoffTransforms = kCutoffTransforms;
56 
57   dict->parent = 0;
58 
59   dict->hash_table_data_words_ = 0;
60   dict->hash_table_data_lengths_ = 0;
61   dict->buckets_alloc_size_ = 0;
62   dict->buckets_data_ = 0;
63   dict->dict_words_alloc_size_ = 0;
64   dict->dict_words_data_ = 0;
65   dict->words_instance_ = 0;
66   dict->has_words_heavy = BROTLI_FALSE;
67   BrotliTrieInit(&dict->trie);
68 }
69 
BrotliDestroyEncoderDictionary(MemoryManager * m,BrotliEncoderDictionary * dict)70 static void BrotliDestroyEncoderDictionary(MemoryManager* m,
71     BrotliEncoderDictionary* dict) {
72   BrotliFree(m, dict->hash_table_data_words_);
73   BrotliFree(m, dict->hash_table_data_lengths_);
74   BrotliFree(m, dict->buckets_data_);
75   BrotliFree(m, dict->dict_words_data_);
76   BrotliFree(m, dict->words_instance_);
77   BrotliTrieFree(m, &dict->trie);
78 }
79 
80 /* Word length must be at least 4 bytes */
Hash(const uint8_t * data,int bits)81 static uint32_t Hash(const uint8_t* data, int bits) {
82   uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
83   /* The higher bits contain more mixture from the multiplication,
84      so we take our results from there. */
85   return h >> (32 - bits);
86 }
87 
88 /* Theoretical max possible word size after transform */
89 #define kTransformedBufferSize \
90     (256 + 256 + SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH)
91 
92 /* To be safe buffer must have at least kTransformedBufferSize */
TransformedDictionaryWord(uint32_t word_idx,int len,int transform,const BrotliTransforms * transforms,const BrotliEncoderDictionary * dict,uint8_t * buffer,size_t * size)93 static void TransformedDictionaryWord(uint32_t word_idx, int len, int transform,
94     const BrotliTransforms* transforms,
95     const BrotliEncoderDictionary* dict,
96     uint8_t* buffer, size_t* size) {
97   const uint8_t* dict_word = &dict->words->data[
98       dict->words->offsets_by_length[len] + (uint32_t)len * word_idx];
99   *size = (size_t)BrotliTransformDictionaryWord(buffer, dict_word, len,
100       transforms, transform);
101 }
102 
MakeDictWord(uint8_t len,uint8_t transform,uint16_t idx)103 static DictWord MakeDictWord(uint8_t len, uint8_t transform, uint16_t idx) {
104   DictWord result;
105   result.len = len;
106   result.transform = transform;
107   result.idx = idx;
108   return result;
109 }
110 
BrotliTrieAlloc(MemoryManager * m,size_t num,BrotliTrie * trie,BrotliTrieNode ** keep)111 static uint32_t BrotliTrieAlloc(MemoryManager* m, size_t num, BrotliTrie* trie,
112                                 BrotliTrieNode** keep) {
113   uint32_t result;
114   uint32_t keep_index = 0;
115   if (keep && *keep != &trie->root) {
116     /* Optional node to keep, since address may change after re-allocating */
117     keep_index = (uint32_t)(*keep - trie->pool);
118   }
119   if (trie->pool_size == 0) {
120     /* Have a dummy node in the front. We do not want the result to be 0, it
121     must be at least 1, 0 represents "null pointer" */
122     trie->pool_size = 1;
123   }
124   BROTLI_ENSURE_CAPACITY(m, BrotliTrieNode, trie->pool, trie->pool_capacity,
125                          trie->pool_size + num);
126   if (BROTLI_IS_OOM(m)) return 0;
127   /* Init the new nodes to empty */
128   memset(trie->pool + trie->pool_size, 0, sizeof(*trie->pool) * num);
129   result = (uint32_t)trie->pool_size;
130   trie->pool_size += num;
131   if (keep && *keep != &trie->root) {
132     *keep = trie->pool + keep_index;
133   }
134   return result;
135 }
136 
137 /**
138  * len and idx: payload for last node
139  * word, size: the string
140  * index: position in the string
141  */
BrotliTrieNodeAdd(MemoryManager * m,uint8_t len,uint32_t idx,const uint8_t * word,size_t size,int index,BrotliTrieNode * node,BrotliTrie * trie)142 static BROTLI_BOOL BrotliTrieNodeAdd(MemoryManager* m, uint8_t len,
143     uint32_t idx, const uint8_t* word, size_t size, int index,
144     BrotliTrieNode* node, BrotliTrie* trie) {
145   BrotliTrieNode* child = 0;
146   uint8_t c;
147   if ((size_t)index == size) {
148     if (!node->len_ || idx < node->idx_) {
149       node->len_ = len;
150       node->idx_ = idx;
151     }
152     return BROTLI_TRUE;
153   }
154   c = word[index];
155   if (node->single && c != node->c) {
156     BrotliTrieNode old = trie->pool[node->sub];
157     uint32_t new_nodes = BrotliTrieAlloc(m, 32, trie, &node);
158     if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
159     node->single = 0;
160     node->sub = new_nodes;
161     trie->pool[node->sub + (node->c >> 4)].sub = new_nodes + 16;
162     trie->pool[trie->pool[node->sub + (node->c >> 4)].sub + (node->c & 15)] =
163         old;
164   }
165   if (!node->sub) {
166     uint32_t new_node = BrotliTrieAlloc(m, 1, trie, &node);
167     if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
168     node->single = 1;
169     node->c = c;
170     node->sub = new_node;
171   }
172   if (node->single) {
173     child = &trie->pool[node->sub];
174   } else {
175     if (!trie->pool[node->sub + (c >> 4)].sub) {
176       uint32_t new_nodes = BrotliTrieAlloc(m, 16, trie, &node);
177       if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
178       trie->pool[node->sub + (c >> 4)].sub = new_nodes;
179     }
180     child = &trie->pool[trie->pool[node->sub + (c >> 4)].sub + (c & 15)];
181   }
182   return BrotliTrieNodeAdd(m, len, idx, word, size, index + 1, child, trie);
183 }
184 
BrotliTrieAdd(MemoryManager * m,uint8_t len,uint32_t idx,const uint8_t * word,size_t size,BrotliTrie * trie)185 static BROTLI_BOOL BrotliTrieAdd(MemoryManager* m, uint8_t len, uint32_t idx,
186                           const uint8_t* word, size_t size, BrotliTrie* trie) {
187   return BrotliTrieNodeAdd(m, len, idx, word, size, 0, &trie->root, trie);
188 }
189 
BrotliTrieSub(const BrotliTrie * trie,const BrotliTrieNode * node,uint8_t c)190 const BrotliTrieNode* BrotliTrieSub(const BrotliTrie* trie,
191                                     const BrotliTrieNode* node, uint8_t c) {
192   BrotliTrieNode* temp_node;
193   if (node->single) {
194     if (node->c == c) return &trie->pool[node->sub];
195     return 0;
196   }
197   if (!node->sub) return 0;
198   temp_node = &trie->pool[node->sub + (c >> 4)];
199   if (!temp_node->sub) return 0;
200   return &trie->pool[temp_node->sub + (c & 15)];
201 }
202 
BrotliTrieFind(const BrotliTrie * trie,const uint8_t * word,size_t size)203 static const BrotliTrieNode* BrotliTrieFind(const BrotliTrie* trie,
204                                             const uint8_t* word, size_t size) {
205   const BrotliTrieNode* node = &trie->root;
206   size_t i;
207   for (i = 0; i < size; i++) {
208     node = BrotliTrieSub(trie, node, word[i]);
209     if (!node) return 0;
210   }
211   return node;
212 }
213 
BuildDictionaryLut(MemoryManager * m,const BrotliTransforms * transforms,BrotliEncoderDictionary * dict)214 static BROTLI_BOOL BuildDictionaryLut(MemoryManager* m,
215     const BrotliTransforms* transforms,
216     BrotliEncoderDictionary* dict) {
217   uint32_t i;
218   DictWord* dict_words;
219   uint16_t* buckets;
220   DictWord** words_by_hash;
221   size_t* words_by_hash_size;
222   size_t* words_by_hash_capacity;
223   BrotliTrie dedup;
224   uint8_t word[kTransformedBufferSize];
225   size_t word_size;
226   size_t total = 0;
227   uint8_t l;
228   uint16_t idx;
229 
230   BrotliTrieInit(&dedup);
231 
232   words_by_hash = (DictWord**)BrotliAllocate(m,
233       sizeof(*words_by_hash) * NUM_HASH_BUCKETS);
234   words_by_hash_size = (size_t*)BrotliAllocate(m,
235       sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS);
236   words_by_hash_capacity = (size_t*)BrotliAllocate(m,
237       sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS);
238   if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
239   memset(words_by_hash, 0, sizeof(*words_by_hash) * NUM_HASH_BUCKETS);
240   memset(words_by_hash_size, 0, sizeof(*words_by_hash_size) * NUM_HASH_BUCKETS);
241   memset(words_by_hash_capacity, 0,
242          sizeof(*words_by_hash_capacity) * NUM_HASH_BUCKETS);
243 
244   if (transforms->num_transforms > 0) {
245     for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH;
246         l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) {
247       uint16_t n = dict->words->size_bits_by_length[l] ?
248           (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u;
249       for (idx = 0; idx < n; ++idx) {
250         uint32_t key;
251         /* First transform (usually identity) */
252         TransformedDictionaryWord(idx, l, 0, transforms, dict, word,
253                                   &word_size);
254         /* Cannot hash words smaller than 4 bytes */
255         if (word_size < 4) {
256           /* Break instead of continue, all next words of this length will have
257              same length after transform */
258           break;
259         }
260         if (!BrotliTrieAdd(m, 0, idx, word, word_size, &dedup)) {
261           return BROTLI_FALSE;
262         }
263         key = Hash(word, NUM_HASH_BITS);
264         BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
265             words_by_hash_capacity[key], words_by_hash_size[key],
266             MakeDictWord(l, 0, idx));
267         ++total;
268       }
269     }
270   }
271 
272   /* These LUT transforms only supported if no custom transforms. This is
273      ok, we will use the heavy trie instead. */
274   if (transforms == BrotliGetTransforms()) {
275     for (l = SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH;
276         l <= SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH; ++l) {
277       uint16_t n = dict->words->size_bits_by_length[l] ?
278           (uint16_t)(1 << dict->words->size_bits_by_length[l]) : 0u;
279       for (idx = 0; idx < n; ++idx) {
280         int k;
281         BROTLI_BOOL is_ascii = BROTLI_TRUE;
282         size_t offset = dict->words->offsets_by_length[l] + (size_t)l * idx;
283         const uint8_t* data = &dict->words->data[offset];
284         for (k = 0; k < l; ++k) {
285           if (data[k] >= 128) is_ascii = BROTLI_FALSE;
286         }
287         if (data[0] < 128) {
288           int transform = 9;  /* {empty, uppercase first, empty} */
289           uint32_t ix = idx + (uint32_t)transform * n;
290           const BrotliTrieNode* it;
291           TransformedDictionaryWord(idx, l, transform, transforms,
292                                    dict, word, &word_size);
293           it = BrotliTrieFind(&dedup, word, word_size);
294           if (!it || it->idx_ > ix) {
295             uint32_t key = Hash(word, NUM_HASH_BITS);
296             if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) {
297               return BROTLI_FALSE;
298             }
299             BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
300                 words_by_hash_capacity[key], words_by_hash_size[key],
301                 MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_FIRST, idx));
302             ++total;
303           }
304         }
305         if (is_ascii) {
306           int transform = 44;  /* {empty, uppercase all, empty} */
307           uint32_t ix = idx + (uint32_t)transform * n;
308           const BrotliTrieNode* it;
309           TransformedDictionaryWord(idx, l, transform, transforms,
310                                     dict, word, &word_size);
311           it = BrotliTrieFind(&dedup, word, word_size);
312           if (!it || it->idx_ > ix) {
313             uint32_t key = Hash(word, NUM_HASH_BITS);
314             if (!BrotliTrieAdd(m, 0, ix, word, word_size, &dedup)) {
315               return BROTLI_FALSE;
316             }
317             BROTLI_ENSURE_CAPACITY_APPEND(m, DictWord, words_by_hash[key],
318                 words_by_hash_capacity[key], words_by_hash_size[key],
319                 MakeDictWord(l, BROTLI_TRANSFORM_UPPERCASE_ALL, idx));
320             ++total;
321           }
322         }
323       }
324     }
325   }
326 
327   dict_words = (DictWord*)BrotliAllocate(m,
328       sizeof(*dict->dict_words) * (total + 1));
329   buckets = (uint16_t*)BrotliAllocate(m,
330       sizeof(*dict->buckets) * NUM_HASH_BUCKETS);
331   if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
332   dict->dict_words_alloc_size_ = total + 1;
333   dict->dict_words = dict->dict_words_data_ = dict_words;
334   dict->buckets_alloc_size_ = NUM_HASH_BUCKETS;
335   dict->buckets = dict->buckets_data_ = buckets;
336 
337   /* Unused; makes offsets start from 1. */
338   dict_words[0] = MakeDictWord(0, 0, 0);
339   total = 1;
340   for (i = 0; i < NUM_HASH_BUCKETS; ++i) {
341     size_t num_words = words_by_hash_size[i];
342     if (num_words > 0) {
343       buckets[i] = (uint16_t)(total);
344       memcpy(&dict_words[total], &words_by_hash[i][0],
345           sizeof(dict_words[0]) * num_words);
346       total += num_words;
347       dict_words[total - 1].len |= 0x80;
348     } else {
349       buckets[i] = 0;
350     }
351   }
352 
353   for (i = 0; i < NUM_HASH_BUCKETS; ++i) {
354     BrotliFree(m, words_by_hash[i]);
355   }
356   BrotliFree(m, words_by_hash);
357   BrotliFree(m, words_by_hash_size);
358   BrotliFree(m, words_by_hash_capacity);
359   BrotliTrieFree(m, &dedup);
360 
361   return BROTLI_TRUE;
362 }
363 
BuildDictionaryHashTable(uint16_t * hash_table_words,uint8_t * hash_table_lengths,const BrotliDictionary * dict)364 static void BuildDictionaryHashTable(uint16_t* hash_table_words,
365     uint8_t* hash_table_lengths, const BrotliDictionary* dict) {
366   int j, len;
367   /* The order of the loops is such that in case of collision, words with
368      shorter length are preferred, and in case of same length, words with
369      smaller index. There is only a single word per bucket. */
370   /* TODO(lode): consider adding optional user-supplied frequency_map to use
371      for preferred words instead, this can make the encoder better for
372      quality 9 and below without affecting the decoder */
373   memset(hash_table_words, 0, sizeof(kStaticDictionaryHashWords));
374   memset(hash_table_lengths, 0, sizeof(kStaticDictionaryHashLengths));
375   for (len = SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH;
376       len >= SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH; --len) {
377     const size_t num_words = dict->size_bits_by_length[len] ?
378         (1u << dict->size_bits_by_length[len]) : 0;
379     for (j = (int)num_words - 1; j >= 0; --j) {
380       size_t offset = dict->offsets_by_length[len] +
381           (size_t)len * (size_t)j;
382       const uint8_t* word = &dict->data[offset];
383       const uint32_t key = Hash(word, 14);
384       int idx = (int)(key << 1) + (len < 8 ? 1 : 0);
385       BROTLI_DCHECK(idx < (int)NUM_HASH_BUCKETS);
386       hash_table_words[idx] = (uint16_t)j;
387       hash_table_lengths[idx] = (uint8_t)len;
388     }
389   }
390 }
391 
GenerateWordsHeavy(MemoryManager * m,const BrotliTransforms * transforms,BrotliEncoderDictionary * dict)392 static BROTLI_BOOL GenerateWordsHeavy(MemoryManager* m,
393     const BrotliTransforms* transforms,
394     BrotliEncoderDictionary* dict) {
395   int i, j, l;
396   for (j = (int)transforms->num_transforms - 1; j >= 0 ; --j) {
397     for (l = 0; l < 32; l++) {
398       int num = (int)((1u << dict->words->size_bits_by_length[l]) & ~1u);
399       for (i = 0; i < num; i++) {
400         uint8_t transformed[kTransformedBufferSize];
401         size_t size;
402         TransformedDictionaryWord(
403             (uint32_t)i, l, j, transforms, dict, transformed, &size);
404         if (size < 4) continue;
405         if (!BrotliTrieAdd(m, (uint8_t)l, (uint32_t)(i + num * j),
406             transformed, size, &dict->trie)) {
407           return BROTLI_FALSE;
408         }
409       }
410     }
411   }
412   return BROTLI_TRUE;
413 }
414 
415 /* Computes cutoffTransformsCount (in count) and cutoffTransforms (in data) for
416    the custom transforms, where possible within the limits of the
417    cutoffTransforms encoding. The fast encoder uses this to do fast lookup for
418    transforms that remove the N last characters (OmitLast). */
ComputeCutoffTransforms(const BrotliTransforms * transforms,uint32_t * count,uint64_t * data)419 static void ComputeCutoffTransforms(
420     const BrotliTransforms* transforms,
421     uint32_t* count, uint64_t* data) {
422   int i;
423   /* The encoding in a 64-bit integer of transform N in the data is: (N << 2) +
424      ((cutoffTransforms >> (N * 6)) & 0x3F), so for example the identity
425      transform code must be 0-63, for N=1 the transform code must be 4-67, ...,
426      for N=9 it must be 36-99.
427      TODO(lode): consider a simple flexible uint8_t[10] instead of the uint64_t
428      for the cutoff transforms, so that shared dictionaries can have the
429      OmitLast transforms anywhere without loss. */
430   *count = 0;
431   *data = 0;
432   for (i = 0; i < BROTLI_TRANSFORMS_MAX_CUT_OFF + 1; i++) {
433     int idx = transforms->cutOffTransforms[i];
434     if (idx == -1) break;  /* Not found */
435     if (idx < (i << 2)) break;  /* Too small for the encoding */
436     if (idx >= (i << 2) + 64) break;  /* Too large for the encoding */
437     (*count)++;
438     *data |= (uint64_t)(((uint64_t)idx -
439         ((uint64_t)i << 2u)) << ((uint64_t)i * 6u));
440   }
441 }
442 
ComputeDictionary(MemoryManager * m,int quality,const BrotliTransforms * transforms,BrotliEncoderDictionary * current)443 static BROTLI_BOOL ComputeDictionary(MemoryManager* m, int quality,
444     const BrotliTransforms* transforms,
445     BrotliEncoderDictionary* current) {
446   int default_words = current->words == BrotliGetDictionary();
447   int default_transforms = transforms == BrotliGetTransforms();
448 
449   if (default_words && default_transforms) {
450     /* hashes are already set to Brotli defaults */
451     return BROTLI_TRUE;
452   }
453 
454   current->hash_table_data_words_ = (uint16_t*)BrotliAllocate(
455       m, sizeof(kStaticDictionaryHashWords));
456   current->hash_table_data_lengths_ = (uint8_t*)BrotliAllocate(
457       m, sizeof(kStaticDictionaryHashLengths));
458   if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
459   current->hash_table_words = current->hash_table_data_words_;
460   current->hash_table_lengths = current->hash_table_data_lengths_;
461 
462   BuildDictionaryHashTable(current->hash_table_data_words_,
463       current->hash_table_data_lengths_, current->words);
464 
465   ComputeCutoffTransforms(transforms,
466       &current->cutoffTransformsCount, &current->cutoffTransforms);
467 
468   /* Only compute the data for slow encoder if the requested quality is high
469      enough to need it */
470   if (quality >= ZOPFLIFICATION_QUALITY) {
471     if (!BuildDictionaryLut(m, transforms, current)) return BROTLI_FALSE;
472 
473     /* For the built-in Brotli transforms, there is a hard-coded function to
474        handle all transforms, but for custom transforms, we use the following
475        large hammer instead */
476     current->has_words_heavy = !default_transforms;
477     if (current->has_words_heavy) {
478       if (!GenerateWordsHeavy(m, transforms, current)) return BROTLI_FALSE;
479     }
480   }
481 
482   return BROTLI_TRUE;
483 }
484 
BrotliInitSharedEncoderDictionary(SharedEncoderDictionary * dict)485 void BrotliInitSharedEncoderDictionary(SharedEncoderDictionary* dict) {
486   dict->magic = kSharedDictionaryMagic;
487 
488   dict->compound.num_chunks = 0;
489   dict->compound.total_size = 0;
490   dict->compound.chunk_offsets[0] = 0;
491   dict->compound.num_prepared_instances_ = 0;
492 
493   dict->contextual.context_based = 0;
494   dict->contextual.num_dictionaries = 1;
495   dict->contextual.instances_ = 0;
496   dict->contextual.num_instances_ = 1;  /* The instance_ field */
497   dict->contextual.dict[0] = &dict->contextual.instance_;
498   InitEncoderDictionary(&dict->contextual.instance_);
499   dict->contextual.instance_.parent = &dict->contextual;
500 
501   dict->max_quality = BROTLI_MAX_QUALITY;
502 }
503 
504 /* TODO(eustas): make sure that tooling will warn user if not all the cutoff
505    transforms are available (for low-quality encoder). */
InitCustomSharedEncoderDictionary(MemoryManager * m,const BrotliSharedDictionary * decoded_dict,int quality,SharedEncoderDictionary * dict)506 static BROTLI_BOOL InitCustomSharedEncoderDictionary(
507     MemoryManager* m, const BrotliSharedDictionary* decoded_dict,
508     int quality, SharedEncoderDictionary* dict) {
509   ContextualEncoderDictionary* contextual;
510   CompoundDictionary* compound;
511   BrotliEncoderDictionary* instances;
512   int i;
513   BrotliInitSharedEncoderDictionary(dict);
514 
515   contextual = &dict->contextual;
516   compound = &dict->compound;
517 
518   for (i = 0; i < (int)decoded_dict->num_prefix; i++) {
519     PreparedDictionary* prepared = CreatePreparedDictionary(m,
520         decoded_dict->prefix[i], decoded_dict->prefix_size[i]);
521     AttachPreparedDictionary(compound, prepared);
522     /* remember for cleanup */
523     compound->prepared_instances_[
524         compound->num_prepared_instances_++] = prepared;
525   }
526 
527   dict->max_quality = quality;
528   contextual->context_based = decoded_dict->context_based;
529   if (decoded_dict->context_based) {
530     memcpy(contextual->context_map, decoded_dict->context_map,
531         SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS);
532   }
533 
534   contextual->num_dictionaries = decoded_dict->num_dictionaries;
535   contextual->num_instances_ = decoded_dict->num_dictionaries;
536   if (contextual->num_instances_ == 1) {
537     instances = &contextual->instance_;
538   } else {
539     contextual->instances_ = (BrotliEncoderDictionary*)
540         BrotliAllocate(m, sizeof(*contextual->instances_) *
541         contextual->num_instances_);
542     if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
543     instances = contextual->instances_;
544   }
545   for (i = 0; i < (int)contextual->num_instances_; i++) {
546     BrotliEncoderDictionary* current = &instances[i];
547     InitEncoderDictionary(current);
548     current->parent = &dict->contextual;
549     if (decoded_dict->words[i] == BrotliGetDictionary()) {
550       current->words = BrotliGetDictionary();
551     } else {
552       current->words_instance_ = (BrotliDictionary*)BrotliAllocate(
553           m, sizeof(BrotliDictionary));
554       if (BROTLI_IS_OOM(m)) return BROTLI_FALSE;
555       *current->words_instance_ = *decoded_dict->words[i];
556       current->words = current->words_instance_;
557     }
558     current->num_transforms =
559         (uint32_t)decoded_dict->transforms[i]->num_transforms;
560     if (!ComputeDictionary(
561         m, quality, decoded_dict->transforms[i], current)) {
562       return BROTLI_FALSE;
563     }
564 
565     contextual->dict[i] = current;
566   }
567 
568   return BROTLI_TRUE;  /* success */
569 }
570 
BrotliInitCustomSharedEncoderDictionary(MemoryManager * m,const uint8_t * encoded_dict,size_t size,int quality,SharedEncoderDictionary * dict)571 BROTLI_BOOL BrotliInitCustomSharedEncoderDictionary(
572     MemoryManager* m, const uint8_t* encoded_dict, size_t size,
573     int quality, SharedEncoderDictionary* dict) {
574   BROTLI_BOOL success = BROTLI_FALSE;
575   BrotliSharedDictionary* decoded_dict = BrotliSharedDictionaryCreateInstance(
576       m->alloc_func, m->free_func, m->opaque);
577   if (!decoded_dict) {  /* OOM */
578     return BROTLI_FALSE;
579   }
580   success = BrotliSharedDictionaryAttach(
581       decoded_dict, BROTLI_SHARED_DICTIONARY_SERIALIZED, size, encoded_dict);
582   if (success) {
583     success = InitCustomSharedEncoderDictionary(m,
584         decoded_dict, quality, dict);
585   }
586   BrotliSharedDictionaryDestroyInstance(decoded_dict);
587   return success;
588 }
589 
BrotliCleanupSharedEncoderDictionary(MemoryManager * m,SharedEncoderDictionary * dict)590 void BrotliCleanupSharedEncoderDictionary(MemoryManager* m,
591                                           SharedEncoderDictionary* dict) {
592   size_t i;
593   for (i = 0; i < dict->compound.num_prepared_instances_; i++) {
594     DestroyPreparedDictionary(m,
595         (PreparedDictionary*)dict->compound.prepared_instances_[i]);
596   }
597   if (dict->contextual.num_instances_ == 1) {
598     BrotliDestroyEncoderDictionary(m, &dict->contextual.instance_);
599   } else if (dict->contextual.num_instances_ > 1) {
600     for (i = 0; i < dict->contextual.num_instances_; i++) {
601       BrotliDestroyEncoderDictionary(m, &dict->contextual.instances_[i]);
602     }
603     BrotliFree(m, dict->contextual.instances_);
604   }
605 }
606 
BrotliCreateManagedDictionary(brotli_alloc_func alloc_func,brotli_free_func free_func,void * opaque)607 ManagedDictionary* BrotliCreateManagedDictionary(
608     brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque) {
609   ManagedDictionary* result = (ManagedDictionary*)BrotliBootstrapAlloc(
610       sizeof(ManagedDictionary), alloc_func, free_func, opaque);
611   if (result == NULL) return NULL;
612 
613   result->magic = kManagedDictionaryMagic;
614   BrotliInitMemoryManager(
615       &result->memory_manager_, alloc_func, free_func, opaque);
616   result->dictionary = NULL;
617 
618   return result;
619 }
620 
BrotliDestroyManagedDictionary(ManagedDictionary * dictionary)621 void BrotliDestroyManagedDictionary(ManagedDictionary* dictionary) {
622   if (!dictionary) return;
623   BrotliBootstrapFree(dictionary, &dictionary->memory_manager_);
624 }
625 
626 /* Escalate internal functions visibility; for testing purposes only. */
627 #if defined(BROTLI_TEST)
628 void InitEncoderDictionaryForTest(BrotliEncoderDictionary*);
InitEncoderDictionaryForTest(BrotliEncoderDictionary * d)629 void InitEncoderDictionaryForTest(BrotliEncoderDictionary* d) {
630   InitEncoderDictionary(d);
631 }
632 #endif
633 
634 #if defined(__cplusplus) || defined(c_plusplus)
635 }  /* extern "C" */
636 #endif
637