1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-05-28 13:46:02 +02:00

let the portable implementations handle any degree

This commit is contained in:
Jack O'Connor 2023-06-18 18:58:55 -07:00
parent 01f5e7e6d7
commit 4761fd1bb6
2 changed files with 66 additions and 72 deletions

View File

@ -1,4 +1,4 @@
use crate::platform::{ParentInOut, TransposedVectors};
use crate::platform::{ParentInOut, TransposedVectors, MAX_SIMD_DEGREE};
use crate::{
counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, CHUNK_LEN, IV,
MSG_SCHEDULE, OUT_LEN, UNIVERSAL_HASH_LEN,
@ -179,11 +179,6 @@ pub fn hash_many<const N: usize>(
}
}
// Using DEGREE=2 instead of DEGREE=1 here (and so guaranteeing that all vectorized implementations
// have DEGREE>=2) makes it easier to avoid screwing up the root parent node in a recursive hash.
#[cfg(test)]
pub const DEGREE: usize = 2;
/// General contract:
/// - `input` is N chunks, each exactly 1 KiB, 1 <= N <= DEGREE
/// - `output_column` is a multiple of DEGREE.
@ -191,8 +186,8 @@ pub const DEGREE: usize = 2;
/// from `output_column` to `output_column+N-1`. Columns prior to `output_column` must be
/// unmodified.
///
/// The DEGREE of this portable implementation is 2, so the input here is either exactly 1 KiB or
/// exactly 2 KiB.
/// This portable implementation has no particular DEGREE. It will accept any number of chunks up
/// to MAX_SIMD_DEGREE.
pub fn hash_chunks(
input: &[u8],
key: &[u32; 8],
@ -201,8 +196,9 @@ pub fn hash_chunks(
output: &mut TransposedVectors,
output_column: usize,
) {
debug_assert!(input.len() == CHUNK_LEN || input.len() == 2 * CHUNK_LEN);
debug_assert_eq!(input.len() % CHUNK_LEN, 0);
let num_chunks = input.len() / CHUNK_LEN;
debug_assert!(num_chunks <= MAX_SIMD_DEGREE);
for chunk_index in 0..num_chunks {
let mut cv = *key;
for block_index in 0..16 {
@ -230,14 +226,15 @@ pub fn hash_chunks(
/// General contract:
/// - `cvs` contains `2*num_parents` transposed CVs, 1 <= num_parents <= DEGREE, starting at column 0
/// There may be additional CVs present beyond the `2*num_parents` CVs indicated, but this function
/// isn't aware of them and must not modify them. No flags are set internally (the caller must set
/// `PARENT` in `flags`). Writes `num_parents` transposed parent CVs to the output, starting at
/// column 0.
/// isn't aware of them and must not modify them. (The caller will take care of an odd remaining
/// CV, if any.) No flags are set internally. (The caller must set `PARENT` in `flags`). Writes
/// `num_parents` transposed parent CVs to the output, starting at column 0.
///
/// The DEGREE of this portable implementation is 2, so num_parents is either 1 or 2.
/// This portable implementation has no particular DEGREE. It will accept any number of parents up
/// to MAX_SIMD_DEGREE.
pub fn hash_parents(mut in_out: ParentInOut, key: &[u32; 8], flags: u8) {
let (_, num_parents) = in_out.input();
debug_assert!(num_parents == 1 || num_parents == 2);
debug_assert!(num_parents <= MAX_SIMD_DEGREE);
for parent_index in 0..num_parents {
let (input, _) = in_out.input();
let mut block = [0u8; BLOCK_LEN];
@ -340,14 +337,20 @@ pub mod test {
crate::test::test_hash_many_fn(hash_many, hash_many);
}
// The portable implementations of the vectorized APIs aren't actually vectorized and don't
// have any inherent DEGREE. They loop internally over any number of inputs. Here we
// arbitrarily pick degree 4 to test them (against themselves, so not an especially interesting
// test).
const TEST_DEGREE: usize = 4;
#[test]
fn test_hash_chunks() {
crate::test::test_hash_chunks_fn(hash_chunks, DEGREE);
crate::test::test_hash_chunks_fn(hash_chunks, TEST_DEGREE);
}
#[test]
fn test_hash_parents() {
crate::test::test_hash_parents_fn(hash_parents, DEGREE);
crate::test::test_hash_parents_fn(hash_parents, TEST_DEGREE);
}
#[test]

View File

@ -249,18 +249,23 @@ pub fn test_hash_chunks_fn(target_fn: HashChunksFn, degree: usize) {
);
}
// Here always hash one chunk at a time, even though portable::DEGREE is 2.
let mut portable_output = TransposedVectors::default();
for i in 0..(2 * test_degree) {
crate::portable::hash_chunks(
&input[i * CHUNK_LEN..][..CHUNK_LEN],
TEST_KEY_WORDS,
initial_counter + i as u64,
crate::KEYED_HASH,
&mut portable_output,
i,
);
}
crate::portable::hash_chunks(
&input[..test_degree * CHUNK_LEN],
TEST_KEY_WORDS,
initial_counter,
crate::KEYED_HASH,
&mut portable_output,
0,
);
crate::portable::hash_chunks(
&input[test_degree * CHUNK_LEN..][..test_degree * CHUNK_LEN],
TEST_KEY_WORDS,
initial_counter + test_degree as u64,
crate::KEYED_HASH,
&mut portable_output,
test_degree,
);
assert_eq!(portable_output.0, test_output.0);
}
@ -302,22 +307,16 @@ pub fn test_hash_parents_fn(target_fn: HashParentsFn, degree: usize) {
}
let mut portable_output = TransposedVectors(input.0);
for i in 0..test_degree {
for row in 0..8 {
input[row][0] = input[row][2 * i];
input[row][1] = input[row][2 * i + 1];
}
crate::portable::hash_parents(
ParentInOut::Separate {
input: &input,
num_parents: 1,
output: &mut portable_output,
output_column: i,
},
TEST_KEY_WORDS,
crate::KEYED_HASH | crate::PARENT,
);
}
crate::portable::hash_parents(
ParentInOut::Separate {
input: &input,
num_parents: test_degree,
output: &mut portable_output,
output_column: 0,
},
TEST_KEY_WORDS,
crate::KEYED_HASH | crate::PARENT,
);
assert_eq!(portable_output.0, test_output.0);
}
@ -337,28 +336,18 @@ pub fn test_hash_parents_fn(target_fn: HashParentsFn, degree: usize) {
);
}
let mut portable_input = TransposedVectors::default();
let mut portable_output = TransposedVectors::default();
paint_transposed_input(&mut portable_input);
paint_transposed_input(&mut portable_output);
for i in 0..test_degree {
for row in 0..8 {
portable_input[row][0] = portable_input[row][2 * i];
portable_input[row][1] = portable_input[row][2 * i + 1];
}
crate::portable::hash_parents(
ParentInOut::Separate {
input: &portable_input,
num_parents: 1,
output: &mut portable_output,
output_column: i,
},
TEST_KEY_WORDS,
crate::KEYED_HASH | crate::PARENT,
);
}
let mut portable_io = TransposedVectors::default();
paint_transposed_input(&mut portable_io);
crate::portable::hash_parents(
ParentInOut::InPlace {
in_out: &mut portable_io,
num_parents: test_degree,
},
TEST_KEY_WORDS,
crate::KEYED_HASH | crate::PARENT,
);
assert_eq!(portable_output.0, test_io.0);
assert_eq!(portable_io.0, test_io.0);
}
}
}
@ -432,6 +421,7 @@ fn root_hash_with_chunks_and_parents(
degree: usize,
input: &[u8],
) -> [u8; 32] {
assert_eq!(degree.count_ones(), 1, "power of 2");
// TODO: handle the 1-chunk case?
assert!(input.len() >= 2 * CHUNK_LEN);
// TODO: hash partial chunks?
@ -486,19 +476,20 @@ pub fn test_compare_reference_impl_chunks_and_hashes() {
#[cfg(feature = "std")]
dbg!(num_chunks);
let test_output = root_hash_with_chunks_and_parents(
crate::portable::hash_chunks,
crate::portable::hash_parents,
crate::portable::DEGREE,
&input[..num_chunks * CHUNK_LEN],
);
let mut reference_output = [0u8; 32];
let mut reference_hasher = reference_impl::Hasher::new();
reference_hasher.update(&input[..num_chunks * CHUNK_LEN]);
reference_hasher.finalize(&mut reference_output);
assert_eq!(reference_output, test_output);
for test_degree in [2, 4, 8, 16] {
let test_output = root_hash_with_chunks_and_parents(
crate::portable::hash_chunks,
crate::portable::hash_parents,
test_degree,
&input[..num_chunks * CHUNK_LEN],
);
assert_eq!(reference_output, test_output);
}
}
}