]> wimlib.net Git - wimlib/blob - include/wimlib/bt_matchfinder.h
Get rid of matchfinder_common.h and manual memsets
[wimlib] / include / wimlib / bt_matchfinder.h
1 /*
2  * bt_matchfinder.h
3  *
4  * Author:      Eric Biggers
5  * Year:        2014, 2015
6  *
7  * The author dedicates this file to the public domain.
8  * You can do whatever you want with this file.
9  *
10  * ----------------------------------------------------------------------------
11  *
12  * This is a Binary Trees (bt) based matchfinder.
13  *
14  * The data structure is a hash table where each hash bucket contains a binary
15  * tree of sequences whose first 3 bytes share the same hash code.  Each
16  * sequence is identified by its starting position in the input buffer.  Each
17  * binary tree is always sorted such that each left child represents a sequence
18  * lexicographically lesser than its parent and each right child represents a
19  * sequence lexicographically greater than its parent.
20  *
21  * The algorithm processes the input buffer sequentially.  At each byte
22  * position, the hash code of the first 3 bytes of the sequence beginning at
23  * that position (the sequence being matched against) is computed.  This
24  * identifies the hash bucket to use for that position.  Then, a new binary tree
25  * node is created to represent the current sequence.  Then, in a single tree
26  * traversal, the hash bucket's binary tree is searched for matches and is
27  * re-rooted at the new node.
28  *
29  * Compared to the simpler algorithm that uses linked lists instead of binary
30  * trees (see hc_matchfinder.h), the binary tree version gains more information
31  * at each node visitation.  Ideally, the binary tree version will examine only
32  * 'log(n)' nodes to find the same matches that the linked list version will
33  * find by examining 'n' nodes.  In addition, the binary tree version can
34  * examine fewer bytes at each node by taking advantage of the common prefixes
35  * that result from the sort order, whereas the linked list version may have to
36  * examine up to the full length of the match at each node.
37  *
38  * However, it is not always best to use the binary tree version.  It requires
39  * nearly twice as much memory as the linked list version, and it takes time to
40  * keep the binary trees sorted, even at positions where the compressor does not
41  * need matches.  Generally, when doing fast compression on small buffers,
42  * binary trees are the wrong approach.  They are best suited for thorough
43  * compression and/or large buffers.
44  *
45  * ----------------------------------------------------------------------------
46  */
47
48 #ifndef _BT_MATCHFINDER_H
49 #define _BT_MATCHFINDER_H
50
51 #ifndef MATCHFINDER_MAX_WINDOW_ORDER
52 #  error "MATCHFINDER_MAX_WINDOW_ORDER must be defined!"
53 #endif
54
55 #include <string.h>
56
57 #include "wimlib/lz_extend.h"
58 #include "wimlib/lz_hash.h"
59
60 #if MATCHFINDER_MAX_WINDOW_ORDER < 13
61 #  define BT_MATCHFINDER_HASH_ORDER 14
62 #elif MATCHFINDER_MAX_WINDOW_ORDER < 15
63 #  define BT_MATCHFINDER_HASH_ORDER 15
64 #else
65 #  define BT_MATCHFINDER_HASH_ORDER 16
66 #endif
67
68 #if MATCHFINDER_MAX_WINDOW_ORDER <= 16
69 typedef u16 pos_t;
70 #else
71 typedef u32 pos_t;
72 #endif
73
74 /* Representation of a match found by the bt_matchfinder  */
75 struct lz_match {
76
77         /* The number of bytes matched.  */
78         pos_t length;
79
80         /* The offset back from the current position that was matched.  */
81         pos_t offset;
82 };
83
84 struct bt_matchfinder {
85         pos_t hash_tab[1UL << BT_MATCHFINDER_HASH_ORDER];
86         pos_t child_tab[];
87 };
88
89 /* Return the number of bytes that must be allocated for a 'bt_matchfinder' that
90  * can work with buffers up to the specified size.  */
91 static inline size_t
92 bt_matchfinder_size(size_t max_bufsize)
93 {
94         return sizeof(struct bt_matchfinder) + (2 * max_bufsize * sizeof(pos_t));
95 }
96
97 /* Prepare the matchfinder for a new input buffer.  */
98 static inline void
99 bt_matchfinder_init(struct bt_matchfinder *mf)
100 {
101         memset(mf, 0, sizeof(*mf));
102 }
103
104 static inline u32
105 bt_matchfinder_hash_3_bytes(const u8 *in_next)
106 {
107         return lz_hash_3_bytes(in_next, BT_MATCHFINDER_HASH_ORDER);
108 }
109
110 static inline pos_t *
111 bt_child(struct bt_matchfinder *mf, pos_t node, int offset)
112 {
113         if (MATCHFINDER_MAX_WINDOW_ORDER < sizeof(pos_t) * 8) {
114                 /* no cast needed */
115                 return &mf->child_tab[(node << 1) + offset];
116         } else {
117                 return &mf->child_tab[((size_t)node << 1) + offset];
118         }
119 }
120
121 static inline pos_t *
122 bt_left_child(struct bt_matchfinder *mf, pos_t node)
123 {
124         return bt_child(mf, node, 0);
125 }
126
127 static inline pos_t *
128 bt_right_child(struct bt_matchfinder *mf, pos_t node)
129 {
130         return bt_child(mf, node, 1);
131 }
132
133 /*
134  * Retrieve a list of matches with the current position.
135  *
136  * @mf
137  *      The matchfinder structure.
138  * @in_begin
139  *      Pointer to the beginning of the input buffer.
140  * @in_next
141  *      Pointer to the next byte in the input buffer to process.  This is the
142  *      pointer to the sequence being matched against.
143  * @min_len
144  *      Only record matches that are at least this long.
145  * @max_len
146  *      The maximum permissible match length at this position.
147  * @nice_len
148  *      Stop searching if a match of at least this length is found.
149  *      Must be <= @max_len.
150  * @max_search_depth
151  *      Limit on the number of potential matches to consider.  Must be >= 1.
152  * @next_hash
153  *      Pointer to the hash code for the current sequence, which was computed
154  *      one position in advance so that the binary tree root could be
155  *      prefetched.  This is an input/output parameter.
156  * @best_len_ret
157  *      The length of the longest match found is written here.  (This is
158  *      actually redundant with the 'struct lz_match' array, but this is easier
159  *      for the compiler to optimize when inlined and the caller immediately
160  *      does a check against 'best_len'.)
161  * @lz_matchptr
162  *      An array in which this function will record the matches.  The recorded
163  *      matches will be sorted by strictly increasing length and strictly
164  *      increasing offset.  The maximum number of matches that may be found is
165  *      'min(nice_len, max_len) - 3 + 1'.
166  *
167  * The return value is a pointer to the next available slot in the @lz_matchptr
168  * array.  (If no matches were found, this will be the same as @lz_matchptr.)
169  */
170 static inline struct lz_match *
171 bt_matchfinder_get_matches(struct bt_matchfinder * const restrict mf,
172                            const u8 * const in_begin,
173                            const u8 * const in_next,
174                            const unsigned min_len,
175                            const unsigned max_len,
176                            const unsigned nice_len,
177                            const unsigned max_search_depth,
178                            u32 * restrict next_hash,
179                            unsigned * restrict best_len_ret,
180                            struct lz_match * restrict lz_matchptr)
181 {
182         unsigned depth_remaining = max_search_depth;
183         u32 hash;
184         pos_t cur_node;
185         const u8 *matchptr;
186         pos_t *pending_lt_ptr, *pending_gt_ptr;
187         unsigned best_lt_len, best_gt_len;
188         unsigned len;
189         unsigned best_len = min_len - 1;
190
191         if (unlikely(max_len < LZ_HASH3_REQUIRED_NBYTES + 1)) {
192                 *best_len_ret = best_len;
193                 return lz_matchptr;
194         }
195
196         hash = *next_hash;
197         *next_hash = bt_matchfinder_hash_3_bytes(in_next + 1);
198         cur_node = mf->hash_tab[hash];
199         mf->hash_tab[hash] = in_next - in_begin;
200         prefetchw(&mf->hash_tab[*next_hash]);
201
202         pending_lt_ptr = bt_left_child(mf, in_next - in_begin);
203         pending_gt_ptr = bt_right_child(mf, in_next - in_begin);
204         best_lt_len = 0;
205         best_gt_len = 0;
206         len = 0;
207
208         if (!cur_node) {
209                 *pending_lt_ptr = 0;
210                 *pending_gt_ptr = 0;
211                 *best_len_ret = best_len;
212                 return lz_matchptr;
213         }
214
215         for (;;) {
216                 matchptr = &in_begin[cur_node];
217
218                 if (matchptr[len] == in_next[len]) {
219                         len = lz_extend(in_next, matchptr, len + 1, max_len);
220                         if (len > best_len) {
221                                 best_len = len;
222                                 lz_matchptr->length = len;
223                                 lz_matchptr->offset = in_next - matchptr;
224                                 lz_matchptr++;
225                                 if (len >= nice_len) {
226                                         *pending_lt_ptr = *bt_left_child(mf, cur_node);
227                                         *pending_gt_ptr = *bt_right_child(mf, cur_node);
228                                         *best_len_ret = best_len;
229                                         return lz_matchptr;
230                                 }
231                         }
232                 }
233
234                 if (matchptr[len] < in_next[len]) {
235                         *pending_lt_ptr = cur_node;
236                         pending_lt_ptr = bt_right_child(mf, cur_node);
237                         cur_node = *pending_lt_ptr;
238                         best_lt_len = len;
239                         if (best_gt_len < len)
240                                 len = best_gt_len;
241                 } else {
242                         *pending_gt_ptr = cur_node;
243                         pending_gt_ptr = bt_left_child(mf, cur_node);
244                         cur_node = *pending_gt_ptr;
245                         best_gt_len = len;
246                         if (best_lt_len < len)
247                                 len = best_lt_len;
248                 }
249
250                 if (!cur_node || !--depth_remaining) {
251                         *pending_lt_ptr = 0;
252                         *pending_gt_ptr = 0;
253                         *best_len_ret = best_len;
254                         return lz_matchptr;
255                 }
256         }
257 }
258
259 /*
260  * Advance the matchfinder, but don't record any matches.
261  *
262  * @mf
263  *      The matchfinder structure.
264  * @in_begin
265  *      Pointer to the beginning of the input buffer.
266  * @in_next
267  *      Pointer to the next byte in the input buffer to process.
268  * @in_end
269  *      Pointer to the end of the input buffer.
270  * @nice_len
271  *      Stop searching if a match of at least this length is found.
272  * @max_search_depth
273  *      Limit on the number of potential matches to consider.
274  * @next_hash
275  *      Pointer to the hash code for the current sequence, which was computed
276  *      one position in advance so that the binary tree root could be
277  *      prefetched.  This is an input/output parameter.
278  *
279  * Note: this is very similar to bt_matchfinder_get_matches() because both
280  * functions must do hashing and tree re-rooting.  This version just doesn't
281  * actually record any matches.
282  */
283 static inline void
284 bt_matchfinder_skip_position(struct bt_matchfinder * const restrict mf,
285                              const u8 * const in_begin,
286                              const u8 * const in_next,
287                              const u8 * const in_end,
288                              const unsigned nice_len,
289                              const unsigned max_search_depth,
290                              u32 * restrict next_hash)
291 {
292         unsigned depth_remaining = max_search_depth;
293         u32 hash;
294         pos_t cur_node;
295         const u8 *matchptr;
296         pos_t *pending_lt_ptr, *pending_gt_ptr;
297         unsigned best_lt_len, best_gt_len;
298         unsigned len;
299
300         if (unlikely(in_end - in_next < LZ_HASH3_REQUIRED_NBYTES + 1))
301                 return;
302
303         hash = *next_hash;
304         *next_hash = bt_matchfinder_hash_3_bytes(in_next + 1);
305         cur_node = mf->hash_tab[hash];
306         mf->hash_tab[hash] = in_next - in_begin;
307         prefetchw(&mf->hash_tab[*next_hash]);
308
309         depth_remaining = max_search_depth;
310         pending_lt_ptr = bt_left_child(mf, in_next - in_begin);
311         pending_gt_ptr = bt_right_child(mf, in_next - in_begin);
312         best_lt_len = 0;
313         best_gt_len = 0;
314         len = 0;
315
316         if (!cur_node) {
317                 *pending_lt_ptr = 0;
318                 *pending_gt_ptr = 0;
319                 return;
320         }
321
322         for (;;) {
323                 matchptr = &in_begin[cur_node];
324
325                 if (matchptr[len] == in_next[len]) {
326                         len = lz_extend(in_next, matchptr, len + 1, nice_len);
327                         if (len == nice_len) {
328                                 *pending_lt_ptr = *bt_left_child(mf, cur_node);
329                                 *pending_gt_ptr = *bt_right_child(mf, cur_node);
330                                 return;
331                         }
332                 }
333
334                 if (matchptr[len] < in_next[len]) {
335                         *pending_lt_ptr = cur_node;
336                         pending_lt_ptr = bt_right_child(mf, cur_node);
337                         cur_node = *pending_lt_ptr;
338                         best_lt_len = len;
339                         if (best_gt_len < len)
340                                 len = best_gt_len;
341                 } else {
342                         *pending_gt_ptr = cur_node;
343                         pending_gt_ptr = bt_left_child(mf, cur_node);
344                         cur_node = *pending_gt_ptr;
345                         best_gt_len = len;
346                         if (best_lt_len < len)
347                                 len = best_lt_len;
348                 }
349
350                 if (!cur_node || !--depth_remaining) {
351                         *pending_lt_ptr = 0;
352                         *pending_gt_ptr = 0;
353                         return;
354                 }
355         }
356 }
357
358 #endif /* _BT_MATCHFINDER_H */