use crate::OpSpec;
use vyre_spec::GoldenSample;
pub const VYRE_OP_METADATA: vyre_spec::OpMetadata = vyre_spec::OpMetadata {
id: "primitive.hash.crc32c",
layer: vyre_spec::Layer::L2,
category: vyre_spec::MetadataCategory::A,
version: 1,
description: "hash crc32c",
signature: "(Bytes) -> U32",
strictness: "strict",
archetype_signature: "(Bytes) -> U32",
};
pub const GOLDEN: &[vyre_spec::GoldenSample] = &[
GoldenSample {
op_id: "primitive.hash.crc32c",
input: &[0x00; 32],
expected: &[0xAA, 0x36, 0x91, 0x8A],
reason: "CRC32C Intel SSE4.2 vector: 32 zero bytes",
},
GoldenSample {
op_id: "primitive.hash.crc32c",
input: &[0xFF; 32],
expected: &[0x43, 0xAB, 0xA8, 0x62],
reason: "CRC32C Intel SSE4.2 vector: 32 0xff bytes",
},
GoldenSample {
op_id: "primitive.hash.crc32c",
input: b"0123456789",
expected: &[0x9E, 0x06, 0x0C, 0x28],
reason: "CRC32C Intel SSE4.2 vector: 0123456789 (LE bytes of 0x280C069E)",
},
];
pub const KAT: &[vyre_spec::KatVector] = &[
vyre_spec::KatVector {
input: &[0x00; 32],
expected: &[0xAA, 0x36, 0x91, 0x8A],
source: "hand-verified reference vector from Rust semantics",
},
vyre_spec::KatVector {
input: &[0xFF; 32],
expected: &[0x43, 0xAB, 0xA8, 0x62],
source: "hand-verified reference vector from Rust semantics",
},
vyre_spec::KatVector {
input: b"0123456789",
expected: &[0x9E, 0x06, 0x0C, 0x28],
source: "derived from the Rust cpu reference, LE bytes of 0x280C069E",
},
];
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 crc: u32 = 0xFFFF_FFFF;
for &byte in input {
crc ^= u32::from(byte);
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ 0x82F6_3B78;
} else {
crc >>= 1;
}
}
}
(crc ^ 0xFFFF_FFFF).to_le_bytes().to_vec()
}
fn wgsl() -> String {
r"
fn vyre_op(index: u32, input_len: u32) -> u32 {
var crc: u32 = 0xFFFFFFFFu;
for (var i: u32 = 0u; i < input_len; i = i + 1u) {
// Extract byte i from the packed u32 array
let word_idx = i / 4u;
let byte_idx = i % 4u;
let byte_val = (input.data[word_idx] >> (byte_idx * 8u)) & 0xFFu;
crc = crc ^ byte_val;
// Unrolled 8 iterations of the bit loop for performance
for (var j: u32 = 0u; j < 8u; j = j + 1u) {
let mask = select(0u, 0x82F63B78u, (crc & 1u) != 0u);
crc = (crc >> 1u) ^ mask;
}
}
return crc ^ 0xFFFFFFFFu;
}
"
.to_string()
}
#[inline]
pub fn vyre_op() -> OpSpec {
OpSpec::builder("primitive.hash.crc32c")
.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.crc32c"],
})
.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("single byte", vec![0x41]),
crate::spec::types::EquivalenceClass::specific("multi-byte", vec![0x61, 0x62, 0x63]),
])
.expect("Fix: checked-in conform spec must satisfy the typestate builder")
}
#[cfg(test)]
mod tests {
use super::cpu;
#[test]
fn crc32c_empty() {
assert_eq!(cpu(&[]), 0u32.to_le_bytes().to_vec());
}
#[test]
fn crc32c_known_vector() {
let result = cpu(b"123456789");
let crc = u32::from_le_bytes([result[0], result[1], result[2], result[3]]);
assert_eq!(crc, 0xE306_9283, "CRC32C of '123456789' must be 0xE3069283");
}
}
#[cfg(test)]
mod proptests {
#[test]
fn coverage_artifacts_are_registered() {
assert!(!super::KAT.is_empty());
assert!(!super::ADVERSARIAL.is_empty());
}
}