1/* NOLINT(build/header_guard) */
2/* Copyright 2016 Google Inc. All Rights Reserved.
3
4 Distributed under MIT license.
5 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
6*/
7
8/* template parameters: FN, BUCKET_BITS, MAX_TREE_COMP_LENGTH,
9 MAX_TREE_SEARCH_DEPTH */
10
11/* A (forgetful) hash table where each hash bucket contains a binary tree of
12 sequences whose first 4 bytes share the same hash code.
13 Each sequence is MAX_TREE_COMP_LENGTH long and is identified by its starting
14 position in the input data. The binary tree is sorted by the lexicographic
15 order of the sequences, and it is also a max-heap with respect to the
16 starting positions. */
17
18#define HashToBinaryTree HASHER()
19
20#define BUCKET_SIZE (1 << BUCKET_BITS)
21
22static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }
23static BROTLI_INLINE size_t FN(StoreLookahead)(void) {
24 return MAX_TREE_COMP_LENGTH;
25}
26
27static uint32_t FN(HashBytes)(const uint8_t* data) {
28 uint32_t h = BROTLI_UNALIGNED_LOAD32LE(data) * kHashMul32;
29 /* The higher bits contain more mixture from the multiplication,
30 so we take our results from there. */
31 return h >> (32 - BUCKET_BITS);
32}
33
34typedef struct HashToBinaryTree {
35 /* The window size minus 1 */
36 size_t window_mask_;
37
38 /* Hash table that maps the 4-byte hashes of the sequence to the last
39 position where this hash was found, which is the root of the binary
40 tree of sequences that share this hash bucket. */
41 uint32_t buckets_[BUCKET_SIZE];
42
43 /* A position used to mark a non-existent sequence, i.e. a tree is empty if
44 its root is at invalid_pos_ and a node is a leaf if both its children
45 are at invalid_pos_. */
46 uint32_t invalid_pos_;
47
48 /* --- Dynamic size members --- */
49
50 /* The union of the binary trees of each hash bucket. The root of the tree
51 corresponding to a hash is a sequence starting at buckets_[hash] and
52 the left and right children of a sequence starting at pos are
53 forest_[2 * pos] and forest_[2 * pos + 1]. */
54 /* uint32_t forest[2 * num_nodes] */
55} HashToBinaryTree;
56
57static BROTLI_INLINE HashToBinaryTree* FN(Self)(HasherHandle handle) {
58 return (HashToBinaryTree*)&(GetHasherCommon(handle)[1]);
59}
60
61static BROTLI_INLINE uint32_t* FN(Forest)(HashToBinaryTree* self) {
62 return (uint32_t*)(&self[1]);
63}
64
65static void FN(Initialize)(
66 HasherHandle handle, const BrotliEncoderParams* params) {
67 HashToBinaryTree* self = FN(Self)(handle);
68 self->window_mask_ = (1u << params->lgwin) - 1u;
69 self->invalid_pos_ = (uint32_t)(0 - self->window_mask_);
70}
71
72static void FN(Prepare)(HasherHandle handle, BROTLI_BOOL one_shot,
73 size_t input_size, const uint8_t* data) {
74 HashToBinaryTree* self = FN(Self)(handle);
75 uint32_t invalid_pos = self->invalid_pos_;
76 uint32_t i;
77 BROTLI_UNUSED(data);
78 BROTLI_UNUSED(one_shot);
79 BROTLI_UNUSED(input_size);
80 for (i = 0; i < BUCKET_SIZE; i++) {
81 self->buckets_[i] = invalid_pos;
82 }
83}
84
85static BROTLI_INLINE size_t FN(HashMemAllocInBytes)(
86 const BrotliEncoderParams* params, BROTLI_BOOL one_shot,
87 size_t input_size) {
88 size_t num_nodes = (size_t)1 << params->lgwin;
89 if (one_shot && input_size < num_nodes) {
90 num_nodes = input_size;
91 }
92 return sizeof(HashToBinaryTree) + 2 * sizeof(uint32_t) * num_nodes;
93}
94
95static BROTLI_INLINE size_t FN(LeftChildIndex)(HashToBinaryTree* self,
96 const size_t pos) {
97 return 2 * (pos & self->window_mask_);
98}
99
100static BROTLI_INLINE size_t FN(RightChildIndex)(HashToBinaryTree* self,
101 const size_t pos) {
102 return 2 * (pos & self->window_mask_) + 1;
103}
104
105/* Stores the hash of the next 4 bytes and in a single tree-traversal, the
106 hash bucket's binary tree is searched for matches and is re-rooted at the
107 current position.
108
109 If less than MAX_TREE_COMP_LENGTH data is available, the hash bucket of the
110 current position is searched for matches, but the state of the hash table
111 is not changed, since we can not know the final sorting order of the
112 current (incomplete) sequence.
113
114 This function must be called with increasing cur_ix positions. */
115static BROTLI_INLINE BackwardMatch* FN(StoreAndFindMatches)(
116 HashToBinaryTree* self, const uint8_t* const BROTLI_RESTRICT data,
117 const size_t cur_ix, const size_t ring_buffer_mask, const size_t max_length,
118 const size_t max_backward, size_t* const BROTLI_RESTRICT best_len,
119 BackwardMatch* BROTLI_RESTRICT matches) {
120 const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
121 const size_t max_comp_len =
122 BROTLI_MIN(size_t, max_length, MAX_TREE_COMP_LENGTH);
123 const BROTLI_BOOL should_reroot_tree =
124 TO_BROTLI_BOOL(max_length >= MAX_TREE_COMP_LENGTH);
125 const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);
126 uint32_t* forest = FN(Forest)(self);
127 size_t prev_ix = self->buckets_[key];
128 /* The forest index of the rightmost node of the left subtree of the new
129 root, updated as we traverse and re-root the tree of the hash bucket. */
130 size_t node_left = FN(LeftChildIndex)(self, cur_ix);
131 /* The forest index of the leftmost node of the right subtree of the new
132 root, updated as we traverse and re-root the tree of the hash bucket. */
133 size_t node_right = FN(RightChildIndex)(self, cur_ix);
134 /* The match length of the rightmost node of the left subtree of the new
135 root, updated as we traverse and re-root the tree of the hash bucket. */
136 size_t best_len_left = 0;
137 /* The match length of the leftmost node of the right subtree of the new
138 root, updated as we traverse and re-root the tree of the hash bucket. */
139 size_t best_len_right = 0;
140 size_t depth_remaining;
141 if (should_reroot_tree) {
142 self->buckets_[key] = (uint32_t)cur_ix;
143 }
144 for (depth_remaining = MAX_TREE_SEARCH_DEPTH; ; --depth_remaining) {
145 const size_t backward = cur_ix - prev_ix;
146 const size_t prev_ix_masked = prev_ix & ring_buffer_mask;
147 if (backward == 0 || backward > max_backward || depth_remaining == 0) {
148 if (should_reroot_tree) {
149 forest[node_left] = self->invalid_pos_;
150 forest[node_right] = self->invalid_pos_;
151 }
152 break;
153 }
154 {
155 const size_t cur_len = BROTLI_MIN(size_t, best_len_left, best_len_right);
156 size_t len;
157 BROTLI_DCHECK(cur_len <= MAX_TREE_COMP_LENGTH);
158 len = cur_len +
159 FindMatchLengthWithLimit(&data[cur_ix_masked + cur_len],
160 &data[prev_ix_masked + cur_len],
161 max_length - cur_len);
162 BROTLI_DCHECK(
163 0 == memcmp(&data[cur_ix_masked], &data[prev_ix_masked], len));
164 if (matches && len > *best_len) {
165 *best_len = len;
166 InitBackwardMatch(matches++, backward, len);
167 }
168 if (len >= max_comp_len) {
169 if (should_reroot_tree) {
170 forest[node_left] = forest[FN(LeftChildIndex)(self, prev_ix)];
171 forest[node_right] = forest[FN(RightChildIndex)(self, prev_ix)];
172 }
173 break;
174 }
175 if (data[cur_ix_masked + len] > data[prev_ix_masked + len]) {
176 best_len_left = len;
177 if (should_reroot_tree) {
178 forest[node_left] = (uint32_t)prev_ix;
179 }
180 node_left = FN(RightChildIndex)(self, prev_ix);
181 prev_ix = forest[node_left];
182 } else {
183 best_len_right = len;
184 if (should_reroot_tree) {
185 forest[node_right] = (uint32_t)prev_ix;
186 }
187 node_right = FN(LeftChildIndex)(self, prev_ix);
188 prev_ix = forest[node_right];
189 }
190 }
191 }
192 return matches;
193}
194
195/* Finds all backward matches of &data[cur_ix & ring_buffer_mask] up to the
196 length of max_length and stores the position cur_ix in the hash table.
197
198 Sets *num_matches to the number of matches found, and stores the found
199 matches in matches[0] to matches[*num_matches - 1]. The matches will be
200 sorted by strictly increasing length and (non-strictly) increasing
201 distance. */
202static BROTLI_INLINE size_t FN(FindAllMatches)(HasherHandle handle,
203 const BrotliEncoderDictionary* dictionary, const uint8_t* data,
204 const size_t ring_buffer_mask, const size_t cur_ix,
205 const size_t max_length, const size_t max_backward,
206 const size_t gap, const BrotliEncoderParams* params,
207 BackwardMatch* matches) {
208 BackwardMatch* const orig_matches = matches;
209 const size_t cur_ix_masked = cur_ix & ring_buffer_mask;
210 size_t best_len = 1;
211 const size_t short_match_max_backward =
212 params->quality != HQ_ZOPFLIFICATION_QUALITY ? 16 : 64;
213 size_t stop = cur_ix - short_match_max_backward;
214 uint32_t dict_matches[BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1];
215 size_t i;
216 if (cur_ix < short_match_max_backward) { stop = 0; }
217 for (i = cur_ix - 1; i > stop && best_len <= 2; --i) {
218 size_t prev_ix = i;
219 const size_t backward = cur_ix - prev_ix;
220 if (BROTLI_PREDICT_FALSE(backward > max_backward)) {
221 break;
222 }
223 prev_ix &= ring_buffer_mask;
224 if (data[cur_ix_masked] != data[prev_ix] ||
225 data[cur_ix_masked + 1] != data[prev_ix + 1]) {
226 continue;
227 }
228 {
229 const size_t len =
230 FindMatchLengthWithLimit(&data[prev_ix], &data[cur_ix_masked],
231 max_length);
232 if (len > best_len) {
233 best_len = len;
234 InitBackwardMatch(matches++, backward, len);
235 }
236 }
237 }
238 if (best_len < max_length) {
239 matches = FN(StoreAndFindMatches)(FN(Self)(handle), data, cur_ix,
240 ring_buffer_mask, max_length, max_backward, &best_len, matches);
241 }
242 for (i = 0; i <= BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN; ++i) {
243 dict_matches[i] = kInvalidMatch;
244 }
245 {
246 size_t minlen = BROTLI_MAX(size_t, 4, best_len + 1);
247 if (BrotliFindAllStaticDictionaryMatches(dictionary,
248 &data[cur_ix_masked], minlen, max_length, &dict_matches[0])) {
249 size_t maxlen = BROTLI_MIN(
250 size_t, BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN, max_length);
251 size_t l;
252 for (l = minlen; l <= maxlen; ++l) {
253 uint32_t dict_id = dict_matches[l];
254 if (dict_id < kInvalidMatch) {
255 size_t distance = max_backward + gap + (dict_id >> 5) + 1;
256 if (distance <= params->dist.max_distance) {
257 InitDictionaryBackwardMatch(matches++, distance, l, dict_id & 31);
258 }
259 }
260 }
261 }
262 }
263 return (size_t)(matches - orig_matches);
264}
265
266/* Stores the hash of the next 4 bytes and re-roots the binary tree at the
267 current sequence, without returning any matches.
268 REQUIRES: ix + MAX_TREE_COMP_LENGTH <= end-of-current-block */
269static BROTLI_INLINE void FN(Store)(HasherHandle handle, const uint8_t* data,
270 const size_t mask, const size_t ix) {
271 HashToBinaryTree* self = FN(Self)(handle);
272 /* Maximum distance is window size - 16, see section 9.1. of the spec. */
273 const size_t max_backward = self->window_mask_ - BROTLI_WINDOW_GAP + 1;
274 FN(StoreAndFindMatches)(self, data, ix, mask, MAX_TREE_COMP_LENGTH,
275 max_backward, NULL, NULL);
276}
277
278static BROTLI_INLINE void FN(StoreRange)(HasherHandle handle,
279 const uint8_t* data, const size_t mask, const size_t ix_start,
280 const size_t ix_end) {
281 size_t i = ix_start;
282 size_t j = ix_start;
283 if (ix_start + 63 <= ix_end) {
284 i = ix_end - 63;
285 }
286 if (ix_start + 512 <= i) {
287 for (; j < i; j += 8) {
288 FN(Store)(handle, data, mask, j);
289 }
290 }
291 for (; i < ix_end; ++i) {
292 FN(Store)(handle, data, mask, i);
293 }
294}
295
296static BROTLI_INLINE void FN(StitchToPreviousBlock)(HasherHandle handle,
297 size_t num_bytes, size_t position, const uint8_t* ringbuffer,
298 size_t ringbuffer_mask) {
299 HashToBinaryTree* self = FN(Self)(handle);
300 if (num_bytes >= FN(HashTypeLength)() - 1 &&
301 position >= MAX_TREE_COMP_LENGTH) {
302 /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher.
303 These could not be calculated before, since they require knowledge
304 of both the previous and the current block. */
305 const size_t i_start = position - MAX_TREE_COMP_LENGTH + 1;
306 const size_t i_end = BROTLI_MIN(size_t, position, i_start + num_bytes);
307 size_t i;
308 for (i = i_start; i < i_end; ++i) {
309 /* Maximum distance is window size - 16, see section 9.1. of the spec.
310 Furthermore, we have to make sure that we don't look further back
311 from the start of the next block than the window size, otherwise we
312 could access already overwritten areas of the ring-buffer. */
313 const size_t max_backward =
314 self->window_mask_ - BROTLI_MAX(size_t,
315 BROTLI_WINDOW_GAP - 1,
316 position - i);
317 /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the
318 end of the current block and that we have at least
319 MAX_TREE_COMP_LENGTH tail in the ring-buffer. */
320 FN(StoreAndFindMatches)(self, ringbuffer, i, ringbuffer_mask,
321 MAX_TREE_COMP_LENGTH, max_backward, NULL, NULL);
322 }
323 }
324}
325
326#undef BUCKET_SIZE
327
328#undef HashToBinaryTree
329