lodestone-types 0.0.17

Shared semantic types for Lodestone components and public Rust APIs
Documentation
use std::error::Error;
use std::hint::black_box;
use std::time::Instant;

use serde::Deserialize;

#[derive(Deserialize)]
struct KnownAnswers {
    format_version: u32,
    key: String,
    context_string: String,
    cases: Vec<Case>,
}

#[derive(Deserialize)]
struct Case {
    input_len: usize,
    hash: String,
    keyed_hash: String,
    derive_key: String,
}

fn answers() -> Result<KnownAnswers, Box<dyn Error>> {
    let result: KnownAnswers =
        serde_json::from_str(include_str!("../../../tests/performance/blake3-known-answers-v1.json"))?;
    assert_eq!(result.format_version, 1);
    assert_eq!(result.cases.len(), 35);
    assert!(result.cases.iter().all(|case| case.input_len <= 102_400));
    Ok(result)
}

fn painted(length: usize) -> Vec<u8> {
    (0..length).map(|index| (index % 251) as u8).collect()
}

fn expected(hex: &str) -> Result<Vec<u8>, Box<dyn Error>> {
    assert_eq!(hex.len(), 262);
    let mut output = Vec::with_capacity(131);
    for pair in hex.as_bytes().as_chunks::<2>().0 {
        output.push(u8::from_str_radix(std::str::from_utf8(pair)?, 16)?);
    }
    Ok(output)
}

fn modes<'a>(answers: &KnownAnswers, case: &'a Case) -> Result<[(blake3::Hasher, &'a str); 3], Box<dyn Error>> {
    let key: [u8; 32] = answers.key.as_bytes().try_into()?;
    Ok([
        (blake3::Hasher::new(), &case.hash),
        (blake3::Hasher::new_keyed(&key), &case.keyed_hash),
        (
            blake3::Hasher::new_derive_key(&answers.context_string),
            &case.derive_key,
        ),
    ])
}

#[test]
fn blake3_upstream_known_answers_preserve_all_modes_and_extended_output() -> Result<(), Box<dyn Error>> {
    let answers = answers()?;
    let key: [u8; 32] = answers.key.as_bytes().try_into()?;
    for case in &answers.cases {
        let input = painted(case.input_len);
        assert_eq!(blake3::hash(&input).as_bytes(), &expected(&case.hash)?[..32]);
        assert_eq!(
            blake3::keyed_hash(&key, &input).as_bytes(),
            &expected(&case.keyed_hash)?[..32]
        );
        assert_eq!(
            blake3::derive_key(&answers.context_string, &input),
            expected(&case.derive_key)?[..32]
        );
        for (mut hasher, hex) in modes(&answers, case)? {
            hasher.update(&input);
            let mut output = [0_u8; 131];
            hasher.finalize_xof().fill(&mut output);
            assert_eq!(output.as_slice(), expected(hex)?);
        }
    }
    Ok(())
}

#[test]
fn blake3_fragmented_unaligned_and_cloned_hashers_preserve_exact_bytes() -> Result<(), Box<dyn Error>> {
    let answers = answers()?;
    for case in &answers.cases {
        let mut storage = vec![255];
        storage.extend(painted(case.input_len));
        let input = &storage[1..];
        for (hasher, hex) in modes(&answers, case)? {
            let expected = expected(hex)?;
            for chunk_size in [1, 63, 64, 65, 1_023, 1_024, 1_025, 4_097] {
                let mut fragmented = hasher.clone();
                for chunk in input.chunks(chunk_size) {
                    fragmented.update(chunk);
                }
                assert_eq!(fragmented.finalize().as_bytes(), &expected[..32]);
                let mut reader = fragmented.finalize_xof();
                reader.set_position(17);
                let mut tail = [0_u8; 64];
                reader.fill(&mut tail);
                assert_eq!(tail.as_slice(), &expected[17..81]);
            }
            let split = input.len() / 2;
            let mut prefix = hasher;
            prefix.update(&input[..split]);
            let mut cloned = prefix.clone();
            cloned.update(&input[split..]);
            assert_eq!(cloned.finalize().as_bytes(), &expected[..32]);
        }
    }
    Ok(())
}

#[test]
fn blake3_lodestone_fingerprint_and_identity_projections_remain_stable() -> Result<(), Box<dyn Error>> {
    let answers = answers()?;
    for case in &answers.cases {
        let mut input = painted(case.input_len);
        let expected = expected(&case.hash)?;
        assert_eq!(
            super::OperationFingerprint::from_payload(&input).as_bytes(),
            &expected[..32]
        );
        assert_eq!(
            super::identity_hash_v1(&input),
            super::IdHash::new(u128::from_le_bytes(expected[..16].try_into()?))
        );
        if let Some(first) = input.first_mut() {
            *first ^= 1;
            assert_ne!(blake3::hash(&input).as_bytes(), &expected[..32]);
        }
    }
    Ok(())
}

#[test]
#[ignore = "explicit bounded hash-backend performance probe; not an ordinary correctness gate"]
fn blake3_backend_effect_probe() {
    let mut rows = Vec::new();
    for bytes in [32_usize, 1_024, 65_536, 1_048_576, 16_777_216] {
        let input = painted(bytes);
        let iterations = (67_108_864 / bytes).clamp(8, 1_048_576);
        black_box(blake3::hash(black_box(&input)));
        let mut seconds = Vec::with_capacity(7);
        for _ in 0..7 {
            let started = Instant::now();
            for _ in 0..iterations {
                black_box(blake3::hash(black_box(&input)));
            }
            seconds.push(started.elapsed().as_secs_f64());
        }
        let mut ordered = seconds.clone();
        ordered.sort_by(f64::total_cmp);
        rows.push(serde_json::json!({
            "input_bytes": bytes,
            "iterations_per_trial": iterations,
            "trial_seconds": seconds,
            "median_mib_per_second": (bytes * iterations) as f64 / 1_048_576.0 / ordered[3],
            "digest": blake3::hash(&input).to_hex().as_str(),
        }));
    }
    println!(
        "{}",
        serde_json::json!({
            "format_version": 1,
            "classification": "bounded_hash_backend_probe_not_endpoint_capacity",
            "architecture": std::env::consts::ARCH,
            "os": std::env::consts::OS,
            "rows": rows,
        })
    );
}