1use crate::platform::Platform;
12use crate::{CHUNK_END, CHUNK_LEN, CHUNK_START, CVBytes, CVWords, ChunkState, Hash, Hasher, IncrementCounter, Mode, OUT_LEN};
13
14#[cfg(feature = "std")]
30pub fn hash_each_multithreaded_with(mode: Mode, items: &[&[u8]], out: &mut [[u8; OUT_LEN]]) {
31 crate::lanes::require();
32 assert_eq!(items.len(), out.len(), "one digest per item");
33 let (key, flags) = mode.key_and_flags();
34 let alone = |item: &[u8]| item.len() >= crate::lanes::MIN_SPLIT_LEN;
35 let small: Vec<&[u8]> = items.iter().map(|&i| if alone(i) { &[][..] } else { i }).collect();
38 if small.iter().map(|i| i.len()).sum::<usize>() < crate::lanes::MIN_SPLIT_LEN {
39 hash_each_on(&key, flags, &small, out, Platform::detect(), |item| crate::hash_with(mode, item));
40 } else {
41 crate::lanes::hash_each(&key, flags, &small, out);
42 }
43 for (item, out) in items.iter().zip(out.iter_mut()) {
44 if alone(item) {
45 *out = *crate::lanes::hash_with_key(item, &key, flags).as_bytes();
46 }
47 }
48}
49
50impl Hasher {
55 pub fn update_each(hashers: &mut [Hasher], pieces: &[(usize, &[u8])]) {
71 update_turn(hashers, pieces, false);
72 }
73
74 #[cfg(feature = "std")]
79 pub fn update_each_multithreaded(hashers: &mut [Hasher], pieces: &[(usize, &[u8])]) {
80 crate::lanes::require();
81 update_turn(hashers, pieces, true);
82 }
83
84 pub fn finalize_each(hashers: &[Hasher], which: &[usize], out: &mut [[u8; OUT_LEN]]) {
88 assert_eq!(which.len(), out.len(), "one digest per message named");
89 for (&i, out) in which.iter().zip(out) {
90 *out = *hashers[i].finalize().as_bytes();
91 }
92 }
93}
94
95fn update_turn(hashers: &mut [Hasher], pieces: &[(usize, &[u8])], pooled: bool) {
103 let mut turn: Vec<(usize, &[u8])> = pieces.to_vec();
104 let mut waiting: Vec<(usize, &[u8])> = Vec::new();
105 let mut held: Vec<usize> = Vec::new();
106 while !turn.is_empty() {
107 for &(i, bytes) in &turn {
108 if hashers[i].held_group().is_some() {
111 waiting.push((i, bytes));
112 continue;
113 }
114 let rest = hashers[i].gather_part(bytes, pooled, true);
115 if hashers[i].held_group().is_some() {
116 held.push(i);
117 if !rest.is_empty() {
118 waiting.push((i, rest));
119 }
120 }
121 }
122 hash_held(hashers, &held);
123 assert!(held.iter().all(|&i| hashers[i].held_group().is_none()), "a held group outlived its turn");
128 let (before, after): (usize, usize) = (turn.iter().map(|p| p.1.len()).sum(), waiting.iter().map(|p| p.1.len()).sum());
129 assert!(
130 after < before || (after == before && waiting.len() < turn.len()),
131 "a turn's pass gathered nothing ({} pieces of {before} bytes before, {} of {after} after)",
132 turn.len(),
133 waiting.len()
134 );
135 held.clear();
136 core::mem::swap(&mut turn, &mut waiting);
137 waiting.clear();
138 }
139}
140
141#[cfg(feature = "std")]
145fn hash_held(hashers: &mut [Hasher], held: &[usize]) {
146 use crate::{BLOCK_LEN, PARENT};
147 let Some(&first) = held.first() else { return };
148 let (key, flags) = (hashers[first].core.key, hashers[first].core.chunk_state.flags);
149 let (alike, others): (Vec<usize>, Vec<usize>) = held.iter().partition(|&&i| hashers[i].core.key == key && hashers[i].core.chunk_state.flags == flags);
150 let turn = crate::platform::Sme2Turn::take(hashers[first].core.chunk_state.platform, true);
151 let platform = turn.platform();
152 let n = alike.len();
153 let per = crate::STAGE_LEN / CHUNK_LEN;
154 let chunks = n * per;
155 let mut level = vec![0u8; chunks * OUT_LEN];
156 let refs: Vec<&[u8; CHUNK_LEN]> =
157 alike.iter().flat_map(|&i| hashers[i].held_group().expect("a held group").1.chunks_exact(CHUNK_LEN)).map(|c| c.try_into().unwrap()).collect();
158 let counters: Vec<u64> = alike.iter().map(|&i| hashers[i].held_group().unwrap().0).collect();
159 #[cfg(blake3_sme2)]
162 let on_sme2 = matches!(platform, Platform::SME2);
163 #[cfg(not(blake3_sme2))]
164 let on_sme2 = false;
165 if on_sme2 {
166 #[cfg(blake3_sme2)]
168 unsafe {
169 crate::sme2::hash_groups_at(&refs, &counters, &key, flags, CHUNK_START, CHUNK_END, &mut level)
170 };
171 } else {
172 for (k, &counter) in counters.iter().enumerate() {
173 platform.hash_many(
174 &refs[k * per..(k + 1) * per],
175 &key,
176 counter,
177 IncrementCounter::Yes,
178 flags,
179 CHUNK_START,
180 CHUNK_END,
181 &mut level[k * per * OUT_LEN..(k + 1) * per * OUT_LEN],
182 );
183 }
184 }
185 let mut values = chunks;
188 let mut next = vec![0u8; chunks / 2 * OUT_LEN];
189 let mut halves = vec![0u8; 2 * n * OUT_LEN];
190 for depth in 0..4 {
191 if depth == 3 {
192 halves.copy_from_slice(&level[..2 * n * OUT_LEN]);
193 }
194 let blocks: Vec<&[u8; BLOCK_LEN]> = level[..values * OUT_LEN].chunks_exact(BLOCK_LEN).map(|b| b.try_into().unwrap()).collect();
195 platform.hash_many(&blocks, &key, 0, IncrementCounter::No, flags | PARENT, 0, 0, &mut next[..values / 2 * OUT_LEN]);
196 values /= 2;
197 level[..values * OUT_LEN].copy_from_slice(&next[..values * OUT_LEN]);
198 }
199 drop(turn);
200 for (k, &i) in alike.iter().enumerate() {
201 let (counter, _) = hashers[i].held_group().unwrap();
202 let mut result = [0u8; BLOCK_LEN];
203 if counter == 0 {
204 result.copy_from_slice(&halves[k * BLOCK_LEN..(k + 1) * BLOCK_LEN]);
205 } else {
206 result[..OUT_LEN].copy_from_slice(&level[k * OUT_LEN..(k + 1) * OUT_LEN]);
207 }
208 hashers[i].take_held(&result);
209 }
210 hash_held(hashers, &others);
211}
212
213#[cfg(not(feature = "std"))]
214fn hash_held(_: &mut [Hasher], held: &[usize]) {
215 assert!(held.is_empty(), "nothing held without std");
216}
217
218const ALONE_CHUNKS: usize = 16;
220
221pub fn hash_each_with(mode: Mode, items: &[&[u8]], out: &mut [[u8; OUT_LEN]]) {
237 assert_eq!(items.len(), out.len(), "one digest per item");
238 let (key, flags) = mode.key_and_flags();
239 hash_each_on(&key, flags, items, out, Platform::detect(), |item| crate::hash_with(mode, item));
240}
241
242pub(crate) fn hash_each_on(key: &CVWords, flags: u8, items: &[&[u8]], out: &mut [[u8; OUT_LEN]], platform: Platform, alone: impl Fn(&[u8]) -> Hash) {
246 let key = *key;
247 let mut shared: Vec<(usize, usize)> = Vec::new();
250 let mut slots = 0;
251 let mut most_full = 0;
252 for (i, item) in items.iter().enumerate() {
253 let chunks = item.len().div_ceil(CHUNK_LEN);
254 if chunks <= 1 || chunks >= ALONE_CHUNKS {
255 out[i] = *alone(item).as_bytes();
256 } else {
257 most_full = most_full.max(item.len() / CHUNK_LEN);
258 shared.push((i, slots));
259 slots += chunks;
260 }
261 }
262 let mut cvs = vec![[0u8; OUT_LEN]; slots];
263 let mut refs: Vec<&[u8; CHUNK_LEN]> = Vec::with_capacity(shared.len());
264 let mut owners: Vec<usize> = Vec::with_capacity(shared.len());
265 let mut lane_out = vec![0u8; shared.len() * OUT_LEN];
266 for k in 0..most_full {
267 refs.clear();
268 owners.clear();
269 for &(i, at) in &shared {
270 if let Some(chunk) = items[i].get(k * CHUNK_LEN..(k + 1) * CHUNK_LEN) {
271 refs.push(chunk.try_into().unwrap());
272 owners.push(at + k);
273 }
274 }
275 let lanes = &mut lane_out[..refs.len() * OUT_LEN];
276 platform.hash_many(&refs, &key, k as u64, IncrementCounter::No, flags, CHUNK_START, CHUNK_END, lanes);
277 for (j, &slot) in owners.iter().enumerate() {
278 cvs[slot].copy_from_slice(&lanes[j * OUT_LEN..(j + 1) * OUT_LEN]);
279 }
280 }
281 for &(i, at) in &shared {
282 let item = items[i];
283 let chunks = item.len().div_ceil(CHUNK_LEN);
284 let full = item.len() / CHUNK_LEN;
285 if full < chunks {
286 cvs[at + full] = ChunkState::new(&key, full as u64, flags, platform).update(&item[full * CHUNK_LEN..]).output().chaining_value();
287 }
288 out[i] = *root(&cvs[at..at + chunks], &key, flags, platform).as_bytes();
289 }
290}
291
292fn root(cvs: &[CVBytes], key: &CVWords, flags: u8, platform: Platform) -> Hash {
295 let split = left_len(cvs.len());
296 let left = subtree(&cvs[..split], key, flags, platform);
297 let right = subtree(&cvs[split..], key, flags, platform);
298 crate::hasher::parent_output(&left, &right, key, flags, platform).root_hash()
299}
300
301fn subtree(cvs: &[CVBytes], key: &CVWords, flags: u8, platform: Platform) -> CVBytes {
303 if cvs.len() == 1 {
304 return cvs[0];
305 }
306 let split = left_len(cvs.len());
307 let left = subtree(&cvs[..split], key, flags, platform);
308 let right = subtree(&cvs[split..], key, flags, platform);
309 crate::hasher::parent_output(&left, &right, key, flags, platform).chaining_value()
310}
311
312fn left_len(n: usize) -> usize {
314 debug_assert!(n >= 2);
315 1 << (usize::BITS - 1 - (n - 1).leading_zeros())
316}
317
318#[cfg(test)]
319mod test {
320 use super::*;
321
322 #[test]
326 fn test_miri_hash_each_multithreaded() {
327 let mut input = vec![0u8; crate::lanes::MIN_SPLIT_LEN + 8 * 1024];
328 crate::test::paint_test_input(&mut input);
329 let items: Vec<&[u8]> = input.chunks(crate::lanes::MIN_SPLIT_LEN / 40 + 7).collect();
330 let (mut a, mut b) = (vec![[0u8; OUT_LEN]; items.len()], vec![[0u8; OUT_LEN]; items.len()]);
331 crate::RAN.with(|r| r.set(0));
332 hash_each_multithreaded_with(Mode::Hash, &items, &mut a);
333 crate::lanes::assert_past_the_split("the items");
334 hash_each_with(Mode::Hash, &items, &mut b);
335 assert_eq!(a, b);
336 }
337
338 #[test]
344 fn test_hash_each_multithreaded() {
345 let mut input = vec![0u8; 3 << 20];
346 crate::test::paint_test_input(&mut input);
347 let lens = [0, 1, 1024, 1025, 3000, 15 * 1024, 16 * 1024, 100_000, 511 * 1024, 512 * 1024, 700_000];
348 let items: Vec<&[u8]> = (0..400)
349 .map(|k| {
350 let n = lens[k * 7 % lens.len()].min(input.len() - k);
351 &input[k..k + n]
352 })
353 .collect();
354 let key = [9u8; 32];
355 for mode in [Mode::Hash, Mode::Keyed(&key), Mode::DeriveKey("each mt")] {
356 let (mut a, mut b) = (vec![[0u8; OUT_LEN]; items.len()], vec![[0u8; OUT_LEN]; items.len()]);
357 hash_each_multithreaded_with(mode, &items, &mut a);
358 hash_each_with(mode, &items, &mut b);
359 assert_eq!(a, b);
360 let small: Vec<usize> = (0..items.len()).filter(|&k| items[k].len() <= 16 * 1024).take(30).collect();
363 assert!(small.iter().map(|&k| items[k].len()).sum::<usize>() < crate::lanes::MIN_SPLIT_LEN);
364 let small_items: Vec<&[u8]> = small.iter().map(|&k| items[k]).collect();
365 let mut c = vec![[0u8; OUT_LEN]; small.len()];
366 hash_each_multithreaded_with(mode, &small_items, &mut c);
367 assert!(small.iter().zip(&c).all(|(&k, d)| *d == b[k]));
368 }
369 }
370
371 #[test]
379 fn test_update_each_holds_groups_together() {
380 for platform in crate::test::every_platform() {
381 holds_groups_together(platform);
382 }
383 }
384
385 #[test]
389 fn test_update_each_at_offsets() {
390 use crate::test::SubtreeHasher;
391 let mut input = vec![0u8; 1 << 20];
392 crate::test::paint_test_input(&mut input);
393 let subtrees = [(64 * CHUNK_LEN, 64 * CHUNK_LEN), (16 * CHUNK_LEN, 16 * CHUNK_LEN), (CHUNK_LEN, CHUNK_LEN), (128 * CHUNK_LEN, 100_000)];
395 let whole = [70_000usize, 300_000];
396 let mut hashers: Vec<Hasher> = subtrees
397 .iter()
398 .map(|&(offset, _)| {
399 let mut h = Hasher::new();
400 h.set_input_offset(offset as u64);
401 h
402 })
403 .collect();
404 hashers.extend(whole.iter().map(|_| Hasher::new()));
405 let lens: Vec<usize> = subtrees.iter().map(|&(_, len)| len).chain(whole).collect();
406 let mut done = vec![0usize; lens.len()];
407 let pieces = [1448usize, 31, 16384, 4096, 1];
408 let mut k = 0;
409 while done.iter().zip(&lens).any(|(d, l)| d < l) {
410 let mut turn: Vec<(usize, &[u8])> = Vec::new();
411 for _ in 0..9 {
412 let i = k % lens.len();
413 let n = pieces[k % pieces.len()].min(lens[i] - done[i]);
414 turn.push((i, &input[done[i]..done[i] + n]));
415 done[i] += n;
416 k += 1;
417 }
418 Hasher::update_each(&mut hashers, &turn);
419 }
420 for (i, &(offset, len)) in subtrees.iter().enumerate() {
421 let mut one = Hasher::new();
422 one.set_input_offset(offset as u64).update(&input[..len]);
423 assert_eq!(hashers[i].finalize_non_root(), one.finalize_non_root(), "subtree at {offset}, {len} bytes");
424 }
425 for (j, &len) in whole.iter().enumerate() {
426 assert_eq!(hashers[subtrees.len() + j].finalize(), crate::hash(&input[..len]), "whole message of {len} bytes");
427 }
428 }
429
430 fn holds_groups_together(platform: crate::platform::Platform) {
431 let mut input = vec![0u8; 3 * crate::STAGE_LEN];
432 crate::test::paint_test_input(&mut input);
433 let keys = [[1u8; 32], [2u8; 32]];
434 let on = |mut h: Hasher| {
435 h.core.chunk_state.platform = platform;
436 h
437 };
438 let mut hashers: Vec<Hasher> = (0..6).map(|i| on(Hasher::new_with(crate::Mode::Keyed(&keys[i % 2])))).collect();
439 let start = |i: usize| i * 1000;
443 let group = crate::STAGE_LEN;
444 for (i, h) in hashers.iter_mut().enumerate() {
445 h.update(&input[start(i)..start(i) + group]);
446 }
447 let turn: Vec<(usize, &[u8])> = (0..6)
448 .flat_map(|i| [(i, &input[start(i) + group..start(i) + group + 100]), (i, &input[start(i) + group + 100..start(i) + 2 * group + 7])])
449 .collect();
450 Hasher::update_each(&mut hashers, &turn);
451 for (i, h) in hashers.iter().enumerate() {
452 let message = &input[start(i)..start(i) + 2 * crate::STAGE_LEN + 7];
453 assert_eq!(h.finalize(), crate::hash_with(crate::Mode::Keyed(&keys[i % 2]), message), "hasher {i}, {platform:?}");
454 }
455 let mut fresh: Vec<Hasher> = (0..6).map(|i| on(Hasher::new_with(crate::Mode::Keyed(&keys[i % 2])))).collect();
459 let turn: Vec<(usize, &[u8])> =
460 (0..6).flat_map(|i| [(i, &input[start(i)..start(i) + 1100]), (i, &input[start(i) + 1100..start(i) + group + 7])]).collect();
461 Hasher::update_each(&mut fresh, &turn);
462 for (i, h) in fresh.iter().enumerate() {
463 assert_eq!(
464 h.finalize(),
465 crate::hash_with(crate::Mode::Keyed(&keys[i % 2]), &input[start(i)..start(i) + group + 7]),
466 "fresh hasher {i}, {platform:?}"
467 );
468 }
469 }
470
471 #[test]
472 fn test_update_each() {
473 let mut input = vec![0u8; 1 << 20];
474 crate::test::paint_test_input(&mut input);
475 let lens = [0, 1, 63, 64, 1023, 1024, 1025, 2048, 4470, 8191, 16384, 16385, 65536, 100_000, 300_000];
476 let piece_lens = [1, 1448, 4096, 1448, 16384, 31, 65536];
477 for multithreaded in [false, true] {
478 let key = [5u8; 32];
479 let mut hashers: Vec<Hasher> =
481 (0..lens.len()).map(|i| if i % 3 == 1 { Hasher::new_with(crate::Mode::Keyed(&key)) } else { Hasher::new() }).collect();
482 let mut done = vec![0usize; lens.len()];
483 let mut k = 0;
484 while done.iter().zip(&lens).any(|(d, l)| d < l) {
485 let mut pieces: Vec<(usize, &[u8])> = Vec::new();
486 for _ in 0..23 {
487 let i = k % lens.len();
488 let n = piece_lens[k % piece_lens.len()].min(lens[i] - done[i]);
489 pieces.push((i, &input[done[i]..done[i] + n]));
490 done[i] += n;
491 k += 1;
492 }
493 if multithreaded {
494 Hasher::update_each_multithreaded(&mut hashers, &pieces);
495 } else {
496 Hasher::update_each(&mut hashers, &pieces);
497 }
498 }
499 let which: Vec<usize> = (0..lens.len()).rev().collect();
500 let mut out = vec![[0u8; OUT_LEN]; lens.len()];
501 Hasher::finalize_each(&hashers, &which, &mut out);
502 for (k, &i) in which.iter().enumerate() {
503 let want = if i % 3 == 1 { crate::hash_with(crate::Mode::Keyed(&key), &input[..lens[i]]) } else { crate::hash(&input[..lens[i]]) };
504 assert_eq!(&out[k], want.as_bytes(), "len {} multithreaded {multithreaded}", lens[i]);
505 }
506 }
507 }
508
509 #[test]
514 fn test_hash_each_matches_hash_with() {
515 let mut input = vec![0u8; 40 * CHUNK_LEN];
516 crate::test::paint_test_input(&mut input);
517 let mut lens = vec![
518 0,
519 1,
520 63,
521 64,
522 65,
523 1023,
524 1024,
525 1025,
526 2047,
527 2048,
528 2049,
529 3000,
530 4096,
531 5000,
532 15 * CHUNK_LEN,
533 15 * CHUNK_LEN + 1,
534 16 * CHUNK_LEN - 1,
535 16 * CHUNK_LEN,
536 16 * CHUNK_LEN + 1,
537 40 * CHUNK_LEN,
538 ];
539 for n in 2..16 {
540 lens.push(n * CHUNK_LEN);
541 lens.push(n * CHUNK_LEN - 7);
542 }
543 let items: Vec<&[u8]> = lens.iter().enumerate().map(|(j, &l)| &input[j % 3..j % 3 + l.min(input.len() - 2)]).collect();
544 let key = [9u8; 32];
545 for mode in [Mode::Hash, Mode::Keyed(&key), Mode::DeriveKey("hash_each test")] {
546 let mut out = vec![[0u8; OUT_LEN]; items.len()];
547 hash_each_with(mode, &items, &mut out);
548 for (item, digest) in items.iter().zip(&out) {
549 assert_eq!(digest, crate::hash_with(mode, item).as_bytes(), "length {}", item.len());
550 }
551 }
552 }
553}