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