structured-zstd 0.0.51

Pure-Rust Zstandard (zstd) compression and decompression: all levels, streaming, dictionaries, no_std and WebAssembly ready — no FFI, no cmake
Documentation
use super::*;

fn count(a: &[u8], b: &[u8]) -> usize {
    let min_len = a.len().min(b.len());
    // SAFETY: both slices have at least `min_len` readable bytes,
    // `iend = a.as_ptr() + min_len` stays in range.
    unsafe { count_forward(a.as_ptr(), b.as_ptr(), a.as_ptr().add(min_len)) }
}

#[test]
fn empty_inputs_return_zero() {
    // Empty range → loop body never executes.
    let a: [u8; 0] = [];
    let b: [u8; 0] = [];
    // SAFETY: iend == ip, the function never dereferences.
    let n = unsafe { count_forward(a.as_ptr(), b.as_ptr(), a.as_ptr()) };
    assert_eq!(n, 0);
}

#[test]
fn full_match_inside_8_byte_chunk() {
    let a = [1, 2, 3, 4, 5, 6, 7, 8];
    let b = [1, 2, 3, 4, 5, 6, 7, 8];
    assert_eq!(count(&a, &b), 8);
}

#[test]
fn diff_at_byte_3_in_first_chunk() {
    let a = [1, 2, 3, 9, 5, 6, 7, 8];
    let b = [1, 2, 3, 4, 5, 6, 7, 8];
    assert_eq!(count(&a, &b), 3);
}

#[test]
fn match_spanning_two_chunks() {
    let mut a = [0u8; 16];
    let mut b = [0u8; 16];
    for i in 0..16 {
        a[i] = i as u8;
        b[i] = i as u8;
    }
    a[13] = 99;
    assert_eq!(count(&a, &b), 13);
}

#[test]
fn match_terminates_at_iend_within_tail() {
    // 11 bytes: 1×8-chunk + 3 tail bytes (u16 + u8 fall-through).
    let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    let b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    assert_eq!(count(&a, &b), 11);
}

#[test]
fn diff_in_u32_tail() {
    // 12 bytes: 1×8-chunk match, then 4-byte tail diverges at
    // BYTE INDEX 9 (`99` vs `10`). After the 8-chunk advances
    // ip/m by 8, the upstream zstd's u32 tail check compares
    // a[8..12]=[9,99,11,12] vs b[8..12]=[9,10,11,12] → unequal,
    // so the u32 advance is skipped. Same for u16
    // (a[8..10]=[9,99] vs b[8..10]=[9,10] → unequal). The single
    // byte cmp THEN sees a[8]=9 == b[8]=9 and advances ip by 1.
    // Final match length: 8 + 1 = 9.
    let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 99, 11, 12];
    let b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
    assert_eq!(count(&a, &b), 9);
}

#[test]
fn diff_in_u16_tail_after_u32_match() {
    // 14 bytes total, first 12 match, then u16 differs at byte 12.
    let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99, 14];
    let b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
    // 8 chunk + 4 u32 = 12 matched. u16 cmp on (99,14) vs (13,14)
    // says unequal → 0 more. Single byte cmp on 99 vs 13 → 0 more.
    assert_eq!(count(&a, &b), 12);
}

#[test]
fn diff_in_single_byte_tail() {
    // 13 bytes, first 12 match, then single byte differs.
    let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 99];
    let b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
    // 8 chunk + 4 u32 = 12; u16 cmp not entered (only 1 byte left).
    // Single byte cmp 99 != 13 → 0 more.
    assert_eq!(count(&a, &b), 12);
}

#[test]
fn long_match_thousand_bytes() {
    let a = [0x5Au8; 1024];
    let b = [0x5Au8; 1024];
    assert_eq!(count(&a, &b), 1024);
}

#[test]
fn no_match_first_byte() {
    let a = [0u8, 1, 2, 3, 4, 5, 6, 7];
    let b = [9u8, 1, 2, 3, 4, 5, 6, 7];
    assert_eq!(count(&a, &b), 0);
}

#[test]
fn dict_2segment_within_dict_only() {
    // Candidate fully inside the dict; current input matches the dict tail.
    let dict = [10u8, 20, 30, 40];
    let inp = [30u8, 40, 99];
    // cand=2: dict[2]=30 vs inp[0]=30 ✓; dict[3]=40 vs inp[1]=40 ✓;
    // cand_idx=4 == dict.len() → inp[0]=30 vs inp[2]=99 ✗ → len 2.
    assert_eq!(count_forward_dict_2segment(&dict, 2, &inp, 0, 0), 2);
}

