]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/C/BrotliCompress/enc/hash_longest_match_inc.h
BaseTools: resolve initialization order errors in VfrFormPkg.h
[mirror_edk2.git] / BaseTools / Source / C / BrotliCompress / enc / hash_longest_match_inc.h
CommitLineData
11b7501a
SB
1/* NOLINT(build/header_guard) */\r
2/* Copyright 2010 Google Inc. All Rights Reserved.\r
3\r
4 Distributed under MIT license.\r
5 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT\r
6*/\r
7\r
8/* template parameters: FN, BUCKET_BITS, BLOCK_BITS,\r
9 NUM_LAST_DISTANCES_TO_CHECK */\r
10\r
11/* A (forgetful) hash table to the data seen by the compressor, to\r
12 help create backward references to previous data.\r
13\r
14 This is a hash map of fixed size (BUCKET_SIZE) to a ring buffer of\r
15 fixed size (BLOCK_SIZE). The ring buffer contains the last BLOCK_SIZE\r
16 index positions of the given hash key in the compressed data. */\r
17\r
18#define HashLongestMatch HASHER()\r
19\r
20/* Number of hash buckets. */\r
21#define BUCKET_SIZE (1 << BUCKET_BITS)\r
22\r
23/* Only BLOCK_SIZE newest backward references are kept,\r
24 and the older are forgotten. */\r
25#define BLOCK_SIZE (1u << BLOCK_BITS)\r
26\r
27/* Mask for accessing entries in a block (in a ringbuffer manner). */\r
28#define BLOCK_MASK ((1 << BLOCK_BITS) - 1)\r
29\r
30#define HASH_MAP_SIZE (2 << BUCKET_BITS)\r
31\r
32static BROTLI_INLINE size_t FN(HashTypeLength)(void) { return 4; }\r
33static BROTLI_INLINE size_t FN(StoreLookahead)(void) { return 4; }\r
34\r
35/* HashBytes is the function that chooses the bucket to place\r
36 the address in. The HashLongestMatch and HashLongestMatchQuickly\r
37 classes have separate, different implementations of hashing. */\r
38static uint32_t FN(HashBytes)(const uint8_t *data) {\r
39 uint32_t h = BROTLI_UNALIGNED_LOAD32(data) * kHashMul32;\r
40 /* The higher bits contain more mixture from the multiplication,\r
41 so we take our results from there. */\r
42 return h >> (32 - BUCKET_BITS);\r
43}\r
44\r
45typedef struct HashLongestMatch {\r
46 /* Number of entries in a particular bucket. */\r
47 uint16_t num_[BUCKET_SIZE];\r
48\r
49 /* Buckets containing BLOCK_SIZE of backward references. */\r
50 uint32_t buckets_[BLOCK_SIZE << BUCKET_BITS];\r
51\r
52 /* True if num_ array needs to be initialized. */\r
53 BROTLI_BOOL is_dirty_;\r
54\r
55 DictionarySearchStatictics dict_search_stats_;\r
56} HashLongestMatch;\r
57\r
58static void FN(Reset)(HashLongestMatch* self) {\r
59 self->is_dirty_ = BROTLI_TRUE;\r
60 DictionarySearchStaticticsReset(&self->dict_search_stats_);\r
61}\r
62\r
63static void FN(InitEmpty)(HashLongestMatch* self) {\r
64 if (self->is_dirty_) {\r
65 memset(self->num_, 0, sizeof(self->num_));\r
66 self->is_dirty_ = BROTLI_FALSE;\r
67 }\r
68}\r
69\r
70static void FN(InitForData)(HashLongestMatch* self, const uint8_t* data,\r
71 size_t num) {\r
72 size_t i;\r
73 for (i = 0; i < num; ++i) {\r
74 const uint32_t key = FN(HashBytes)(&data[i]);\r
75 self->num_[key] = 0;\r
76 }\r
77 if (num != 0) {\r
78 self->is_dirty_ = BROTLI_FALSE;\r
79 }\r
80}\r
81\r
82static void FN(Init)(\r
83 MemoryManager* m, HashLongestMatch* self, const uint8_t* data,\r
84 const BrotliEncoderParams* params, size_t position, size_t bytes,\r
85 BROTLI_BOOL is_last) {\r
86 /* Choose which init method is faster.\r
87 Init() is about 100 times faster than InitForData(). */\r
88 const size_t kMaxBytesForPartialHashInit = HASH_MAP_SIZE >> 7;\r
89 BROTLI_UNUSED(m);\r
90 BROTLI_UNUSED(params);\r
91 if (position == 0 && is_last && bytes <= kMaxBytesForPartialHashInit) {\r
92 FN(InitForData)(self, data, bytes);\r
93 } else {\r
94 FN(InitEmpty)(self);\r
95 }\r
96}\r
97\r
98/* Look at 4 bytes at &data[ix & mask].\r
99 Compute a hash from these, and store the value of ix at that position. */\r
100static BROTLI_INLINE void FN(Store)(HashLongestMatch* self, const uint8_t *data,\r
101 const size_t mask, const size_t ix) {\r
102 const uint32_t key = FN(HashBytes)(&data[ix & mask]);\r
103 const size_t minor_ix = self->num_[key] & BLOCK_MASK;\r
104 self->buckets_[minor_ix + (key << BLOCK_BITS)] = (uint32_t)ix;\r
105 ++self->num_[key];\r
106}\r
107\r
108static BROTLI_INLINE void FN(StoreRange)(HashLongestMatch* self,\r
109 const uint8_t *data, const size_t mask, const size_t ix_start,\r
110 const size_t ix_end) {\r
111 size_t i;\r
112 for (i = ix_start; i < ix_end; ++i) {\r
113 FN(Store)(self, data, mask, i);\r
114 }\r
115}\r
116\r
117static BROTLI_INLINE void FN(StitchToPreviousBlock)(HashLongestMatch* self,\r
118 size_t num_bytes, size_t position, const uint8_t* ringbuffer,\r
119 size_t ringbuffer_mask) {\r
120 if (num_bytes >= FN(HashTypeLength)() - 1 && position >= 3) {\r
121 /* Prepare the hashes for three last bytes of the last write.\r
122 These could not be calculated before, since they require knowledge\r
123 of both the previous and the current block. */\r
124 FN(Store)(self, ringbuffer, ringbuffer_mask, position - 3);\r
125 FN(Store)(self, ringbuffer, ringbuffer_mask, position - 2);\r
126 FN(Store)(self, ringbuffer, ringbuffer_mask, position - 1);\r
127 }\r
128}\r
129\r
130/* Find a longest backward match of &data[cur_ix] up to the length of\r
131 max_length and stores the position cur_ix in the hash table.\r
132\r
133 Does not look for matches longer than max_length.\r
134 Does not look for matches further away than max_backward.\r
135 Writes the best match into |out|.\r
136 Returns true when match is found, otherwise false. */\r
137static BROTLI_INLINE BROTLI_BOOL FN(FindLongestMatch)(HashLongestMatch* self,\r
138 const uint8_t* BROTLI_RESTRICT data, const size_t ring_buffer_mask,\r
139 const int* BROTLI_RESTRICT distance_cache, const size_t cur_ix,\r
140 const size_t max_length, const size_t max_backward,\r
141 HasherSearchResult* BROTLI_RESTRICT out) {\r
142 const size_t cur_ix_masked = cur_ix & ring_buffer_mask;\r
143 BROTLI_BOOL is_match_found = BROTLI_FALSE;\r
144 /* Don't accept a short copy from far away. */\r
145 score_t best_score = out->score;\r
146 size_t best_len = out->len;\r
147 size_t i;\r
148 out->len = 0;\r
149 out->len_x_code = 0;\r
150 /* Try last distance first. */\r
151 for (i = 0; i < NUM_LAST_DISTANCES_TO_CHECK; ++i) {\r
152 const size_t idx = kDistanceCacheIndex[i];\r
153 const size_t backward =\r
154 (size_t)(distance_cache[idx] + kDistanceCacheOffset[i]);\r
155 size_t prev_ix = (size_t)(cur_ix - backward);\r
156 if (prev_ix >= cur_ix) {\r
157 continue;\r
158 }\r
159 if (PREDICT_FALSE(backward > max_backward)) {\r
160 continue;\r
161 }\r
162 prev_ix &= ring_buffer_mask;\r
163\r
164 if (cur_ix_masked + best_len > ring_buffer_mask ||\r
165 prev_ix + best_len > ring_buffer_mask ||\r
166 data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {\r
167 continue;\r
168 }\r
169 {\r
170 const size_t len = FindMatchLengthWithLimit(&data[prev_ix],\r
171 &data[cur_ix_masked],\r
172 max_length);\r
173 if (len >= 3 || (len == 2 && i < 2)) {\r
174 /* Comparing for >= 2 does not change the semantics, but just saves for\r
175 a few unnecessary binary logarithms in backward reference score,\r
176 since we are not interested in such short matches. */\r
177 score_t score = BackwardReferenceScoreUsingLastDistance(len, i);\r
178 if (best_score < score) {\r
179 best_score = score;\r
180 best_len = len;\r
181 out->len = best_len;\r
182 out->distance = backward;\r
183 out->score = best_score;\r
184 is_match_found = BROTLI_TRUE;\r
185 }\r
186 }\r
187 }\r
188 }\r
189 {\r
190 const uint32_t key = FN(HashBytes)(&data[cur_ix_masked]);\r
191 uint32_t* BROTLI_RESTRICT bucket = &self->buckets_[key << BLOCK_BITS];\r
192 const size_t down =\r
193 (self->num_[key] > BLOCK_SIZE) ? (self->num_[key] - BLOCK_SIZE) : 0u;\r
194 for (i = self->num_[key]; i > down;) {\r
195 size_t prev_ix = bucket[--i & BLOCK_MASK];\r
196 const size_t backward = cur_ix - prev_ix;\r
197 if (PREDICT_FALSE(backward > max_backward)) {\r
198 break;\r
199 }\r
200 prev_ix &= ring_buffer_mask;\r
201 if (cur_ix_masked + best_len > ring_buffer_mask ||\r
202 prev_ix + best_len > ring_buffer_mask ||\r
203 data[cur_ix_masked + best_len] != data[prev_ix + best_len]) {\r
204 continue;\r
205 }\r
206 {\r
207 const size_t len = FindMatchLengthWithLimit(&data[prev_ix],\r
208 &data[cur_ix_masked],\r
209 max_length);\r
210 if (len >= 4) {\r
211 /* Comparing for >= 3 does not change the semantics, but just saves\r
212 for a few unnecessary binary logarithms in backward reference\r
213 score, since we are not interested in such short matches. */\r
214 score_t score = BackwardReferenceScore(len, backward);\r
215 if (best_score < score) {\r
216 best_score = score;\r
217 best_len = len;\r
218 out->len = best_len;\r
219 out->distance = backward;\r
220 out->score = best_score;\r
221 is_match_found = BROTLI_TRUE;\r
222 }\r
223 }\r
224 }\r
225 }\r
226 bucket[self->num_[key] & BLOCK_MASK] = (uint32_t)cur_ix;\r
227 ++self->num_[key];\r
228 }\r
229 if (!is_match_found) {\r
230 is_match_found = SearchInStaticDictionary(&self->dict_search_stats_,\r
231 &data[cur_ix_masked], max_length, max_backward, out, BROTLI_FALSE);\r
232 }\r
233 return is_match_found;\r
234}\r
235\r
236#undef HASH_MAP_SIZE\r
237#undef BLOCK_MASK\r
238#undef BLOCK_SIZE\r
239#undef BUCKET_SIZE\r
240\r
241#undef HashLongestMatch\r