base64-ng 2.0.0

no_std-first Base64 encoding and decoding with strict APIs and a security-heavy release process
Documentation
use crate::runtime::{Backend, CandidateDetectionMode, SecurityPosture};
use crate::{Alphabet, Standard, UrlSafe, checked_encoded_len, scalar};

#[test]
fn qemu_runtime_detects_sve_but_keeps_public_dispatch_neon() {
    let vector_length = super::sve::vector_length_bytes().expect("SVE must be available");
    assert_eq!(super::sve::instruction_vector_length_bytes(), vector_length);
    eprintln!("SVE candidate vector length={} bits", vector_length * 8);
    assert_eq!(super::detected_candidate(), super::Candidate::Sve);

    let report = crate::runtime::backend_report();
    assert_eq!(report.candidate, Backend::Sve);
    assert_eq!(
        report.candidate_detection_mode,
        CandidateDetectionMode::RuntimeCpuFeatures
    );
    assert_eq!(report.active, Backend::Neon);
    assert_eq!(report.encode_backend.backend.as_str(), "neon");
    assert_eq!(report.strict_decode_backend.backend.as_str(), "neon");
    assert!(report.ordinary_acceleration_active);
    assert_eq!(report.security_posture, SecurityPosture::Accelerated);
}

#[test]
fn sve_candidate_matches_scalar_for_profiles_blocks_and_tails() {
    assert_candidate_round_trips::<Standard, true>();
    assert_candidate_round_trips::<Standard, false>();
    assert_candidate_round_trips::<UrlSafe, true>();
    assert_candidate_round_trips::<UrlSafe, false>();
}

#[test]
fn sve_candidate_rejects_before_writing_output() {
    let mut encoded = [b'A'; 64];
    encoded[47] = b'!';
    let mut output = [0x5a; 48];
    let before = output;

    assert!(super::sve::decode_slice::<Standard, false>(&encoded, &mut output).is_err());
    assert_eq!(output, before);
}

#[test]
fn sve_probe_fails_closed_for_missing_or_malformed_kernel_results() {
    use super::sve::probe_vector_length;

    assert_eq!(probe_vector_length(true, 16), Some(16));
    assert_eq!(probe_vector_length(true, 32), Some(32));
    assert_eq!(probe_vector_length(true, (1 << 17) | 64), Some(64));
    assert_eq!(probe_vector_length(false, 32), None);
    assert_eq!(probe_vector_length(true, -1), None);
    assert_eq!(probe_vector_length(true, 0), None);
    assert_eq!(probe_vector_length(true, 15), None);
    assert_eq!(probe_vector_length(true, 24), None);
    assert_eq!(probe_vector_length(true, 272), None);
}

#[cfg(target_os = "linux")]
#[test]
fn sve_candidate_observes_per_thread_vector_length_changes() {
    let original = super::sve::vector_length_bytes().expect("SVE must be available");
    if original == 16 {
        return;
    }

    let reduced = super::sve::set_vector_length_for_test(16);
    assert_eq!(reduced, Some(16));
    assert_eq!(super::sve::vector_length_bytes(), Some(16));
    assert_candidate_round_trips::<Standard, true>();
    let restored = super::sve::set_vector_length_for_test(original);
    assert_eq!(restored, Some(original));
    assert_eq!(super::sve::vector_length_bytes(), Some(original));
}

fn assert_candidate_round_trips<A: Alphabet, const PAD: bool>() {
    let mut input = [0u8; 513];
    for (index, byte) in input.iter_mut().enumerate() {
        *byte = index.to_le_bytes()[0].wrapping_mul(73).wrapping_add(19);
    }

    for len in 0..=input.len() {
        let required = checked_encoded_len(len, PAD).unwrap();
        let mut expected_encoded = [0u8; 684];
        let mut candidate_encoded = [0u8; 684];
        let expected_written =
            scalar::encode_slice::<A, PAD>(&input[..len], &mut expected_encoded).unwrap();
        let candidate_written =
            super::sve::encode_slice::<A, PAD>(&input[..len], &mut candidate_encoded).unwrap();
        assert_eq!(expected_written, required);
        assert_eq!(candidate_written, expected_written);
        assert_eq!(
            &candidate_encoded[..candidate_written],
            &expected_encoded[..expected_written]
        );

        let mut expected_decoded = [0u8; 513];
        let mut candidate_decoded = [0u8; 513];
        let expected_decoded_len = scalar::decode_slice::<A, PAD>(
            &expected_encoded[..expected_written],
            &mut expected_decoded,
        )
        .unwrap();
        let candidate_decoded_len = super::sve::decode_slice::<A, PAD>(
            &candidate_encoded[..candidate_written],
            &mut candidate_decoded,
        )
        .unwrap();
        assert_eq!(candidate_decoded_len, expected_decoded_len);
        assert_eq!(&candidate_decoded[..candidate_decoded_len], &input[..len]);
    }
}