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