mirror of
https://github.com/BLAKE3-team/BLAKE3
synced 2024-05-28 01:16:02 +02:00
move hash_chunks tests
This commit is contained in:
parent
41ebf95fe2
commit
7827f5e836
|
@ -13,4 +13,5 @@ edition = "2021"
|
|||
cfg-if = "1.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.3"
|
||||
reference_impl = { path = "../../reference_impl" }
|
||||
|
|
|
@ -587,6 +587,7 @@ unsafe fn hash_chunks_using_compress(
|
|||
) {
|
||||
debug_assert!(input_len > 0);
|
||||
debug_assert!(input_len <= MAX_SIMD_DEGREE * CHUNK_LEN);
|
||||
input_len = cmp::min(input_len, MAX_SIMD_DEGREE * CHUNK_LEN);
|
||||
while input_len > 0 {
|
||||
let mut chunk_len = cmp::min(input_len, CHUNK_LEN);
|
||||
input_len -= chunk_len;
|
||||
|
@ -757,14 +758,18 @@ const TRANSPOSED_STRIDE: usize = 2 * MAX_SIMD_DEGREE;
|
|||
pub struct TransposedVectors([[u32; 2 * MAX_SIMD_DEGREE]; 8]);
|
||||
|
||||
impl TransposedVectors {
|
||||
pub fn parent_node(&self, parent_index: usize) -> BlockBytes {
|
||||
let mut bytes = [0u8; 64];
|
||||
pub fn extract_cv(&self, cv_index: usize) -> CVBytes {
|
||||
let mut words = [0u32; 8];
|
||||
for word_index in 0..8 {
|
||||
bytes[word_index * WORD_LEN..][..WORD_LEN]
|
||||
.copy_from_slice(&self.0[word_index][2 * parent_index].to_le_bytes());
|
||||
bytes[(word_index + 8) * WORD_LEN..][..WORD_LEN]
|
||||
.copy_from_slice(&self.0[word_index][2 * parent_index + 1].to_le_bytes());
|
||||
words[word_index] = self.0[word_index][cv_index];
|
||||
}
|
||||
le_bytes_from_words_32(&words)
|
||||
}
|
||||
|
||||
pub fn extract_parent_node(&self, parent_index: usize) -> BlockBytes {
|
||||
let mut bytes = [0u8; 64];
|
||||
bytes[..32].copy_from_slice(&self.extract_cv(parent_index / 2));
|
||||
bytes[32..].copy_from_slice(&self.extract_cv(parent_index / 2 + 1));
|
||||
bytes
|
||||
}
|
||||
|
||||
|
|
|
@ -204,4 +204,10 @@ mod test {
|
|||
fn test_compress_vs_reference() {
|
||||
crate::test::test_compress_vs_reference(compress);
|
||||
}
|
||||
|
||||
// This is circular but do it anyway.
|
||||
#[test]
|
||||
fn test_hash_chunks_vs_portable() {
|
||||
crate::test::test_hash_chunks_vs_portable(hash_chunks, DEGREE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ pub fn paint_test_input(buf: &mut [u8]) {
|
|||
}
|
||||
|
||||
pub fn test_compress_vs_portable(compress_fn: CompressFn) {
|
||||
let flags = KEYED_HASH;
|
||||
for block_len in BLOCK_LENGTHS {
|
||||
dbg!(block_len);
|
||||
let mut block = [0; BLOCK_LEN];
|
||||
|
@ -36,7 +35,7 @@ pub fn test_compress_vs_portable(compress_fn: CompressFn) {
|
|||
block_len as u32,
|
||||
&TEST_KEY,
|
||||
counter,
|
||||
flags,
|
||||
KEYED_HASH,
|
||||
);
|
||||
|
||||
let mut test_cv = TEST_KEY;
|
||||
|
@ -47,7 +46,7 @@ pub fn test_compress_vs_portable(compress_fn: CompressFn) {
|
|||
block_len as u32,
|
||||
test_cv_ptr,
|
||||
counter,
|
||||
flags,
|
||||
KEYED_HASH,
|
||||
test_cv_ptr,
|
||||
);
|
||||
}
|
||||
|
@ -58,7 +57,6 @@ pub fn test_compress_vs_portable(compress_fn: CompressFn) {
|
|||
}
|
||||
|
||||
pub fn test_compress_vs_reference(compress_fn: CompressFn) {
|
||||
let flags = CHUNK_START | CHUNK_END | ROOT | KEYED_HASH;
|
||||
for block_len in BLOCK_LENGTHS {
|
||||
dbg!(block_len);
|
||||
let mut block = [0; BLOCK_LEN];
|
||||
|
@ -72,9 +70,97 @@ pub fn test_compress_vs_reference(compress_fn: CompressFn) {
|
|||
let mut test_cv = TEST_KEY;
|
||||
unsafe {
|
||||
let test_cv_ptr: *mut CVBytes = &mut test_cv;
|
||||
compress_fn(&block, block_len as u32, test_cv_ptr, 0, flags, test_cv_ptr);
|
||||
compress_fn(
|
||||
&block,
|
||||
block_len as u32,
|
||||
test_cv_ptr,
|
||||
0,
|
||||
CHUNK_START | CHUNK_END | ROOT | KEYED_HASH,
|
||||
test_cv_ptr,
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(ref_hash, test_cv);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_transposed_eq(output_a: &TransposedVectors, output_b: &TransposedVectors) {
|
||||
let mut mismatch = false;
|
||||
for cv_index in 0..2 * MAX_SIMD_DEGREE {
|
||||
let cv_a = output_a.extract_cv(cv_index);
|
||||
let cv_b = output_b.extract_cv(cv_index);
|
||||
if cv_a == [0; 32] && cv_b == [0; 32] {
|
||||
println!("CV {cv_index:2} empty");
|
||||
} else if cv_a == cv_b {
|
||||
println!("CV {cv_index:2} matches");
|
||||
} else {
|
||||
mismatch = true;
|
||||
println!("CV {cv_index:2} mismatch:");
|
||||
println!(" {}", hex::encode(cv_a));
|
||||
println!(" {}", hex::encode(cv_b));
|
||||
}
|
||||
}
|
||||
if mismatch {
|
||||
panic!("transposed outputs are not equal");
|
||||
}
|
||||
assert_eq!(output_a, output_b, "just double check");
|
||||
}
|
||||
|
||||
pub fn test_hash_chunks_vs_portable(hash_chunks_fn: HashChunksFn, degree: usize) {
|
||||
assert!(degree <= MAX_SIMD_DEGREE);
|
||||
let mut input = [0u8; 2 * MAX_SIMD_DEGREE * CHUNK_LEN];
|
||||
paint_test_input(&mut input);
|
||||
dbg!(degree * CHUNK_LEN);
|
||||
for input_2_len in [
|
||||
1,
|
||||
degree * CHUNK_LEN / 3,
|
||||
2 * degree * CHUNK_LEN / 3,
|
||||
degree * CHUNK_LEN,
|
||||
] {
|
||||
dbg!(input_2_len);
|
||||
let input1 = &input[..degree * CHUNK_LEN];
|
||||
let input2 = &input[degree * CHUNK_LEN..][..input_2_len];
|
||||
for initial_counter in INITIAL_COUNTERS {
|
||||
// Make two calls, to test the output_column parameter.
|
||||
let mut portable_output = TransposedVectors::default();
|
||||
let (portable_left, portable_right) = portable_output.split(degree);
|
||||
Implementation::portable().hash_chunks(
|
||||
input1,
|
||||
&TEST_KEY,
|
||||
initial_counter,
|
||||
KEYED_HASH,
|
||||
portable_left,
|
||||
);
|
||||
Implementation::portable().hash_chunks(
|
||||
input2,
|
||||
&TEST_KEY,
|
||||
initial_counter + degree as u64,
|
||||
KEYED_HASH,
|
||||
portable_right,
|
||||
);
|
||||
|
||||
let mut test_output = TransposedVectors::default();
|
||||
let (test_left, test_right) = test_output.split(degree);
|
||||
unsafe {
|
||||
hash_chunks_fn(
|
||||
input1.as_ptr(),
|
||||
input1.len(),
|
||||
&TEST_KEY,
|
||||
initial_counter,
|
||||
KEYED_HASH,
|
||||
test_left.ptr,
|
||||
);
|
||||
hash_chunks_fn(
|
||||
input2.as_ptr(),
|
||||
input2.len(),
|
||||
&TEST_KEY,
|
||||
initial_counter + degree as u64,
|
||||
KEYED_HASH,
|
||||
test_right.ptr,
|
||||
);
|
||||
}
|
||||
|
||||
check_transposed_eq(&portable_output, &test_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
191
src/test.rs
191
src/test.rs
|
@ -51,197 +51,6 @@ pub const TEST_CASES_MAX: usize = 100 * CHUNK_LEN;
|
|||
pub const TEST_KEY: &CVBytes = b"whats the Elvish word for friend";
|
||||
pub const TEST_KEY_WORDS: &CVWords = &guts::words_from_le_bytes_32(TEST_KEY);
|
||||
|
||||
// Test a few different initial counter values.
|
||||
// - 0: The base case.
|
||||
// - i32::MAX: *No* overflow. But carry bugs in tricky SIMD code can screw this up, if you XOR
|
||||
// when you're supposed to ANDNOT...
|
||||
// - u32::MAX: The low word of the counter overflows for all inputs except the first.
|
||||
const INITIAL_COUNTERS: &[u64] = &[0, i32::MAX as u64, u32::MAX as u64];
|
||||
|
||||
// Paint the input with a repeating byte pattern. We use a cycle length of 251,
|
||||
// because that's the largest prime number less than 256. This makes it
|
||||
// unlikely to swapping any two adjacent input blocks or chunks will give the
|
||||
// same answer.
|
||||
pub fn paint_test_input(buf: &mut [u8]) {
|
||||
for (i, b) in buf.iter_mut().enumerate() {
|
||||
*b = (i % 251) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
type HashManyFn<A> = unsafe fn(
|
||||
inputs: &[&A],
|
||||
key: &CVWords,
|
||||
counter: u64,
|
||||
increment_counter: IncrementCounter,
|
||||
flags: u8,
|
||||
flags_start: u8,
|
||||
flags_end: u8,
|
||||
out: &mut [u8],
|
||||
);
|
||||
|
||||
// A shared helper function for platform-specific tests.
|
||||
pub fn test_hash_many_fn(
|
||||
hash_many_chunks_fn: HashManyFn<[u8; CHUNK_LEN]>,
|
||||
hash_many_parents_fn: HashManyFn<[u8; 2 * OUT_LEN]>,
|
||||
) {
|
||||
for &counter in INITIAL_COUNTERS {
|
||||
#[cfg(feature = "std")]
|
||||
dbg!(counter);
|
||||
|
||||
// 31 (16 + 8 + 4 + 2 + 1) inputs
|
||||
const NUM_INPUTS: usize = 31;
|
||||
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
|
||||
paint_test_input(&mut input_buf);
|
||||
|
||||
// First hash chunks.
|
||||
let mut chunks = ArrayVec::<&[u8; CHUNK_LEN], NUM_INPUTS>::new();
|
||||
for i in 0..NUM_INPUTS {
|
||||
chunks.push(array_ref!(input_buf, i * CHUNK_LEN, CHUNK_LEN));
|
||||
}
|
||||
let mut portable_chunks_out = [0; NUM_INPUTS * OUT_LEN];
|
||||
crate::portable::hash_many(
|
||||
&chunks,
|
||||
TEST_KEY_WORDS,
|
||||
counter,
|
||||
IncrementCounter::Yes,
|
||||
crate::KEYED_HASH,
|
||||
crate::CHUNK_START,
|
||||
crate::CHUNK_END,
|
||||
&mut portable_chunks_out,
|
||||
);
|
||||
|
||||
let mut test_chunks_out = [0; NUM_INPUTS * OUT_LEN];
|
||||
unsafe {
|
||||
hash_many_chunks_fn(
|
||||
&chunks[..],
|
||||
TEST_KEY_WORDS,
|
||||
counter,
|
||||
IncrementCounter::Yes,
|
||||
crate::KEYED_HASH,
|
||||
crate::CHUNK_START,
|
||||
crate::CHUNK_END,
|
||||
&mut test_chunks_out,
|
||||
);
|
||||
}
|
||||
for n in 0..NUM_INPUTS {
|
||||
#[cfg(feature = "std")]
|
||||
dbg!(n);
|
||||
assert_eq!(
|
||||
&portable_chunks_out[n * OUT_LEN..][..OUT_LEN],
|
||||
&test_chunks_out[n * OUT_LEN..][..OUT_LEN]
|
||||
);
|
||||
}
|
||||
|
||||
// Then hash parents.
|
||||
let mut parents = ArrayVec::<&[u8; 2 * OUT_LEN], NUM_INPUTS>::new();
|
||||
for i in 0..NUM_INPUTS {
|
||||
parents.push(array_ref!(input_buf, i * 2 * OUT_LEN, 2 * OUT_LEN));
|
||||
}
|
||||
let mut portable_parents_out = [0; NUM_INPUTS * OUT_LEN];
|
||||
crate::portable::hash_many(
|
||||
&parents,
|
||||
TEST_KEY_WORDS,
|
||||
counter,
|
||||
IncrementCounter::No,
|
||||
crate::KEYED_HASH | crate::PARENT,
|
||||
0,
|
||||
0,
|
||||
&mut portable_parents_out,
|
||||
);
|
||||
|
||||
let mut test_parents_out = [0; NUM_INPUTS * OUT_LEN];
|
||||
unsafe {
|
||||
hash_many_parents_fn(
|
||||
&parents[..],
|
||||
TEST_KEY_WORDS,
|
||||
counter,
|
||||
IncrementCounter::No,
|
||||
crate::KEYED_HASH | crate::PARENT,
|
||||
0,
|
||||
0,
|
||||
&mut test_parents_out,
|
||||
);
|
||||
}
|
||||
for n in 0..NUM_INPUTS {
|
||||
#[cfg(feature = "std")]
|
||||
dbg!(n);
|
||||
assert_eq!(
|
||||
&portable_parents_out[n * OUT_LEN..][..OUT_LEN],
|
||||
&test_parents_out[n * OUT_LEN..][..OUT_LEN]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Both xof() and xof_xof() have this signature.
|
||||
type HashChunksFn = unsafe fn(
|
||||
input: *const u8,
|
||||
input_len: usize,
|
||||
key: *const u32,
|
||||
initial_counter: u64,
|
||||
counter_group: u64,
|
||||
flags: u32,
|
||||
transposed_output: *mut u32,
|
||||
);
|
||||
|
||||
pub fn test_hash_chunks_fn(target_fn: HashChunksFn, degree: usize) {
|
||||
assert!(degree <= MAX_SIMD_DEGREE);
|
||||
let mut input = [0u8; 2 * MAX_SIMD_DEGREE * CHUNK_LEN];
|
||||
paint_test_input(&mut input);
|
||||
for test_degree in 1..=degree {
|
||||
let input1 = &input[..test_degree * CHUNK_LEN];
|
||||
let input2 = &input[test_degree * CHUNK_LEN..][..test_degree * CHUNK_LEN];
|
||||
for &initial_counter in INITIAL_COUNTERS {
|
||||
// Make two calls, to test the output_column parameter.
|
||||
let mut test_output = TransposedVectors::default();
|
||||
unsafe {
|
||||
target_fn(
|
||||
input1.as_ptr(),
|
||||
input1.len(),
|
||||
TEST_KEY_WORDS.as_ptr(),
|
||||
initial_counter,
|
||||
0,
|
||||
crate::KEYED_HASH as u32,
|
||||
test_output[0].as_mut_ptr(),
|
||||
);
|
||||
target_fn(
|
||||
input2.as_ptr(),
|
||||
input2.len(),
|
||||
TEST_KEY_WORDS.as_ptr(),
|
||||
initial_counter + test_degree as u64,
|
||||
0,
|
||||
crate::KEYED_HASH as u32,
|
||||
test_output[0].as_mut_ptr().add(test_degree),
|
||||
);
|
||||
}
|
||||
|
||||
let mut portable_output = TransposedVectors::default();
|
||||
unsafe {
|
||||
crate::portable::hash_chunks(
|
||||
input1.as_ptr(),
|
||||
input1.len(),
|
||||
TEST_KEY_WORDS.as_ptr(),
|
||||
initial_counter,
|
||||
0,
|
||||
crate::KEYED_HASH as u32,
|
||||
test_output[0].as_mut_ptr(),
|
||||
);
|
||||
crate::portable::hash_chunks(
|
||||
input2.as_ptr(),
|
||||
input2.len(),
|
||||
TEST_KEY_WORDS.as_ptr(),
|
||||
initial_counter + test_degree as u64,
|
||||
0,
|
||||
crate::KEYED_HASH as u32,
|
||||
test_output[0].as_mut_ptr().add(test_degree),
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(portable_output, test_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_transposed_input(input: &mut TransposedVectors) {
|
||||
let mut val = 0;
|
||||
for row in 0..8 {
|
||||
|
|
Loading…
Reference in New Issue