use crate::OpSpec;
use vyre_spec::GoldenSample;
pub const VYRE_OP_METADATA: vyre_spec::OpMetadata = vyre_spec::OpMetadata {
id: "primitive.hash.fnv1a32",
layer: vyre_spec::Layer::L2,
category: vyre_spec::MetadataCategory::A,
version: 1,
description: "hash fnv1a32",
signature: "(Bytes) -> U32",
strictness: "strict",
archetype_signature: "(Bytes) -> U32",
};
pub const GOLDEN: &[vyre_spec::GoldenSample] = &[
GoldenSample {
op_id: "primitive.hash.fnv1a32",
input: &[],
expected: &[0xC5, 0x9D, 0x1C, 0x81],
reason: "FNV-1a 32 empty input offset basis",
},
GoldenSample {
op_id: "primitive.hash.fnv1a32",
input: b"a",
expected: &[0x2C, 0x29, 0x0C, 0xE4],
reason: "FNV-1a 32 of a",
},
GoldenSample {
op_id: "primitive.hash.fnv1a32",
input: b"foobar",
expected: &[0x68, 0xF9, 0x9C, 0xBF],
reason: "FNV-1a 32 of foobar",
},
];
pub const KAT: &[vyre_spec::KatVector] = &[
vyre_spec::KatVector {
input: &[],
expected: &[0xC5, 0x9D, 0x1C, 0x81],
source: "hand-verified reference vector from Rust semantics",
},
vyre_spec::KatVector {
input: b"a",
expected: &[0x2C, 0x29, 0x0C, 0xE4],
source: "hand-verified reference vector from Rust semantics",
},
vyre_spec::KatVector {
input: b"foobar",
expected: &[0x68, 0xF9, 0x9C, 0xBF],
source: "hand-verified reference vector from Rust semantics",
},
];
pub const ADVERSARIAL: &[vyre_spec::AdversarialInput] = &[vyre_spec::AdversarialInput {
input: &[],
reason: "empty input exercises validation and boundary handling",
}];
fn cpu(input: &[u8]) -> Vec<u8> {
let mut hash: u32 = 0x811c_9dc5; for &byte in input {
hash ^= u32::from(byte);
hash = hash.wrapping_mul(0x0100_0193); }
hash.to_le_bytes().to_vec()
}
fn wgsl() -> String {
r"
fn vyre_op(index: u32, input_len: u32) -> u32 {
var hash: u32 = 0x811C9DC5u;
for (var i: u32 = 0u; i < input_len; i = i + 1u) {
let word_idx = i / 4u;
let byte_idx = i % 4u;
let byte_val = (input.data[word_idx] >> (byte_idx * 8u)) & 0xFFu;
hash = hash ^ byte_val;
hash = hash * 0x01000193u;
}
return hash;
}
"
.to_string()
}
#[inline]
pub fn vyre_op() -> OpSpec {
OpSpec::builder("primitive.hash.fnv1a32")
.signature(crate::spec::types::OpSignature {
inputs: vec![crate::spec::types::DataType::Bytes],
output: crate::spec::types::DataType::U32,
})
.cpu_fn(cpu)
.wgsl_fn(wgsl)
.category(crate::Category::A {
composition_of: vec!["primitive.hash.fnv1a32"],
})
.laws(vec![crate::spec::law::AlgebraicLaw::Bounded {
lo: 0,
hi: u32::MAX,
}])
.overflow_contract(crate::spec::types::OverflowContract::Unchecked)
.strictness(crate::spec::types::Strictness::Strict)
.version(1)
.boundary_values(vec![
crate::spec::types::BoundaryValue {
label: "empty",
inputs: vec![0],
},
crate::spec::types::BoundaryValue {
label: "single_zero",
inputs: vec![0],
},
crate::spec::types::BoundaryValue {
label: "single_ff",
inputs: vec![255],
},
crate::spec::types::BoundaryValue {
label: "ascii_a",
inputs: vec![0x61],
},
])
.equivalence_classes(vec![
crate::spec::types::EquivalenceClass::specific("empty input", vec![0]),
crate::spec::types::EquivalenceClass::specific("ascii text", vec![0x41]),
crate::spec::types::EquivalenceClass::specific("binary data", vec![0xFF]),
])
.expect("Fix: checked-in conform spec must satisfy the typestate builder")
}
#[cfg(test)]
mod tests {
use super::cpu;
#[test]
fn fnv1a32_empty() {
let result = cpu(&[]);
let hash = u32::from_le_bytes([result[0], result[1], result[2], result[3]]);
assert_eq!(
hash, 0x811c_9dc5,
"FNV-1a 32 of empty input is the offset basis"
);
}
#[test]
fn fnv1a32_known_vector() {
let result = cpu(b"foobar");
let hash = u32::from_le_bytes([result[0], result[1], result[2], result[3]]);
assert_eq!(hash, 0xBF9C_F968);
}
}
#[cfg(test)]
mod proptests {
#[test]
fn coverage_artifacts_are_registered() {
assert!(!super::KAT.is_empty());
assert!(!super::ADVERSARIAL.is_empty());
}
}