//! Fuzzer for encoder/decoder self-consistency.
//!
//! Interprets the fuzzer's byte input as a sequence of 24-bit signed PCM
//! samples, clamped to the LAC input range, encodes the frame, and
//! verifies the decoded output matches the original samples byte-for-byte.
//!
//! Catches any encoder bug that produces a bitstream the decoder can't
//! reconstruct exactly — which is the whole point of a lossless codec.
//!
//! This complements `decode_arbitrary`:
//! - `decode_arbitrary` asserts the decoder is robust to arbitrary input.
//! - `roundtrip_arbitrary` asserts encoder and decoder agree on meaning.

#![no_main]

use libfuzzer_sys::fuzz_target;

/// LAC's documented input range (spec §1): `|sample| ≤ 2^23 − 1`.
/// Fuzzer bytes are interpreted as i32 then clamped to this symmetric
/// range — note the lower bound is `-(2^23 − 1)`, NOT the two's-complement
/// `-2^23`; the encoder's debug-asserted input contract excludes the
/// extra negative value.
const SAMPLE_MIN: i32 = -((1 << 23) - 1);
const SAMPLE_MAX: i32 = (1 << 23) - 1;

fuzz_target!(|data: &[u8]| {
    // Need at least one sample (4 bytes interpreted as i32) and a
    // reasonable upper bound to keep each fuzz iteration fast. 16k samples
    // at 48 kHz is ~340 ms of audio, more than enough to exercise every
    // encoder path including the full MAX_PARTITION_ORDER search.
    if data.len() < 4 || data.len() > 65_536 {
        return;
    }

    // Read i32 chunks little-endian, clamp into LAC's 24-bit range.
    let samples: Vec<i32> = data
        .chunks_exact(4)
        .map(|c| {
            let raw = i32::from_le_bytes([c[0], c[1], c[2], c[3]]);
            raw.clamp(SAMPLE_MIN, SAMPLE_MAX)
        })
        .collect();
    if samples.is_empty() {
        return;
    }

    let encoded = lac::encode_frame(&samples);
    let decoded = lac::decode_frame(&encoded)
        .expect("decoder rejected its own encoder's output");
    assert_eq!(
        decoded, samples,
        "round-trip mismatch: {} samples, encoded {} bytes",
        samples.len(),
        encoded.len()
    );
});
