blake3_servil/lib.rs
1//! `blake3-servil` is a fast [BLAKE3] implementation for 64-bit Arm and
2//! x86-64. It produces the same digests as official BLAKE3, with a call
3//! for each way programs hash (the table below).
4//!
5//! # Examples
6//!
7//! ```
8//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
9//! // Once per process, before any other call (see Startup self-test).
10//! blake3_servil::initialize();
11//!
12//! // Hash an input all at once.
13//! let hash1 = blake3_servil::hash(b"foobarbaz");
14//!
15//! // Hash an input incrementally.
16//! let mut hasher = blake3_servil::Hasher::new();
17//! hasher.update(b"foo");
18//! hasher.update(b"bar");
19//! hasher.update(b"baz");
20//! let hash2 = hasher.finalize();
21//! assert_eq!(hash1, hash2);
22//!
23//! // Extended output. OutputReader also implements Read and Seek.
24//! # #[cfg(feature = "std")] {
25//! let mut output = [0; 1000];
26//! let mut output_reader = hasher.finalize_xof();
27//! output_reader.fill(&mut output);
28//! assert_eq!(hash1, output[..32]);
29//! # }
30//!
31//! // Print a hash as hex.
32//! println!("{}", hash1);
33//! # Ok(())
34//! # }
35//! ```
36//!
37//! # Which call to use
38//!
39//! | Your data | Call |
40//! |---|---|
41//! | One message in memory | [`hash_multithreaded`] |
42//! | One message arriving in pieces | [`Hasher::update`] |
43//! | Many messages of one length, in one buffer | [`hash_many_multithreaded`] |
44//! | Many messages of different lengths, in memory | [`hash_each_multithreaded_with`] |
45//! | Many messages arriving at once (a server's uploads) | [`Hasher::update_each`] |
46//! | A stream of messages read, made, or arriving | [`stream`] |
47//! | A message read in verified ranges (Bao, iroh-blobs) | [`outboard_with`], [`verify_range_with`], [`Verifier`] |
48//!
49//! Each call returns once its hash is done, and uses other CPU cores
50//! where that is faster (the forms without `multithreaded` stay on the
51//! calling thread). A [`stream`] is different: your program submits
52//! messages and goes on to read or make the next ones while other threads
53//! hash them; each digest arrives in the place your program named, a
54//! little later. Choose it when reading or making the data keeps your
55//! thread busy, such as files read from memory or a fast disk: it hashes
56//! the most bytes per second.
57//!
58//! Short messages hash fastest together: 1024 messages of 64 bytes in one
59//! [`hash_many`] call take about a fifth of the time of 1024 [`hash`]
60//! calls.
61//!
62//! **Keyed hashing and key derivation** run at the plain hash's speed
63//! through a [`Mode`], passed to a call's `_with` form ([`hash_with`],
64//! [`hash_many_with`], ...), to [`Hasher::new_with`], or to a [`stream`].
65//!
66//! **Hashing now and then** (rather than nonstop) is slower per byte on
67//! every hash function, since its code and data must come back into the
68//! CPU's caches and a resting core runs slower at first: hashing 64 KiB
69//! after a pause takes about three times as long as nonstop on an Apple M4
70//! Max.
71//!
72//! **Apple M4 and later** have one matrix unit that this crate uses for
73//! inputs of 16 KiB and more; when several threads of a program hash such
74//! inputs at the same moment, one runs at full speed and the others at
75//! about two thirds of it. The multithreaded calls share the work among
76//! the cores themselves, so a program gets the most from one thread making
77//! them.
78//!
79//! # Memory
80//!
81//! - **A [`Hasher`] is about 18 KiB**; box it where that matters.
82//! - **Single-threaded calls allocate nothing**: [`hash`], [`hash_with`], [`hash_many`], [`hash_many_with`],
83//! [`Hasher::update`] and [`Hasher::finalize`], and [`OutputReader`].
84//! [`Hasher::update_reader`] uses a 1 MiB buffer while it reads past
85//! 64 KiB, and [`hash_each_with`] 32 bytes for each chunk of its messages
86//! of 2 to 15 chunks, each freed when it returns. [`outboard_with`]
87//! returns the outboard it allocates (64 bytes for each 16 KiB), and
88//! [`verify_range_with`] allocates 32 bytes for each group of its range,
89//! freed when it returns. A [`Verifier`] keeps up to 16 KiB, for a group
90//! that arrives in pieces, and 48 bytes for each level of the tree; each
91//! [`Verifier::update`] allocates about 100 bytes for each node its piece
92//! holds, and its groups' working space (about 60 bytes a chunk), freed when it
93//! returns.
94//! - **Multithreaded calls** allocate nothing below 512 KiB, where they
95//! hash on the calling thread. Above it they keep a list of the pieces
96//! they hand out, freed when they return: 48 bytes for each 128 KiB of
97//! input, and at most 200 bytes for each CPU (1 GiB on 16 CPUs: 390 KiB).
98//! - **A [`stream`]** takes its storage (its 2 MiB ring and its entries)
99//! from a pool the process keeps: a thread's first stream allocates it
100//! and later streams reuse it, never freed.
101//! - **Once per process**: the startup self-test's buffers, about 45 KiB
102//! of the calling thread's stack while it runs (it allocates nothing),
103//! and the threads: by default a worker per CPU beyond the first, each with the
104//! standard library's stack, started by [`initialize_multithreaded`].
105//!
106//! # Startup self-test
107//!
108//! **Every program calls [`initialize`] (or [`initialize_multithreaded`])
109//! once, before any other call of this crate**: a call before it panics, in
110//! every build, saying so. The check costs nothing: it is part of the
111//! platform load every call makes. **A program that makes multithreaded
112//! calls or streams calls [`initialize_multithreaded`]**: a
113//! multithreaded call or a stream before it panics (one load per call).
114//!
115//! `initialize` checks the build and the CPU before the process first
116//! hashes, in well under a millisecond. It runs every assembly kernel built
117//! for this machine on fixed inputs and compares the outputs with the
118//! reference implementation's. A mismatch stops the program with a panic
119//! naming the code path. Other threads that call `initialize` meanwhile
120//! wait for it, and stop with it if it fails.
121//!
122//! # Cargo Features
123//!
124//! The `std` feature (the only feature enabled by default) enables the
125//! [`Write`] implementation and the [`update_reader`](Hasher::update_reader)
126//! method for [`Hasher`], and also the [`Read`] and [`Seek`] implementations
127//! for [`OutputReader`].
128//!
129//! The `zeroize` feature (off by default, on for [docs.rs])
130//! implements
131//! [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) for
132//! this crate's types.
133//!
134//! The `serde` feature (off by default, on for [docs.rs]) implements
135//! [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) and
136//! [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html)
137//! for [`Hash`](struct@Hash).
138//!
139//! [BLAKE3]: https://blake3.io
140//! [docs.rs]: https://docs.rs/
141//! [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html
142//! [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
143//! [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html
144//! [`digest`]: https://crates.io/crates/digest
145//! [`signature`]: https://crates.io/crates/signature
146
147#![cfg_attr(not(feature = "std"), no_std)]
148
149// The tests use std in every build, the no_std one included.
150#[cfg(all(test, not(feature = "std")))]
151#[macro_use]
152extern crate std;
153
154#[cfg(test)]
155mod test;
156
157#[cfg(feature = "std")]
158mod each;
159#[cfg(feature = "std")]
160pub use each::{hash_each_multithreaded_with, hash_each_with};
161#[cfg(feature = "std")]
162mod outboard;
163#[cfg(feature = "std")]
164#[doc(hidden)]
165#[cfg(feature = "std")]
166pub use outboard::{GROUP_LEN, Verifier, outboard_multithreaded_with, outboard_with, verify_range_with};
167
168mod platform;
169
170// Platform-specific implementations of the compression function. These
171// BLAKE3-specific cfg flags are set in build.rs.
172// The intrinsics AVX2 kernels: `avx2` itself without the assembly, and
173// everywhere on x86 the pass of whole chunks with a partial one (as in
174// rust_sse41).
175#[cfg(target_arch = "x86_64")]
176#[cfg_attr(not(blake3_avx2_rust), allow(dead_code))]
177mod rust_avx2;
178#[cfg(blake3_avx2_rust)]
179use rust_avx2 as avx2;
180#[cfg(blake3_avx2_ffi)]
181#[path = "ffi_avx2.rs"]
182mod avx2;
183#[cfg(blake3_avx512_ffi)]
184#[path = "ffi_avx512.rs"]
185mod avx512;
186#[cfg(blake3_neon)]
187#[path = "ffi_neon_hybrid.rs"]
188mod neon_hybrid;
189mod portable;
190#[cfg(target_arch = "x86_64")]
191#[cfg_attr(not(blake3_sse41_rust), allow(dead_code))]
192mod rust_sse41;
193#[cfg(blake3_sme2)]
194#[path = "ffi_sme2.rs"]
195mod sme2;
196#[cfg(blake3_sse41_rust)]
197use rust_sse41 as sse41;
198#[cfg(blake3_sse41_ffi)]
199#[path = "ffi_sse41.rs"]
200mod sse41;
201
202#[cfg(feature = "std")]
203mod io;
204/// The primitives src/finish.rs runs on: the standard library's here, and
205/// Shuttle's in tests/shuttle_finish.rs, which compiles that file beside its own
206/// `finish_sync`.
207#[cfg(feature = "std")]
208mod finish_sync {
209 pub use std::sync::Mutex;
210 pub use std::sync::atomic::{AtomicUsize, Ordering};
211 pub use std::thread;
212}
213#[cfg(feature = "std")]
214mod finish;
215/// The primitives src/park.rs runs on: the standard library's here, and
216/// Shuttle's in tests/shuttle_park.rs, which compiles that file beside its
217/// own `park_sync`.
218#[cfg(feature = "std")]
219mod park_sync {
220 pub use std::sync::OnceLock;
221 pub use std::sync::atomic::{AtomicU64, Ordering};
222 pub use std::thread;
223}
224#[cfg(feature = "std")]
225mod lanes;
226mod many;
227#[cfg(feature = "std")]
228mod park;
229#[cfg(feature = "std")]
230#[cfg(feature = "std")]
231#[cfg(feature = "std")]
232mod stream;
233mod tree;
234#[cfg(feature = "std")]
235pub use stream::{Stream, stream};
236mod pair;
237mod self_test;
238
239/// A kind of kernel or path a call ran, for the tests that check which
240/// path a call took. Each build notes the kinds it has.
241#[derive(Clone, Copy)]
242#[allow(dead_code)]
243pub(crate) enum Ran {
244 /// The call went to the pool.
245 Pool = 1,
246 /// The call wanted SME2 and another thread held the turn.
247 TurnTaken = 2,
248}
249
250#[cfg(all(test, feature = "std"))]
251std::thread_local! {
252 /// The kinds this thread's calls ran since the test last cleared it.
253 pub(crate) static RAN: core::cell::Cell<u32> = const { core::cell::Cell::new(0) };
254}
255
256/// Note that `kind` happened: in the tests' record, and nothing in any
257/// other build (some of which have no kernel to note).
258#[inline(always)]
259#[allow(dead_code)]
260pub(crate) fn ran(kind: Ran) {
261 #[cfg(all(test, feature = "std"))]
262 RAN.with(|r| r.set(r.get() | kind as u32));
263 let _ = kind;
264}
265
266#[cfg(all(test, feature = "std"))]
267std::thread_local! {
268 /// The assembly entry points this thread's calls ran since the test
269 /// last cleared it, by address.
270 pub(crate) static RAN_ENTRIES: core::cell::RefCell<std::collections::BTreeSet<usize>> = const { core::cell::RefCell::new(std::collections::BTreeSet::new()) };
271}
272
273/// Note that the assembly entry point at `entry` ran (in the tests'
274/// record; nothing in any other build).
275#[inline(always)]
276#[allow(dead_code)]
277pub(crate) fn ran_kernel(entry: *const ()) {
278 #[cfg(all(test, feature = "std"))]
279 RAN_ENTRIES.with(|r| r.borrow_mut().insert(entry as usize));
280 let _ = entry;
281}
282
283use arrayvec::{ArrayString, ArrayVec};
284use core::cmp;
285use core::fmt;
286use platform::{MAX_SIMD_DEGREE, MAX_SIMD_DEGREE_OR_2, Platform};
287#[cfg(feature = "zeroize")]
288use zeroize::Zeroize;
289
290/// The number of bytes in a [`Hash`](struct.Hash.html), 32.
291pub const OUT_LEN: usize = 32;
292
293/// The number of bytes in a key, 32.
294pub const KEY_LEN: usize = 32;
295
296/// The number of bytes in a block, 64.
297///
298/// You don't usually need to think about this number. One case where it matters is calling
299/// [`OutputReader::fill`] in a loop, where using a `buf` argument that's a multiple of `BLOCK_LEN`
300/// avoids repeating work.
301pub const BLOCK_LEN: usize = 64;
302
303/// The number of bytes in a chunk, 1024.
304///
305/// You don't usually need to think about this number, but it often comes up in benchmarks, because
306/// the maximum degree of parallelism used by the implementation equals the number of chunks.
307pub const CHUNK_LEN: usize = 1024;
308
309const MAX_DEPTH: usize = 54; // 2^54 * CHUNK_LEN = 2^64
310
311// While iterating the compression function within a chunk, the CV is
312// represented as words, to avoid doing two extra endianness conversions for
313// each compression in the portable implementation. But the hash_many interface
314// needs to hash both input bytes and parent nodes, so it's better for its
315// output CVs to be represented as bytes.
316type CVWords = [u32; 8];
317type CVBytes = [u8; 32]; // little-endian
318
319const IV: &CVWords = &[0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
320
321const MSG_SCHEDULE: [[usize; 16]; 7] = [
322 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
323 [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8],
324 [3, 4, 10, 12, 13, 2, 7, 14, 6, 5, 9, 0, 11, 15, 8, 1],
325 [10, 7, 12, 9, 14, 3, 13, 15, 4, 0, 11, 2, 5, 8, 1, 6],
326 [12, 13, 9, 11, 15, 10, 14, 8, 7, 2, 5, 3, 0, 1, 6, 4],
327 [9, 14, 11, 5, 8, 12, 15, 1, 13, 3, 0, 10, 2, 6, 4, 7],
328 [11, 15, 5, 0, 1, 9, 8, 6, 14, 10, 2, 12, 3, 4, 7, 13],
329];
330
331// These are the internal flags that we use to domain separate root/non-root,
332// chunk/parent, and chunk beginning/middle/end. These get set at the high end
333// of the block flags word in the compression function, so their values start
334// high and go down.
335const CHUNK_START: u8 = 1 << 0;
336const CHUNK_END: u8 = 1 << 1;
337const PARENT: u8 = 1 << 2;
338const ROOT: u8 = 1 << 3;
339const KEYED_HASH: u8 = 1 << 4;
340const DERIVE_KEY_CONTEXT: u8 = 1 << 5;
341const DERIVE_KEY_MATERIAL: u8 = 1 << 6;
342
343#[inline]
344fn counter_low(counter: u64) -> u32 {
345 counter as u32
346}
347
348#[inline]
349fn counter_high(counter: u64) -> u32 {
350 (counter >> 32) as u32
351}
352
353/// An output of the default size, 32 bytes, which provides constant-time
354/// equality checking.
355///
356/// `Hash` implements [`From`] and [`Into`] for `[u8; 32]`, and it provides
357/// [`from_bytes`] and [`as_bytes`] for explicit conversions between itself and
358/// `[u8; 32]`. However, byte arrays and slices don't provide constant-time
359/// equality checking, which is often a security requirement in software that
360/// handles private data. `Hash` doesn't implement [`Deref`] or [`AsRef`], to
361/// avoid situations where a type conversion happens implicitly and the
362/// constant-time property is accidentally lost.
363///
364/// `Hash` provides the [`to_hex`] and [`from_hex`] methods for converting to
365/// and from hexadecimal. It also implements [`Display`] and [`FromStr`].
366///
367/// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
368/// [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html
369/// [`as_bytes`]: #method.as_bytes
370/// [`from_bytes`]: #method.from_bytes
371/// [`Deref`]: https://doc.rust-lang.org/stable/std/ops/trait.Deref.html
372/// [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
373/// [`to_hex`]: #method.to_hex
374/// [`from_hex`]: #method.from_hex
375/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
376/// [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
377#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
378#[derive(Clone, Copy, Hash, Eq)]
379#[repr(transparent)]
380pub struct Hash([u8; OUT_LEN]);
381
382impl Hash {
383 /// The raw bytes of the `Hash`. Note that byte arrays don't provide
384 /// constant-time equality checking, so if you need to compare hashes,
385 /// prefer the `Hash` type.
386 #[inline]
387 pub const fn as_bytes(&self) -> &[u8; OUT_LEN] {
388 &self.0
389 }
390
391 /// Create a `Hash` from its raw bytes representation.
392 pub const fn from_bytes(bytes: [u8; OUT_LEN]) -> Self {
393 Self(bytes)
394 }
395
396 /// The raw bytes of the `Hash`, as a slice. Useful for serialization. Note that byte arrays
397 /// don't provide constant-time equality checking, so if you need to compare hashes, prefer
398 /// the `Hash` type.
399 #[inline]
400 pub const fn as_slice(&self) -> &[u8] {
401 self.0.as_slice()
402 }
403
404 /// Create a `Hash` from its raw bytes representation as a slice.
405 ///
406 /// Returns an error if the slice is not exactly 32 bytes long.
407 pub fn from_slice(bytes: &[u8]) -> Result<Self, core::array::TryFromSliceError> {
408 Ok(Self::from_bytes(bytes.try_into()?))
409 }
410
411 /// Encode a `Hash` in lowercase hexadecimal.
412 ///
413 /// The returned [`ArrayString`] is a fixed size and doesn't allocate memory
414 /// on the heap. Note that [`ArrayString`] doesn't provide constant-time
415 /// equality checking, so if you need to compare hashes, prefer the `Hash`
416 /// type.
417 ///
418 /// [`ArrayString`]: https://docs.rs/arrayvec/0.5.1/arrayvec/struct.ArrayString.html
419 pub fn to_hex(&self) -> ArrayString<{ 2 * OUT_LEN }> {
420 let mut s = ArrayString::new();
421 let table = b"0123456789abcdef";
422 for &b in self.0.iter() {
423 s.push(table[(b >> 4) as usize] as char);
424 s.push(table[(b & 0xf) as usize] as char);
425 }
426 s
427 }
428
429 /// Decode a `Hash` from hexadecimal. Both uppercase and lowercase ASCII
430 /// bytes are supported.
431 ///
432 /// Any byte outside the ranges `'0'...'9'`, `'a'...'f'`, and `'A'...'F'`
433 /// results in an error. An input length other than 64 also results in an
434 /// error.
435 ///
436 /// Note that `Hash` also implements `FromStr`, so `Hash::from_hex("...")`
437 /// is equivalent to `"...".parse()`.
438 pub fn from_hex(hex: impl AsRef<[u8]>) -> Result<Self, HexError> {
439 fn hex_val(byte: u8) -> Result<u8, HexError> {
440 match byte {
441 b'A'..=b'F' => Ok(byte - b'A' + 10),
442 b'a'..=b'f' => Ok(byte - b'a' + 10),
443 b'0'..=b'9' => Ok(byte - b'0'),
444 _ => Err(HexError(HexErrorInner::InvalidByte(byte))),
445 }
446 }
447 let hex_bytes: &[u8] = hex.as_ref();
448 if hex_bytes.len() != OUT_LEN * 2 {
449 return Err(HexError(HexErrorInner::InvalidLen(hex_bytes.len())));
450 }
451 let mut hash_bytes: [u8; OUT_LEN] = [0; OUT_LEN];
452 for i in 0..OUT_LEN {
453 hash_bytes[i] = 16 * hex_val(hex_bytes[2 * i])? + hex_val(hex_bytes[2 * i + 1])?;
454 }
455 Ok(Hash::from(hash_bytes))
456 }
457}
458
459impl From<[u8; OUT_LEN]> for Hash {
460 #[inline]
461 fn from(bytes: [u8; OUT_LEN]) -> Self {
462 Self::from_bytes(bytes)
463 }
464}
465
466impl From<Hash> for [u8; OUT_LEN] {
467 #[inline]
468 fn from(hash: Hash) -> Self {
469 hash.0
470 }
471}
472
473impl core::str::FromStr for Hash {
474 type Err = HexError;
475
476 fn from_str(s: &str) -> Result<Self, Self::Err> {
477 Hash::from_hex(s)
478 }
479}
480
481#[cfg(feature = "zeroize")]
482impl Zeroize for Hash {
483 fn zeroize(&mut self) {
484 // Destructuring to trigger compile error as a reminder to update this impl.
485 let Self(bytes) = self;
486 bytes.zeroize();
487 }
488}
489
490/// This implementation is constant-time.
491impl PartialEq for Hash {
492 #[inline]
493 fn eq(&self, other: &Hash) -> bool {
494 constant_time_eq::constant_time_eq_32(&self.0, &other.0)
495 }
496}
497
498/// This implementation is constant-time.
499impl PartialEq<[u8; OUT_LEN]> for Hash {
500 #[inline]
501 fn eq(&self, other: &[u8; OUT_LEN]) -> bool {
502 constant_time_eq::constant_time_eq_32(&self.0, other)
503 }
504}
505
506/// This implementation is constant-time if the target is 32 bytes long.
507impl PartialEq<[u8]> for Hash {
508 #[inline]
509 fn eq(&self, other: &[u8]) -> bool {
510 constant_time_eq::constant_time_eq(&self.0, other)
511 }
512}
513
514impl fmt::Display for Hash {
515 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516 // Formatting field as `&str` to reduce code size since the `Debug`
517 // dynamic dispatch table for `&str` is likely needed elsewhere already,
518 // but that for `ArrayString<[u8; 64]>` is not.
519 let hex = self.to_hex();
520 let hex: &str = hex.as_str();
521
522 f.write_str(hex)
523 }
524}
525
526impl fmt::Debug for Hash {
527 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528 // Formatting field as `&str` to reduce code size since the `Debug`
529 // dynamic dispatch table for `&str` is likely needed elsewhere already,
530 // but that for `ArrayString<[u8; 64]>` is not.
531 let hex = self.to_hex();
532 let hex: &str = hex.as_str();
533
534 f.debug_tuple("Hash").field(&hex).finish()
535 }
536}
537
538/// The error type for [`Hash::from_hex`].
539///
540/// The `.to_string()` representation of this error currently distinguishes between bad length
541/// errors and bad character errors. This is to help with logging and debugging, but it isn't a
542/// stable API detail, and it may change at any time.
543#[derive(Clone, Debug)]
544pub struct HexError(HexErrorInner);
545
546#[derive(Clone, Debug)]
547enum HexErrorInner {
548 InvalidByte(u8),
549 InvalidLen(usize),
550}
551
552impl fmt::Display for HexError {
553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554 match self.0 {
555 HexErrorInner::InvalidByte(byte) => {
556 if byte < 128 {
557 write!(f, "invalid hex character: {:?}", byte as char)
558 } else {
559 write!(f, "invalid hex character: 0x{:x}", byte)
560 }
561 }
562 HexErrorInner::InvalidLen(len) => {
563 write!(f, "expected 64 hex bytes, received {}", len)
564 }
565 }
566 }
567}
568
569#[cfg(feature = "std")]
570impl std::error::Error for HexError {}
571
572type Output = hasher::Output<Platform>;
573
574impl Clone for Output {
575 fn clone(&self) -> Self {
576 hasher::Output {
577 block: self.block,
578 input_chaining_value: self.input_chaining_value,
579 block_len: self.block_len,
580 counter: self.counter,
581 flags: self.flags,
582 platform: self.platform,
583 }
584 }
585}
586
587impl Output {
588 fn root_hash(&self) -> Hash {
589 debug_assert_eq!(self.counter, 0);
590 Hash(self.root_bytes())
591 }
592
593 fn root_output_block(&self) -> [u8; 2 * OUT_LEN] {
594 self.platform.compress_xof(&self.input_chaining_value, &self.block, self.block_len, self.counter, self.flags | ROOT)
595 }
596}
597
598#[cfg(feature = "zeroize")]
599impl Zeroize for Output {
600 fn zeroize(&mut self) {
601 // Destructuring to trigger compile error as a reminder to update this impl.
602 let Self { input_chaining_value, block, block_len, counter, flags, platform: _ } = self;
603
604 input_chaining_value.zeroize();
605 block.zeroize();
606 block_len.zeroize();
607 counter.zeroize();
608 flags.zeroize();
609 }
610}
611
612type ChunkState = hasher::ChunkState<Platform>;
613
614impl hasher::Blocks for Platform {
615 #[inline(always)]
616 fn compress_in_place(&self, cv: &mut CVWords, block: &[u8; BLOCK_LEN], block_len: u8, counter: u64, flags: u8) {
617 Platform::compress_in_place(self, cv, block, block_len, counter, flags)
618 }
619
620 #[inline(always)]
621 fn compress_blocks(&self, cv: &mut CVWords, blocks: &[u8], count: usize, counter: u64, flags: u8, flags_start: u8) {
622 Platform::compress_blocks(self, cv, blocks, count, counter, flags, flags_start)
623 }
624}
625
626impl Clone for ChunkState {
627 fn clone(&self) -> Self {
628 hasher::ChunkState {
629 buf: self.buf,
630 cv: self.cv,
631 chunk_counter: self.chunk_counter,
632 buf_len: self.buf_len,
633 blocks_compressed: self.blocks_compressed,
634 flags: self.flags,
635 platform: self.platform,
636 }
637 }
638}
639
640impl ChunkState {
641 // Try to avoid buffering as much as possible, by compressing directly from
642 // the input slice when full blocks are available (src/hasher.rs).
643 #[inline(always)]
644 fn update(&mut self, input: &[u8]) -> &mut Self {
645 self.absorb(input);
646 debug_assert!(self.count() <= CHUNK_LEN);
647 self
648 }
649}
650
651#[cfg(feature = "zeroize")]
652impl Zeroize for ChunkState {
653 fn zeroize(&mut self) {
654 // Destructuring to trigger compile error as a reminder to update this impl.
655 let Self { cv, chunk_counter, buf, buf_len, blocks_compressed, flags, platform: _ } = self;
656
657 cv.zeroize();
658 chunk_counter.zeroize();
659 buf.zeroize();
660 buf_len.zeroize();
661 blocks_compressed.zeroize();
662 flags.zeroize();
663 }
664}
665
666// IMPLEMENTATION NOTE
667// ===================
668/// Undocumented and unstable, for benchmarks only.
669#[doc(hidden)]
670#[derive(Clone, Copy)]
671pub enum IncrementCounter {
672 Yes,
673 No,
674}
675
676impl IncrementCounter {
677 #[inline]
678 fn yes(&self) -> bool {
679 match self {
680 IncrementCounter::Yes => true,
681 IncrementCounter::No => false,
682 }
683 }
684}
685
686/// The lanes of the one pass that hashes `whole` whole chunks with a
687/// partial one of `partial` bytes after them, where the pass costs less
688/// than the partial chunk's blocks after the whole ones: three whole
689/// chunks in four lanes (SSE4.1 and AVX2), and in eight (AVX2) six or
690/// seven, or five with a partial chunk of six blocks or more (with fewer,
691/// an i7-12700K's efficiency core ran the eight lanes up to 7% slower; a
692/// pass of eight lanes costs 1.6 times one of four on its performance
693/// core). AVX-512 keeps its own widths (unmeasured).
694#[cfg(target_arch = "x86_64")]
695#[inline]
696fn spare_lane(platform: Platform, whole: usize, partial: usize) -> Option<usize> {
697 match (platform, whole) {
698 (Platform::SSE41 | Platform::AVX2, 3) => Some(4),
699 (Platform::AVX2, 6..=7) => Some(8),
700 (Platform::AVX2, 5) if partial > 5 * BLOCK_LEN => Some(8),
701 _ => None,
702 }
703}
704
705// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE chunks at the same time
706// on a single thread. Write out the chunk chaining values and return the
707// number of chunks hashed. These chunks are never the root and never empty;
708// those cases use a different codepath.
709fn compress_chunks_parallel(input: &[u8], key: &CVWords, chunk_counter: u64, flags: u8, platform: Platform, out: &mut [u8]) -> usize {
710 debug_assert!(!input.is_empty(), "empty chunks below the root");
711 debug_assert!(input.len() <= MAX_SIMD_DEGREE * CHUNK_LEN);
712
713 let mut chunks_exact = input.chunks_exact(CHUNK_LEN);
714 let mut chunks_array = ArrayVec::<&[u8; CHUNK_LEN], MAX_SIMD_DEGREE>::new();
715 for chunk in &mut chunks_exact {
716 chunks_array.push(chunk.try_into().unwrap());
717 }
718 // A whole chunk and a partial one, on AVX2 or AVX-512: their first
719 // blocks side by side (pair.rs).
720 if pair::takes(platform, input.len()) {
721 out[..2 * OUT_LEN].copy_from_slice(&pair::parent(input, key, chunk_counter, flags, platform));
722 return 2;
723 }
724 // Whole chunks that leave a lane spare, and a partial one: one pass,
725 // the partial chunk in the spare lane.
726 #[cfg(target_arch = "x86_64")]
727 if let (false, Some(lanes)) = (chunks_exact.remainder().is_empty(), spare_lane(platform, chunks_array.len(), chunks_exact.remainder().len())) {
728 let (whole, partial) = (&chunks_array[..], chunks_exact.remainder());
729 // Sound: spare_lane admits each width only on a platform whose CPU
730 // has its instructions.
731 if lanes == 4 {
732 unsafe { rust_sse41::hash_chunks_with_partial(whole, partial, key, chunk_counter, flags, out) };
733 } else {
734 unsafe { rust_avx2::hash_chunks_with_partial(whole, partial, key, chunk_counter, flags, out) };
735 }
736 return whole.len() + 1;
737 }
738 // Whole chunks and a partial one, on the NEON hybrids: where a q kernel
739 // covers the count, the partial chunk runs on the scalar units beside
740 // the whole chunks rather than after them.
741 #[cfg(blake3_neon)]
742 let hybrids = matches!(platform, Platform::NEON);
743 #[cfg(blake3_sme2)]
744 let hybrids = hybrids || matches!(platform, Platform::SME2);
745 #[cfg(blake3_neon)]
746 if !chunks_exact.remainder().is_empty()
747 && hybrids
748 && neon_hybrid::partial_pays(chunks_array.len(), chunks_exact.remainder().len())
749 && neon_hybrid::sha3_detected()
750 {
751 // Safe: the kernel exists for this count and the CPU has SHA-3.
752 unsafe {
753 neon_hybrid::hash_chunks_with_partial(&chunks_array, chunks_exact.remainder(), key, chunk_counter, flags, CHUNK_START, CHUNK_END, out);
754 }
755 return chunks_array.len() + 1;
756 }
757 platform.hash_many(&chunks_array, key, chunk_counter, IncrementCounter::Yes, flags, CHUNK_START, CHUNK_END, out);
758
759 // Hash the remaining partial chunk, if there is one. Note that the empty
760 // chunk (meaning the empty message) is a different codepath.
761 let chunks_so_far = chunks_array.len();
762 if !chunks_exact.remainder().is_empty() {
763 let counter = chunk_counter + chunks_so_far as u64;
764 let mut chunk_state = ChunkState::new(key, counter, flags, platform);
765 chunk_state.update(chunks_exact.remainder());
766 *<&mut [u8; OUT_LEN]>::try_from(&mut out[chunks_so_far * OUT_LEN..][..OUT_LEN]).unwrap() = chunk_state.output().chaining_value();
767 chunks_so_far + 1
768 } else {
769 chunks_so_far
770 }
771}
772
773// Use SIMD parallelism to hash up to MAX_SIMD_DEGREE parents at the same time
774// on a single thread. Write out the parent chaining values and return the
775// number of parents hashed. (If there's an odd input chaining value left over,
776// return it as an additional output.) These parents are never the root and
777// never empty; those cases use a different codepath.
778fn compress_parents_parallel(child_chaining_values: &[u8], key: &CVWords, flags: u8, platform: Platform, out: &mut [u8]) -> usize {
779 debug_assert_eq!(child_chaining_values.len() % OUT_LEN, 0, "wacky hash bytes");
780 let num_children = child_chaining_values.len() / OUT_LEN;
781 debug_assert!(num_children >= 2, "not enough children");
782 debug_assert!(num_children <= 2 * MAX_SIMD_DEGREE_OR_2, "too many");
783
784 let mut parents_exact = child_chaining_values.chunks_exact(BLOCK_LEN);
785 // Use MAX_SIMD_DEGREE_OR_2 rather than MAX_SIMD_DEGREE here: the walk
786 // (src/tree.rs) returns at least 2 values.
787 let mut parents_array = ArrayVec::<&[u8; BLOCK_LEN], MAX_SIMD_DEGREE_OR_2>::new();
788 for parent in &mut parents_exact {
789 parents_array.push(parent.try_into().unwrap());
790 }
791 platform.hash_many(
792 &parents_array,
793 key,
794 0, // Parents always use counter 0.
795 IncrementCounter::No,
796 flags | PARENT,
797 0, // Parents have no start flags.
798 0, // Parents have no end flags.
799 out,
800 );
801
802 // If there's an odd child left over, it becomes an output.
803 let parents_so_far = parents_array.len();
804 if !parents_exact.remainder().is_empty() {
805 out[parents_so_far * OUT_LEN..][..OUT_LEN].copy_from_slice(parents_exact.remainder());
806 parents_so_far + 1
807 } else {
808 parents_so_far
809 }
810}
811
812// Hash a subtree with the walk (src/tree.rs, `children`), condensing its
813// chaining values down to a single parent node. Don't compress that
814// last parent node, however. Instead, return its message bytes (the
815// concatenated chaining values of its children). This is necessary when the
816// first call to update() supplies a complete subtree, because the topmost
817// parent node of that subtree could end up being the root. It's also necessary
818// for extended output in the general case.
819//
820// This function is not used on inputs of 1 chunk or less. That's a
821// different codepath. Its arrays live in tree::root_children, out of line.
822#[inline]
823fn compress_subtree_to_parent_node(input: &[u8], ahead: usize, key: &CVWords, chunk_counter: u64, flags: u8, platform: Platform) -> [u8; BLOCK_LEN] {
824 debug_assert!(input.len() > CHUNK_LEN);
825 let kernels = tree::PlatformKernels { key, flags, platform };
826 let [left, right] = tree::root_children(&kernels, input, ahead, chunk_counter);
827 let mut block = [0u8; BLOCK_LEN];
828 block[..OUT_LEN].copy_from_slice(&left);
829 block[OUT_LEN..].copy_from_slice(&right);
830 block
831}
832
833// The root hash of an input of one chunk or less, in one kernel call. The
834// whole blocks are read in place, the final one too when it is full; a
835// short final block (and the empty block for the empty input) is copied
836// into a zero-padded buffer, which is the copy ChunkState::update() makes
837// too. The scalar kernel runs on every AArch64 core, so this path needs no
838// platform check.
839#[cfg(blake3_neon)]
840#[inline]
841fn hash_one_chunk_root(input: &[u8], key: &CVWords, flags: u8) -> Hash {
842 debug_assert!(input.len() <= CHUNK_LEN);
843 let blocks = cmp::max(1, input.len().div_ceil(BLOCK_LEN));
844 let whole = (blocks - 1) * BLOCK_LEN;
845 let tail = &input[whole..];
846 /// The padded last block, 64-byte aligned as hasher::Output's is
847 /// (src/hasher.rs explains).
848 #[repr(align(64))]
849 struct Aligned64([u8; BLOCK_LEN]);
850 let padded;
851 let last: &[u8; BLOCK_LEN] = match tail.try_into() {
852 Ok(full) => full,
853 Err(_) => {
854 let mut block = Aligned64([0; BLOCK_LEN]);
855 block.0[..tail.len()].copy_from_slice(tail);
856 padded = block;
857 &padded.0
858 }
859 };
860 let mut out = [0u8; OUT_LEN];
861 // Safe: `input` holds `whole` bytes before `tail`, and blocks is 1..=16.
862 unsafe {
863 neon_hybrid::hash_chunk(input.as_ptr(), blocks, last, tail.len() as u8, key, 0, flags, CHUNK_START, CHUNK_END | ROOT, &mut out);
864 }
865 Hash(out)
866}
867
868// Hash a whole subtree at its chunk counter (or the whole input at zero).
869// Unlike the wider helpers above, this also handles one chunk or less.
870// The pool's pieces (lanes.rs) use it; the one-shot calls go through
871// tree::hash.
872#[cfg(feature = "std")]
873#[inline]
874fn hash_all_at_once(input: &[u8], key: &CVWords, chunk_counter: u64, flags: u8, platform: Platform) -> Output {
875 // The input is a complete subtree at chunk_counter, or the whole
876 // input (including empty) at counter zero. Its byte offset fits u64.
877 debug_assert!(chunk_counter <= u64::MAX / CHUNK_LEN as u64);
878 debug_assert!(chunk_counter == 0 || (!input.is_empty() && input.len() as u64 <= max_subtree_len(chunk_counter * CHUNK_LEN as u64).unwrap()));
879
880 // If the whole subtree is one chunk, hash it directly with a ChunkState.
881 if input.len() <= CHUNK_LEN {
882 return ChunkState::new(key, chunk_counter, flags, platform).update(input).output();
883 }
884
885 // Otherwise construct an Output object from the parent node returned by
886 // compress_subtree_to_parent_node().
887 hasher::Output {
888 input_chaining_value: *key,
889 block: compress_subtree_to_parent_node(input, 0, key, chunk_counter, flags, platform),
890 block_len: BLOCK_LEN as u8,
891 counter: 0,
892 flags: flags | PARENT,
893 platform,
894 }
895}
896
897/// The hash of `input`, on the calling thread.
898///
899/// ```
900/// # blake3_servil::initialize();
901/// let hash = blake3_servil::hash(b"foo");
902/// # let hash1 = hash;
903///
904/// let hash = blake3_servil::Hasher::new().update(b"foo").finalize();
905/// # let hash2 = hash;
906/// # assert_eq!(hash1, hash2);
907/// ```
908///
909/// Choose it for short inputs, or where your program wants one thread; for
910/// any input in memory, [`hash_multithreaded`] is never slower. For output
911/// sizes other than 32 bytes, see [`Hasher::finalize_xof`] and
912/// [`OutputReader`].
913///
914/// **Speed**, nonstop on an Apple M4 Max: about 50 ns for 64 bytes, 0.7 µs
915/// for 1 KiB, 4 GB/s at 16 KiB, and 6 GB/s from 1 MiB. A call after a pause
916/// or after other work takes longer (its code and data have left the
917/// CPU's caches): 64 bytes then take about 0.2 to 0.6 µs. On Apple M4 and
918/// later, threads that hash inputs of 16 KiB or more at the same moment
919/// share one accelerator: one runs at full speed and the others at about
920/// two thirds of it.
921///
922/// The first hash in a process first runs a self-test (see the crate's
923/// documentation), under 0.2 ms; [`initialize`] moves it to start-up.
924// Inlined into its caller: its one call, out of line, sat apart from
925// hash_serial, one more code line to fetch from DRAM in a call after a pause
926// (i7-12700K, after idling, 64 B: -16.5%, 8 fresh layouts a side).
927#[inline]
928pub fn hash(input: &[u8]) -> Hash {
929 hash_serial(input, IV, 0)
930}
931
932/// The hash of `input`, using other CPU cores where that is faster: the same
933/// [`Hash`](struct@Hash) as [`hash`], never slower.
934///
935/// ```
936/// # blake3_servil::initialize_multithreaded(None);
937/// let hash = blake3_servil::hash_multithreaded(&[0u8; 1 << 20]);
938/// assert_eq!(hash, blake3_servil::hash(&[0u8; 1 << 20]));
939/// ```
940///
941/// The default choice for one message in memory.
942///
943/// **Speed**, on an Apple M4 Max (16 cores): below 512 KiB it hashes on the
944/// calling thread, at [`hash`]'s speed; from 512 KiB it uses the other cores
945/// too: about 12 GB/s at 1 MiB, 20 GB/s at 4 MiB, and 25 GB/s from 16 MiB,
946/// 4 to 6 times [`hash`]. The extra threads wait asleep between calls and
947/// use no CPU then.
948///
949/// **Several callers at once**: calls from several threads of one program
950/// share the cores in turn, each getting a fair part; a call that finds the
951/// cores all busy hashes on its own thread, at [`hash`]'s speed.
952///
953/// **CPUs with performance and efficiency cores** (Intel's since Alder
954/// Lake): a multithreaded call or stream uses every core, and afterwards
955/// the OS sometimes leaves the calling thread on an efficiency core for a
956/// while, where single-threaded hashing runs at about half speed (AVX2's
957/// 256-bit operations run as two halves there). Measured on an i7-12700K
958/// under Linux 7.0: beside nonstop multithreaded streams, 7% of 16 KiB
959/// single-threaded hashes ran partly on an efficiency core, 2.2 times
960/// slower, about 6% on their average. A program that alternates the two
961/// and needs its single-threaded speed steady can keep that thread on the
962/// performance cores (`sched_setaffinity` on Linux).
963///
964/// The program calls [`initialize_multithreaded`] first; a call before it
965/// panics.
966#[cfg(feature = "std")]
967pub fn hash_multithreaded(input: &[u8]) -> Hash {
968 lanes::require();
969 lanes::hash(input)
970}
971
972/// Run the startup self-test and detect the CPU: every program calls this
973/// (or [`initialize_multithreaded`]) once, before any other call of this
974/// crate, and a call before it panics. Under 0.2 ms, once per process
975/// (see the crate's documentation); later calls return at once.
976///
977/// ```
978/// blake3_servil::initialize();
979/// let hash = blake3_servil::hash(b"foo");
980/// # assert_eq!(hash, blake3_servil::Hasher::new().update(b"foo").finalize());
981/// ```
982pub fn initialize() {
983 self_test::ensure();
984 platform::Platform::detect_unchecked().record();
985}
986
987/// [`initialize`], and start the threads that the multithreaded calls and
988/// [`stream`] use: every program that makes such calls calls this once,
989/// before them, in place of `initialize`. It starts `threads - 1` workers,
990/// and each multithreaded call hashes on its calling thread and the
991/// workers (calls made at once share the workers), and on CPUs with SME2
992/// (Apple M4) the SME2 thread too; `None` means one thread
993/// a CPU (`std::thread::available_parallelism`, which follows the CPU
994/// affinity the program runs under). With 1, calls hash on their calling
995/// thread alone: no workers start, nor the SME2 thread. Otherwise it
996/// starts the first worker and returns; the first worker starts the others
997/// (each 20-30 us on Linux x86-64), and calls run on the workers started
998/// so far. The
999/// threads wait asleep whenever no call has work for them (their memory:
1000/// see [Memory](crate#memory)). Later calls return at once, and must ask
1001/// for the same number: another `Some` panics. A panic on a worker thread
1002/// (a bug in this crate) ends the process, its message printed, rather
1003/// than leaving the call that waits for that worker's piece waiting
1004/// forever.
1005///
1006/// ```
1007/// blake3_servil::initialize_multithreaded(None);
1008/// let hash = blake3_servil::hash_multithreaded(&[0u8; 1 << 20]);
1009/// assert_eq!(hash, blake3_servil::hash(&[0u8; 1 << 20]));
1010/// ```
1011#[cfg(feature = "std")]
1012pub fn initialize_multithreaded(threads: Option<core::num::NonZeroUsize>) {
1013 static STARTED: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
1014 let cpus = || std::thread::available_parallelism().map_or(1, |n| n.get());
1015 let started = *STARTED.get_or_init(|| {
1016 let threads = threads.map_or_else(cpus, |n| n.get());
1017 initialize();
1018 lanes::initialize(threads);
1019 threads
1020 });
1021 if let Some(threads) = threads {
1022 assert_eq!(
1023 started,
1024 threads.get(),
1025 "blake3-servil: the multithreaded calls were started with {started} threads; initialize_multithreaded cannot change that to {threads}"
1026 );
1027 }
1028}
1029
1030/// Whether every worker of the multithreaded calls' pool is parked (false
1031/// before [`initialize_multithreaded`]). Undocumented and unstable, for
1032/// tests only: a test that needs the pool idle waits for this, not for a
1033/// sleep that a loaded machine could outlast.
1034#[cfg(feature = "std")]
1035#[doc(hidden)]
1036pub fn __all_workers_parked() -> bool {
1037 lanes::all_workers_parked()
1038}
1039
1040/// An alias to distinguish [`hash_derive_key_context`] outputs from other keys.
1041pub type ContextKey = [u8; KEY_LEN];
1042
1043/// Hash a key-derivation context string and return a [`ContextKey`].
1044///
1045/// This has the same security requirement as key derivation. **The context
1046/// string should be hardcoded, globally unique, and application-specific.**
1047///
1048/// Its one use is [`Mode::DeriveKeyMaterial`]: a program that derives many
1049/// keys from one context hashes the context once.
1050///
1051/// # Example
1052///
1053/// ```
1054/// # blake3_servil::initialize();
1055/// use blake3_servil::Mode;
1056///
1057/// let context_key = blake3_servil::hash_derive_key_context("foo");
1058/// let derived = blake3_servil::hash_with(Mode::DeriveKeyMaterial(&context_key), b"bar");
1059/// assert_eq!(derived, blake3_servil::hash_with(Mode::DeriveKey("foo"), b"bar"));
1060/// ```
1061pub fn hash_derive_key_context(context: &str) -> ContextKey {
1062 hash_serial(context.as_bytes(), IV, DERIVE_KEY_CONTEXT).0
1063}
1064
1065/// The most bytes a subtree starting at byte `input_offset` of a message
1066/// can hold: `None` at offset zero (the whole message), else the largest
1067/// power-of-two multiple of a chunk the offset is aligned to. Requires a
1068/// whole-chunk offset.
1069#[inline(always)]
1070pub(crate) fn max_subtree_len(input_offset: u64) -> Option<u64> {
1071 if input_offset == 0 {
1072 return None;
1073 }
1074 assert_eq!(input_offset % CHUNK_LEN as u64, 0);
1075 let counter = input_offset / CHUNK_LEN as u64;
1076 let max_chunks = 1 << counter.trailing_zeros();
1077 Some(max_chunks * CHUNK_LEN as u64)
1078}
1079
1080/// Which of BLAKE3's three functions a call computes: the plain hash
1081/// ([`hash`]), the keyed hash, or key derivation. Every call that takes a mode runs each one at the same
1082/// speed, but for `DeriveKey`, which first hashes its context string (once
1083/// per call; [`DeriveKeyMaterial`](Mode::DeriveKeyMaterial) takes it hashed
1084/// once for many calls).
1085#[derive(Clone, Copy)]
1086pub enum Mode<'a> {
1087 /// The default hash function, as [`hash`].
1088 Hash,
1089 /// The keyed hash function with this key.
1090 Keyed(&'a [u8; KEY_LEN]),
1091 /// The key derivation function with this context string; the input
1092 /// is the key material.
1093 DeriveKey(&'a str),
1094 /// The key derivation function with its context string already hashed
1095 /// ([`hash_derive_key_context`]); the input is the key material.
1096 DeriveKeyMaterial(&'a ContextKey),
1097}
1098
1099impl Mode<'_> {
1100 /// The key words and flags this mode hashes with.
1101 fn key_and_flags(self) -> (CVWords, u8) {
1102 match self {
1103 Mode::Hash => (*IV, 0),
1104 Mode::Keyed(key) => (platform::words_from_le_bytes_32(key), KEYED_HASH),
1105 Mode::DeriveKey(context) => (platform::words_from_le_bytes_32(&hash_derive_key_context(context)), DERIVE_KEY_MATERIAL),
1106 Mode::DeriveKeyMaterial(context_key) => (platform::words_from_le_bytes_32(context_key), DERIVE_KEY_MATERIAL),
1107 }
1108 }
1109}
1110
1111/// [`hash`] in any [`Mode`]: the plain hash, the keyed hash, or key
1112/// derivation, at [`hash`]'s speed.
1113///
1114/// ```
1115/// # blake3_servil::initialize();
1116/// use blake3_servil::Mode;
1117/// let key = [7u8; 32];
1118/// assert_eq!(blake3_servil::hash_with(Mode::Keyed(&key), b"foo").as_bytes().len(), 32);
1119/// ```
1120pub fn hash_with(mode: Mode, input: &[u8]) -> Hash {
1121 let (key, flags) = mode.key_and_flags();
1122 hash_serial(input, &key, flags)
1123}
1124
1125/// [`hash_multithreaded`] in any [`Mode`], at its speed.
1126///
1127/// ```
1128/// # blake3_servil::initialize_multithreaded(None);
1129/// use blake3_servil::Mode;
1130/// let key = [7u8; 32];
1131/// let input = vec![1u8; 1 << 20];
1132/// assert_eq!(blake3_servil::hash_multithreaded_with(Mode::Keyed(&key), &input), blake3_servil::hash_with(Mode::Keyed(&key), &input));
1133/// ```
1134#[cfg(feature = "std")]
1135pub fn hash_multithreaded_with(mode: Mode, input: &[u8]) -> Hash {
1136 lanes::require();
1137 let (key, flags) = mode.key_and_flags();
1138 lanes::hash_with_key(input, &key, flags)
1139}
1140
1141/// The whole input on the calling thread, in the given mode: the one-chunk
1142/// kernel call up to CHUNK_LEN, the subtree walk above. hash(),
1143/// hash() in its modes and hash_multithreaded()'s short path all come
1144/// through here.
1145#[inline]
1146fn hash_serial(input: &[u8], key: &CVWords, flags: u8) -> Hash {
1147 hash_serial_from(Platform::detect(), input, key, flags)
1148}
1149
1150/// [`hash_serial`] from `platform`: the startup self-test's way in, which
1151/// has not run `Platform::detect`'s check (it is that check).
1152#[inline]
1153fn hash_serial_from(platform: Platform, input: &[u8], key: &CVWords, flags: u8) -> Hash {
1154 let turn = platform::Sme2Turn::take(platform, input.len() >= SME2_SIZED_LEN);
1155 // One comparison on the path of a chunk or less: everything else is
1156 // out of line, so that path's code stays as small as it was (a call
1157 // after other work fetches every line of it from DRAM).
1158 #[cfg(all(blake3_neon, feature = "std"))]
1159 if (CHUNK_LEN + 1..PREFETCH_BELOW).contains(&input.len()) {
1160 prefetch_after_pause(input.len(), turn.platform(), false);
1161 }
1162 hash_serial_on(input, key, flags, turn.platform())
1163}
1164
1165/// Below this length a one-shot call that follows a pause prefetches its
1166/// kernels' code: after other work the call then fetches it from DRAM in
1167/// parallel, not one miss at a time (M4 Max, after other work: 4 KiB
1168/// x0.61, 8 KiB x0.51, 16 KiB x0.66; 32 KiB and up level, the hashing's
1169/// own time dwarfing it; jobs 971-975). The streaming paths (Hasher,
1170/// batches, the streams' tasks) run back to back with their code near.
1171/// A cold call of 2-8 KiB spends about a third of its time on its code
1172/// (10-24 KB of kernels and glue); from 3 KiB what remains is the kernels
1173/// starting before their lines arrive. Tried and set aside: prefetching
1174/// for a chunk or less (2-10% gained, less than the pause check costs);
1175/// prefetching the 16 KiB of Rust around the kernels too (every call
1176/// 400-500 ns slower: prefetches beyond some number in flight are
1177/// dropped); a rolled four-lane NEON path for calls after a pause (1.3 KB,
1178/// 1.2-1.5x slower: a four-lane NEON BLAKE3 is bound by its chains'
1179/// latency, where the hybrids' integer lanes fill the idle issue slots).
1180#[cfg(all(blake3_neon, feature = "std"))]
1181const PREFETCH_BELOW: usize = 64 * 1024;
1182
1183/// After a pause (code_may_be_cold), prefetch the kernels hashing `len`
1184/// bytes runs: a one-shot call's (prefetch_kernels), or with `fresh_hasher`
1185/// a fresh Hasher's first update's (prefetch_update_kernels).
1186#[cfg(all(blake3_neon, feature = "std"))]
1187// Cold for layout: the path of a chunk or less falls through past its
1188// call, on the lines it had before the call existed.
1189#[cold]
1190#[inline(never)]
1191fn prefetch_after_pause(len: usize, platform: Platform, fresh_hasher: bool) {
1192 if code_may_be_cold() {
1193 if fresh_hasher {
1194 prefetch_update_kernels(len, platform);
1195 } else {
1196 prefetch_kernels(len, platform);
1197 }
1198 }
1199}
1200
1201/// Whether this thread's last call in the prefetching range was over
1202/// 100 us ago (or never). Calls back to back find their code near, where
1203/// the prefetch would cost them 2-4% (jobs 972-975); one that follows a
1204/// pause is the one other work may have taken the code from.
1205#[cfg(all(blake3_neon, feature = "std"))]
1206#[inline]
1207fn code_may_be_cold() -> bool {
1208 std::thread_local! {
1209 static LAST_CALL: core::cell::Cell<u64> = const { core::cell::Cell::new(0) };
1210 }
1211 let now = platform::counter();
1212 let last = LAST_CALL.with(|last| last.replace(now));
1213 now.wrapping_sub(last) > platform::ticks_in_100_us()
1214}
1215
1216/// Batches of one-block messages up to this many prefetch their code after
1217/// a pause: past it the hashing's own time dwarfs a cold kernel's.
1218#[cfg(all(blake3_neon, feature = "std"))]
1219const PREFETCH_BATCH_BELOW: usize = 64;
1220
1221/// Batches of 1 to this many one-block messages run on the pair kernel
1222/// alone after a pause (neon_hybrid::hash_pairs): its code (2.9 KB) is a
1223/// fraction of the plans' for 3 to 16 (up to 17 KB) and less than the one
1224/// message's kernel (3.9 KB), and after a pause the code comes from memory
1225/// (Mac jobs 1320-1337: 12 and 16 run faster after idling and slower after
1226/// other work this way).
1227#[cfg(all(blake3_neon, feature = "std"))]
1228const PAIRS_AFTER_PAUSE_UPTO: usize = 8;
1229
1230/// After a pause (code_may_be_cold), a batch of `count` one-block
1231/// messages: 1 to PAIRS_AFTER_PAUSE_UPTO hashed here on the pair kernel
1232/// (true: done); otherwise its kernels' code prefetched (false: hash it as
1233/// usual), SME2's message kernel where it takes the batch, else the
1234/// hybrids' parent plans.
1235#[cfg(all(blake3_neon, feature = "std"))]
1236#[cold]
1237#[inline(never)]
1238fn batch_after_pause(input: &[u8], message_len: usize, key: &CVWords, flags: u8, out: &mut [[u8; OUT_LEN]], platform: Platform) -> bool {
1239 if !code_may_be_cold() {
1240 return false;
1241 }
1242 let count = out.len();
1243 #[cfg(blake3_sme2)]
1244 if matches!(platform, Platform::SME2) && count >= many::ONE_BLOCK_PAD_MIN {
1245 sme2::prefetch_code();
1246 return false;
1247 }
1248 let _ = platform;
1249 if !neon_hybrid::sha3_detected() {
1250 return false;
1251 }
1252 if message_len == 0 || !(1..=PAIRS_AFTER_PAUSE_UPTO).contains(&count) {
1253 neon_hybrid::prefetch_message_code(count);
1254 return false;
1255 }
1256 many::assert_slots(input, message_len, count);
1257 neon_hybrid::prefetch_pair_code();
1258 let mut table: [&[u8; BLOCK_LEN]; PAIRS_AFTER_PAUSE_UPTO] = [&[0; BLOCK_LEN]; PAIRS_AFTER_PAUSE_UPTO];
1259 for (slot, message) in table.iter_mut().zip(input.chunks_exact(BLOCK_LEN)) {
1260 *slot = message.try_into().unwrap();
1261 }
1262 // Sound: the SHA-3 extension is here (above).
1263 unsafe { neon_hybrid::hash_pairs(&table[..count], key, flags, CHUNK_START, CHUNK_END | ROOT, message_len, out.as_flattened_mut()) };
1264 true
1265}
1266
1267/// Prefetch the code of the kernels hashing `len` bytes on `platform`
1268/// runs: SME2's from 16 chunks, else the hybrids', chunks to root.
1269#[cfg(all(blake3_neon, feature = "std"))]
1270fn prefetch_kernels(len: usize, platform: Platform) {
1271 #[cfg(blake3_sme2)]
1272 if matches!(platform, Platform::SME2) && len >= SME2_SIZED_LEN {
1273 sme2::prefetch_code();
1274 // Below the flat walk, sixteen chunks' values merge on the hybrids.
1275 if !sme2::flat_takes(len) {
1276 neon_hybrid::prefetch_parent_code(16);
1277 }
1278 return;
1279 }
1280 // Below 16 chunks the SME2 platform runs the hybrids too.
1281 #[cfg(blake3_sme2)]
1282 let hybrids = matches!(platform, Platform::NEON | Platform::SME2);
1283 #[cfg(not(blake3_sme2))]
1284 let hybrids = matches!(platform, Platform::NEON);
1285 if hybrids && neon_hybrid::sha3_detected() {
1286 let whole = (len / CHUNK_LEN).min(16);
1287 neon_hybrid::prefetch_tree_code(whole, if whole == 16 { 0 } else { len % CHUNK_LEN });
1288 }
1289}
1290
1291/// Prefetch the code a fresh Hasher's update of `len` bytes runs: the
1292/// subtrees its loop cuts (hasher::subtree_len from counter 0, up to the last
1293/// chunk, which waits in the chunk state), each one's kernels.
1294#[cfg(all(blake3_neon, feature = "std"))]
1295fn prefetch_update_kernels(len: usize, platform: Platform) {
1296 if !neon_hybrid::sha3_detected() {
1297 return;
1298 }
1299 let (mut counter, mut left) = (0u64, len);
1300 while left > CHUNK_LEN {
1301 let subtree = hasher::subtree_len(counter, left);
1302 let chunks = subtree / CHUNK_LEN;
1303 #[cfg(blake3_sme2)]
1304 if matches!(platform, Platform::SME2) && chunks >= 16 {
1305 sme2::prefetch_code();
1306 if !sme2::flat_takes(subtree) {
1307 neon_hybrid::prefetch_parent_code(16);
1308 }
1309 counter += chunks as u64;
1310 left -= subtree;
1311 continue;
1312 }
1313 neon_hybrid::prefetch_tree_code(chunks.min(16), 0);
1314 counter += chunks as u64;
1315 left -= subtree;
1316 }
1317 let _ = platform;
1318}
1319
1320/// The smallest input the SME2 kernels take part in: one group of sixteen
1321/// chunks. Below it the SME2 platform runs the NEON hybrids.
1322pub(crate) const SME2_SIZED_LEN: usize = 16 * CHUNK_LEN;
1323
1324/// The fewest messages a batch has when the SME2 kernels take part: one
1325/// group of sixteen messages.
1326pub(crate) const SME2_SIZED_BATCH: usize = 16;
1327
1328/// [`hash_serial`] with the tree's kernels chosen by the caller (a pool
1329/// piece runs the NEON hybrids); one chunk or less runs the scalar kernel
1330/// on every platform.
1331#[inline]
1332fn hash_serial_on(input: &[u8], key: &CVWords, flags: u8, platform: Platform) -> Hash {
1333 Hash(tree::hash(&tree::PlatformKernels { key, flags, platform }, input))
1334}
1335
1336/// The hashes of many messages of one length, laid out in one buffer, on
1337/// the calling thread: `out[i]` becomes the [`hash`] of message `i`.
1338///
1339/// Message `i` starts at byte `i * stride` of `input`, where `stride` is
1340/// `message_len` rounded up to a multiple of 64 (64 for empty messages), so
1341/// messages whose length is a multiple of 64 sit back to back. `input`
1342/// holds exactly `out.len()` strides, and the bytes between a message's end
1343/// and the next stride are zero (debug builds check; another byte there
1344/// changes that message's hash). This is the shape of a Merkle tree's
1345/// layer: leaves of one size, or nodes of two 32-byte children
1346/// (`message_len` 64).
1347///
1348/// ```
1349/// # blake3_servil::initialize();
1350/// let leaves = vec![7u8; 1000 * 256];
1351/// let mut hashes = vec![[0u8; 32]; 1000];
1352/// blake3_servil::hash_many(&leaves, 256, &mut hashes);
1353/// assert_eq!(hashes[3], *blake3_servil::hash(&leaves[3 * 256..4 * 256]).as_bytes());
1354///
1355/// // 100-byte messages sit 128 bytes apart, zero between them.
1356/// let mut records = vec![0u8; 10 * 128];
1357/// for (i, record) in records.chunks_mut(128).enumerate() {
1358/// record[..100].fill(i as u8 + 1);
1359/// }
1360/// let mut digests = vec![[0u8; 32]; 10];
1361/// blake3_servil::hash_many(&records, 100, &mut digests);
1362/// assert_eq!(digests[9], *blake3_servil::hash(&[10u8; 100]).as_bytes());
1363/// ```
1364///
1365/// **Speed**: short messages hash several at a time, so a batch beats a
1366/// loop of [`hash`]: on an Apple M4 Max, 64-byte messages take about 10 to
1367/// 20 ns each in batches of 16 or more, against about 50 for one [`hash`]
1368/// each. Messages longer than 15 KiB cost what [`hash`] costs. For the
1369/// same hashes over several cores, see [`hash_many_multithreaded`].
1370pub fn hash_many(input: &[u8], message_len: usize, out: &mut [[u8; OUT_LEN]]) {
1371 hash_many_serial(input, message_len, IV, 0, out);
1372}
1373
1374/// [`hash_many`] in the mode of `key` and `flags`, on the calling thread.
1375#[inline]
1376pub(crate) fn hash_many_serial(input: &[u8], message_len: usize, key: &CVWords, flags: u8, out: &mut [[u8; OUT_LEN]]) {
1377 hash_many_serial_from(Platform::detect(), input, message_len, key, flags, out);
1378}
1379
1380/// [`hash_many_serial`] from `platform` (the self-test's way in, as
1381/// [`hash_serial_from`]).
1382#[inline]
1383fn hash_many_serial_from(platform: Platform, input: &[u8], message_len: usize, key: &CVWords, flags: u8, out: &mut [[u8; OUT_LEN]]) {
1384 let turn = platform::Sme2Turn::take(platform, many::sme2_sized(message_len, out.len()));
1385 // A small batch of one-block messages after a pause fetches its kernels'
1386 // code in parallel (as hash_serial does): its time is mostly that code.
1387 #[cfg(all(blake3_neon, feature = "std"))]
1388 if message_len <= BLOCK_LEN && (1..=PREFETCH_BATCH_BELOW).contains(&out.len()) && batch_after_pause(input, message_len, key, flags, out, turn.platform()) {
1389 return;
1390 }
1391 many::hash_many_on(input, message_len, key, flags, out, turn.platform());
1392}
1393
1394/// [`hash_many`], using other CPU cores where that is faster: the same
1395/// hashes, never slower.
1396///
1397/// ```
1398/// # blake3_servil::initialize_multithreaded(None);
1399/// let leaves = vec![7u8; 4096 * 256];
1400/// let mut hashes = vec![[0u8; 32]; 4096];
1401/// blake3_servil::hash_many_multithreaded(&leaves, 256, &mut hashes);
1402/// assert!(hashes.iter().all(|h| h == blake3_servil::hash(&[7u8; 256]).as_bytes()));
1403/// ```
1404///
1405/// **Speed**: below 512 KiB in all it runs on the calling thread, at
1406/// [`hash_many`]'s speed; larger batches use the other cores too (on an
1407/// Apple M4 Max, 64-byte messages from about 10 down to 3 ns each). It
1408/// shares the cores with other callers as [`hash_multithreaded`] does.
1409#[cfg(feature = "std")]
1410pub fn hash_many_multithreaded(input: &[u8], message_len: usize, out: &mut [[u8; OUT_LEN]]) {
1411 lanes::require();
1412 lanes::hash_many(input, message_len, IV, 0, out);
1413}
1414
1415/// [`hash_many`] in any [`Mode`], at its speed.
1416///
1417/// ```
1418/// # blake3_servil::initialize();
1419/// use blake3_servil::Mode;
1420/// let key = [7u8; 32];
1421/// let nodes = vec![1u8; 20 * 64];
1422/// let mut macs = vec![[0u8; 32]; 20];
1423/// blake3_servil::hash_many_with(Mode::Keyed(&key), &nodes, 64, &mut macs);
1424/// assert_eq!(macs[5], *blake3_servil::hash_with(blake3_servil::Mode::Keyed(&key), &[1u8; 64]).as_bytes());
1425/// ```
1426pub fn hash_many_with(mode: Mode, input: &[u8], message_len: usize, out: &mut [[u8; OUT_LEN]]) {
1427 let (key, flags) = mode.key_and_flags();
1428 hash_many_serial(input, message_len, &key, flags, out);
1429}
1430
1431/// [`hash_many_multithreaded`] in any [`Mode`], at its speed.
1432///
1433/// ```
1434/// # blake3_servil::initialize_multithreaded(None);
1435/// use blake3_servil::Mode;
1436/// let key = [7u8; 32];
1437/// let nodes = vec![1u8; 20000 * 64];
1438/// let mut macs = vec![[0u8; 32]; 20000];
1439/// blake3_servil::hash_many_multithreaded_with(Mode::Keyed(&key), &nodes, 64, &mut macs);
1440/// assert_eq!(macs[5], *blake3_servil::hash_with(blake3_servil::Mode::Keyed(&key), &[1u8; 64]).as_bytes());
1441/// ```
1442#[cfg(feature = "std")]
1443pub fn hash_many_multithreaded_with(mode: Mode, input: &[u8], message_len: usize, out: &mut [[u8; OUT_LEN]]) {
1444 lanes::require();
1445 let (key, flags) = mode.key_and_flags();
1446 lanes::hash_many(input, message_len, &key, flags, out);
1447}
1448
1449/// A message's hash, fed in pieces: [`update`](Hasher::update) with each
1450/// piece, in order, then [`finalize`](Hasher::finalize). The pieces may be
1451/// of any lengths; the hash is the same as [`hash`] of them all at once.
1452///
1453/// A `Hasher` is about 18 KiB (it holds short pieces until it has 16 KiB
1454/// to hash at once); box it where that matters.
1455///
1456/// # Examples
1457///
1458/// ```
1459/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1460/// # blake3_servil::initialize();
1461/// // Hash an input incrementally.
1462/// let mut hasher = blake3_servil::Hasher::new();
1463/// hasher.update(b"foo");
1464/// hasher.update(b"bar");
1465/// hasher.update(b"baz");
1466/// assert_eq!(hasher.finalize(), blake3_servil::hash(b"foobarbaz"));
1467///
1468/// // Extended output. OutputReader also implements Read and Seek.
1469/// # #[cfg(feature = "std")] {
1470/// let mut output = [0; 1000];
1471/// let mut output_reader = hasher.finalize_xof();
1472/// output_reader.fill(&mut output);
1473/// assert_eq!(&output[..32], blake3_servil::hash(b"foobarbaz").as_bytes());
1474/// # }
1475/// # Ok(())
1476/// # }
1477/// ```
1478#[derive(Clone)]
1479pub struct Hasher {
1480 core: HasherCore,
1481 /// The message's bytes from its last 16 KiB boundary, gathered while
1482 /// they arrive in shorter updates: the first `staged`.
1483 staged: usize,
1484 stage: [core::mem::MaybeUninit<u8>; STAGE_LEN],
1485}
1486
1487/// The bytes a Hasher gathers before they hash side by side: 16 chunks,
1488/// the widest kernels' lanes.
1489const STAGE_LEN: usize = 16 * CHUNK_LEN;
1490
1491/// The tree's state behind a [`Hasher`]: its chunk, and the chaining values
1492/// of its finished subtrees.
1493#[derive(Clone)]
1494pub(crate) struct HasherCore {
1495 key: CVWords,
1496 chunk_state: ChunkState,
1497 initial_chunk_counter: u64,
1498 // The stack size is MAX_DEPTH + 1 because we do lazy merging. For example,
1499 // with 7 chunks, we have 3 entries in the stack. Adding an 8th chunk
1500 // requires a 4th entry, rather than merging everything down to 1, because
1501 // we don't know whether more input is coming. This is different from how
1502 // the reference implementation does things.
1503 cv_stack: CvStack,
1504}
1505
1506mod hasher;
1507use hasher::CvStack;
1508
1509/// What the Hasher's kernels take from the library (hasher::Hooks): with
1510/// `pooled`, subtrees of lanes::MIN_SPLIT_LEN and more on the pool; whole
1511/// subtrees on `platform` (an SME2 turn's); `chunk_platform`, the chunk
1512/// state's, otherwise.
1513struct HasherHooks<'a> {
1514 key: &'a CVWords,
1515 flags: u8,
1516 chunk_platform: Platform,
1517 platform: Platform,
1518 pooled: bool,
1519}
1520
1521impl hasher::Hooks for HasherHooks<'_> {
1522 #[inline(always)]
1523 #[allow(unused_variables)]
1524 fn subtree_cv(&self, input: &[u8], ahead: usize, counter: u64) -> Option<CVBytes> {
1525 // After the first input, no subtree is the root: on SME2 the flat
1526 // walk runs it down to its own chaining value, so one value goes
1527 // on the stack and about one merge runs on the core between SME2
1528 // kernels (about 0.2 us of other work between them puts the SME
1529 // unit in its slow state: `sme2`'s documentation).
1530 #[cfg(blake3_sme2)]
1531 if !self.pooled && matches!(self.platform, Platform::SME2) && sme2::flat_takes(input.len()) {
1532 // Safe: the SME2 platform is selected only where the CPU has it.
1533 return Some(unsafe { sme2::compress_subtree_flat_to_cv(input, ahead, self.key, counter, self.flags) });
1534 }
1535 None
1536 }
1537
1538 #[inline(always)]
1539 fn children(&self, input: &[u8], ahead: usize, counter: u64) -> [CVBytes; 2] {
1540 #[cfg(feature = "std")]
1541 let pool_takes = self.pooled && input.len() >= lanes::MIN_SPLIT_LEN;
1542 #[cfg(not(feature = "std"))]
1543 let pool_takes = false;
1544 let pair = if pool_takes {
1545 #[cfg(feature = "std")]
1546 {
1547 lanes::subtree_children(input, self.key, counter, self.flags)
1548 }
1549 #[cfg(not(feature = "std"))]
1550 unreachable!()
1551 } else if self.pooled {
1552 // Pooled, the subtrees below take the lock one at a time, and
1553 // the pool's own never use SME2.
1554 let own = platform::Sme2Turn::take(self.chunk_platform, input.len() >= SME2_SIZED_LEN);
1555 compress_subtree_to_parent_node(input, ahead, self.key, counter, self.flags, own.platform())
1556 } else {
1557 compress_subtree_to_parent_node(input, ahead, self.key, counter, self.flags, self.platform)
1558 };
1559 [pair[..OUT_LEN].try_into().unwrap(), pair[OUT_LEN..].try_into().unwrap()]
1560 }
1561}
1562
1563/// The Hasher's kernels: chunks, nodes, and parents on the chunk state's
1564/// platform, the rest from `hooks`.
1565type HasherKernels<'a> = hasher::Kernels<'a, Platform, HasherHooks<'a>>;
1566
1567/// A Hasher's kernels on its key and chunk state (see HasherHooks).
1568#[inline(always)]
1569fn kernels_on<'a>(key: &'a CVWords, chunk_state: &ChunkState, platform: Platform, pooled: bool) -> HasherKernels<'a> {
1570 hasher::Kernels {
1571 key,
1572 flags: chunk_state.flags,
1573 platform: chunk_state.platform,
1574 hooks: HasherHooks { key, flags: chunk_state.flags, chunk_platform: chunk_state.platform, platform, pooled },
1575 }
1576}
1577
1578impl Hasher {
1579 fn from_core(core: HasherCore) -> Self {
1580 Self { core, staged: 0, stage: [core::mem::MaybeUninit::uninit(); STAGE_LEN] }
1581 }
1582
1583 /// The bytes gathered in the stage.
1584 fn staged_bytes(&self) -> &[u8] {
1585 // Sound: the first `staged` bytes of the stage were written (gather).
1586 unsafe { core::slice::from_raw_parts(self.stage.as_ptr() as *const u8, self.staged) }
1587 }
1588
1589 /// Copy `bytes` into the stage after what it holds.
1590 fn stage(&mut self, bytes: &[u8]) {
1591 // Sound: MaybeUninit<u8> has u8's layout, and the bytes fit (gather).
1592 let room = unsafe { core::slice::from_raw_parts_mut(self.stage.as_mut_ptr().add(self.staged) as *mut u8, bytes.len()) };
1593 room.copy_from_slice(bytes);
1594 self.staged += bytes.len();
1595 }
1596
1597 /// `update`'s gathering: an update of STAGE_LEN or more from a 16 KiB
1598 /// boundary of the message (or an empty stage) goes to the core whole;
1599 /// shorter ones gather in the stage until it reaches the next boundary
1600 /// (but a fresh hasher's first chunk goes to the core),
1601 /// and the stage goes to the core as one update, its chunks side by
1602 /// side in the lanes.
1603 fn gather(&mut self, input: &[u8], pooled: bool) -> &mut Self {
1604 self.gather_part(input, pooled, false);
1605 self
1606 }
1607
1608 /// [`gather`](Hasher::gather), but with `hold`, a stage that fills a
1609 /// whole group (from offset zero) is kept for the caller to hash with
1610 /// others' (`held_group`), and the rest of `input`
1611 /// returned; else nothing is left.
1612 fn gather_part<'a>(&mut self, mut input: &'a [u8], pooled: bool, hold: bool) -> &'a [u8] {
1613 self.core.assert_room(self.count(), input.len());
1614 while !input.is_empty() {
1615 // Whole groups, and a fresh hasher's first chunk (which hashes
1616 // serially either way), skip the stage.
1617 if self.staged == 0 && (input.len() >= STAGE_LEN || self.core.count() + input.len() as u64 <= CHUNK_LEN as u64) {
1618 self.core.update_pooled(input, pooled);
1619 return &[];
1620 }
1621 let at = (self.core.initial_chunk_counter * CHUNK_LEN as u64 + self.count()) % STAGE_LEN as u64;
1622 let to_boundary = STAGE_LEN - at as usize;
1623 let take = cmp::min(to_boundary, input.len());
1624 // The loop's variant: every pass stages at least one byte of
1625 // the input, which is not empty (`at` is below STAGE_LEN).
1626 assert!(take > 0, "the stage took nothing");
1627 self.stage(&input[..take]);
1628 input = &input[take..];
1629 if take < to_boundary {
1630 return &[];
1631 }
1632 if hold && self.held_group().is_some() {
1633 return input;
1634 }
1635 let staged = core::mem::replace(&mut self.staged, 0);
1636 // Sound: the first `staged` bytes were written; the core reads
1637 // them before the stage is written again.
1638 let bytes = unsafe { core::slice::from_raw_parts(self.stage.as_ptr() as *const u8, staged) };
1639 self.core.update_pooled(bytes, pooled);
1640 }
1641 &[]
1642 }
1643
1644 /// A whole group in the stage, from offset zero: its first chunk's
1645 /// counter and its bytes.
1646 #[cfg(feature = "std")]
1647 fn held_group(&self) -> Option<(u64, &[u8; STAGE_LEN])> {
1648 (self.staged == STAGE_LEN && self.core.initial_chunk_counter == 0)
1649 .then(|| (self.core.count() / CHUNK_LEN as u64, self.staged_bytes().try_into().unwrap()))
1650 }
1651
1652 #[cfg(not(feature = "std"))]
1653 fn held_group(&self) -> Option<(u64, &[u8; STAGE_LEN])> {
1654 None
1655 }
1656
1657 /// The held group's subtree, as [`HasherCore::update_with_results`]
1658 /// takes it (its two halves' values at chunk zero, else its own), into
1659 /// the core; the stage empty again.
1660 #[cfg(feature = "std")]
1661 fn take_held(&mut self, result: &[u8; BLOCK_LEN]) {
1662 let staged = core::mem::replace(&mut self.staged, 0);
1663 // Sound: as in gather_part.
1664 let bytes = unsafe { core::slice::from_raw_parts(self.stage.as_ptr() as *const u8, staged) };
1665 self.core.update_with_results(bytes, &mut core::iter::once(result));
1666 }
1667
1668 /// The root's output: the core's, after the stage's bytes.
1669 fn final_output(&self) -> Output {
1670 if self.staged == 0 {
1671 return self.core.final_output();
1672 }
1673 let mut core = self.core.clone();
1674 core.update(self.staged_bytes());
1675 core.final_output()
1676 }
1677
1678 /// Construct a new `Hasher` for the regular hash function.
1679 pub fn new() -> Self {
1680 Self::from_core(HasherCore::new_internal(IV, 0))
1681 }
1682
1683 /// Construct a new `Hasher` in a [`Mode`]: the keyed hash, or key
1684 /// derivation with a context string or a context hashed once.
1685 pub fn new_with(mode: Mode) -> Self {
1686 let (key, flags) = mode.key_and_flags();
1687 Self::from_core(HasherCore::new_internal(&key, flags))
1688 }
1689
1690 /// Reset the `Hasher` to its initial state.
1691 ///
1692 /// This is functionally the same as overwriting the `Hasher` with a new
1693 /// one, using the same key or context string if any.
1694 pub fn reset(&mut self) -> &mut Self {
1695 self.core.reset();
1696 self.staged = 0;
1697 self
1698 }
1699
1700 /// Add the message's next piece, of any length, on the calling thread.
1701 ///
1702 /// **Speed**: pieces of any size hash at nearly the speed of whole
1703 /// buffers: short pieces are copied into the `Hasher` until it holds
1704 /// 16 KiB, which then hash at once. Pieces of 16 KiB and more, starting
1705 /// at a multiple of 16 KiB of the message, skip the copy (about 5% of the
1706 /// time on an Apple M4). For a whole message in memory, [`hash`] is a
1707 /// little faster; for a file or reader, see
1708 /// [`update_reader`](#method.update_reader).
1709 pub fn update(&mut self, input: &[u8]) -> &mut Self {
1710 // A fresh hasher's first update is a one-shot call's work (a
1711 // Hasher per message, the digest traits): it prefetches its code
1712 // after a pause, as hash() does.
1713 #[cfg(all(blake3_neon, feature = "std"))]
1714 if self.count() == 0 && (CHUNK_LEN + 1..PREFETCH_BELOW).contains(&input.len()) {
1715 prefetch_after_pause(input.len(), self.core.chunk_state.platform, true);
1716 }
1717 self.gather(input, false)
1718 }
1719
1720 /// [`update`](Hasher::update), using other CPU cores where that is
1721 /// faster: the same result, never slower. Pieces of 512 KiB and more
1722 /// use them, as [`hash_multithreaded`] does; shorter ones hash on the
1723 /// calling thread. One `Hasher` may mix the two calls. When reading
1724 /// the pieces keeps your thread busy, a [`stream`] is faster
1725 /// ([`Stream::submit_reader`], or [`Stream::submit_with`]): it hashes
1726 /// each piece while your thread reads the next.
1727 ///
1728 /// ```
1729 /// # blake3_servil::initialize_multithreaded(None);
1730 /// let input = vec![7u8; 3 << 20];
1731 /// let mut hasher = blake3_servil::Hasher::new();
1732 /// for piece in input.chunks(64 * 1024) {
1733 /// hasher.update_multithreaded(piece);
1734 /// }
1735 /// assert_eq!(hasher.finalize(), blake3_servil::hash(&input));
1736 /// ```
1737 #[cfg(feature = "std")]
1738 pub fn update_multithreaded(&mut self, input: &[u8]) -> &mut Self {
1739 lanes::require();
1740 self.gather(input, true)
1741 }
1742
1743 /// Finalize the hash state and return the [`Hash`](struct.Hash.html) of
1744 /// the input.
1745 ///
1746 /// This method is idempotent. Calling it twice will give the same result.
1747 /// You can also add more input and finalize again.
1748 pub fn finalize(&self) -> Hash {
1749 assert_eq!(self.core.initial_chunk_counter, 0, "set_input_offset must be used with finalize_non_root",);
1750 self.final_output().root_hash()
1751 }
1752
1753 /// Finalize the hash state and return an [`OutputReader`], which can
1754 /// supply any number of output bytes.
1755 ///
1756 /// This method is idempotent. Calling it twice will give the same result.
1757 /// You can also add more input and finalize again.
1758 ///
1759 /// [`OutputReader`]: struct.OutputReader.html
1760 pub fn finalize_xof(&self) -> OutputReader {
1761 assert_eq!(self.core.initial_chunk_counter, 0, "set_input_offset must be used with finalize_non_root",);
1762 OutputReader::new(self.final_output())
1763 }
1764
1765 /// The number of bytes passed to [`update`](Hasher::update) so far.
1766 pub fn count(&self) -> u64 {
1767 self.core.count() + self.staged as u64
1768 }
1769
1770 /// As [`update`](Hasher::update), but reading from a
1771 /// [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) implementation.
1772 ///
1773 /// [`Hasher`] implements
1774 /// [`std::io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html), so it's possible to
1775 /// use [`std::io::copy`](https://doc.rust-lang.org/std/io/fn.copy.html) to update a [`Hasher`]
1776 /// from any reader. Unfortunately, this standard approach can limit performance, because
1777 /// `copy` currently uses an internal 8 KiB buffer that isn't big enough to take advantage of
1778 /// all SIMD instruction sets. (In particular, [AVX-512](https://en.wikipedia.org/wiki/AVX-512)
1779 /// needs a 16 KiB buffer.) `update_reader` avoids this performance problem and is slightly
1780 /// more convenient.
1781 ///
1782 /// The internal buffer size this method uses may change at any time, and it may be different
1783 /// for different targets. The only guarantee is that it will be large enough for all of this
1784 /// crate's SIMD implementations on the current platform.
1785 ///
1786 /// The most common implementer of
1787 /// [`std::io::Read`](https://doc.rust-lang.org/std/io/trait.Read.html) might be
1788 /// [`std::fs::File`](https://doc.rust-lang.org/std/fs/struct.File.html).
1789 ///
1790 /// This method requires the `std` Cargo feature, which is enabled by default.
1791 ///
1792 /// # Example
1793 ///
1794 /// ```no_run
1795 /// # use std::fs::File;
1796 /// # use std::io;
1797 /// # fn main() -> io::Result<()> {
1798 /// # blake3_servil::initialize();
1799 /// // Hash standard input.
1800 /// let mut hasher = blake3_servil::Hasher::new();
1801 /// hasher.update_reader(std::io::stdin().lock())?;
1802 /// println!("{}", hasher.finalize());
1803 /// # Ok(())
1804 /// # }
1805 /// ```
1806 #[cfg(feature = "std")]
1807 pub fn update_reader(&mut self, reader: impl std::io::Read) -> std::io::Result<&mut Self> {
1808 io::copy_wide(reader, self)?;
1809 Ok(self)
1810 }
1811}
1812
1813impl HasherCore {
1814 pub(crate) fn new_internal(key: &CVWords, flags: u8) -> Self {
1815 Self::new_from(key, flags, Platform::detect())
1816 }
1817
1818 /// [`HasherCore::new_internal`] on `platform` (the self-test's way in,
1819 /// as [`hash_serial_from`]).
1820 pub(crate) fn new_from(key: &CVWords, flags: u8, platform: Platform) -> Self {
1821 Self { key: *key, chunk_state: ChunkState::new(key, 0, flags, platform), initial_chunk_counter: 0, cv_stack: CvStack::new() }
1822 }
1823
1824 /// Back to the empty message, with the same key, flags, and platform.
1825 pub(crate) fn reset(&mut self) -> &mut Self {
1826 self.chunk_state = ChunkState::new(&self.key, 0, self.chunk_state.flags, self.chunk_state.platform);
1827 self.cv_stack.clear();
1828 self
1829 }
1830
1831 // As described in push_cv() below, we do "lazy merging", delaying merges
1832 // until right before the next CV is about to be added. This is different
1833 // from the reference implementation. Another difference is that we aren't
1834 // always merging 1 chunk at a time. Instead, each CV might represent any
1835 // power-of-two number of chunks, as long as the smaller-above-larger stack
1836 // order is maintained. Instead of the "count the trailing 0-bits"
1837 // algorithm described in the spec (which assumes you're adding one chunk
1838 // at a time), we use a "count the total number of 1-bits" variant (which
1839 // doesn't assume that). The principle is the same: each CV that should
1840 // remain in the stack is represented by a 1-bit in the total number of
1841 // chunks (or bytes) so far.
1842 #[cfg(feature = "std")]
1843 fn merge_cv_stack(&mut self, chunk_counter: u64) {
1844 // Its callers (the queue's hashers, and a Hasher's held group) start
1845 // at offset zero; a positioned subtree's offset takes the core's path.
1846 debug_assert_eq!(self.initial_chunk_counter, 0, "a hasher at offset zero");
1847 let post_merge_stack_len = hasher::ones(chunk_counter);
1848 let kernels = kernels_on(&self.key, &self.chunk_state, self.chunk_state.platform, false);
1849 self.cv_stack.merge(&kernels, post_merge_stack_len);
1850 }
1851
1852 /// The kernels on this Hasher's key and flags (see HasherKernels).
1853 #[inline(always)]
1854 fn kernels(&self, platform: Platform, pooled: bool) -> HasherKernels<'_> {
1855 kernels_on(&self.key, &self.chunk_state, platform, pooled)
1856 }
1857
1858 // In reference_impl.rs, we merge the new CV with existing CVs from the
1859 // stack before pushing it. We can do that because we know more input is
1860 // coming, so we know none of the merges are root.
1861 //
1862 // This setting is different. We want to feed as much input as possible to
1863 // the walk (`tree::wide`), without setting aside anything for the
1864 // chunk_state. If the user gives us 64 KiB, we want to parallelize over
1865 // all 64 KiB at once as a single subtree, if at all possible.
1866 //
1867 // This leads to two problems:
1868 // 1) This 64 KiB input might be the only call that ever gets made to
1869 // update. In this case, the root node of the 64 KiB subtree would be
1870 // the root node of the whole tree, and it would need to be ROOT
1871 // finalized. We can't compress it until we know.
1872 // 2) This 64 KiB input might complete a larger tree, whose root node is
1873 // similarly going to be the root of the whole tree. For example,
1874 // maybe we have 196 KiB (that is, 128 + 64) hashed so far. We can't
1875 // compress the node at the root of the 256 KiB subtree until we know
1876 // how to finalize it.
1877 //
1878 // The second problem is solved with "lazy merging". That is, when we're
1879 // about to add a CV to the stack, we don't merge it with anything first,
1880 // as the reference impl does. Instead we do merges using the *previous* CV
1881 // that was added, which is sitting on top of the stack, and we put the new
1882 // CV (unmerged) on top of the stack afterwards. This guarantees that we
1883 // never merge the root node until finalize().
1884 //
1885 // Solving the first problem requires an additional tool,
1886 // compress_subtree_to_parent_node(). That function always returns the top
1887 // *two* chaining values of the subtree it's compressing. We then do lazy
1888 // merging with each of them separately, so that the second CV will always
1889 // remain unmerged. (That also helps us support extendable output when
1890 // we're hashing an input all-at-once.)
1891 #[cfg(feature = "std")]
1892 fn push_cv(&mut self, new_cv: &CVBytes, chunk_counter: u64) {
1893 self.merge_cv_stack(chunk_counter);
1894 self.cv_stack.push(*new_cv);
1895 }
1896
1897 /// [`update`](Hasher::update) with every whole subtree's result taken
1898 /// from `results`, one a whole subtree in order (in parts of
1899 /// [`lanes::TASK_LEN`], as [`hasher::subtree_len`] cuts them), as
1900 /// [`lanes::Task`] computes them.
1901 #[cfg(feature = "std")]
1902 pub(crate) fn update_with_results<'a>(&mut self, input: &[u8], results: &mut impl Iterator<Item = &'a [u8; BLOCK_LEN]>) {
1903 for part in input.chunks(lanes::TASK_LEN) {
1904 self.update_part_with_results(part, results);
1905 }
1906 }
1907
1908 #[cfg(feature = "std")]
1909 fn update_part_with_results<'a>(&mut self, mut input: &[u8], results: &mut impl Iterator<Item = &'a [u8; BLOCK_LEN]>) {
1910 // Its one caller hands it a whole held group, from a group boundary,
1911 // where the core's chunk is empty (Hasher::take_held).
1912 debug_assert_eq!(self.chunk_state.count(), 0, "a held group starts where the core's chunk is empty");
1913 while input.len() > CHUNK_LEN {
1914 let len = hasher::subtree_len(self.chunk_state.chunk_counter, input.len());
1915 let chunks = (len / CHUNK_LEN) as u64;
1916 let result = results.next().expect("a result for every planned subtree");
1917 let counter = self.chunk_state.chunk_counter;
1918 if counter == 0 && len > CHUNK_LEN {
1919 self.push_cv(result[..OUT_LEN].try_into().unwrap(), counter);
1920 self.push_cv(result[OUT_LEN..].try_into().unwrap(), counter + chunks / 2);
1921 } else {
1922 self.push_cv(result[..OUT_LEN].try_into().unwrap(), counter);
1923 }
1924 self.chunk_state.chunk_counter += chunks;
1925 input = &input[len..];
1926 }
1927 if !input.is_empty() {
1928 self.chunk_state.update(input);
1929 self.merge_cv_stack(self.chunk_state.chunk_counter);
1930 }
1931 }
1932
1933 /// `update`'s loop; with `pooled`, whole subtrees of
1934 /// `lanes::MIN_SPLIT_LEN` and more go to the pool.
1935 pub(crate) fn update_pooled(&mut self, input: &[u8], pooled: bool) -> &mut Self {
1936 #[cfg(not(feature = "std"))]
1937 let _ = pooled;
1938 self.assert_room(self.count(), input.len());
1939 // The partial chunk first (hasher::fill), then whole subtrees and the
1940 // last chunk (hasher::feed), as src/hasher.rs describes.
1941 let fill_kernels = kernels_on(&self.key, &self.chunk_state, self.chunk_state.platform, pooled);
1942 let rest = hasher::fill(&fill_kernels, &mut self.chunk_state, &mut self.cv_stack, self.initial_chunk_counter, input);
1943 if rest.is_empty() {
1944 return self;
1945 }
1946 // Pooled, the subtrees take the lock one at a time (children).
1947 let turn = platform::Sme2Turn::take(self.chunk_state.platform, !pooled && rest.len() >= SME2_SIZED_LEN);
1948 let kernels = kernels_on(&self.key, &self.chunk_state, turn.platform(), pooled);
1949 hasher::feed(&kernels, &mut self.chunk_state, &mut self.cv_stack, self.initial_chunk_counter, rest);
1950 self
1951 }
1952
1953 pub(crate) fn final_output(&self) -> Output {
1954 hasher::final_node(&self.kernels(self.chunk_state.platform, false), &self.chunk_state, &self.cv_stack)
1955 }
1956
1957 /// The serial update: whole subtrees now, the last chunk kept.
1958 pub(crate) fn update(&mut self, input: &[u8]) -> &mut Self {
1959 self.update_pooled(input, false)
1960 }
1961
1962 /// Requires that `len` more bytes after `count` fit the subtree that
1963 /// set_input_offset started (max_subtree_len).
1964 pub(crate) fn assert_room(&self, count: u64, len: usize) {
1965 let input_offset = self.initial_chunk_counter * CHUNK_LEN as u64;
1966 if let Some(max) = max_subtree_len(input_offset) {
1967 assert!(len as u64 <= max - count, "the subtree starting at {} contains at most {} bytes (found {})", input_offset, max, count + len as u64,);
1968 }
1969 }
1970
1971 /// Start the subtree at byte `offset` of the message (the outboard's
1972 /// positioned subtrees; set_input_offset in the tests).
1973 #[cfg(any(feature = "std", test))]
1974 pub(crate) fn set_input_offset(&mut self, offset: u64) -> &mut Self {
1975 assert_eq!(self.count(), 0, "hasher has already accepted input");
1976 assert_eq!(offset % CHUNK_LEN as u64, 0, "offset ({offset}) must be a chunk boundary (divisible by {CHUNK_LEN})",);
1977 let counter = offset / CHUNK_LEN as u64;
1978 self.chunk_state.chunk_counter = counter;
1979 self.initial_chunk_counter = counter;
1980 self
1981 }
1982
1983 /// Bytes so far, counted from the input offset.
1984 pub(crate) fn count(&self) -> u64 {
1985 (self.chunk_state.chunk_counter - self.initial_chunk_counter) * CHUNK_LEN as u64 + self.chunk_state.count() as u64
1986 }
1987}
1988
1989// Don't derive(Debug), because the state may be secret.
1990impl fmt::Debug for Hasher {
1991 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1992 f.debug_struct("Hasher").field("flags", &self.core.chunk_state.flags).field("platform", &self.core.chunk_state.platform).finish()
1993 }
1994}
1995
1996impl Default for Hasher {
1997 #[inline]
1998 fn default() -> Self {
1999 Self::new()
2000 }
2001}
2002
2003#[cfg(feature = "std")]
2004impl std::io::Write for Hasher {
2005 /// This is equivalent to [`update`](#method.update).
2006 #[inline]
2007 fn write(&mut self, input: &[u8]) -> std::io::Result<usize> {
2008 self.update(input);
2009 Ok(input.len())
2010 }
2011
2012 #[inline]
2013 fn flush(&mut self) -> std::io::Result<()> {
2014 Ok(())
2015 }
2016}
2017
2018#[cfg(feature = "zeroize")]
2019impl Zeroize for Hasher {
2020 fn zeroize(&mut self) {
2021 self.core.zeroize();
2022 self.stage.fill(core::mem::MaybeUninit::new(0));
2023 self.staged = 0;
2024 }
2025}
2026
2027#[cfg(feature = "zeroize")]
2028impl Zeroize for HasherCore {
2029 fn zeroize(&mut self) {
2030 // Destructuring to trigger compile error as a reminder to update this impl.
2031 let Self { key, chunk_state, initial_chunk_counter, cv_stack } = self;
2032
2033 key.zeroize();
2034 chunk_state.zeroize();
2035 initial_chunk_counter.zeroize();
2036 cv_stack.zeroize();
2037 }
2038}
2039
2040/// An incremental reader for extended output, returned by
2041/// [`Hasher::finalize_xof`](struct.Hasher.html#method.finalize_xof).
2042///
2043/// Shorter BLAKE3 outputs are prefixes of longer ones, and explicitly requesting a short output is
2044/// equivalent to truncating the default-length output. Note that this is a difference between
2045/// BLAKE2 and BLAKE3.
2046///
2047/// # Security notes
2048///
2049/// Outputs shorter than the default length of 32 bytes (256 bits) provide less security. An N-bit
2050/// BLAKE3 output is intended to provide N bits of first and second preimage resistance and N/2
2051/// bits of collision resistance, for any N up to 256. Longer outputs don't provide any additional
2052/// security.
2053///
2054/// Avoid relying on the secrecy of the output offset, that is, the number of output bytes read or
2055/// the arguments to [`seek`](struct.OutputReader.html#method.seek) or
2056/// [`set_position`](struct.OutputReader.html#method.set_position). [_Block-Cipher-Based Tree
2057/// Hashing_ by Aldo Gunsing](https://eprint.iacr.org/2022/283) shows that an attacker who knows
2058/// both the message and the key (if any) can easily determine the offset of an extended output.
2059/// For comparison, AES-CTR has a similar property: if you know the key, you can decrypt a block
2060/// from an unknown position in the output stream to recover its block index. Callers with strong
2061/// secret keys aren't affected in practice, but secret offsets are a [design
2062/// smell](https://en.wikipedia.org/wiki/Design_smell) in any case.
2063#[derive(Clone)]
2064pub struct OutputReader {
2065 inner: Output,
2066 position_within_block: u8,
2067}
2068
2069impl OutputReader {
2070 fn new(inner: Output) -> Self {
2071 Self { inner, position_within_block: 0 }
2072 }
2073
2074 // This helper function handles both the case where the output buffer is
2075 // shorter than one block, and the case where our position_within_block is
2076 // non-zero.
2077 fn fill_one_block(&mut self, buf: &mut &mut [u8]) {
2078 let output_block: [u8; BLOCK_LEN] = self.inner.root_output_block();
2079 let output_bytes = &output_block[self.position_within_block as usize..];
2080 let take = cmp::min(buf.len(), output_bytes.len());
2081 buf[..take].copy_from_slice(&output_bytes[..take]);
2082 self.position_within_block += take as u8;
2083 if self.position_within_block == BLOCK_LEN as u8 {
2084 self.inner.counter += 1;
2085 self.position_within_block = 0;
2086 }
2087 // Advance the dest buffer. mem::take() is a borrowck workaround.
2088 *buf = &mut core::mem::take(buf)[take..];
2089 }
2090
2091 /// Fill a buffer with output bytes and advance the position of the
2092 /// `OutputReader`. This is equivalent to [`Read::read`], except that it
2093 /// doesn't return a `Result`. Both methods always fill the entire buffer.
2094 ///
2095 /// Note that `OutputReader` doesn't buffer output bytes internally, so
2096 /// calling `fill` repeatedly with a short-length or odd-length slice will
2097 /// end up performing the same compression multiple times. If you're
2098 /// reading output in a loop, prefer a slice length that's a multiple of
2099 /// [`BLOCK_LEN`] (64 bytes).
2100 ///
2101 /// The maximum output size of BLAKE3 is 2<sup>64</sup>-1 bytes. If you try
2102 /// to extract more than that, for example by seeking near the end and
2103 /// reading further, the behavior is unspecified.
2104 ///
2105 /// [`Read::read`]: #method.read
2106 pub fn fill(&mut self, mut buf: &mut [u8]) {
2107 if buf.is_empty() {
2108 return;
2109 }
2110
2111 // If we're partway through a block, try to get to a block boundary.
2112 if self.position_within_block != 0 {
2113 self.fill_one_block(&mut buf);
2114 }
2115
2116 let full_blocks = buf.len() / BLOCK_LEN;
2117 let full_blocks_len = full_blocks * BLOCK_LEN;
2118 if full_blocks > 0 {
2119 debug_assert_eq!(0, self.position_within_block);
2120 self.inner.platform.xof_many(
2121 &self.inner.input_chaining_value,
2122 &self.inner.block,
2123 self.inner.block_len,
2124 self.inner.counter,
2125 self.inner.flags | ROOT,
2126 &mut buf[..full_blocks_len],
2127 );
2128 self.inner.counter += full_blocks as u64;
2129 buf = &mut buf[full_blocks * BLOCK_LEN..];
2130 }
2131
2132 if !buf.is_empty() {
2133 debug_assert!(buf.len() < BLOCK_LEN);
2134 self.fill_one_block(&mut buf);
2135 debug_assert!(buf.is_empty());
2136 }
2137 }
2138
2139 /// Return the current read position in the output stream. This is
2140 /// equivalent to [`Seek::stream_position`], except that it doesn't return
2141 /// a `Result`. The position of a new `OutputReader` starts at 0, and each
2142 /// call to [`fill`] or [`Read::read`] moves the position forward by the
2143 /// number of bytes read.
2144 ///
2145 /// [`Seek::stream_position`]: #method.stream_position
2146 /// [`fill`]: #method.fill
2147 /// [`Read::read`]: #method.read
2148 pub fn position(&self) -> u64 {
2149 self.inner.counter * BLOCK_LEN as u64 + self.position_within_block as u64
2150 }
2151
2152 /// Seek to a new read position in the output stream. This is equivalent to
2153 /// calling [`Seek::seek`] with [`SeekFrom::Start`], except that it doesn't
2154 /// return a `Result`.
2155 ///
2156 /// [`Seek::seek`]: #method.seek
2157 /// [`SeekFrom::Start`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html
2158 pub fn set_position(&mut self, position: u64) {
2159 self.position_within_block = (position % BLOCK_LEN as u64) as u8;
2160 self.inner.counter = position / BLOCK_LEN as u64;
2161 }
2162}
2163
2164// Don't derive(Debug), because the state may be secret.
2165impl fmt::Debug for OutputReader {
2166 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2167 f.debug_struct("OutputReader").field("position", &self.position()).finish()
2168 }
2169}
2170
2171#[cfg(feature = "std")]
2172impl std::io::Read for OutputReader {
2173 #[inline]
2174 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2175 self.fill(buf);
2176 Ok(buf.len())
2177 }
2178}
2179
2180#[cfg(feature = "std")]
2181impl std::io::Seek for OutputReader {
2182 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
2183 let max_position = u64::MAX as i128;
2184 let target_position: i128 = match pos {
2185 std::io::SeekFrom::Start(x) => x as i128,
2186 std::io::SeekFrom::Current(x) => self.position() as i128 + x as i128,
2187 std::io::SeekFrom::End(_) => {
2188 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "seek from end not supported"));
2189 }
2190 };
2191 if target_position < 0 {
2192 return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "seek before start"));
2193 }
2194 self.set_position(cmp::min(target_position, max_position) as u64);
2195 Ok(self.position())
2196 }
2197}
2198
2199#[cfg(feature = "zeroize")]
2200impl Zeroize for OutputReader {
2201 fn zeroize(&mut self) {
2202 // Destructuring to trigger compile error as a reminder to update this impl.
2203 let Self { inner, position_within_block } = self;
2204
2205 inner.zeroize();
2206 position_within_block.zeroize();
2207 }
2208}