#[test]
fn dict_2segment_crosses_boundary_into_input() {
    // Match starts in the dict and continues past the boundary into the
    // input (the logical [dict][input] window), then stops at a mismatch.
    let dict = [1u8, 2, 3];
    let inp = [1u8, 2, 3, 1, 2, 3, 9]; // cur=3 → [1,2,3,9...]
    // cand=0: dict[0..3] match inp[3..6]; cand_idx=3 → inp[0]=1 vs inp[6]=9 ✗ → 3.
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 3, 0), 3);
}

#[test]
fn dict_2segment_continues_word_at_a_time_past_boundary() {
    // Candidate exhausts the dict mid-match and keeps matching into the
    // input segment — exercises the segment-2 count_forward path.
    let dict = [1u8, 2, 3];
    let inp = [1u8, 2, 3, 1, 2, 3, 1, 2]; // cur=3 → [1,2,3,1,2]
    // seg1: dict[0..3]=[1,2,3] vs inp[3..6]=[1,2,3] → m1=3 (dict exhausted).
    // seg2: inp[0..]=[1,2,...] vs inp[6..]=[1,2] → m2=2. total 5.
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 3, 0), 5);
}

#[test]
fn dict_2segment_stops_at_input_end() {
    let dict = [7u8, 7];
    let inp = [7u8, 7, 7, 7]; // cur=2 → only 2 bytes left
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 2, 0), 2);
}

/// The caller's gate establishes a prefix, and the count must resume past it
/// and still report the TOTAL length — every case a fresh count would report.
#[test]
fn dict_2segment_resumes_past_an_established_prefix() {
    // Same three fixtures as above, each answered identically whether the
    // caller established nothing or the four bytes its gate compared.
    let dict = [1u8, 2, 3, 4, 5, 6];
    let inp = [1u8, 2, 3, 4, 5, 6, 9];
    for known in [0usize, 4] {
        assert_eq!(
            count_forward_dict_2segment(&dict, 0, &inp, 0, known),
            6,
            "established {known} bytes",
        );
    }

    // A prefix that carries the candidate out of the dictionary: the rest of
    // the match comes from the input that follows it in the window.
    let dict = [1u8, 2, 3, 4];
    let inp = [1u8, 2, 3, 4, 1, 2, 3, 4, 1, 2, 7];
    // cand=0 against cur=4: four dict bytes, then the candidate continues at
    // inp[0] against inp[8] — [1,2] more, then 3 vs 7 stops it.
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 4, 0), 6);
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 4, 4), 6);

    // And when the established prefix reaches exactly the end of the input,
    // there is nothing left to count — whether or not the candidate still has
    // dictionary left of its own.
    let dict = [5u8, 5, 5, 5];
    let inp = [5u8, 5, 5, 5];
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 0, 4), 4);
    let dict = [5u8, 5, 5, 5, 5, 5];
    let inp = [5u8, 5, 5, 5];
    assert_eq!(count_forward_dict_2segment(&dict, 0, &inp, 0, 4), 4);
}

/// The counter does raw pointer math from a safe signature, so it enforces its
/// contract rather than trusting it. Both bounds are release assertions; a
/// caller that broke one would otherwise read outside the buffers.
#[test]
#[should_panic(expected = "cand")]
fn dict_2segment_rejects_a_candidate_outside_the_dict() {
    let dict = [1u8, 2, 3];
    let inp = [1u8, 2, 3];
    let _ = count_forward_dict_2segment(&dict, 3, &inp, 0, 0);
}

#[test]
#[should_panic(expected = "cur")]
fn dict_2segment_rejects_a_cursor_past_the_input() {
    let dict = [1u8, 2, 3];
    let inp = [1u8, 2, 3];
    let _ = count_forward_dict_2segment(&dict, 0, &inp, 4, 0);
}

/// The established-prefix bound has to hold without adding to the cursor:
/// `cur + known` wraps in a release build, and a wrapped sum passes a bound it
/// should have failed, after which the pointer arithmetic below leaves the
/// input. Expressed as a subtraction from a length already known to be at
/// least the cursor, there is nothing to wrap.
#[test]
#[should_panic(expected = "count_forward_dict_2segment requires")]
fn dict_2segment_rejects_a_known_prefix_that_would_wrap() {
    let dict = [1u8, 2, 3];
    let inp = [1u8];
    let _ = count_forward_dict_2segment(&dict, 0, &inp, 1, usize::MAX);
}