sark-json 0.24.0

Borrowed JSON decoding and streaming encoding for sark
Documentation
use std::{num::NonZero, pin::Pin};

use o3::base::cell::region::Token;
pub mod common;

use dope_test::alloc::Tracker;
use o3::buffer::{resident, storage};
use sark_json::{
    input::Input as _,
    parsing, segment,
    segment::{Decode, Decoder, Nested, Outcome, Progress, Push, Transfer},
};

use crate::common::{Bytes as _, segment::*};

#[global_allocator]
static ALLOCATOR: Tracker = Tracker::new();

#[test]
fn rejects_late_owner() {
    let decoder = Decoder::<Compound>::try_stream().expect("decoder").close();
    let stored = (storage::Shared::try_copy(b"{}")).expect("allocate bounded JSON fixture");
    let owner = resident::Shared::from(stored);
    assert!(
        matches!(
            decoder.push(owner),
            Transfer::Rejected { error: Push::Complete(owner), .. }
                if owner.as_slice() == b"{}"
        ),
        "closed decoder must reject input and return the original bytes"
    );
}

fn drive<'d, T: Decode>(
    decoder: &mut Decoder<'d, T>,
    budget: NonZero<usize>,
) -> Result<T, parsing::Error<'d>> {
    let mut turns = 0;
    loop {
        assert!(
            turns < 16_384,
            "decoder must complete within its bounded test turns"
        );
        turns += 1;
        match decoder.poll(budget) {
            Progress::Ready(value) => return Ok(value),
            Progress::Pending => {}
            Progress::NeedInput => {
                return Err(sark_json::Error::Invalid.into());
            }
            Progress::Failed(error) => return Err(error),
        }
    }
}

#[test]
fn flat_delimiters() {
    let bad = br#"{"value":[}}"#;
    let mut index = 0;
    assert!(bad.as_slice().skip_value(&mut index).is_err());

    let valid = br#"{"value":"[}]\\\"","nested":[{}]}tail"#;
    let mut index = 0;
    valid
        .as_slice()
        .skip_value(&mut index)
        .expect("skip valid nested JSON value");
    assert_eq!(&valid[index..], b"tail");
}

#[test]
fn flat_depth_limit() {
    let accepted = Compound::arrays(NonZero::new(128).expect("nesting depth"));
    let mut index = 0;
    let (result, (alloc, bytes)) = Tracker::usage(|| accepted.as_slice().skip_value(&mut index));
    result.expect("accept JSON nesting at the depth limit");
    assert_eq!(index, accepted.len());
    assert_eq!((alloc, bytes), (0, 0));

    let rejected = Compound::arrays(NonZero::new(129).expect("nesting depth"));
    let mut index = 0;
    let (result, (alloc, bytes)) = Tracker::usage(|| rejected.as_slice().skip_value(&mut index));
    assert!(result.is_err());
    assert_eq!((alloc, bytes), (0, 0));
}

#[test]
fn scoped_admission() {
    const PAYLOAD: usize = 64 * 1024;
    let mut domain = resident::Domain::new();
    Token::scope_pinned(Pin::new(&mut domain), |token, domain| {
        let budget = domain
            .get_mut()
            .try_budget(PAYLOAD, token.proof())
            .expect("resident test domain must admit its budget");
        let mut input = vec![b' '; PAYLOAD];
        input[..11].copy_from_slice(br#"{"value":1}"#);
        let owner = (resident::Exact::try_copy(&budget, &input))
            .expect("the scoped JSON fixture must fit its resident budget");
        let owner = owner.into_shared();
        let segments = vec![owner];

        let (mut decoder, (_, bytes)) =
            Tracker::usage(|| Decoder::<Number>::from_scoped(segments).expect("decoder input"));
        assert!(
            bytes < 4096,
            "decoder admission copied a {PAYLOAD}-byte driver owner ({bytes} bytes allocated)",
        );
        let number =
            drive(&mut decoder, segment::DEFAULT_STEP_BUDGET).expect("decode scoped JSON fixture");
        assert_eq!(number.value, 1);
    });
}

#[test]
fn segmented_delimiters() {
    let bad = br#"{"value":{[}}}"#;
    for split in 0..=bad.len() {
        let mut decoder = Decoder::<Compound>::new(bad.try_split(split).expect("JSON fragments"))
            .expect("decoder input");
        assert!(
            drive(&mut decoder, NonZero::<usize>::MIN).is_err(),
            "crossed delimiters accepted at split {split}"
        );
    }

    let valid = br#"{"value":{"text":"[}]\\\"","nested":[{}]}}"#;
    for split in 0..=valid.len() {
        let mut decoder = Decoder::<Compound>::new(valid.try_split(split).expect("JSON fragments"))
            .expect("decoder input");
        drive(&mut decoder, NonZero::<usize>::MIN)
            .expect("decode valid JSON at every fragment boundary");
    }
}

#[test]
fn segmented_depth_limit() {
    let accepted = Compound::object(NonZero::new(128).expect("nesting depth"));
    let segments = accepted
        .try_segments()
        .expect("copy one-byte JSON test fragments");
    let mut decoder = Decoder::<Compound>::new(segments).expect("decoder input");
    drive(&mut decoder, NonZero::<usize>::MIN).expect("decode one-byte fragments at depth limit");

    let mut rejected = Compound::object(NonZero::new(129).expect("nesting depth"));
    let rejected =
        storage::Shared::try_from(&mut rejected).expect("store over-depth JSON test input");
    let mut decoder = Decoder::<Compound>::new(vec![rejected]).expect("decoder input");
    assert!(drive(&mut decoder, NonZero::<usize>::MAX).is_err());
}

#[test]
fn rejects_delimiters() {
    let bad =
        (storage::Shared::try_copy(br#"[{"value":[}}]"#)).expect("allocate bounded JSON fixture");
    let bad = Nested::<Number>::one(bad).expect("sequence input");
    let Outcome::Pending(bad) = bad.step(NonZero::<usize>::MAX) else {
        panic!("malformed sequence must release its input");
    };
    assert!(matches!(
        bad.step(NonZero::<usize>::MAX),
        Outcome::Failed(sark_json::parsing::Error::Parse(sark_json::Error::Invalid))
    ));
}