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,
})
);
}