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 ¤t->cutoffTransformsCount, ¤t->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