1use std::cell::Cell;
11
12thread_local! {
13 static SAFE_ONLY: Cell<bool> = const { Cell::new(false) };
14}
15
16#[inline]
18pub fn fast() -> bool {
19 !SAFE_ONLY.with(Cell::get)
20}
21
22pub fn set_safe_only(on: bool) {
25 SAFE_ONLY.with(|c| c.set(on));
26}
27
28pub const WILD: usize = 32;
30
31#[repr(C)]
34#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
35pub struct SeqEntry {
36 pub base: u32,
37 pub add: u8,
38 pub nbits: u8,
39 pub next: u16,
40}
41
42pub struct Sequences<'a> {
44 pub table: &'a [SeqEntry; 2048],
46 pub logs: [u32; 3],
48 pub bits: &'a [u8],
50 pub n: usize,
52 pub lits: &'a [u8],
54 pub out: &'a mut [u8],
57 pub start: usize,
58 pub reps: [u32; 3],
59 pub wmax: usize,
61 pub hist: usize,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum SeqError {
69 Literals,
70 ZeroOffset,
71 Window,
72 Size,
73 Bitstream,
74}
75
76pub fn zstd_sequences(s: Sequences<'_>) -> Result<(usize, usize, [u32; 3]), SeqError> {
84 #[cfg(target_arch = "x86_64")]
85 if crate::bits::bmi2() {
86 return unsafe { sequences_bmi2(s) };
89 }
90 sequences(s)
91}
92
93#[cfg(target_arch = "x86_64")]
95#[target_feature(enable = "bmi1,bmi2,lzcnt")]
96unsafe fn sequences_bmi2(s: Sequences<'_>) -> Result<(usize, usize, [u32; 3]), SeqError> {
97 sequences(s)
98}
99
100#[allow(unused_assignments)]
102#[inline(always)]
103fn sequences(s: Sequences<'_>) -> Result<(usize, usize, [u32; 3]), SeqError> {
104 let Sequences { table, logs, bits, n, lits, out, start, mut reps, wmax, hist } = s;
105 assert!(bits.len() >= 8 && bits[bits.len() - 1] != 0 && n >= 1);
106 assert!(lits.len() >= WILD && out.len() >= start + WILD && logs.iter().all(|&l| l <= 9));
107 let lim = out.len() - WILD;
108 let tp = table.as_ptr();
109 let bp = bits.as_ptr();
110 let mut ptr = bits.len() - 8;
113 let last = bits[bits.len() - 1];
114 let mut used = 8 - (31 - (last as u32).leading_zeros());
115 let mut c = u64::from_le_bytes(unsafe { std::ptr::read_unaligned(bp.add(ptr) as *const [u8; 8]) });
117 macro_rules! reload {
118 () => {{
119 let step = ((used >> 3) as usize).min(ptr);
120 ptr -= step;
121 used -= 8 * step as u32;
122 c = u64::from_le_bytes(unsafe { std::ptr::read_unaligned(bp.add(ptr) as *const [u8; 8]) });
125 }};
126 }
127 macro_rules! read {
128 ($nb:expr) => {{
129 let nb = $nb as u32;
130 let v = c.wrapping_shl(used) >> 1 >> (63u32.wrapping_sub(nb) & 63);
131 used += nb;
132 v
133 }};
134 }
135 macro_rules! entry {
136 ($s:expr) => {
137 unsafe { *tp.add($s & 2047) }
139 };
140 }
141 let mut s_ll = read!(logs[0]) as usize;
142 let mut s_of = 512 + read!(logs[1]) as usize;
143 let mut s_ml = 1024 + read!(logs[2]) as usize;
144 reload!();
145 let lits_n = lits.len() - WILD;
146 let mut litp = lits.as_ptr();
148 let mut lits_left = lits_n;
149 let o = out.as_mut_ptr();
150 let mut op = start;
151 let h = if (hist as isize) < 0 { hist } else { 0 };
155 macro_rules! decode {
160 ($update:expr) => {{
161 let ll_e = entry!(s_ll);
162 let of_e = entry!(s_of);
163 let ml_e = entry!(s_ml);
164 let ofb = read!(of_e.add) as u32;
165 let ml = (ml_e.base + read!(ml_e.add) as u32) as usize;
166 if of_e.add as u32 + ml_e.add as u32 + ll_e.add as u32 >= 31 {
167 reload!();
168 }
169 let ll = (ll_e.base + read!(ll_e.add) as u32) as usize;
170 if $update {
171 s_ll = ll_e.next as usize + read!(ll_e.nbits) as usize;
172 s_ml = ml_e.next as usize + read!(ml_e.nbits) as usize;
173 s_of = of_e.next as usize + read!(of_e.nbits) as usize;
174 reload!();
175 }
176 let off = if of_e.add > 1 {
177 let x = of_e.base.wrapping_add(ofb);
178 reps = [x, reps[0], reps[1]];
179 x
180 } else {
181 let v = of_e.base + ofb + (ll == 0) as u32;
182 let x = match v {
183 1 => reps[0],
184 2 => reps[1],
185 3 => reps[2],
186 _ => reps[0].wrapping_sub(1),
187 };
188 if v != 1 {
189 reps = [x, reps[0], if v == 2 { reps[2] } else { reps[1] }];
190 }
191 x
192 } as usize;
193 (ll, ml, off)
194 }};
195 }
196 macro_rules! execute {
197 ($ll:expr, $ml:expr, $off:expr) => {{
198 let (ll, ml, off) = ($ll, $ml, $off);
199 let lit_end = op + ll;
200 let end = lit_end + ml;
201 if (ll > lits_left) | (end > lim) | (off.wrapping_sub(1) >= wmax) | (off > lit_end.wrapping_add(h)) {
205 return Err(if ll > lits_left {
206 SeqError::Literals
207 } else if off == 0 {
208 SeqError::ZeroOffset
209 } else if off >= wmax.wrapping_add(1) || off > lit_end.wrapping_add(h) {
210 SeqError::Window
211 } else {
212 SeqError::Size
213 });
214 }
215 unsafe {
220 let d = o.add(op);
221 let mut i = 0;
222 loop {
223 std::ptr::copy_nonoverlapping(litp.add(i), d.add(i), 16);
224 i += 16;
225 if i >= ll {
226 break;
227 }
228 }
229 litp = litp.add(ll);
230 }
231 lits_left -= ll;
232 unsafe { copy_match(o, lit_end, off, ml) };
236 op = end;
237 }};
238 }
239 for _ in 1..n {
240 let (ll, ml, off) = decode!(true);
241 execute!(ll, ml, off);
242 }
243 let (ll, ml, off) = decode!(false);
244 execute!(ll, ml, off);
245 if !(ptr == 0 && used == 64) {
246 return Err(SeqError::Bitstream);
247 }
248 Ok((op, lits_n - lits_left, reps))
249}
250
251#[inline(always)]
257unsafe fn copy_match(o: *mut u8, op: usize, off: usize, ml: usize) {
258 let end = op + ml;
259 unsafe {
264 if off >= 16 {
265 let (mut sp, mut dp) = (o.add(op - off), o.add(op));
266 let e = o.add(end);
267 loop {
268 std::ptr::copy_nonoverlapping(sp, dp, 16);
269 dp = dp.add(16);
270 sp = sp.add(16);
271 if dp >= e {
272 break;
273 }
274 }
275 } else if off == 1 {
276 std::ptr::write_bytes(o.add(op), *o.add(op - 1), ml);
277 } else {
278 const D32: [usize; 8] = [0, 1, 2, 1, 4, 4, 4, 4];
279 const D64: [usize; 8] = [8, 8, 8, 7, 8, 9, 10, 11];
280 let mut d = op;
281 let mut s = op - off;
282 if off < 8 {
283 *o.add(d) = *o.add(s);
284 *o.add(d + 1) = *o.add(s + 1);
285 *o.add(d + 2) = *o.add(s + 2);
286 *o.add(d + 3) = *o.add(s + 3);
287 s += D32[off];
288 std::ptr::copy_nonoverlapping(o.add(s), o.add(d + 4), 4);
289 d += 8;
290 s = d - (off - D32[off] + D64[off]);
291 } else {
292 std::ptr::copy_nonoverlapping(o.add(s), o.add(d), 8);
293 d += 8;
294 s += 8;
295 }
296 while d < end {
297 std::ptr::copy_nonoverlapping(o.add(s), o.add(d), 8);
298 d += 8;
299 s += 8;
300 }
301 }
302 }
303}
304
305pub struct Huf4<'a> {
309 pub table: &'a [u16; 2048],
311 pub src: &'a [u8],
313 pub starts: [usize; 4],
315 pub pos: [usize; 4],
318 pub bits: [u64; 4],
319 pub out: &'a mut [u8],
321 pub seg: usize,
322 pub done: usize,
324}
325
326pub fn huf4(h: Huf4<'_>) -> ([usize; 4], [u64; 4], usize) {
333 #[cfg(target_arch = "x86_64")]
334 if crate::bits::bmi2() {
335 return unsafe { huf4_bmi2(h) };
338 }
339 huf4_loop(h)
340}
341
342#[cfg(target_arch = "x86_64")]
343#[target_feature(enable = "bmi1,bmi2,lzcnt")]
344unsafe fn huf4_bmi2(h: Huf4<'_>) -> ([usize; 4], [u64; 4], usize) {
345 huf4_loop(h)
346}
347
348#[inline(always)]
349fn huf4_loop(h: Huf4<'_>) -> ([usize; 4], [u64; 4], usize) {
350 let Huf4 { table, src, starts, mut pos, mut bits, out, seg, mut done } = h;
351 assert!(out.len() >= 3 * seg && done <= out.len() - 3 * seg && done <= seg);
352 for j in 0..4 {
353 assert!(starts[j] <= pos[j] && pos[j] + 8 <= src.len());
354 }
355 let n3 = out.len() - 3 * seg;
356 let t = table.as_ptr();
357 let sp = src.as_ptr();
358 let o = out.as_mut_ptr();
359 loop {
360 let mut rounds = (n3 - done) / 5;
361 for j in 0..4 {
362 rounds = rounds.min((pos[j] - starts[j]) / 7);
363 }
364 if rounds == 0 {
365 break;
366 }
367 for _ in 0..rounds {
368 for i in 0..5 {
369 for j in 0..4 {
370 unsafe {
374 let e = *t.add((bits[j] >> 53) as usize);
375 bits[j] <<= e & 63;
376 *o.add(j * seg + done + i) = (e >> 8) as u8;
377 }
378 }
379 }
380 done += 5;
381 for j in 0..4 {
382 let z = bits[j].trailing_zeros();
383 pos[j] -= (z >> 3) as usize;
384 bits[j] = (u64::from_le_bytes(unsafe { std::ptr::read_unaligned(sp.add(pos[j]) as *const [u8; 8]) }) | 1) << (z & 7);
388 }
389 }
390 }
391 (pos, bits, done)
392}
393
394#[repr(C)]
396#[derive(Clone, Copy, Debug, PartialEq, Eq)]
397pub struct Match {
398 pub lit: u32,
399 pub len: u32,
400 pub dist: u32,
401}
402
403pub struct FastIn<'a> {
405 pub buf: &'a [u8],
406 pub base: u64,
408 pub from: usize,
409 pub to: usize,
411 pub small: &'a mut [u32],
413 pub small_log: u32,
414 pub mls: u32,
415 pub long: &'a mut [u32],
417 pub long_log: u32,
418 pub max_dist: usize,
419 pub rep: [u32; 2],
420 pub strength: u32,
422 pub out: &'a mut Vec<Match>,
423}
424
425
426#[inline(always)]
427fn fhash(v: u64, mls: u32, log: u32) -> usize {
428 let p: u64 = match mls {
429 5 => 889_523_592_379,
430 6 => 227_718_039_650_203,
431 7 => 58_295_818_150_454_627,
432 _ => 0xCF1B_BCDC_B7A5_6463,
433 };
434 ((v << (64 - 8 * mls)).wrapping_mul(p) >> (64 - log)) as usize
435}
436
437#[inline(always)]
442unsafe fn count_raw(p: *const u8, a: usize, b: usize, max: usize) -> usize {
443 let mut n = 0;
444 unsafe {
446 while n + 8 <= max {
447 let x = std::ptr::read_unaligned(p.add(a + n) as *const u64) ^ std::ptr::read_unaligned(p.add(b + n) as *const u64);
448 if x != 0 {
449 return n + (x.to_le().trailing_zeros() / 8) as usize;
450 }
451 n += 8;
452 }
453 while n < max && *p.add(a + n) == *p.add(b + n) {
454 n += 1;
455 }
456 }
457 n
458}
459
460pub fn zstd_fast(p: FastIn<'_>) -> (usize, [u32; 2]) {
471 #[cfg(target_arch = "x86_64")]
472 if crate::bits::bmi2() {
473 return unsafe { fast_bmi2(p) };
476 }
477 fast_impl(p)
478}
479
480#[cfg(target_arch = "x86_64")]
481#[target_feature(enable = "bmi1,bmi2,lzcnt")]
482unsafe fn fast_bmi2(p: FastIn<'_>) -> (usize, [u32; 2]) {
483 fast_impl(p)
484}
485
486#[inline(always)]
487fn fast_impl(p: FastIn<'_>) -> (usize, [u32; 2]) {
488 let FastIn { buf, base, from, to, small, small_log: slog, mls, long, long_log: llog, max_dist, rep, strength, out } = p;
489 assert!((2..=12).contains(&strength));
490 assert!(from <= to && to <= buf.len() && to.saturating_sub(max_dist) <= from && small.len().is_power_of_two() && (5..=8).contains(&mls));
493 assert!(llog == 0 || long.len().is_power_of_two());
494 let bp = buf.as_ptr();
495 let sp = small.as_mut_ptr();
496 let lp = long.as_mut_ptr();
497 macro_rules! r32 {
501 ($i:expr) => {
502 unsafe { std::ptr::read_unaligned(bp.add($i) as *const u32).to_le() }
503 };
504 }
505 macro_rules! r64 {
506 ($i:expr) => {
507 unsafe { std::ptr::read_unaligned(bp.add($i) as *const u64).to_le() }
508 };
509 }
510 macro_rules! b8 {
511 ($i:expr) => {
512 unsafe { *bp.add($i) }
513 };
514 }
515 macro_rules! cnt {
516 ($a:expr, $b:expr, $m:expr) => {
517 unsafe { count_raw(bp, $a, $b, $m) }
518 };
519 }
520 macro_rules! tget {
521 (small, $h:expr) => {
522 {
523 let h = $h;
524 unsafe { *sp.add(h) }
525 }
526 };
527 (long, $h:expr) => {
528 {
529 let h = $h;
530 unsafe { *lp.add(h) }
531 }
532 };
533 }
534 macro_rules! tset {
535 (small, $h:expr, $v:expr) => {
536 {
537 let (h, v) = ($h, $v);
538 unsafe { *sp.add(h) = v }
539 }
540 };
541 (long, $h:expr, $v:expr) => {
542 {
543 let (h, v) = ($h, $v);
544 unsafe { *lp.add(h) = v }
545 }
546 };
547 }
548 let _ = (&lp, &max_dist);
549 if llog > 0 {
550 let small = &mut *small;
551 let long = &mut *long;
552 let _ = (&small, &long);
553
554
555 let (smask, lmask) = (small.len() - 1, long.len() - 1);
556
557
558 let [mut r0, mut r1] = rep;
559 let pos = |i: usize| (base + i as u64) as u32;
560 let lowest = |i: usize| i.saturating_sub(max_dist);
561 let mut anchor = from;
562 let mut ip = from + (from == 0) as usize;
563 if to < 9 {
564 return (to - from, rep);
565 }
566 let ilimit = to - 9;
567 while ip < ilimit {
568 let v = r64!(ip);
569 let hl = fhash(v, 8, llog) & lmask;
570 let hs = fhash(v, mls, slog) & smask;
571 let cl = tget!(long, hl);
572 let cs = tget!(small, hs);
573 tset!(long, hl, pos(ip));
574 tset!(small, hs, pos(ip));
575 let low = lowest(ip);
576 let start;
577 let ml;
578 let r = r0 as usize;
579 if r > 0 && ip + 1 - lowest(ip + 1) >= r && r32!(ip + 1 - r) == r32!(ip + 1) {
580 start = ip + 1;
581 ml = 4 + cnt!(start + 4 - r, start + 4, to - start - 4);
582 out.push(Match { lit: (start - anchor) as u32, len: ml as u32, dist: r as u32 });
583 } else {
584 let dl = pos(ip).wrapping_sub(cl) as usize;
585 let ds = pos(ip).wrapping_sub(cs) as usize;
586 let (mut s, d, mut len);
587 if dl != 0 && dl <= ip - low && r64!(ip - dl) == v {
588 s = ip;
589 d = dl;
590 len = 8 + cnt!(ip + 8 - dl, ip + 8, to - ip - 8);
591 } else if ds != 0 && ds <= ip - low && r32!(ip - ds) == v as u32 {
592 let v1 = r64!(ip + 1);
594 let hl1 = fhash(v1, 8, llog) & lmask;
595 let cl1 = tget!(long, hl1);
596 tset!(long, hl1, pos(ip + 1));
597 let dl1 = pos(ip + 1).wrapping_sub(cl1) as usize;
598 if dl1 != 0 && dl1 <= ip + 1 - lowest(ip + 1) && r64!(ip + 1 - dl1) == v1 {
599 s = ip + 1;
600 d = dl1;
601 len = 8 + cnt!(ip + 9 - dl1, ip + 9, to - ip - 9);
602 } else {
603 s = ip;
604 d = ds;
605 len = 4 + cnt!(ip + 4 - ds, ip + 4, to - ip - 4);
606 }
607 } else {
608 ip += ((ip - anchor) >> strength) + 1;
609 continue;
610 }
611 let mut m = s - d;
612 let low = lowest(s);
613 while s > anchor && m > low && b8!(s - 1) == b8!(m - 1) {
614 s -= 1;
615 m -= 1;
616 len += 1;
617 }
618 start = s;
619 ml = len;
620 r1 = r0;
621 r0 = d as u32;
622 out.push(Match { lit: (start - anchor) as u32, len: ml as u32, dist: d as u32 });
623 }
624 ip = start + ml;
625 anchor = ip;
626 if ip <= ilimit {
627 let p2 = start + 2;
629 let v2 = r64!(p2);
630 tset!(long, fhash(v2, 8, llog) & lmask, pos(p2));
631 tset!(small, fhash(v2, mls, slog) & smask, pos(p2));
632 let v3 = r64!(ip - 2);
633 tset!(long, fhash(v3, 8, llog) & lmask, pos(ip - 2));
634 tset!(small, fhash(v3, mls, slog) & smask, pos(ip - 2));
635 tset!(long, fhash(r64!(ip - 1), 8, llog) & lmask, pos(ip - 1));
636 while ip <= ilimit {
637 let r = r1 as usize;
638 if r == 0 || ip - lowest(ip) < r || r32!(ip - r) != r32!(ip) {
639 break;
640 }
641 let ml = 4 + cnt!(ip + 4 - r, ip + 4, to - ip - 4);
642 std::mem::swap(&mut r0, &mut r1);
643 let v = r64!(ip);
644 tset!(small, fhash(v, mls, slog) & smask, pos(ip));
645 tset!(long, fhash(v, 8, llog) & lmask, pos(ip));
646 out.push(Match { lit: 0, len: ml as u32, dist: r as u32 });
647 ip += ml;
648 anchor = ip;
649 }
650 }
651 }
652 (to - anchor, [r0, r1])
653 } else {
654 let small = &mut *small;
655 let _ = &small;
656
657
658 let smask = small.len() - 1;
659
660 let pos = |i: usize| (base + i as u64) as u32;
661 let hash_at = |i: usize| fhash(r64!(i), mls, slog) & smask;
662 if to < from + 16 {
663 return (to - from, rep);
664 }
665 let low = to.saturating_sub(max_dist);
667 let ilimit = to - 8;
668 let mut anchor = from;
669 let mut ip0 = from + (from == low) as usize;
670 let [mut r0, mut r1] = rep;
671 let (mut saved0, mut saved1) = (0, 0);
672 let max_rep = (ip0 - low) as u32;
673 if r1 > max_rep {
674 saved1 = r1;
675 r1 = 0;
676 }
677 if r0 > max_rep {
678 saved0 = r0;
679 r0 = 0;
680 }
681 let found = |ip: usize, m: u32| {
682 let d = pos(ip).wrapping_sub(m) as usize;
683 d != 0 && d <= ip - low && r32!(ip - d) == r32!(ip)
684 };
685 'start: loop {
686 let mut step = 2;
687 let mut next_step = ip0 + (1 << (strength - 1));
688 let mut ip1 = ip0 + 1;
689 let mut ip2 = ip0 + step;
690 let mut ip3 = ip2 + 1;
691 if ip3 >= ilimit {
692 break;
693 }
694 let mut h0 = hash_at(ip0);
695 let mut h1 = hash_at(ip1);
696 let mut midx = tget!(small, h0);
697 let cur0;
698 let mut m0;
699 let mut ml;
700 let rep_match;
701 loop {
702 let mut c = ip0;
703 tset!(small, h0, pos(ip0));
704 if r0 > 0 && r32!(ip2 - r0 as usize) == r32!(ip2) {
705 cur0 = c;
706 ip0 = ip2;
707 m0 = ip0 - r0 as usize;
708 ml = (m0 > low && b8!(ip0 - 1) == b8!(m0 - 1)) as usize;
709 ip0 -= ml;
710 m0 -= ml;
711 ml += 4;
712 tset!(small, h1, pos(ip1));
713 rep_match = true;
714 break;
715 }
716 if found(ip0, midx) {
717 cur0 = c;
718 tset!(small, h1, pos(ip1));
719 m0 = 0;
720 ml = 0;
721 rep_match = false;
722 break;
723 }
724 midx = tget!(small, h1);
725 h0 = h1;
726 h1 = hash_at(ip2);
727 ip0 = ip1;
728 ip1 = ip2;
729 ip2 = ip3;
730 c = ip0;
731 tset!(small, h0, pos(ip0));
732 if found(ip0, midx) {
733 cur0 = c;
734 if step <= 4 {
735 tset!(small, h1, pos(ip1));
736 }
737 m0 = 0;
738 ml = 0;
739 rep_match = false;
740 break;
741 }
742 midx = tget!(small, h1);
743 h0 = h1;
744 h1 = hash_at(ip2);
745 ip0 = ip1;
746 ip1 = ip2;
747 ip2 = ip0 + step;
748 ip3 = ip1 + step;
749 if ip2 >= next_step {
750 step += 1;
751 next_step += 1 << (strength - 1);
752 }
753 if ip3 >= ilimit {
754 break 'start;
755 }
756 }
757 if !rep_match {
758 let d = pos(ip0).wrapping_sub(midx) as usize;
760 m0 = ip0 - d;
761 r1 = r0;
762 r0 = d as u32;
763 ml = 4;
764 while ip0 > anchor && m0 > low && b8!(ip0 - 1) == b8!(m0 - 1) {
765 ip0 -= 1;
766 m0 -= 1;
767 ml += 1;
768 }
769 }
770 ml += cnt!(m0 + ml, ip0 + ml, to - ip0 - ml);
771 out.push(Match { lit: (ip0 - anchor) as u32, len: ml as u32, dist: (ip0 - m0) as u32 });
772 ip0 += ml;
773 anchor = ip0;
774 if ip0 <= ilimit {
775 tset!(small, hash_at(cur0 + 2), pos(cur0 + 2));
776 tset!(small, hash_at(ip0 - 2), pos(ip0 - 2));
777 while r1 > 0 && ip0 <= ilimit && r32!(ip0) == r32!(ip0 - r1 as usize) {
779 let r = r1 as usize;
780 let ml = 4 + cnt!(ip0 + 4 - r, ip0 + 4, to - ip0 - 4);
781 std::mem::swap(&mut r0, &mut r1);
782 tset!(small, hash_at(ip0), pos(ip0));
783 out.push(Match { lit: 0, len: ml as u32, dist: r as u32 });
784 ip0 += ml;
785 anchor = ip0;
786 }
787 }
788 }
789 saved1 = if saved0 != 0 && r0 != 0 { saved0 } else { saved1 };
791 (to - anchor, [if r0 != 0 { r0 } else { saved0 }, if r1 != 0 { r1 } else { saved1 }])
792 }
793}
794
795#[repr(C)]
797#[derive(Clone, Copy, Default, Debug)]
798pub struct SymTT {
799 pub delta_nbits: u32,
800 pub delta_find: i32,
801}
802
803#[derive(Clone, Copy)]
806pub struct FseEnc<'a> {
807 pub states: &'a [u16; 512],
808 pub tt: &'a [SymTT; 64],
809 pub log: u32,
810}
811
812#[repr(C)]
815#[derive(Clone, Copy, Debug, Default)]
816pub struct SeqCode {
817 pub ll_code: u8,
818 pub of_code: u8,
819 pub ml_code: u8,
820 pub ll_bits: u8,
821 pub ml_bits: u8,
822 pub ll: u32,
823 pub ofv: u32,
824 pub mlb: u32,
825}
826
827pub fn zstd_seq_stream(codes: &[SeqCode], t: [FseEnc<'_>; 3], out: &mut Vec<u8>) {
832 #[cfg(target_arch = "x86_64")]
833 if crate::bits::bmi2() {
834 return unsafe { seq_stream_bmi2(codes, t, out) };
837 }
838 seq_stream(codes, t, out)
839}
840
841#[cfg(target_arch = "x86_64")]
842#[target_feature(enable = "bmi1,bmi2,lzcnt")]
843unsafe fn seq_stream_bmi2(codes: &[SeqCode], t: [FseEnc<'_>; 3], out: &mut Vec<u8>) {
844 seq_stream(codes, t, out)
845}
846
847#[allow(unused_assignments)]
849#[inline(always)]
850fn seq_stream(codes: &[SeqCode], t: [FseEnc<'_>; 3], out: &mut Vec<u8>) {
851 assert!(!codes.is_empty() && t.iter().all(|x| x.log <= 9));
852 let n = codes.len();
853 let start = out.len();
854 let cap = 13 * n + 64;
856 out.resize(start + cap + 8, 0);
857 let o = out.as_mut_ptr();
858 let mut pos = start;
859 let mut acc = 0u64;
860 let mut nb = 0u32;
861 macro_rules! add {
862 ($v:expr, $b:expr) => {{
863 let b = $b as u32;
864 acc |= ($v as u64 & ((1u64 << b) - 1)) << nb;
865 nb += b;
866 }};
867 }
868 macro_rules! flush {
869 () => {{
870 unsafe { std::ptr::write_unaligned(o.add(pos) as *mut [u8; 8], acc.to_le_bytes()) };
873 let k = nb >> 3;
874 pos += k as usize;
875 acc = if k == 8 { 0 } else { acc >> (8 * k) };
876 nb &= 7;
877 }};
878 }
879 let init = |x: &FseEnc<'_>, s: u8| {
880 let tt = x.tt[s as usize & 63];
881 let b = (tt.delta_nbits.wrapping_add(1 << 15)) >> 16;
882 let v = (b << 16).wrapping_sub(tt.delta_nbits);
883 x.states[((v >> b) as i32).wrapping_add(tt.delta_find) as usize & 511] as u32
884 };
885 let [ll_t, of_t, ml_t] = t;
886 let last = codes[n - 1];
887 let mut ll_s = init(&ll_t, last.ll_code);
888 let mut of_s = init(&of_t, last.of_code);
889 let mut ml_s = init(&ml_t, last.ml_code);
890 macro_rules! enc {
891 ($x:expr, $st:expr, $sym:expr) => {{
892 let tt = $x.tt[$sym as usize & 63];
893 let b = $st.wrapping_add(tt.delta_nbits) >> 16;
894 add!($st, b);
895 $st = $x.states[(($st >> b) as i32).wrapping_add(tt.delta_find) as usize & 511] as u32;
896 }};
897 }
898 macro_rules! extras {
899 ($c:expr) => {{
900 let c = $c;
901 if nb + c.ll_bits as u32 + c.ml_bits as u32 > 63 {
902 flush!();
903 }
904 add!(c.ll, c.ll_bits);
905 add!(c.mlb, c.ml_bits);
906 if nb + c.of_code as u32 > 63 {
907 flush!();
908 }
909 add!(c.ofv, c.of_code);
910 flush!();
911 }};
912 }
913 extras!(last);
914 for c in codes[..n - 1].iter().rev() {
915 enc!(of_t, of_s, c.of_code);
916 enc!(ml_t, ml_s, c.ml_code);
917 enc!(ll_t, ll_s, c.ll_code);
918 flush!();
919 extras!(c);
920 }
921 add!(ml_s, ml_t.log);
922 flush!();
923 add!(of_s, of_t.log);
924 flush!();
925 add!(ll_s, ll_t.log);
926 add!(1u64, 1u32);
927 nb = (nb + 7) & !7;
928 flush!();
929 out.truncate(pos);
930}