brotli/enc/
command.rs

1use super::encode::BROTLI_NUM_DISTANCE_SHORT_CODES;
2use super::util::Log2FloorNonZero;
3#[derive(Copy, Clone, Debug)]
4pub struct BrotliDistanceParams {
5    pub distance_postfix_bits: u32,
6    pub num_direct_distance_codes: u32,
7    pub alphabet_size: u32,
8    pub max_distance: usize,
9}
10
11#[derive(Clone, Copy, Debug, Default)]
12pub struct Command {
13    // stores copy_len in low 25 bits and copy_code - copy_len in high 7 bit
14    pub insert_len_: u32,
15    pub copy_len_: u32,
16    //stores distance_extra bits
17    pub dist_extra_: u32,
18    pub cmd_prefix_: u16,
19    // stores distance code in low 10 bits and num extra bits in high 6 bits
20    pub dist_prefix_: u16,
21}
22
23impl Command {
24    pub fn copy_len(&self) -> u32 {
25        self.copy_len_ & 0x01ff_ffff
26    }
27
28    pub fn distance_context(&self) -> u32 {
29        let r: u32 = (self.cmd_prefix_ as i32 >> 6) as u32;
30        let c: u32 = (self.cmd_prefix_ as i32 & 7i32) as u32;
31        if (r == 0 || r == 2 || r == 4 || r == 7) && c <= 2 {
32            c
33        } else {
34            3
35        }
36    }
37
38    pub fn init_insert(&mut self, insertlen: usize) {
39        self.insert_len_ = insertlen as u32;
40        self.copy_len_ = (4i32 << 25) as u32;
41        self.dist_extra_ = 0u32;
42        self.dist_prefix_ = (1u16 << 10) | BROTLI_NUM_DISTANCE_SHORT_CODES as u16;
43        get_length_code(insertlen, 4usize, false, &mut self.cmd_prefix_);
44    }
45}
46
47#[inline(always)]
48pub fn ComputeDistanceCode(distance: usize, max_distance: usize, dist_cache: &[i32]) -> usize {
49    if distance <= max_distance {
50        let distance_plus_3: usize = distance.wrapping_add(3);
51        let offset0: usize = distance_plus_3.wrapping_sub(dist_cache[0] as usize);
52        let offset1: usize = distance_plus_3.wrapping_sub(dist_cache[1] as usize);
53        if distance == dist_cache[0] as usize {
54            return 0usize;
55        } else if distance == dist_cache[1] as usize {
56            return 1;
57        } else if offset0 < 7usize {
58            return (0x0975_0468_i32 >> (4usize).wrapping_mul(offset0) & 0xfi32) as usize;
59        } else if offset1 < 7usize {
60            return (0x0fdb_1ace_i32 >> (4usize).wrapping_mul(offset1) & 0xfi32) as usize;
61        } else if distance == dist_cache[2] as usize {
62            return 2usize;
63        } else if distance == dist_cache[3] as usize {
64            return 3usize;
65        }
66    }
67    distance.wrapping_add(16).wrapping_sub(1)
68}
69
70#[inline(always)]
71pub fn GetInsertLengthCode(insertlen: usize) -> u16 {
72    if insertlen < 6usize {
73        insertlen as u16
74    } else if insertlen < 130usize {
75        let nbits: u32 = Log2FloorNonZero(insertlen.wrapping_sub(2) as u64).wrapping_sub(1);
76        ((nbits << 1) as usize)
77            .wrapping_add(insertlen.wrapping_sub(2) >> nbits)
78            .wrapping_add(2) as u16
79    } else if insertlen < 2114usize {
80        Log2FloorNonZero(insertlen.wrapping_sub(66) as u64).wrapping_add(10) as u16
81    } else if insertlen < 6210usize {
82        21u32 as u16
83    } else if insertlen < 22594usize {
84        22u32 as u16
85    } else {
86        23u32 as u16
87    }
88}
89
90#[inline(always)]
91pub fn GetCopyLengthCode(copylen: usize) -> u16 {
92    if copylen < 10usize {
93        copylen.wrapping_sub(2) as u16
94    } else if copylen < 134usize {
95        let nbits: u32 = Log2FloorNonZero(copylen.wrapping_sub(6) as u64).wrapping_sub(1);
96        ((nbits << 1) as usize)
97            .wrapping_add(copylen.wrapping_sub(6) >> nbits)
98            .wrapping_add(4) as u16
99    } else if copylen < 2118usize {
100        Log2FloorNonZero(copylen.wrapping_sub(70) as u64).wrapping_add(12) as u16
101    } else {
102        23u32 as u16
103    }
104}
105
106#[deprecated(note = "Use combine_length_codes instead")]
107#[inline(always)]
108pub fn CombineLengthCodes(inscode: u16, copycode: u16, use_last_distance: i32) -> u16 {
109    combine_length_codes(inscode, copycode, use_last_distance != 0)
110}
111
112#[inline(always)]
113pub(crate) fn combine_length_codes(inscode: u16, copycode: u16, use_last_distance: bool) -> u16 {
114    let bits64: u16 = (copycode as u32 & 0x7u32 | (inscode as u32 & 0x7u32) << 3) as u16;
115    if use_last_distance && inscode < 8 && copycode < 16 {
116        if (copycode as i32) < 8i32 {
117            bits64
118        } else {
119            let s64: u16 = 64u16;
120            (bits64 as i32 | s64 as i32) as u16
121        }
122    } else {
123        let sub_offset: i32 = 2i32 * ((copycode as i32 >> 3) + 3i32 * (inscode as i32 >> 3));
124        let offset = (sub_offset << 5) + 0x40i32 + (0x520d40i32 >> sub_offset & 0xc0i32);
125        (offset as u16 as i32 | bits64 as i32) as u16
126    }
127}
128
129#[deprecated(note = "Use get_length_code instead")]
130#[inline(always)]
131pub fn GetLengthCode(insertlen: usize, copylen: usize, use_last_distance: i32, code: &mut u16) {
132    get_length_code(insertlen, copylen, use_last_distance != 0, code)
133}
134
135#[inline(always)]
136pub(crate) fn get_length_code(
137    insertlen: usize,
138    copylen: usize,
139    use_last_distance: bool,
140    code: &mut u16,
141) {
142    let inscode: u16 = GetInsertLengthCode(insertlen);
143    let copycode: u16 = GetCopyLengthCode(copylen);
144    *code = combine_length_codes(inscode, copycode, use_last_distance);
145}
146pub fn PrefixEncodeCopyDistance(
147    distance_code: usize,
148    num_direct_codes: usize,
149    postfix_bits: u64,
150    code: &mut u16,
151    extra_bits: &mut u32,
152) {
153    if distance_code < (BROTLI_NUM_DISTANCE_SHORT_CODES as usize).wrapping_add(num_direct_codes) {
154        *code = distance_code as u16;
155        *extra_bits = 0u32;
156    } else {
157        let dist: u64 = (1u64 << postfix_bits.wrapping_add(2u32 as (u64))).wrapping_add(
158            (distance_code as u64)
159                .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
160                .wrapping_sub(num_direct_codes as u64),
161        );
162        let bucket: u64 = Log2FloorNonZero(dist).wrapping_sub(1) as (u64);
163        let postfix_mask: u64 = (1u32 << postfix_bits).wrapping_sub(1) as (u64);
164        let postfix: u64 = dist & postfix_mask;
165        let prefix: u64 = (dist >> bucket) & 1;
166        let offset: u64 = (2u64).wrapping_add(prefix) << bucket;
167        let nbits: u64 = bucket.wrapping_sub(postfix_bits);
168        *code = ((nbits << 10)
169            | ((BROTLI_NUM_DISTANCE_SHORT_CODES as u64)
170                .wrapping_add(num_direct_codes as u64)
171                .wrapping_add(
172                    2u64.wrapping_mul(nbits.wrapping_sub(1))
173                        .wrapping_add(prefix)
174                        << postfix_bits,
175                )
176                .wrapping_add(postfix))) as u16;
177        *extra_bits = (dist.wrapping_sub(offset) >> postfix_bits) as u32;
178        /*(16u64)
179        .wrapping_add(num_direct_codes as u64)
180        .wrapping_add((2u64).wrapping_mul(nbits.wrapping_sub(1)).wrapping_add(prefix) <<
181                      postfix_bits)
182        .wrapping_add(postfix) as u16;*/
183        //*extra_bits = (nbits << 24 | dist.wrapping_sub(offset) >> postfix_bits) as u32;
184    }
185}
186
187impl Command {
188    pub fn restore_distance_code(&self, dist: &BrotliDistanceParams) -> u32 {
189        if (self.dist_prefix_ as i32 & 0x3ff)
190            < BROTLI_NUM_DISTANCE_SHORT_CODES as i32 + dist.num_direct_distance_codes as i32
191        {
192            self.dist_prefix_ as u32 & 0x3ff
193        } else {
194            let dcode = self.dist_prefix_ as u32 & 0x3ff;
195            let nbits: u32 = u32::from(self.dist_prefix_ >> 10);
196            let extra: u32 = self.dist_extra_;
197            let postfix_mask = (1u32 << dist.distance_postfix_bits) - 1;
198            let hcode = dcode
199                .wrapping_sub(dist.num_direct_distance_codes)
200                .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
201                >> dist.distance_postfix_bits;
202            let lcode = dcode
203                .wrapping_sub(dist.num_direct_distance_codes)
204                .wrapping_sub(BROTLI_NUM_DISTANCE_SHORT_CODES)
205                & postfix_mask;
206            let offset = (2u32.wrapping_add((hcode & 1)) << nbits).wrapping_sub(4);
207            (offset.wrapping_add(extra) << dist.distance_postfix_bits)
208                .wrapping_add(lcode)
209                .wrapping_add(dist.num_direct_distance_codes)
210                .wrapping_add(BROTLI_NUM_DISTANCE_SHORT_CODES)
211        }
212    }
213
214    // returns which distance code to use ( 0 means none, 1 means last, 2 means penultimate, 3 means the prior to penultimate
215    pub fn distance_index_and_offset(&self, dist: &BrotliDistanceParams) -> (usize, isize) {
216        let n_postfix = dist.distance_postfix_bits;
217        let n_direct = dist.num_direct_distance_codes;
218        let dextra = self.dist_extra_;
219        let dprefix = self.dist_prefix_ & 0x3ff;
220        let n_dist_bits = self.dist_prefix_ >> 10;
221        if u32::from(dprefix) < BROTLI_NUM_DISTANCE_SHORT_CODES {
222            let table: [(usize, isize); 16] = [
223                (1, 0),
224                (2, 0),
225                (3, 0),
226                (4, 0),
227                (1, -1),
228                (1, 1),
229                (1, -2),
230                (1, 2),
231                (1, -3),
232                (1, 3),
233                (2, -1),
234                (2, 1),
235                (2, -2),
236                (2, 2),
237                (2, -3),
238                (2, 3),
239            ];
240            //eprint!("AA {:?} {:?} -> {:?}\n",*self, *dist, table[dprefix as usize]);
241            return table[dprefix as usize];
242        }
243        if (dprefix as usize) < BROTLI_NUM_DISTANCE_SHORT_CODES as usize + n_direct as usize {
244            let ret = dprefix as isize + 1 - BROTLI_NUM_DISTANCE_SHORT_CODES as isize;
245            //eprint!("BB {:?} {:?} -> {:?}\n",*self, *dist, ret);
246            return (0, ret);
247        }
248        let postfix_mask = (1 << n_postfix) - 1;
249        let dcode = dprefix as u32 - BROTLI_NUM_DISTANCE_SHORT_CODES - n_direct;
250        let hcode = dcode >> n_postfix;
251        let lcode = dcode & postfix_mask;
252        let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
253
254        let ret = (((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize;
255        //assert!(ret != 0);
256        (0, ret)
257    }
258}
259
260pub fn RecomputeDistancePrefixes(
261    cmds: &mut [Command],
262    num_commands: usize,
263    num_direct_distance_codes: u32,
264    distance_postfix_bits: u32,
265    dist: &BrotliDistanceParams,
266) {
267    if num_direct_distance_codes == 0u32 && (distance_postfix_bits == 0u32) {
268        return;
269    }
270    for i in 0usize..num_commands {
271        let cmd: &mut Command = &mut cmds[i];
272        if cmd.copy_len() != 0 && cmd.cmd_prefix_ >= 128 {
273            PrefixEncodeCopyDistance(
274                cmd.restore_distance_code(dist) as usize,
275                num_direct_distance_codes as usize,
276                distance_postfix_bits as (u64),
277                &mut cmd.dist_prefix_,
278                &mut cmd.dist_extra_,
279            );
280        }
281    }
282}
283
284impl Command {
285    pub fn init(
286        &mut self,
287        dist: &BrotliDistanceParams,
288        insertlen: usize,
289        copylen: usize,
290        copylen_code: usize,
291        distance_code: usize,
292    ) {
293        self.insert_len_ = insertlen as u32;
294        let copylen_code_delta = (copylen_code as i32 - copylen as i32) as i8;
295        self.copy_len_ = (copylen as u32 | (u32::from(copylen_code_delta as u8) << 25));
296        PrefixEncodeCopyDistance(
297            distance_code,
298            dist.num_direct_distance_codes as usize,
299            u64::from(dist.distance_postfix_bits),
300            &mut self.dist_prefix_,
301            &mut self.dist_extra_,
302        );
303        get_length_code(
304            insertlen,
305            copylen_code,
306            (self.dist_prefix_ & 0x3ff) == 0,
307            &mut self.cmd_prefix_,
308        );
309    }
310
311    pub fn new(
312        dist: &BrotliDistanceParams,
313        insertlen: usize,
314        copylen: usize,
315        copylen_code: usize,
316        distance_code: usize,
317    ) -> Self {
318        let mut cmd = Command {
319            insert_len_: insertlen as u32,
320            copy_len_: (copylen | ((copylen_code ^ copylen) << 25)) as u32,
321            dist_extra_: 0,
322            cmd_prefix_: 0,
323            dist_prefix_: 0,
324        };
325        cmd.init(dist, insertlen, copylen, copylen_code, distance_code);
326        cmd
327    }
328}
329
330#[cfg(test)]
331mod test {
332    // returns which distance code to use ( 0 means none, 1 means last, 2 means penultimate, 3 means the prior to penultimate
333    pub fn helperCommandDistanceIndexAndOffset(
334        cmd: &super::Command,
335        dist: &super::BrotliDistanceParams,
336    ) -> (usize, isize) {
337        let n_postfix = dist.distance_postfix_bits;
338        let n_direct = dist.num_direct_distance_codes;
339        let dextra = cmd.dist_extra_;
340        let dist_prefix = cmd.dist_prefix_ & 0x3ff;
341        if dist_prefix < 16 {
342            let table: [(usize, isize); 16] = [
343                (1, 0),
344                (2, 0),
345                (3, 0),
346                (4, 0),
347                (1, -1),
348                (1, 1),
349                (1, -2),
350                (1, 2),
351                (1, -3),
352                (1, 3),
353                (2, -1),
354                (2, 1),
355                (2, -2),
356                (2, 2),
357                (2, -3),
358                (2, 3),
359            ];
360            return table[cmd.dist_prefix_ as usize];
361        }
362        if (dist_prefix as usize) < 16 + n_direct as usize {
363            return (0, dist_prefix as isize + 1 - 16);
364        }
365        let postfix_mask = (1 << n_postfix) - 1;
366        let dcode = dist_prefix as u32 - 16 - n_direct;
367        let n_dist_bits = 1 + (dcode >> (n_postfix + 1));
368
369        let hcode = dcode >> n_postfix;
370        let lcode = dcode & postfix_mask;
371        let offset = ((2 + (hcode & 1)) << n_dist_bits) - 4;
372        (
373            0,
374            (((offset + dextra) << n_postfix) + lcode + n_direct + 1) as isize,
375        )
376    }
377    #[test]
378    fn test_command_return_distance_index_offset() {
379        let param = super::BrotliDistanceParams {
380            distance_postfix_bits: 2,
381            num_direct_distance_codes: 16,
382            alphabet_size: 224,
383            max_distance: 268435456,
384        };
385        let mut cmd = super::Command::default();
386        cmd.insert_len_ = 63;
387        cmd.copy_len_ = 3;
388        cmd.dist_extra_ = 3;
389        cmd.cmd_prefix_ = 297;
390        cmd.dist_prefix_ = 2089;
391
392        assert_eq!(cmd.distance_index_and_offset(&param), (0, 46));
393        assert_eq!(
394            cmd.distance_index_and_offset(&param),
395            helperCommandDistanceIndexAndOffset(&cmd, &param)
396        );
397        cmd = super::Command {
398            insert_len_: 27,
399            copy_len_: 3,
400            dist_extra_: 0,
401            cmd_prefix_: 281,
402            dist_prefix_: 6,
403        };
404        assert_eq!(cmd.distance_index_and_offset(&param), (1, -2));
405        assert_eq!(
406            cmd.distance_index_and_offset(&param),
407            helperCommandDistanceIndexAndOffset(&cmd, &param)
408        );
409        cmd = super::Command {
410            insert_len_: 1,
411            copy_len_: 3,
412            dist_extra_: 0,
413            cmd_prefix_: 137,
414            dist_prefix_: 27,
415        };
416        assert_eq!(cmd.distance_index_and_offset(&param), (0, 12));
417        assert_eq!(
418            cmd.distance_index_and_offset(&param),
419            helperCommandDistanceIndexAndOffset(&cmd, &param)
420        );
421        cmd = super::Command {
422            insert_len_: 5,
423            copy_len_: 4,
424            dist_extra_: 297,
425            cmd_prefix_: 170,
426            dist_prefix_: 11377,
427        };
428        assert_eq!(cmd.distance_index_and_offset(&param), (0, 17574));
429        assert_eq!(
430            cmd.distance_index_and_offset(&param),
431            helperCommandDistanceIndexAndOffset(&cmd, &param)
432        );
433        cmd.init_insert(24);
434        assert_eq!(cmd.distance_index_and_offset(&param), (0, 1));
435    }
436    /*
437    #[test]
438    fn test_restore_distance_code() {
439        for dist_code in 0..50000 {
440            let mut cmd = super::Command::default();
441            let param =super::BrotliDistanceParams{
442                distance_postfix_bits:2,
443                num_direct_distance_codes:16,
444                alphabet_size:224,
445                max_distance:268435456,
446            };
447            super::InitCommand(&mut cmd, &param, 4, 4, 4, dist_code);
448            let exp_dist_code = super::CommandRestoreDistanceCode(&cmd, &param);
449            assert_eq!(exp_dist_code as u32, dist_code as u32);
450        }
451    }*/
452}