Skip to main content

libzstd_rs_sys/lib/decompress/
huf_decompress.rs

1use core::ptr::{self, NonNull};
2use std::ops::Bound;
3
4use libc::size_t;
5
6use crate::lib::common::bitstream::{BIT_DStream_t, BitContainerType, StreamStatus};
7use crate::lib::common::entropy_common::HUF_readStats_wksp;
8use crate::lib::common::error_private::{ERR_isError, Error};
9use crate::lib::common::huf::{
10    HUF_flags_bmi2, HUF_flags_disableAsm, HUF_flags_disableFast, HUF_SYMBOLVALUE_MAX,
11    HUF_TABLELOG_MAX,
12};
13use crate::lib::common::mem::{MEM_read64, MEM_readLEST, MEM_write16};
14use crate::lib::decompress::Workspace;
15
16#[cfg(target_arch = "x86_64")]
17extern "C" {
18    fn HUF_decompress4X1_usingDTable_internal_fast_asm_loop(args: &mut HUF_DecompressFastArgs);
19    fn HUF_decompress4X2_usingDTable_internal_fast_asm_loop(args: &mut HUF_DecompressFastArgs);
20}
21
22#[derive(Copy, Clone)]
23#[repr(C)]
24pub struct algo_time_t {
25    pub tableTime: u32,
26    pub decode256Time: u32,
27}
28
29#[derive(Copy, Clone)]
30#[repr(C)]
31pub struct DTable {
32    pub description: DTableDesc,
33    pub data: DTableData,
34}
35
36#[derive(Copy, Clone)]
37#[repr(C)]
38pub struct DTableData {
39    data: [u32; 4096],
40}
41
42impl DTableData {
43    fn as_x1(&self) -> &[HUF_DEltX1; 4096] {
44        unsafe { core::mem::transmute(&self.data) }
45    }
46
47    fn as_x1_mut(&mut self) -> &mut [HUF_DEltX1; 4096] {
48        unsafe { core::mem::transmute(&mut self.data) }
49    }
50
51    fn as_x2(&self) -> &[HUF_DEltX2; 4096] {
52        unsafe { core::mem::transmute(&self.data) }
53    }
54
55    fn as_x2_mut(&mut self) -> &mut [HUF_DEltX2; 4096] {
56        unsafe { core::mem::transmute(&mut self.data) }
57    }
58
59    fn as_symbols(&self) -> &[u16; 2 * 4096] {
60        unsafe { core::mem::transmute(&self.data) }
61    }
62}
63
64#[derive(Copy, Clone)]
65#[repr(C)]
66pub struct DTableDesc {
67    pub maxTableLog: u8,
68    pub tableType: u8,
69    pub tableLog: u8,
70    pub reserved: u8,
71}
72
73impl DTableDesc {
74    pub fn from_u32(value: u32) -> Self {
75        let [maxTableLog, tableType, tableLog, reserved] = value.to_le_bytes();
76
77        Self {
78            maxTableLog,
79            tableType,
80            tableLog,
81            reserved,
82        }
83    }
84}
85
86#[derive(Copy, Clone)]
87#[repr(C)]
88pub struct HUF_DEltX1 {
89    pub nbBits: u8,
90    pub byte: u8,
91}
92#[repr(C)]
93pub struct HUF_ReadDTableX1_Workspace {
94    rankVal: [u32; 13],
95    rankStart: [u32; 13],
96    statsWksp: crate::lib::common::entropy_common::Workspace,
97    symbols: [u8; 256],
98    huffWeight: [u8; 256],
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
102#[repr(C)]
103pub struct HUF_DEltX2 {
104    pub sequence: u16,
105    pub nbBits: u8,
106    pub length: u8,
107}
108
109pub type rankValCol_t = [u32; 13];
110#[repr(C)]
111pub struct HUF_ReadDTableX2_Workspace {
112    rankVal: [rankValCol_t; 12],
113    rankStats: [u32; 13],
114    rankStart0: [u32; 15],
115    sortedSymbol: [sortedSymbol_t; 256],
116    weightList: [u8; 256],
117    calleeWksp: crate::lib::common::entropy_common::Workspace,
118}
119#[repr(C)]
120pub struct sortedSymbol_t {
121    pub symbol: u8,
122}
123#[repr(C)]
124pub struct HUF_DecompressFastArgs<'a> {
125    pub ip: [*const u8; 4],
126    pub op: [*mut u8; 4],
127    pub bits: [u64; 4],
128    pub dt: &'a DTableData,
129    pub ilowest: *const u8,
130    pub oend: *mut u8,
131    pub iend: [*const u8; 4],
132}
133
134pub const HUF_DECODER_FAST_TABLELOG: core::ffi::c_int = 11;
135pub const HUF_ENABLE_FAST_DECODE: core::ffi::c_int = 1;
136
137unsafe fn HUF_initFastDStream(ip: *const u8) -> size_t {
138    let lastByte = *ip.offset(7);
139    let bitsConsumed = match lastByte.checked_ilog2() {
140        Some(v) => 8 - v,
141        None => 0,
142    };
143    let value = MEM_readLEST(ip as *const core::ffi::c_void) | 1;
144    value << bitsConsumed
145}
146
147impl<'a> HUF_DecompressFastArgs<'a> {
148    unsafe fn new(
149        mut dst: Writer<'_>,
150        src: &[u8],
151        DTable: &'a DTable,
152    ) -> Result<Option<Self>, Error> {
153        // The fast decoding loop assumes 64-bit little-endian.
154        if cfg!(target_endian = "big") || cfg!(target_pointer_width = "32") {
155            return Ok(None);
156        }
157
158        if dst.is_empty() {
159            return Ok(None);
160        }
161
162        // strict minimum : jump table + 1 byte per stream.
163        let [b0, b1, b2, b3, b4, b5, _, _, _, _, ..] = *src else {
164            return Err(Error::corruption_detected);
165        };
166
167        // Must have at least 8 bytes per stream because we don't handle initializing smaller bit containers.
168        // If table log is not correct at this point, fallback to the old decoder.
169        // On small inputs we don't have enough data to trigger the fast loop, so use the old decoder.
170        let dtLog = DTable.description.tableLog as u32;
171        if dtLog != HUF_DECODER_FAST_TABLELOG as u32 {
172            return Ok(None);
173        }
174
175        let length1 = usize::from(u16::from_le_bytes([b0, b1]));
176        let length2 = usize::from(u16::from_le_bytes([b2, b3]));
177        let length3 = usize::from(u16::from_le_bytes([b4, b5]));
178        let length4 = src.len().wrapping_sub(6 + length1 + length2 + length3);
179
180        if 6 + length1 + length2 + length3 > src.len() {
181            return Err(Error::corruption_detected);
182        }
183
184        let istart = src.as_ptr();
185        let mut iend = [core::ptr::null(); 4];
186        iend[0] = istart.add(6); /* jumpTable */
187        iend[1] = iend[0].add(length1);
188        iend[2] = iend[1].add(length2);
189        iend[3] = iend[2].add(length3);
190
191        // HUF_initFastDStream() requires this, and this small of an input won't benefit from the ASM loop anyways.
192        if length1 < 8 || length2 < 8 || length3 < 8 || length4 < 8 {
193            return Ok(None);
194        }
195
196        /* ip[] contains the position that is currently loaded into bits[]. */
197        let mut ip = [core::ptr::null(); 4];
198        ip[0] = iend[1].sub(size_of::<u64>());
199        ip[1] = iend[2].sub(size_of::<u64>());
200        ip[2] = iend[3].sub(size_of::<u64>());
201        ip[3] = src.as_ptr().add(src.len() - size_of::<u64>());
202
203        /* op[] contains the output pointers. */
204        let mut op = [core::ptr::null_mut(); 4];
205        op[0] = dst.as_mut_ptr();
206        op[1] = op[0].add(dst.capacity().div_ceil(4));
207        op[2] = op[1].add(dst.capacity().div_ceil(4));
208        op[3] = op[2].add(dst.capacity().div_ceil(4));
209
210        // No point to call the ASM loop for tiny outputs.
211        if op[3] >= dst.as_mut_ptr_range().end {
212            return Ok(None);
213        }
214
215        // bits[] is the bit container.
216        //
217        // It is read from the MSB down to the LSB.
218        // It is shifted left as it is read, and zeros are
219        // shifted in. After the lowest valid bit a 1 is
220        // set, so that CountTrailingZeros(bits[]) can be used
221        // to count how many bits we've consumed.
222        let bits = ip.map(|v| HUF_initFastDStream(v) as u64);
223
224        // The decoders must be sure to never read beyond ilowest.
225        // This is lower than iend[0], but allowing decoders to read
226        // down to ilowest can allow an extra iteration or two in the
227        // fast loop.
228        let args = Self {
229            ip,
230            op,
231            bits,
232            dt: &DTable.data,
233            ilowest: istart,
234            oend: dst.as_mut_ptr_range().end,
235            iend,
236        };
237
238        Ok(Some(args))
239    }
240}
241
242unsafe fn init_remaining_dstream(
243    args: &HUF_DecompressFastArgs,
244    stream: usize,
245    segmentEnd: *mut u8,
246) -> Result<BIT_DStream_t, Error> {
247    if args.op[stream] > segmentEnd {
248        return Err(Error::corruption_detected);
249    }
250
251    if args.ip[stream] < args.iend[stream].sub(8) {
252        return Err(Error::corruption_detected);
253    }
254
255    let bitContainer = MEM_readLEST(args.ip[stream] as *const core::ffi::c_void) as usize;
256    let bitsConsumed = args.bits[stream].trailing_zeros();
257    let start = args.ilowest as *const core::ffi::c_char;
258    let limitPtr = start.add(::core::mem::size_of::<size_t>());
259    let ptr = args.ip[stream] as *const core::ffi::c_char;
260
261    Ok(BIT_DStream_t {
262        bitContainer,
263        bitsConsumed,
264        ptr,
265        start,
266        limitPtr,
267    })
268}
269
270/// Increase the tableLog to targetTableLog and rescales the stats.
271///
272/// If tableLog > targetTableLog this is a no-op.
273/// @returns New tableLog
274fn HUF_rescaleStats(
275    huffWeight: &mut [u8; 256],
276    rankVal: &mut rankValCol_t,
277    nbSymbols: u32,
278    tableLog: u32,
279    targetTableLog: u32,
280) -> u32 {
281    if tableLog > targetTableLog {
282        return tableLog;
283    }
284
285    if tableLog < targetTableLog {
286        let scale = targetTableLog as usize - tableLog as usize;
287
288        /* Increase the weight for all non-zero probability symbols by scale. */
289        for s in 0..nbSymbols as usize {
290            huffWeight[s] += (if huffWeight[s] == 0 { 0 } else { scale }) as u8;
291        }
292
293        // Update rankVal to reflect the new weights.
294        // All weights except 0 get moved to weight + scale.
295        // Weights [1, scale] are empty.
296        let mut s = targetTableLog as usize;
297        while s > scale {
298            rankVal[s] = rankVal[s - scale];
299            s -= 1;
300        }
301
302        rankVal[1..=scale].fill(0);
303    }
304
305    targetTableLog
306}
307
308pub fn HUF_readDTableX1_wksp(
309    DTable: &mut DTable,
310    src: &[u8],
311    workSpace: &mut Workspace,
312    flags: core::ffi::c_int,
313) -> size_t {
314    let mut dtd = DTable.description;
315    let dt = DTable.data.as_x1_mut();
316
317    let mut tableLog = 0;
318    let mut nbSymbols = 0;
319    let mut iSize: size_t = 0;
320
321    let wksp = workSpace.as_x1_mut();
322
323    iSize = HUF_readStats_wksp(
324        &mut wksp.huffWeight,
325        (HUF_SYMBOLVALUE_MAX + 1) as size_t,
326        &mut wksp.rankVal,
327        &mut nbSymbols,
328        &mut tableLog,
329        src,
330        &mut wksp.statsWksp,
331        flags,
332    );
333    if ERR_isError(iSize) {
334        return iSize;
335    }
336
337    let maxTableLog = (dtd.maxTableLog as core::ffi::c_int + 1) as u32;
338    let targetTableLog = if maxTableLog < 11 { maxTableLog } else { 11 };
339    tableLog = HUF_rescaleStats(
340        &mut wksp.huffWeight,
341        &mut wksp.rankVal,
342        nbSymbols,
343        tableLog,
344        targetTableLog,
345    );
346    if tableLog > (dtd.maxTableLog as core::ffi::c_int + 1) as u32 {
347        return Error::tableLog_tooLarge.to_error_code();
348    }
349    dtd.tableType = 0;
350    dtd.tableLog = tableLog as u8;
351
352    DTable.description = dtd;
353
354    // Compute symbols and rankStart given rankVal:
355    //
356    // rankVal already contains the number of values of each weight.
357    //
358    // symbols contains the symbols ordered by weight. First are the rankVal[0]
359    // weight 0 symbols, followed by the rankVal[1] weight 1 symbols, and so on.
360    // symbols[0] is filled (but unused) to avoid a branch.
361    //
362    // rankStart contains the offset where each rank belongs in the DTable.
363    // rankStart[0] is not filled because there are no entries in the table for
364    // weight 0.
365    let mut nextRankStart = 0u32;
366    for n in 0..tableLog as usize + 1 {
367        let curr = nextRankStart;
368        nextRankStart += wksp.rankVal[n];
369        wksp.rankStart[n] = curr;
370    }
371
372    for (n, w) in wksp.huffWeight[..nbSymbols as usize].iter().enumerate() {
373        let w = usize::from(*w);
374        wksp.symbols[wksp.rankStart[w] as usize] = n as u8;
375        wksp.rankStart[w] += 1;
376    }
377
378    // fill DTable
379    //
380    // We fill all entries of each weight in order.
381    // That way length is a constant for each iteration of the outer loop.
382    // We can switch based on the length to a different inner loop which is
383    // optimized for that particular case.
384    let mut symbol = wksp.rankVal[0] as usize;
385    let mut rankStart = 0;
386    for w in 1..tableLog.wrapping_add(1) {
387        let symbolCount = wksp.rankVal[w as usize] as usize;
388        let length = (1) << w >> 1;
389        let dt = dt[rankStart..][..length * symbolCount].chunks_exact_mut(length);
390        let nbBits = tableLog.wrapping_add(1).wrapping_sub(w) as u8;
391
392        // FIXME: zstd unrolls this loop for low values of `length` (a power of 2).
393        // we should investigate whether that is beneficial here.
394        for (s, chunk) in dt.enumerate() {
395            let byte = wksp.symbols[symbol + s];
396            chunk.fill(HUF_DEltX1 { nbBits, byte });
397        }
398
399        symbol += symbolCount;
400        rankStart += symbolCount * length;
401    }
402
403    iSize
404}
405
406#[inline(always)]
407fn HUF_decodeSymbolX1(Dstream: &mut BIT_DStream_t, dt: &[HUF_DEltX1; 4096], dtLog: u32) -> u8 {
408    let HUF_DEltX1 { byte, nbBits, .. } = dt[Dstream.look_bits_fast(dtLog)];
409    Dstream.skip_bits(u32::from(nbBits));
410    byte
411}
412
413#[inline(always)]
414fn HUF_decodeStreamX1(
415    mut p: Writer<'_>,
416    bitDPtr: &mut BIT_DStream_t,
417    dt: &[HUF_DEltX1; 4096],
418    dtLog: u32,
419) -> size_t {
420    let capacity = p.capacity();
421
422    if p.capacity() >= 4 {
423        while bitDPtr.reload() == StreamStatus::Unfinished && p.capacity() >= 4 {
424            if cfg!(target_pointer_width = "64") {
425                p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
426            }
427            if cfg!(target_pointer_width = "64") || HUF_TABLELOG_MAX <= 12 {
428                p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
429            }
430            if cfg!(target_pointer_width = "64") {
431                p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
432            }
433            p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
434        }
435    } else {
436        bitDPtr.reload();
437    }
438
439    if cfg!(target_pointer_width = "32") {
440        while bitDPtr.reload() == StreamStatus::Unfinished && !p.is_empty() {
441            p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
442        }
443    }
444
445    while !p.is_empty() {
446        p.write_u8(HUF_decodeSymbolX1(bitDPtr, dt, dtLog));
447    }
448
449    capacity - p.capacity()
450}
451
452#[inline(always)]
453fn HUF_decompress1X1_usingDTable_internal_body(
454    mut dst: Writer<'_>,
455    src: &[u8],
456    DTable: &DTable,
457) -> size_t {
458    let dt = DTable.data.as_x1();
459    let dtd = DTable.description;
460    let dtLog = dtd.tableLog as u32;
461
462    let mut bitD = match BIT_DStream_t::new(src) {
463        Ok(v) => v,
464        Err(e) => return e.to_error_code(),
465    };
466
467    HUF_decodeStreamX1(dst.subslice(..), &mut bitD, dt, dtLog);
468
469    if !bitD.is_empty() {
470        return Error::corruption_detected.to_error_code();
471    }
472
473    dst.capacity()
474}
475
476#[inline(always)]
477fn HUF_decompress4X1_usingDTable_internal_body(
478    mut dst: Writer<'_>,
479    src: &[u8],
480    DTable: &DTable,
481) -> size_t {
482    // strict minimum : jump table + 1 byte per stream.
483    let [b0, b1, b2, b3, b4, b5, _, _, _, _, ..] = *src else {
484        return Error::corruption_detected.to_error_code();
485    };
486
487    if dst.capacity() < 6 {
488        return Error::corruption_detected.to_error_code();
489    }
490
491    let length1 = usize::from(u16::from_le_bytes([b0, b1]));
492    let length2 = usize::from(u16::from_le_bytes([b2, b3]));
493    let length3 = usize::from(u16::from_le_bytes([b4, b5]));
494
495    if 6 + length1 + length2 + length3 > src.len() {
496        return Error::corruption_detected.to_error_code();
497    }
498
499    let istart1 = &src[6..][..length1];
500    let istart2 = &src[6 + length1..][..length2];
501    let istart3 = &src[6 + length1 + length2..][..length3];
502    let istart4 = &src[6 + length1 + length2 + length3..];
503
504    let Some((mut w1, mut w2, mut w3, mut w4)) = dst.quarter() else {
505        return Error::corruption_detected.to_error_code();
506    };
507
508    let mut end_signal = true;
509
510    let mut bitD1 = match BIT_DStream_t::new(istart1) {
511        Ok(v) => v,
512        Err(e) => return e.to_error_code(),
513    };
514    let mut bitD2 = match BIT_DStream_t::new(istart2) {
515        Ok(v) => v,
516        Err(e) => return e.to_error_code(),
517    };
518    let mut bitD3 = match BIT_DStream_t::new(istart3) {
519        Ok(v) => v,
520        Err(e) => return e.to_error_code(),
521    };
522    let mut bitD4 = match BIT_DStream_t::new(istart4) {
523        Ok(v) => v,
524        Err(e) => return e.to_error_code(),
525    };
526
527    let dt = DTable.data.as_x1();
528    let dtLog = DTable.description.tableLog as u32;
529
530    if w4.capacity() >= size_of::<size_t>() {
531        while end_signal && w4.capacity() >= 4 {
532            if cfg!(target_pointer_width = "64") {
533                w1.write_u8(HUF_decodeSymbolX1(&mut bitD1, dt, dtLog));
534                w2.write_u8(HUF_decodeSymbolX1(&mut bitD2, dt, dtLog));
535                w3.write_u8(HUF_decodeSymbolX1(&mut bitD3, dt, dtLog));
536                w4.write_u8(HUF_decodeSymbolX1(&mut bitD4, dt, dtLog));
537            }
538
539            if cfg!(target_pointer_width = "64") || HUF_TABLELOG_MAX <= 12 {
540                w1.write_u8(HUF_decodeSymbolX1(&mut bitD1, dt, dtLog));
541                w2.write_u8(HUF_decodeSymbolX1(&mut bitD2, dt, dtLog));
542                w3.write_u8(HUF_decodeSymbolX1(&mut bitD3, dt, dtLog));
543                w4.write_u8(HUF_decodeSymbolX1(&mut bitD4, dt, dtLog));
544            }
545
546            if cfg!(target_pointer_width = "64") {
547                w1.write_u8(HUF_decodeSymbolX1(&mut bitD1, dt, dtLog));
548                w2.write_u8(HUF_decodeSymbolX1(&mut bitD2, dt, dtLog));
549                w3.write_u8(HUF_decodeSymbolX1(&mut bitD3, dt, dtLog));
550                w4.write_u8(HUF_decodeSymbolX1(&mut bitD4, dt, dtLog));
551            }
552
553            w1.write_u8(HUF_decodeSymbolX1(&mut bitD1, dt, dtLog));
554            w2.write_u8(HUF_decodeSymbolX1(&mut bitD2, dt, dtLog));
555            w3.write_u8(HUF_decodeSymbolX1(&mut bitD3, dt, dtLog));
556            w4.write_u8(HUF_decodeSymbolX1(&mut bitD4, dt, dtLog));
557
558            end_signal &= bitD1.reload_fast() == StreamStatus::Unfinished;
559            end_signal &= bitD2.reload_fast() == StreamStatus::Unfinished;
560            end_signal &= bitD3.reload_fast() == StreamStatus::Unfinished;
561            end_signal &= bitD4.reload_fast() == StreamStatus::Unfinished;
562        }
563    }
564
565    HUF_decodeStreamX1(w1, &mut bitD1, dt, dtLog);
566    HUF_decodeStreamX1(w2, &mut bitD2, dt, dtLog);
567    HUF_decodeStreamX1(w3, &mut bitD3, dt, dtLog);
568    HUF_decodeStreamX1(w4, &mut bitD4, dt, dtLog);
569
570    if !(bitD1.is_empty() && bitD2.is_empty() && bitD3.is_empty() && bitD4.is_empty()) {
571        return Error::corruption_detected.to_error_code();
572    }
573
574    dst.capacity()
575}
576
577fn HUF_decompress4X1_usingDTable_internal_bmi2(
578    dst: Writer<'_>,
579    src: &[u8],
580    DTable: &DTable,
581) -> size_t {
582    HUF_decompress4X1_usingDTable_internal_body(dst, src, DTable)
583}
584
585fn HUF_decompress4X1_usingDTable_internal_default(
586    dst: Writer<'_>,
587    src: &[u8],
588    DTable: &DTable,
589) -> size_t {
590    HUF_decompress4X1_usingDTable_internal_body(dst, src, DTable)
591}
592
593macro_rules! HUF_4X_FOR_EACH_STREAM_WITH_VAR {
594    ($mac:ident, $var:literal) => {
595        $mac!(0, $var);
596        $mac!(1, $var);
597        $mac!(2, $var);
598        $mac!(3, $var);
599    };
600}
601
602macro_rules! HUF_4X_FOR_EACH_STREAM {
603    ($mac:ident ) => {
604        $mac!(0);
605        $mac!(1);
606        $mac!(2);
607        $mac!(3);
608    };
609}
610
611unsafe extern "C" fn HUF_decompress4X1_usingDTable_internal_fast_c_loop(
612    args: &mut HUF_DecompressFastArgs,
613) {
614    let dtable = args.dt.as_symbols();
615    let oend = args.oend;
616    let ilowest = args.ilowest;
617
618    // Copy the arguments to local variables.
619    let mut bits = args.bits;
620    let mut ip = args.ip;
621    let mut op = args.op;
622
623    assert!(cfg!(target_endian = "little"));
624    assert!(cfg!(target_pointer_width = "64"));
625
626    'out: loop {
627        /* Assert loop preconditions */
628        if cfg!(debug_assertions) {
629            for stream in 0..4 {
630                assert!(op[stream] <= (if stream == 3 { oend } else { op[stream + 1] }));
631                assert!(ip[stream] >= ilowest);
632            }
633        }
634
635        /* Compute olimit */
636
637        // Each iteration consumes up to 11 bits * 5 = 55 bits < 7 bytes per stream.
638        let oiters = oend.offset_from(op[3]) / 5;
639        let iiters = (ip[0]).offset_from(ilowest) / 7;
640        let iters = Ord::min(oiters, iiters);
641        let symbols = iters * 5;
642
643        // We can simply check that op[3] < olimit, instead of checking all
644        // of our bounds, since we can't hit the other bounds until we've run
645        // iters iterations, which only happens when op[3] == olimit.
646        let olimit = op[3].offset(symbols);
647
648        /* Exit fast decoding loop once we reach the end. */
649        if op[3] == olimit {
650            break;
651        }
652
653        // Exit the decoding loop if any input pointer has crossed the
654        // previous one. This indicates corruption, and a precondition
655        // to our loop is that ip[i] >= ip[0].
656        for stream in 1..4 {
657            if ip[stream] < ip[stream - 1] {
658                break 'out;
659            }
660        }
661
662        if cfg!(debug_assertions) {
663            for stream in 1..4 {
664                assert!(ip[stream] >= ip[stream - 1]);
665            }
666        }
667
668        macro_rules! HUF_4X1_DECODE_SYMBOL {
669            ($stream:expr, $symbol:expr) => {
670                let index = bits[$stream] >> 53;
671                let entry = dtable[index as usize];
672
673                bits[$stream] <<= entry & 0x3F;
674                op[$stream]
675                    .offset($symbol)
676                    .write(((entry >> 8) & 0xFF) as u8)
677            };
678        }
679
680        macro_rules! HUF_4X1_RELOAD_STREAM {
681            ($stream: expr) => {
682                let ctz = bits[$stream].trailing_zeros();
683                let nbBits = ctz & 7;
684                let nbBytes = ctz >> 3;
685
686                op[$stream] = op[$stream].add(5);
687                ip[$stream] = ip[$stream].sub(nbBytes as usize);
688                bits[$stream] = MEM_read64(ip[$stream] as *const core::ffi::c_void) | 1;
689                bits[$stream] <<= nbBits;
690            };
691        }
692
693        /* Manually unroll the loop because compilers don't consistently
694         * unroll the inner loops, which destroys performance.
695         */
696        loop {
697            /* Decode 5 symbols in each of the 4 streams */
698            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X1_DECODE_SYMBOL, 0);
699            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X1_DECODE_SYMBOL, 1);
700            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X1_DECODE_SYMBOL, 2);
701            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X1_DECODE_SYMBOL, 3);
702            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X1_DECODE_SYMBOL, 4);
703
704            /* Reload each of the 4 the bitstreams */
705            HUF_4X_FOR_EACH_STREAM!(HUF_4X1_RELOAD_STREAM);
706
707            if op[3] >= olimit {
708                break;
709            }
710        }
711    }
712
713    // Save the final values of each of the state variables back to args.
714    args.bits = bits;
715    args.ip = ip;
716    args.op = op;
717}
718
719pub type HUF_DecompressFastLoopFn = unsafe extern "C" fn(&mut HUF_DecompressFastArgs) -> ();
720unsafe fn HUF_decompress4X1_usingDTable_internal_fast(
721    mut dst: Writer<'_>,
722    src: &[u8],
723    DTable: &DTable,
724    loopFn: HUF_DecompressFastLoopFn,
725) -> size_t {
726    let oend = dst.as_mut_ptr_range().end;
727
728    let mut args = match HUF_DecompressFastArgs::new(dst.subslice(..), src, DTable) {
729        Ok(Some(args)) => args,
730        Ok(None) => return 0,
731        Err(e) => return e.to_error_code(),
732    };
733
734    assert!(args.ip[0] >= args.ilowest);
735    loopFn(&mut args);
736
737    // Our loop guarantees that ip[] >= ilowest and that we haven't overwritten any op[].
738    let istart = src.as_ptr();
739    assert!(args.ip[0] >= istart);
740    assert!(args.ip[1] >= istart);
741    assert!(args.ip[2] >= istart);
742    assert!(args.ip[3] >= istart);
743    assert!(args.op[3] <= oend);
744
745    assert_eq!(istart, args.ilowest);
746    assert_eq!(istart.add(6), args.iend[0]);
747
748    let segmentSize = dst.capacity().div_ceil(4);
749    let mut segmentEnd = dst.as_mut_ptr_range().start;
750
751    // Finish bit streams one by one.
752    for (i, op) in args.op.iter().copied().enumerate() {
753        segmentEnd = Ord::min(segmentEnd.add(segmentSize), oend);
754
755        let mut bit = match init_remaining_dstream(&args, i, segmentEnd) {
756            Ok(v) => v,
757            Err(e) => return e.to_error_code(),
758        };
759
760        // Decompress and validate that we've produced exactly the expected length.
761        let length = HUF_decodeStreamX1(
762            Writer::from_raw_parts(op, segmentEnd as usize - op as usize),
763            &mut bit,
764            DTable.data.as_x1(),
765            HUF_DECODER_FAST_TABLELOG as u32,
766        );
767
768        if op.add(length as usize) != segmentEnd {
769            return Error::corruption_detected.to_error_code();
770        }
771    }
772
773    dst.capacity()
774}
775
776fn HUF_decompress1X1_usingDTable_internal_bmi2(
777    dst: Writer<'_>,
778    src: &[u8],
779    DTable: &DTable,
780) -> size_t {
781    HUF_decompress1X1_usingDTable_internal_body(dst, src, DTable)
782}
783
784fn HUF_decompress1X1_usingDTable_internal_default(
785    dst: Writer<'_>,
786    src: &[u8],
787    DTable: &DTable,
788) -> size_t {
789    HUF_decompress1X1_usingDTable_internal_body(dst, src, DTable)
790}
791
792fn HUF_decompress1X1_usingDTable_internal(
793    dst: Writer<'_>,
794    src: &[u8],
795    DTable: &DTable,
796    flags: core::ffi::c_int,
797) -> size_t {
798    if flags & HUF_flags_bmi2 as core::ffi::c_int != 0 {
799        HUF_decompress1X1_usingDTable_internal_bmi2(dst, src, DTable)
800    } else {
801        HUF_decompress1X1_usingDTable_internal_default(dst, src, DTable)
802    }
803}
804
805unsafe fn HUF_decompress4X1_usingDTable_internal(
806    mut dst: Writer<'_>,
807    src: &[u8],
808    DTable: &DTable,
809    flags: core::ffi::c_int,
810) -> size_t {
811    if flags & HUF_flags_bmi2 as core::ffi::c_int != 0 {
812        let loopFn = match flags & HUF_flags_disableAsm as i32 {
813            #[cfg(target_arch = "x86_64")]
814            0 => HUF_decompress4X1_usingDTable_internal_fast_asm_loop as HUF_DecompressFastLoopFn,
815            _ => HUF_decompress4X1_usingDTable_internal_fast_c_loop as HUF_DecompressFastLoopFn,
816        };
817
818        if HUF_ENABLE_FAST_DECODE != 0 && flags & HUF_flags_disableFast as core::ffi::c_int == 0 {
819            let ret =
820                HUF_decompress4X1_usingDTable_internal_fast(dst.subslice(..), src, DTable, loopFn);
821            if ret != 0 {
822                return ret;
823            }
824        }
825
826        HUF_decompress4X1_usingDTable_internal_bmi2(dst, src, DTable)
827    } else {
828        HUF_decompress4X1_usingDTable_internal_default(dst, src, DTable)
829    }
830}
831
832unsafe fn HUF_decompress4X1_DCtx_wksp(
833    dctx: &mut DTable,
834    dst: Writer<'_>,
835    src: &[u8],
836    workSpace: &mut Workspace,
837    flags: core::ffi::c_int,
838) -> size_t {
839    let hSize = HUF_readDTableX1_wksp(dctx, src, workSpace, flags);
840    if ERR_isError(hSize) {
841        return hSize;
842    }
843    if hSize as usize >= src.len() {
844        return Error::srcSize_wrong.to_error_code();
845    }
846
847    HUF_decompress4X1_usingDTable_internal(dst, &src[hSize as usize..], dctx, flags)
848}
849
850impl HUF_DEltX2 {
851    #[inline]
852    fn from_u32(value: u32) -> Self {
853        let [a, b, c, d] = value.to_le_bytes();
854
855        Self {
856            sequence: u16::from_le_bytes([a, b]),
857            nbBits: c,
858            length: d,
859        }
860    }
861}
862
863fn HUF_buildDEltX2U32(symbol: u32, nbBits: u32, baseSeq: u32, level: core::ffi::c_int) -> u32 {
864    const _: () = assert!(core::mem::offset_of!(HUF_DEltX2, sequence) == 0);
865    const _: () = assert!(core::mem::offset_of!(HUF_DEltX2, nbBits) == 2);
866    const _: () = assert!(core::mem::offset_of!(HUF_DEltX2, length) == 3);
867    const _: () = assert!(size_of::<HUF_DEltX2>() == size_of::<u32>());
868
869    let seq = if level == 1 {
870        symbol
871    } else {
872        baseSeq.wrapping_add(symbol << 8)
873    };
874    let result = seq
875        .wrapping_add(nbBits << 16)
876        .wrapping_add((level as u32) << 24);
877
878    // FIXME: properly encode as big-endian on big-endian targets. This is tricky because in theory
879    // the wrapping additons above can influence higher bytes. In practice apparently that does not
880    // happen.
881    result
882}
883
884fn HUF_buildDEltX2(symbol: u8, nbBits: u32, baseSeq: u16, level: core::ffi::c_int) -> HUF_DEltX2 {
885    HUF_DEltX2::from_u32(HUF_buildDEltX2U32(
886        symbol as u32,
887        nbBits,
888        baseSeq as u32,
889        level,
890    ))
891}
892
893fn HUF_fillDTableX2ForWeight(
894    DTableRank: &mut [HUF_DEltX2],
895    sorted_symbols: &[sortedSymbol_t],
896    nbBits: u32,
897    tableLog: u32,
898    baseSeq: u16,
899    level: core::ffi::c_int,
900) {
901    let length = (1) << (tableLog.wrapping_sub(nbBits) & 0x1f);
902    let chunks = DTableRank[..sorted_symbols.len() * length].chunks_exact_mut(length);
903
904    for (sorted_symbol, chunk) in sorted_symbols.iter().zip(chunks) {
905        let DElt = HUF_buildDEltX2(sorted_symbol.symbol, nbBits, baseSeq, level);
906        chunk.fill(DElt);
907    }
908}
909
910fn HUF_fillDTableX2Level2(
911    DTable: &mut [HUF_DEltX2],
912    targetLog: u32,
913    consumedBits: u32,
914    rankVal: &[u32; 13],
915    minWeight: core::ffi::c_int,
916    maxWeight1: core::ffi::c_int,
917    sortedSymbols: &[sortedSymbol_t],
918    rankStart: &[u32; 15],
919    nbBitsBaseline: u32,
920    baseSeq: u16,
921) {
922    if minWeight > 1 {
923        let length = 1 << (targetLog.wrapping_sub(consumedBits) & 0x1f);
924        let elem = HUF_DEltX2::from_u32(HUF_buildDEltX2U32(baseSeq as u32, consumedBits, 0, 1));
925        let skipSize = rankVal[minWeight as usize];
926        match length {
927            2 => {
928                DTable[..2].fill(elem);
929            }
930            4 => {
931                DTable[..4].fill(elem);
932            }
933            _ => {
934                for i in (0..skipSize as usize).step_by(8) {
935                    DTable[i..][..8].fill(elem);
936                }
937            }
938        }
939    }
940
941    for w in minWeight as usize..maxWeight1 as usize {
942        let nbBits = nbBitsBaseline.wrapping_sub(w as u32);
943        let totalBits = nbBits.wrapping_add(consumedBits);
944
945        HUF_fillDTableX2ForWeight(
946            &mut DTable[rankVal[w] as usize..],
947            &sortedSymbols[rankStart[w] as usize..rankStart[w + 1] as usize],
948            totalBits,
949            targetLog,
950            baseSeq,
951            2,
952        );
953    }
954}
955
956fn HUF_fillDTableX2(
957    DTable: &mut [HUF_DEltX2; 4096],
958    targetLog: u32,
959    sortedList: &[sortedSymbol_t; 256],
960    rankStart: &[u32; 15],
961    rankValOrigin: &mut [rankValCol_t; 12],
962    maxWeight: u32,
963    nbBitsBaseline: u32,
964) {
965    let rankVal = rankValOrigin[0];
966    let scaleLog = nbBitsBaseline.wrapping_sub(targetLog) as core::ffi::c_int;
967    let minBits = nbBitsBaseline.wrapping_sub(maxWeight);
968
969    let wEnd = maxWeight as core::ffi::c_int + 1;
970    for w in 1..wEnd as usize {
971        let range = rankStart[w] as usize..rankStart[w + 1] as usize;
972
973        let nbBits = nbBitsBaseline.wrapping_sub(w as u32);
974        if targetLog.wrapping_sub(nbBits) >= minBits {
975            let mut start = rankVal[w] as core::ffi::c_int;
976            let length = (1) << (targetLog.wrapping_sub(nbBits) & 0x1f as core::ffi::c_int as u32);
977            let minWeight = Ord::max(nbBits.wrapping_add(scaleLog as u32) as core::ffi::c_int, 1);
978
979            for s in range {
980                HUF_fillDTableX2Level2(
981                    &mut DTable[start as usize..],
982                    targetLog,
983                    nbBits,
984                    &rankValOrigin[nbBits as usize],
985                    minWeight,
986                    wEnd,
987                    sortedList,
988                    rankStart,
989                    nbBitsBaseline,
990                    u16::from(sortedList[s].symbol),
991                );
992                start = (start as u32).wrapping_add(length) as core::ffi::c_int as core::ffi::c_int;
993            }
994        } else {
995            HUF_fillDTableX2ForWeight(
996                &mut DTable[rankVal[w] as usize..],
997                &sortedList[range],
998                nbBits,
999                targetLog,
1000                0,
1001                1,
1002            );
1003        }
1004    }
1005}
1006
1007pub fn HUF_readDTableX2_wksp(
1008    DTable: &mut DTable,
1009    src: &[u8],
1010    wksp: &mut HUF_ReadDTableX2_Workspace,
1011    flags: core::ffi::c_int,
1012) -> size_t {
1013    let mut dtd = DTable.description;
1014
1015    let mut tableLog: u32 = 0;
1016    let mut nbSymbols: u32 = 0;
1017    let mut maxTableLog = dtd.maxTableLog as u32;
1018    let mut iSize: size_t = 0;
1019
1020    let dt = DTable.data.as_x2_mut();
1021
1022    wksp.rankStats.fill(0);
1023    wksp.rankStart0.fill(0);
1024    let rankStart = &mut wksp.rankStart0[1..];
1025
1026    if maxTableLog > HUF_TABLELOG_MAX as u32 {
1027        return Error::tableLog_tooLarge.to_error_code();
1028    }
1029
1030    iSize = HUF_readStats_wksp(
1031        &mut wksp.weightList,
1032        (HUF_SYMBOLVALUE_MAX + 1) as size_t,
1033        &mut wksp.rankStats,
1034        &mut nbSymbols,
1035        &mut tableLog,
1036        src,
1037        &mut wksp.calleeWksp,
1038        flags,
1039    );
1040    if ERR_isError(iSize) {
1041        return iSize;
1042    }
1043    if tableLog > maxTableLog {
1044        return Error::tableLog_tooLarge.to_error_code();
1045    }
1046    if tableLog <= HUF_DECODER_FAST_TABLELOG as u32
1047        && maxTableLog > HUF_DECODER_FAST_TABLELOG as u32
1048    {
1049        maxTableLog = HUF_DECODER_FAST_TABLELOG as u32;
1050    }
1051
1052    /* find maxWeight */
1053    let mut maxW: u32 = tableLog;
1054    while wksp.rankStats[maxW as usize] == 0 {
1055        maxW = maxW.wrapping_sub(1);
1056    }
1057
1058    /* Get start index of each weight */
1059    let mut nextRankStart = 0u32;
1060    for w in 1..maxW + 1 {
1061        let curr = nextRankStart;
1062        nextRankStart += wksp.rankStats[w as usize];
1063        rankStart[w as usize] = curr;
1064    }
1065
1066    rankStart[0] = nextRankStart;
1067    rankStart[maxW.wrapping_add(1) as usize] = nextRankStart;
1068
1069    /* sort symbols by weight */
1070    for s in 0..nbSymbols {
1071        let w = usize::from(wksp.weightList[s as usize]);
1072        let r = rankStart[w];
1073        rankStart[w] += 1;
1074        wksp.sortedSymbol[r as usize].symbol = s as u8;
1075    }
1076
1077    /* forget 0w symbols; this is beginning of weight(1) */
1078    rankStart[0] = 0;
1079
1080    /* Build rankVal */
1081    let rescale = maxTableLog.wrapping_sub(tableLog).wrapping_sub(1) as core::ffi::c_int;
1082    let mut nextRankVal = 0u32;
1083
1084    for w_1 in 1..maxW.wrapping_add(1) {
1085        let curr_0 = nextRankVal;
1086        nextRankVal = nextRankVal
1087            .wrapping_add(wksp.rankStats[w_1 as usize] << w_1.wrapping_add(rescale as u32));
1088
1089        wksp.rankVal[0][w_1 as usize] = curr_0;
1090    }
1091
1092    let minBits = tableLog.wrapping_add(1).wrapping_sub(maxW);
1093    let mut consumed: u32 = 0;
1094    consumed = minBits;
1095    while consumed < maxTableLog.wrapping_sub(minBits).wrapping_add(1) {
1096        for w_2 in 0..maxW.wrapping_add(1) {
1097            wksp.rankVal[consumed as usize][w_2 as usize] =
1098                wksp.rankVal[0][w_2 as usize] >> consumed;
1099        }
1100        consumed = consumed.wrapping_add(1);
1101    }
1102
1103    HUF_fillDTableX2(
1104        dt,
1105        maxTableLog,
1106        &wksp.sortedSymbol,
1107        &wksp.rankStart0,
1108        &mut wksp.rankVal,
1109        maxW,
1110        tableLog.wrapping_add(1),
1111    );
1112
1113    dtd.tableLog = maxTableLog as u8;
1114    dtd.tableType = 1;
1115
1116    DTable.description = dtd;
1117
1118    iSize
1119}
1120
1121#[inline(always)]
1122unsafe fn HUF_decodeSymbolX2(
1123    w: &mut Writer<'_>,
1124    DStream: &mut BIT_DStream_t,
1125    dt: &[HUF_DEltX2; 4096],
1126    dtLog: u32,
1127) {
1128    let HUF_DEltX2 {
1129        sequence,
1130        nbBits,
1131        length,
1132    } = dt[DStream.look_bits_fast(dtLog)];
1133
1134    DStream.skip_bits(nbBits as u32);
1135    w.write_symbol_x2(sequence, length);
1136}
1137
1138#[inline(always)]
1139fn HUF_decodeLastSymbolX2(
1140    w: &mut Writer<'_>,
1141    DStream: &mut BIT_DStream_t,
1142    dt: &[HUF_DEltX2; 4096],
1143    dtLog: u32,
1144) {
1145    let HUF_DEltX2 {
1146        sequence,
1147        nbBits,
1148        length,
1149    } = dt[DStream.look_bits_fast(dtLog)];
1150
1151    w.write_u8(sequence.to_le_bytes()[0]);
1152
1153    if length == 1 {
1154        DStream.skip_bits(u32::from(nbBits));
1155    } else if (DStream.bitsConsumed as usize) < BitContainerType::BITS as usize {
1156        DStream.skip_bits(u32::from(nbBits));
1157        DStream.bitsConsumed = Ord::min(DStream.bitsConsumed, BitContainerType::BITS);
1158    }
1159}
1160
1161macro_rules! HUF_DECODE_SYMBOLX2_0 {
1162    ($($args:expr),*) => {
1163        HUF_decodeSymbolX2($($args),*)
1164    }
1165}
1166
1167macro_rules! HUF_DECODE_SYMBOLX2_1 {
1168    ($($args:expr),*) => {
1169        if cfg!(target_pointer_width = "64") || HUF_TABLELOG_MAX <= 12 {
1170            HUF_decodeSymbolX2($($args),*)
1171        }
1172    }
1173}
1174
1175macro_rules! HUF_DECODE_SYMBOLX2_2 {
1176    ($($args:expr),*) => {
1177        if cfg!(target_pointer_width = "64") {
1178            HUF_decodeSymbolX2($($args),*)
1179        }
1180    }
1181}
1182
1183#[inline(always)]
1184unsafe fn HUF_decodeStreamX2(
1185    mut p: Writer<'_>,
1186    bitDPtr: &mut BIT_DStream_t,
1187    dt: &[HUF_DEltX2; 4096],
1188    dtLog: u32,
1189) -> size_t {
1190    let capacity = p.capacity();
1191
1192    /* up to 8 symbols at a time */
1193    if p.capacity() >= size_of::<usize>() {
1194        if dtLog <= 11 && cfg!(target_pointer_width = "64") {
1195            /* up to 10 symbols at a time */
1196            while (bitDPtr.reload() == StreamStatus::Unfinished) && p.capacity() >= 10 {
1197                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1198                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1199                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1200                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1201                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1202            }
1203        } else {
1204            /* up to 8 symbols at a time */
1205            while bitDPtr.reload() == StreamStatus::Unfinished && p.capacity() >= size_of::<usize>()
1206            {
1207                HUF_DECODE_SYMBOLX2_2!(&mut p, bitDPtr, dt, dtLog);
1208                HUF_DECODE_SYMBOLX2_1!(&mut p, bitDPtr, dt, dtLog);
1209                HUF_DECODE_SYMBOLX2_2!(&mut p, bitDPtr, dt, dtLog);
1210                HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1211            }
1212        }
1213    } else {
1214        bitDPtr.reload();
1215    }
1216
1217    /* closer to end : up to 2 symbols at a time */
1218    if p.capacity() >= 2 {
1219        while (bitDPtr.reload() == StreamStatus::Unfinished) && p.capacity() >= 2 {
1220            HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1221        }
1222
1223        while p.capacity() >= 2 {
1224            HUF_DECODE_SYMBOLX2_0!(&mut p, bitDPtr, dt, dtLog);
1225        }
1226    }
1227
1228    if !p.is_empty() {
1229        HUF_decodeLastSymbolX2(&mut p, bitDPtr, dt, dtLog);
1230    }
1231
1232    capacity - p.capacity()
1233}
1234
1235#[inline(always)]
1236unsafe fn HUF_decompress1X2_usingDTable_internal_body(
1237    mut dst: Writer<'_>,
1238    src: &[u8],
1239    DTable: &DTable,
1240) -> size_t {
1241    let mut bitD = match BIT_DStream_t::new(src) {
1242        Ok(v) => v,
1243        Err(e) => return e.to_error_code(),
1244    };
1245
1246    let dt = DTable.data.as_x2();
1247    let dtd = DTable.description;
1248    HUF_decodeStreamX2(dst.subslice(..), &mut bitD, dt, dtd.tableLog as u32);
1249    if !bitD.is_empty() {
1250        return Error::corruption_detected.to_error_code();
1251    }
1252
1253    dst.capacity()
1254}
1255
1256#[inline(always)]
1257unsafe fn HUF_decompress4X2_usingDTable_internal_body(
1258    mut dst: Writer<'_>,
1259    src: &[u8],
1260    DTable: &DTable,
1261) -> size_t {
1262    // Strict minimum : jump table + 1 byte per stream.
1263    let [b0, b1, b2, b3, b4, b5, _, _, _, _, ..] = *src else {
1264        return Error::corruption_detected.to_error_code();
1265    };
1266
1267    // Stream 4-way split would not work.
1268    if dst.capacity() < 6 {
1269        return Error::corruption_detected.to_error_code();
1270    }
1271
1272    let length1 = usize::from(u16::from_le_bytes([b0, b1]));
1273    let length2 = usize::from(u16::from_le_bytes([b2, b3]));
1274    let length3 = usize::from(u16::from_le_bytes([b4, b5]));
1275
1276    if 6 + length1 + length2 + length3 > src.len() {
1277        return Error::corruption_detected.to_error_code();
1278    }
1279
1280    let istart1 = &src[6..][..length1];
1281    let istart2 = &src[6 + length1..][..length2];
1282    let istart3 = &src[6 + length1 + length2..][..length3];
1283    let istart4 = &src[6 + length1 + length2 + length3..];
1284
1285    let Some((mut op1, mut op2, mut op3, mut op4)) = dst.quarter() else {
1286        return Error::corruption_detected.to_error_code();
1287    };
1288
1289    let mut end_signal = true;
1290
1291    let dtLog = DTable.description.tableLog as u32;
1292
1293    if op4.is_empty() {
1294        return Error::corruption_detected.to_error_code();
1295    }
1296
1297    let mut bitD1 = match BIT_DStream_t::new(istart1) {
1298        Ok(v) => v,
1299        Err(e) => return e.to_error_code(),
1300    };
1301    let mut bitD2 = match BIT_DStream_t::new(istart2) {
1302        Ok(v) => v,
1303        Err(e) => return e.to_error_code(),
1304    };
1305    let mut bitD3 = match BIT_DStream_t::new(istart3) {
1306        Ok(v) => v,
1307        Err(e) => return e.to_error_code(),
1308    };
1309    let mut bitD4 = match BIT_DStream_t::new(istart4) {
1310        Ok(v) => v,
1311        Err(e) => return e.to_error_code(),
1312    };
1313
1314    let dt = DTable.data.as_x2();
1315
1316    /* 16-32 symbols per loop (4-8 symbols per stream) */
1317    if op4.capacity() >= size_of::<usize>() {
1318        while end_signal && op4.capacity() >= size_of::<usize>() {
1319            if cfg!(any(target_arch = "x86_64", target_arch = "x86")) {
1320                HUF_DECODE_SYMBOLX2_2!(&mut op1, &mut bitD1, dt, dtLog);
1321                HUF_DECODE_SYMBOLX2_1!(&mut op1, &mut bitD1, dt, dtLog);
1322                HUF_DECODE_SYMBOLX2_2!(&mut op1, &mut bitD1, dt, dtLog);
1323                HUF_DECODE_SYMBOLX2_0!(&mut op1, &mut bitD1, dt, dtLog);
1324                HUF_DECODE_SYMBOLX2_2!(&mut op2, &mut bitD2, dt, dtLog);
1325                HUF_DECODE_SYMBOLX2_1!(&mut op2, &mut bitD2, dt, dtLog);
1326                HUF_DECODE_SYMBOLX2_2!(&mut op2, &mut bitD2, dt, dtLog);
1327                HUF_DECODE_SYMBOLX2_0!(&mut op2, &mut bitD2, dt, dtLog);
1328
1329                end_signal &= bitD1.reload_fast() == StreamStatus::Unfinished;
1330                end_signal &= bitD2.reload_fast() == StreamStatus::Unfinished;
1331
1332                HUF_DECODE_SYMBOLX2_2!(&mut op3, &mut bitD3, dt, dtLog);
1333                HUF_DECODE_SYMBOLX2_1!(&mut op3, &mut bitD3, dt, dtLog);
1334                HUF_DECODE_SYMBOLX2_2!(&mut op3, &mut bitD3, dt, dtLog);
1335                HUF_DECODE_SYMBOLX2_0!(&mut op3, &mut bitD3, dt, dtLog);
1336                HUF_DECODE_SYMBOLX2_2!(&mut op4, &mut bitD4, dt, dtLog);
1337                HUF_DECODE_SYMBOLX2_1!(&mut op4, &mut bitD4, dt, dtLog);
1338                HUF_DECODE_SYMBOLX2_2!(&mut op4, &mut bitD4, dt, dtLog);
1339                HUF_DECODE_SYMBOLX2_0!(&mut op4, &mut bitD4, dt, dtLog);
1340
1341                end_signal &= bitD3.reload_fast() == StreamStatus::Unfinished;
1342                end_signal &= bitD4.reload_fast() == StreamStatus::Unfinished;
1343            } else {
1344                HUF_DECODE_SYMBOLX2_2!(&mut op1, &mut bitD1, dt, dtLog);
1345                HUF_DECODE_SYMBOLX2_2!(&mut op2, &mut bitD2, dt, dtLog);
1346                HUF_DECODE_SYMBOLX2_2!(&mut op3, &mut bitD3, dt, dtLog);
1347                HUF_DECODE_SYMBOLX2_2!(&mut op4, &mut bitD4, dt, dtLog);
1348                HUF_DECODE_SYMBOLX2_1!(&mut op1, &mut bitD1, dt, dtLog);
1349                HUF_DECODE_SYMBOLX2_1!(&mut op2, &mut bitD2, dt, dtLog);
1350                HUF_DECODE_SYMBOLX2_1!(&mut op3, &mut bitD3, dt, dtLog);
1351                HUF_DECODE_SYMBOLX2_1!(&mut op4, &mut bitD4, dt, dtLog);
1352                HUF_DECODE_SYMBOLX2_2!(&mut op1, &mut bitD1, dt, dtLog);
1353                HUF_DECODE_SYMBOLX2_2!(&mut op2, &mut bitD2, dt, dtLog);
1354                HUF_DECODE_SYMBOLX2_2!(&mut op3, &mut bitD3, dt, dtLog);
1355                HUF_DECODE_SYMBOLX2_2!(&mut op4, &mut bitD4, dt, dtLog);
1356                HUF_DECODE_SYMBOLX2_0!(&mut op1, &mut bitD1, dt, dtLog);
1357                HUF_DECODE_SYMBOLX2_0!(&mut op2, &mut bitD2, dt, dtLog);
1358                HUF_DECODE_SYMBOLX2_0!(&mut op3, &mut bitD3, dt, dtLog);
1359                HUF_DECODE_SYMBOLX2_0!(&mut op4, &mut bitD4, dt, dtLog);
1360
1361                end_signal &= bitD1.reload_fast() == StreamStatus::Unfinished;
1362                end_signal &= bitD2.reload_fast() == StreamStatus::Unfinished;
1363                end_signal &= bitD3.reload_fast() == StreamStatus::Unfinished;
1364                end_signal &= bitD4.reload_fast() == StreamStatus::Unfinished;
1365            }
1366        }
1367    }
1368
1369    // Check for corruption.
1370    // NOTE: these conditions do in fact trigger for invalid input. That is why currently
1371    // `Writer::write_symbol_x2` does not assert that it is in-bounds.
1372    if op1.ptr.unwrap().as_ptr() > op1.end {
1373        return Error::corruption_detected.to_error_code();
1374    }
1375    if op2.ptr.unwrap().as_ptr() > op2.end {
1376        return Error::corruption_detected.to_error_code();
1377    }
1378    if op3.ptr.unwrap().as_ptr() > op3.end {
1379        return Error::corruption_detected.to_error_code();
1380    }
1381    // NOTE: op4 is already verified within main loop.
1382
1383    // Finish bit streams one by one.
1384    HUF_decodeStreamX2(op1, &mut bitD1, dt, dtLog);
1385    HUF_decodeStreamX2(op2, &mut bitD2, dt, dtLog);
1386    HUF_decodeStreamX2(op3, &mut bitD3, dt, dtLog);
1387    HUF_decodeStreamX2(op4, &mut bitD4, dt, dtLog);
1388
1389    // Check.
1390    if !(bitD1.is_empty() && bitD2.is_empty() && bitD3.is_empty() && bitD4.is_empty()) {
1391        return Error::corruption_detected.to_error_code();
1392    }
1393
1394    // The decoded size.
1395    dst.capacity()
1396}
1397
1398unsafe fn HUF_decompress4X2_usingDTable_internal_bmi2(
1399    dst: Writer<'_>,
1400    src: &[u8],
1401    DTable: &DTable,
1402) -> size_t {
1403    HUF_decompress4X2_usingDTable_internal_body(dst, src, DTable)
1404}
1405
1406unsafe fn HUF_decompress4X2_usingDTable_internal_default(
1407    dst: Writer<'_>,
1408    src: &[u8],
1409    DTable: &DTable,
1410) -> size_t {
1411    HUF_decompress4X2_usingDTable_internal_body(dst, src, DTable)
1412}
1413
1414unsafe extern "C" fn HUF_decompress4X2_usingDTable_internal_fast_c_loop(
1415    args: &mut HUF_DecompressFastArgs,
1416) {
1417    let dtable: &[HUF_DEltX2; 4096] = core::mem::transmute(args.dt);
1418    let ilowest = args.ilowest;
1419
1420    let mut bits = args.bits;
1421    let mut ip = args.ip;
1422    let mut op = args.op;
1423
1424    let mut oend: [*mut u8; 4] = [core::ptr::null_mut::<u8>(); 4];
1425    oend[0] = op[1];
1426    oend[1] = op[2];
1427    oend[2] = op[3];
1428    oend[3] = args.oend;
1429
1430    'out: loop {
1431        let mut olimit = core::ptr::null_mut::<u8>();
1432
1433        /* Assert loop preconditions */
1434        if cfg!(debug_assertions) {
1435            for stream in 0..4 {
1436                assert!(op[stream] <= oend[stream]);
1437                assert!(ip[stream] >= ilowest);
1438            }
1439        }
1440
1441        /* Compute olimit */
1442
1443        /* Each loop does 5 table lookups for each of the 4 streams.
1444         * Each table lookup consumes up to 11 bits of input, and produces
1445         * up to 2 bytes of output.
1446         */
1447        /* We can consume up to 7 bytes of input per iteration per stream.
1448         * We also know that each input pointer is >= ip[0]. So we can run
1449         * iters loops before running out of input.
1450         */
1451        let mut iters = ip[0].offset_from(ilowest) as size_t / 7;
1452
1453        /* Each iteration can produce up to 10 bytes of output per stream.
1454         * Each output stream my advance at different rates. So take the
1455         * minimum number of safe iterations among all the output streams.
1456         */
1457        for stream in 0..4 {
1458            let oiters = oend[stream].offset_from(op[stream]) as size_t / 10;
1459            iters = Ord::min(iters, oiters);
1460        }
1461
1462        /* Each iteration produces at least 5 output symbols. So until
1463         * op[3] crosses olimit, we know we haven't executed iters
1464         * iterations yet. This saves us maintaining an iters counter,
1465         * at the expense of computing the remaining # of iterations
1466         * more frequently.
1467         */
1468        olimit = op[3].add(iters * 5);
1469
1470        /* Exit the fast decoding loop once we reach the end. */
1471        if op[3] == olimit {
1472            break;
1473        }
1474
1475        /* Exit the decoding loop if any input pointer has crossed the
1476         * previous one. This indicates corruption, and a precondition
1477         * to our loop is that ip[i] >= ip[0].
1478         */
1479        for stream in 1..4 {
1480            if ip[stream] < ip[stream - 1] {
1481                break 'out;
1482            }
1483        }
1484
1485        for stream in 1..4 {
1486            assert!(ip[stream] >= ip[stream - 1]);
1487        }
1488
1489        macro_rules! HUF_4X2_DECODE_SYMBOL {
1490            ($stream:expr, $decode3:expr) => {
1491                if (($decode3) != 0 || ($stream) != 3) {
1492                    let index = (bits[($stream)] >> 53);
1493                    let entry = dtable[index as usize];
1494                    MEM_write16(op[($stream)].cast(), entry.sequence);
1495                    bits[($stream)] <<= (entry.nbBits) & 0x3F;
1496                    op[($stream)] = op[($stream)].add(usize::from(entry.length));
1497                }
1498            };
1499        }
1500
1501        macro_rules! HUF_4X2_RELOAD_STREAM {
1502            ($stream:expr) => {
1503                HUF_4X2_DECODE_SYMBOL!(3, 1);
1504                {
1505                    let ctz = bits[($stream)].trailing_zeros();
1506                    let nbBits = ctz & 7;
1507                    let nbBytes = ctz >> 3;
1508                    ip[($stream)] = ip[$stream].sub(nbBytes as usize);
1509                    bits[($stream)] = MEM_read64(ip[($stream)].cast()) | 1;
1510                    bits[($stream)] <<= nbBits;
1511                }
1512            };
1513        }
1514
1515        /* Manually unroll the loop because compilers don't consistently
1516         * unroll the inner loops, which destroys performance.
1517         */
1518
1519        loop {
1520            /* Decode 5 symbols from each of the first 3 streams.
1521             * The final stream will be decoded during the reload phase
1522             * to reduce register pressure.
1523             */
1524            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X2_DECODE_SYMBOL, 0);
1525            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X2_DECODE_SYMBOL, 0);
1526            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X2_DECODE_SYMBOL, 0);
1527            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X2_DECODE_SYMBOL, 0);
1528            HUF_4X_FOR_EACH_STREAM_WITH_VAR!(HUF_4X2_DECODE_SYMBOL, 0);
1529
1530            /* Decode one symbol from the final stream */
1531            HUF_4X2_DECODE_SYMBOL!(3, 1);
1532
1533            /* Decode 4 symbols from the final stream & reload bitstreams.
1534             * The final stream is reloaded last, meaning that all 5 symbols
1535             * are decoded from the final stream before it is reloaded.
1536             */
1537            HUF_4X_FOR_EACH_STREAM!(HUF_4X2_RELOAD_STREAM);
1538
1539            if op[3] >= olimit {
1540                break;
1541            }
1542        }
1543    }
1544
1545    // Save the final values of each of the state variables back to args.
1546    args.bits = bits;
1547    args.ip = ip;
1548    args.op = op;
1549}
1550
1551unsafe fn HUF_decompress4X2_usingDTable_internal_fast(
1552    mut dst: Writer<'_>,
1553    src: &[u8],
1554    DTable: &DTable,
1555    loopFn: HUF_DecompressFastLoopFn,
1556) -> size_t {
1557    let oend = dst.as_mut_ptr_range().end;
1558
1559    let mut args = match HUF_DecompressFastArgs::new(dst.subslice(..), src, DTable) {
1560        Ok(Some(args)) => args,
1561        Ok(None) => return 0,
1562        Err(e) => return e.to_error_code(),
1563    };
1564
1565    assert!(args.ip[0] >= args.ilowest);
1566    loopFn(&mut args);
1567
1568    // note : op4 already verified within main loop.
1569    let ilowest = src.as_ptr();
1570    assert!(args.ip[0] >= ilowest);
1571    assert!(args.ip[1] >= ilowest);
1572    assert!(args.ip[2] >= ilowest);
1573    assert!(args.ip[3] >= ilowest);
1574    assert!(args.op[3] <= oend);
1575
1576    assert_eq!(ilowest, args.ilowest);
1577    assert_eq!(ilowest.add(6), args.iend[0]);
1578
1579    let segmentSize = dst.capacity().div_ceil(4) as isize;
1580    let mut segmentEnd = dst.as_mut_ptr();
1581
1582    for (i, op) in args.op.iter().copied().enumerate() {
1583        if segmentSize <= oend.offset_from(segmentEnd) {
1584            segmentEnd = segmentEnd.offset(segmentSize);
1585        } else {
1586            segmentEnd = oend;
1587        }
1588
1589        let mut bit = match init_remaining_dstream(&args, i, segmentEnd) {
1590            Ok(v) => v,
1591            Err(e) => return e.to_error_code(),
1592        };
1593
1594        let length = HUF_decodeStreamX2(
1595            Writer::from_raw_parts(op, segmentEnd as usize - op as usize),
1596            &mut bit,
1597            DTable.data.as_x2(),
1598            HUF_DECODER_FAST_TABLELOG as u32,
1599        );
1600
1601        if op.add(length as usize) != segmentEnd {
1602            return Error::corruption_detected.to_error_code();
1603        }
1604    }
1605
1606    dst.capacity()
1607}
1608
1609unsafe fn HUF_decompress4X2_usingDTable_internal(
1610    mut dst: Writer<'_>,
1611    src: &[u8],
1612    DTable: &DTable,
1613    flags: core::ffi::c_int,
1614) -> size_t {
1615    if flags & HUF_flags_bmi2 as core::ffi::c_int != 0 {
1616        let loopFn = match flags & HUF_flags_disableAsm as core::ffi::c_int {
1617            #[cfg(target_arch = "x86_64")]
1618            0 => HUF_decompress4X2_usingDTable_internal_fast_asm_loop as HUF_DecompressFastLoopFn,
1619            _ => HUF_decompress4X2_usingDTable_internal_fast_c_loop as HUF_DecompressFastLoopFn,
1620        };
1621
1622        if HUF_ENABLE_FAST_DECODE != 0 && flags & HUF_flags_disableFast as core::ffi::c_int == 0 {
1623            let ret =
1624                HUF_decompress4X2_usingDTable_internal_fast(dst.subslice(..), src, DTable, loopFn);
1625            if ret != 0 {
1626                return ret;
1627            }
1628        }
1629
1630        HUF_decompress4X2_usingDTable_internal_bmi2(dst, src, DTable)
1631    } else {
1632        HUF_decompress4X2_usingDTable_internal_default(dst, src, DTable)
1633    }
1634}
1635
1636unsafe fn HUF_decompress1X2_usingDTable_internal_bmi2(
1637    dst: Writer<'_>,
1638    src: &[u8],
1639    DTable: &DTable,
1640) -> size_t {
1641    HUF_decompress1X2_usingDTable_internal_body(dst, src, DTable)
1642}
1643unsafe fn HUF_decompress1X2_usingDTable_internal_default(
1644    dst: Writer<'_>,
1645    src: &[u8],
1646    DTable: &DTable,
1647) -> size_t {
1648    HUF_decompress1X2_usingDTable_internal_body(dst, src, DTable)
1649}
1650
1651unsafe fn HUF_decompress1X2_usingDTable_internal(
1652    dst: Writer<'_>,
1653    src: &[u8],
1654    DTable: &DTable,
1655    flags: core::ffi::c_int,
1656) -> size_t {
1657    if flags & HUF_flags_bmi2 as core::ffi::c_int != 0 {
1658        HUF_decompress1X2_usingDTable_internal_bmi2(dst, src, DTable)
1659    } else {
1660        HUF_decompress1X2_usingDTable_internal_default(dst, src, DTable)
1661    }
1662}
1663
1664pub unsafe fn HUF_decompress1X2_DCtx_wksp(
1665    dctx: &mut DTable,
1666    dst: Writer<'_>,
1667    src: &[u8],
1668    workSpace: &mut Workspace,
1669    flags: core::ffi::c_int,
1670) -> size_t {
1671    let hSize = HUF_readDTableX2_wksp(dctx, src, workSpace.as_x2_mut(), flags);
1672    if ERR_isError(hSize) {
1673        return hSize;
1674    }
1675    if hSize as usize >= src.len() {
1676        return Error::srcSize_wrong.to_error_code();
1677    }
1678
1679    HUF_decompress1X2_usingDTable_internal(dst, &src[hSize as usize..], dctx, flags)
1680}
1681
1682unsafe fn HUF_decompress4X2_DCtx_wksp(
1683    dctx: &mut DTable,
1684    dst: Writer<'_>,
1685    src: &[u8],
1686    workSpace: &mut Workspace,
1687    flags: core::ffi::c_int,
1688) -> size_t {
1689    let hSize = HUF_readDTableX2_wksp(dctx, src, workSpace.as_x2_mut(), flags);
1690    if ERR_isError(hSize) {
1691        return hSize;
1692    }
1693    if hSize as usize >= src.len() {
1694        return Error::srcSize_wrong.to_error_code();
1695    }
1696
1697    HUF_decompress4X2_usingDTable_internal(dst, &src[hSize as usize..], dctx, flags)
1698}
1699
1700static algoTime: [[algo_time_t; 2]; 16] = [
1701    [
1702        {
1703            algo_time_t {
1704                tableTime: 0,
1705                decode256Time: 0,
1706            }
1707        },
1708        {
1709            algo_time_t {
1710                tableTime: 1,
1711                decode256Time: 1,
1712            }
1713        },
1714    ],
1715    [
1716        {
1717            algo_time_t {
1718                tableTime: 0,
1719                decode256Time: 0,
1720            }
1721        },
1722        {
1723            algo_time_t {
1724                tableTime: 1,
1725                decode256Time: 1,
1726            }
1727        },
1728    ],
1729    [
1730        {
1731            algo_time_t {
1732                tableTime: 150,
1733                decode256Time: 216,
1734            }
1735        },
1736        {
1737            algo_time_t {
1738                tableTime: 381,
1739                decode256Time: 119,
1740            }
1741        },
1742    ],
1743    [
1744        {
1745            algo_time_t {
1746                tableTime: 170,
1747                decode256Time: 205,
1748            }
1749        },
1750        {
1751            algo_time_t {
1752                tableTime: 514,
1753                decode256Time: 112,
1754            }
1755        },
1756    ],
1757    [
1758        {
1759            algo_time_t {
1760                tableTime: 177,
1761                decode256Time: 199,
1762            }
1763        },
1764        {
1765            algo_time_t {
1766                tableTime: 539,
1767                decode256Time: 110,
1768            }
1769        },
1770    ],
1771    [
1772        {
1773            algo_time_t {
1774                tableTime: 197,
1775                decode256Time: 194,
1776            }
1777        },
1778        {
1779            algo_time_t {
1780                tableTime: 644,
1781                decode256Time: 107,
1782            }
1783        },
1784    ],
1785    [
1786        {
1787            algo_time_t {
1788                tableTime: 221,
1789                decode256Time: 192,
1790            }
1791        },
1792        {
1793            algo_time_t {
1794                tableTime: 735,
1795                decode256Time: 107,
1796            }
1797        },
1798    ],
1799    [
1800        {
1801            algo_time_t {
1802                tableTime: 256,
1803                decode256Time: 189,
1804            }
1805        },
1806        {
1807            algo_time_t {
1808                tableTime: 881,
1809                decode256Time: 106,
1810            }
1811        },
1812    ],
1813    [
1814        {
1815            algo_time_t {
1816                tableTime: 359,
1817                decode256Time: 188,
1818            }
1819        },
1820        {
1821            algo_time_t {
1822                tableTime: 1167,
1823                decode256Time: 109,
1824            }
1825        },
1826    ],
1827    [
1828        {
1829            algo_time_t {
1830                tableTime: 582,
1831                decode256Time: 187,
1832            }
1833        },
1834        {
1835            algo_time_t {
1836                tableTime: 1570,
1837                decode256Time: 114,
1838            }
1839        },
1840    ],
1841    [
1842        {
1843            algo_time_t {
1844                tableTime: 688,
1845                decode256Time: 187,
1846            }
1847        },
1848        {
1849            algo_time_t {
1850                tableTime: 1712,
1851                decode256Time: 122,
1852            }
1853        },
1854    ],
1855    [
1856        {
1857            algo_time_t {
1858                tableTime: 825,
1859                decode256Time: 186,
1860            }
1861        },
1862        {
1863            algo_time_t {
1864                tableTime: 1965,
1865                decode256Time: 136,
1866            }
1867        },
1868    ],
1869    [
1870        {
1871            algo_time_t {
1872                tableTime: 976,
1873                decode256Time: 185,
1874            }
1875        },
1876        {
1877            algo_time_t {
1878                tableTime: 2131,
1879                decode256Time: 150,
1880            }
1881        },
1882    ],
1883    [
1884        {
1885            algo_time_t {
1886                tableTime: 1180,
1887                decode256Time: 186,
1888            }
1889        },
1890        {
1891            algo_time_t {
1892                tableTime: 2070,
1893                decode256Time: 175,
1894            }
1895        },
1896    ],
1897    [
1898        {
1899            algo_time_t {
1900                tableTime: 1377,
1901                decode256Time: 185,
1902            }
1903        },
1904        {
1905            algo_time_t {
1906                tableTime: 1731,
1907                decode256Time: 202,
1908            }
1909        },
1910    ],
1911    [
1912        {
1913            algo_time_t {
1914                tableTime: 1412,
1915                decode256Time: 185,
1916            }
1917        },
1918        {
1919            algo_time_t {
1920                tableTime: 1695,
1921                decode256Time: 202,
1922            }
1923        },
1924    ],
1925];
1926
1927enum Decoder {
1928    A1,
1929    A2,
1930}
1931
1932///  Tells which decoder is likely to decode faster,
1933///  based on a set of pre-computed metrics.
1934///
1935/// @return : 0==HUF_decompress4X1, 1==HUF_decompress4X2 .
1936///  Assumption : 0 < dst_size <= 128 KB */
1937fn HUF_selectDecoder(dst_size: usize, src_size: usize) -> Decoder {
1938    let D256 = (dst_size >> 8) as u32;
1939
1940    let Q = if src_size >= dst_size {
1941        15
1942    } else {
1943        src_size * 16 / dst_size
1944    };
1945
1946    let [time0, time1] = algoTime[Q];
1947    let DTime0 = time0.tableTime + time0.decode256Time * D256;
1948    let DTime1 = time1.tableTime + time1.decode256Time * D256;
1949
1950    if (DTime1 + (DTime1 >> 5)) < DTime0 {
1951        Decoder::A2
1952    } else {
1953        Decoder::A1
1954    }
1955}
1956
1957pub unsafe fn HUF_decompress1X_DCtx_wksp(
1958    dctx: &mut DTable,
1959    mut dst: Writer<'_>,
1960    src: &[u8],
1961    workSpace: &mut Workspace,
1962    flags: core::ffi::c_int,
1963) -> size_t {
1964    if dst.capacity() == 0 {
1965        return Error::dstSize_tooSmall.to_error_code();
1966    }
1967    if src.len() > dst.capacity() {
1968        return Error::corruption_detected.to_error_code();
1969    }
1970    if src.len() == dst.capacity() {
1971        ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), dst.capacity());
1972        return dst.capacity();
1973    }
1974    if src.len() == 1 {
1975        ptr::write_bytes(dst.as_mut_ptr(), src[0], dst.capacity());
1976        return dst.capacity();
1977    }
1978
1979    match HUF_selectDecoder(dst.capacity(), src.len()) {
1980        Decoder::A1 => HUF_decompress1X1_DCtx_wksp(dctx, dst, src, workSpace, flags),
1981        Decoder::A2 => HUF_decompress1X2_DCtx_wksp(dctx, dst, src, workSpace, flags),
1982    }
1983}
1984
1985pub unsafe fn HUF_decompress1X_usingDTable(
1986    dst: Writer<'_>,
1987    src: &[u8],
1988    DTable: &DTable,
1989    flags: core::ffi::c_int,
1990) -> size_t {
1991    if DTable.description.tableType != 0 {
1992        HUF_decompress1X2_usingDTable_internal(dst, src, DTable, flags)
1993    } else {
1994        HUF_decompress1X1_usingDTable_internal(dst, src, DTable, flags)
1995    }
1996}
1997
1998pub unsafe fn HUF_decompress1X1_DCtx_wksp(
1999    dctx: &mut DTable,
2000    dst: Writer<'_>,
2001    src: &[u8],
2002    workSpace: &mut Workspace,
2003    flags: core::ffi::c_int,
2004) -> size_t {
2005    let hSize = { HUF_readDTableX1_wksp(dctx, src, workSpace, flags) };
2006    if ERR_isError(hSize) {
2007        return hSize;
2008    }
2009    if hSize as usize >= src.len() {
2010        return Error::srcSize_wrong.to_error_code();
2011    }
2012
2013    HUF_decompress1X1_usingDTable_internal(dst, &src[hSize as usize..], dctx, flags)
2014}
2015
2016pub unsafe fn HUF_decompress4X_usingDTable(
2017    dst: Writer<'_>,
2018    src: &[u8],
2019    DTable: &DTable,
2020    flags: core::ffi::c_int,
2021) -> size_t {
2022    if DTable.description.tableType != 0 {
2023        HUF_decompress4X2_usingDTable_internal(dst, src, DTable, flags)
2024    } else {
2025        HUF_decompress4X1_usingDTable_internal(dst, src, DTable, flags)
2026    }
2027}
2028
2029pub unsafe fn HUF_decompress4X_hufOnly_wksp(
2030    dctx: &mut DTable,
2031    dst: Writer<'_>,
2032    src: &[u8],
2033    workSpace: &mut Workspace,
2034    flags: core::ffi::c_int,
2035) -> size_t {
2036    if dst.is_empty() {
2037        return Error::dstSize_tooSmall.to_error_code();
2038    }
2039    if src.is_empty() {
2040        return Error::corruption_detected.to_error_code();
2041    }
2042
2043    match HUF_selectDecoder(dst.capacity(), src.len()) {
2044        Decoder::A1 => HUF_decompress4X1_DCtx_wksp(dctx, dst, src, workSpace, flags),
2045        Decoder::A2 => HUF_decompress4X2_DCtx_wksp(dctx, dst, src, workSpace, flags),
2046    }
2047}
2048
2049#[derive(Debug)]
2050pub struct Writer<'a> {
2051    ptr: Option<NonNull<u8>>,
2052    end: *mut u8,
2053    _marker: core::marker::PhantomData<&'a mut [u8]>,
2054}
2055
2056impl<'a> Writer<'a> {
2057    /// # Safety
2058    ///
2059    /// - `ptr` must point to `len` readable and writable bytes
2060    /// - `ptr` may be NULL only if `len == 0`
2061    pub unsafe fn from_raw_parts(ptr: *mut u8, len: usize) -> Self {
2062        let ptr = NonNull::new(ptr);
2063
2064        if ptr.is_none() {
2065            assert_eq!(len, 0);
2066        }
2067
2068        Self {
2069            ptr,
2070            end: match ptr {
2071                None => core::ptr::null_mut(),
2072                Some(ptr) => unsafe { ptr.as_ptr().add(len) },
2073            },
2074            _marker: core::marker::PhantomData,
2075        }
2076    }
2077
2078    #[inline]
2079    pub fn capacity(&self) -> usize {
2080        match self.ptr {
2081            None => 0,
2082            Some(ptr) => unsafe { self.end.offset_from_unsigned(ptr.as_ptr()) },
2083        }
2084    }
2085
2086    #[inline]
2087    pub fn is_null(&self) -> bool {
2088        self.ptr.is_none()
2089    }
2090
2091    #[inline]
2092    pub fn is_empty(&self) -> bool {
2093        match self.ptr {
2094            None => true,
2095            Some(ptr) => ptr.as_ptr() == self.end,
2096        }
2097    }
2098
2099    #[inline]
2100    pub fn as_mut_ptr(&mut self) -> *mut u8 {
2101        match self.ptr {
2102            None => core::ptr::null_mut(),
2103            Some(ptr) => ptr.as_ptr(),
2104        }
2105    }
2106
2107    #[inline]
2108    pub fn as_mut_ptr_range(&mut self) -> core::ops::Range<*mut u8> {
2109        match self.ptr {
2110            None => core::ptr::null_mut()..core::ptr::null_mut(),
2111            Some(ptr) => ptr.as_ptr()..self.end,
2112        }
2113    }
2114
2115    pub fn subslice<R: core::ops::RangeBounds<usize>>(&mut self, range: R) -> Self {
2116        let Some(ptr) = self.ptr else {
2117            match (range.start_bound(), range.end_bound()) {
2118                (Bound::Unbounded, Bound::Unbounded)
2119                | (
2120                    Bound::Included(&0),
2121                    Bound::Included(&0) | Bound::Excluded(&1) | Bound::Unbounded,
2122                ) => {
2123                    return Self {
2124                        ptr: self.ptr,
2125                        end: self.end,
2126                        _marker: self._marker,
2127                    };
2128                }
2129                _ => panic!("out of bounds"),
2130            }
2131        };
2132
2133        let new_ptr = match range.start_bound() {
2134            Bound::Included(&count) => ptr.as_ptr().wrapping_add(count),
2135            Bound::Excluded(_) => unreachable!("I think?"),
2136            Bound::Unbounded => ptr.as_ptr(),
2137        };
2138
2139        if new_ptr > self.end {
2140            panic!("out of bounds");
2141        }
2142
2143        let new_end = match range.end_bound() {
2144            Bound::Included(&count) => ptr.as_ptr().wrapping_add(count + 1),
2145            Bound::Excluded(&count) => ptr.as_ptr().wrapping_add(count),
2146            Bound::Unbounded => self.end,
2147        };
2148
2149        if new_end > self.end {
2150            panic!("out of bounds");
2151        }
2152
2153        Self {
2154            ptr: NonNull::new(new_ptr),
2155            end: new_end,
2156            _marker: core::marker::PhantomData,
2157        }
2158    }
2159
2160    fn quarter(&mut self) -> Option<(Self, Self, Self, Self)> {
2161        let capacity = self.capacity();
2162        let segment_size = capacity.div_ceil(4);
2163        let range = self.as_mut_ptr_range();
2164        let remainder = capacity - 3 * segment_size;
2165
2166        if (range.end as usize - range.start as usize) < 6 {
2167            panic!("length must be at least six when splitting into 4 streams");
2168        };
2169
2170        unsafe {
2171            let w1 = Self::from_raw_parts(range.start, segment_size);
2172            let w2 = Self::from_raw_parts(range.start.add(segment_size), segment_size);
2173            let w3 = Self::from_raw_parts(range.start.add(2 * segment_size), segment_size);
2174            let w4 = Self::from_raw_parts(range.start.add(3 * segment_size), remainder);
2175
2176            // If the capacity is 6, `6.div_ceil(4)` is 2, but 4 * 2 > 6.
2177            if !range.contains(&w4.ptr.unwrap().as_ptr()) {
2178                return None;
2179            }
2180
2181            assert!(w1.end <= range.end);
2182            assert!(w2.end <= range.end);
2183            assert!(w3.end <= range.end);
2184            assert!(w4.end <= range.end);
2185
2186            Some((w1, w2, w3, w4))
2187        }
2188    }
2189
2190    fn write_u8(&mut self, byte: u8) {
2191        let Some(ptr) = self.ptr else {
2192            panic!("write out of bounds");
2193        };
2194
2195        if ptr.as_ptr() >= self.end {
2196            panic!("write out of bounds");
2197        }
2198
2199        // SAFETY: `ptr < end` and we're allowed to write to this memory.
2200        unsafe { ptr.as_ptr().write(byte) }
2201
2202        // SAFETY: `ptr..end` is a contiguous allocation.
2203        self.ptr = unsafe { NonNull::new(ptr.as_ptr().add(1)) }
2204    }
2205
2206    fn write_symbol_x2(&mut self, value: u16, length: u8) {
2207        debug_assert!(length <= 2);
2208
2209        let Some(ptr) = self.ptr else {
2210            panic!("write out of bounds");
2211        };
2212
2213        // we can't actually assert this, an earlier reader may write into the next.
2214        // that then returns an error. We should return a result here later.
2215        // assert!( self.ptr.wrapping_add(length as usize) <= self.end, "write out of bounds {:?} {length}", self.as_mut_ptr_range());
2216
2217        // SAFETY: `ptr < end` and we're allowed to write to this memory.
2218        unsafe { ptr.as_ptr().cast::<u16>().write_unaligned(value.to_le()) }
2219
2220        // SAFETY: `ptr..end` is a contiguous allocation.
2221        self.ptr = unsafe { NonNull::new(ptr.as_ptr().add(length as usize)) }
2222    }
2223}
2224
2225#[cfg(test)]
2226mod tests {
2227    use super::*;
2228
2229    #[test]
2230    fn test_DEltX2_to_u32() {
2231        assert_eq!(
2232            HUF_buildDEltX2U32(0xAABB, 0xCC, 0xDD, 0xEE).to_le_bytes(),
2233            [0xDD, 0xBB, 0x76, 0xEF]
2234        );
2235        assert_eq!(HUF_buildDEltX2U32(1, 2, 3, 4).to_le_bytes(), [3, 1, 2, 4]);
2236    }
2237
2238    #[test]
2239    fn test_buildDEltX2() {
2240        assert_eq!(
2241            HUF_buildDEltX2(0xAA, 0xBB, 0xCC, 0xDD),
2242            HUF_DEltX2 {
2243                sequence: 0xAACC,
2244                nbBits: 0xBB,
2245                length: 0xDD,
2246            }
2247        );
2248
2249        assert_eq!(
2250            HUF_buildDEltX2(1, 2, 3, 4),
2251            HUF_DEltX2 {
2252                sequence: 0x0103,
2253                nbBits: 2,
2254                length: 4,
2255            }
2256        );
2257    }
2258
2259    #[test]
2260    fn writer_subslice() {
2261        let mut arr = [1u8, 2, 3, 4, 5, 6, 7, 8];
2262        let mut w = unsafe { Writer::from_raw_parts(arr.as_mut_ptr(), arr.len()) };
2263
2264        assert_eq!(w.subslice(..).capacity(), 8);
2265        assert_eq!(w.subslice(..4).capacity(), 4);
2266        assert_eq!(w.subslice(4..).capacity(), 4);
2267
2268        assert_eq!(w.subslice(..=4).capacity(), 5);
2269    }
2270}