1#[link(name = "x16rs", kind = "static")]
2unsafe extern "C" {
3 fn c_x16rs_hash(loopnum: i32, input: *const u8, output: *mut u8);
7}
8
9pub const H32S: usize = 32;
10
11pub fn x16rs_hash(loopnum: i32, indata: &[u8; H32S]) -> [u8; H32S] {
16 let mut outdata = [0u8; H32S];
17 unsafe {
18 c_x16rs_hash(loopnum, indata.as_ptr(), outdata.as_mut_ptr());
19 }
20 outdata
21}
22
23#[cfg(test)]
24mod tests {
25 use super::*;
26
27 fn to_hex(bytes: &[u8; H32S]) -> String {
28 bytes.iter().map(|b| format!("{:02x}", b)).collect()
29 }
30
31 #[test]
32 fn hash_is_not_all_zeros() {
33 let input = [0u8; H32S];
34 let out = x16rs_hash(1, &input);
35 assert_ne!(
36 out,
37 [0u8; H32S],
38 "x16rs_hash must not return all zeros (FFI UB regression)"
39 );
40 }
41
42 #[test]
43 fn hash_zero_input_loop1() {
44 let input = [0u8; H32S];
45 let out = x16rs_hash(1, &input);
46 assert_eq!(
47 to_hex(&out),
48 "6fe2a4b96f71518b7603e5c63702588ba816885aa1ce5908de31335e11473460"
49 );
50 assert_eq!(x16rs_hash(1, &input), out);
51 }
52
53 #[test]
54 fn hash_sequential_input_loop1() {
55 let mut input = [0u8; H32S];
56 for (i, b) in input.iter_mut().enumerate() {
57 *b = i as u8;
58 }
59 assert_eq!(
60 to_hex(&x16rs_hash(1, &input)),
61 "5f4b9c2bc542352be3bd684ce2228447ba14b3cf32a41b04d18b52290435cea5"
62 );
63 }
64
65 #[test]
66 fn hash_loop0_is_identity_copy() {
67 let mut input = [0u8; H32S];
69 input[0] = 0xab;
70 input[31] = 0xcd;
71 assert_eq!(x16rs_hash(0, &input), input);
72 }
73}