Skip to main content

blake3_servil/
stream.rs

1//! A stream of messages hashed in place by this crate's threads, in the
2//! program's own memory, each digest written into the program's own memory
3//! and that memory handed back in order ([`stream`]).
4//!
5//! How it runs:
6//! - Each message of up to [`PIECE`] bytes is one entry in a ring of
7//!   entries; entries gather into tasks of up to [`MEMBERS`] messages (or
8//!   TASK_LEN / 4 bytes), hashed as members of a task: messages of
9//!   one length side by side, the rest one at a time.
10//! - A longer message is split into pieces of PIECE bytes, each a whole
11//!   subtree at its offset and a task of its own; the program's thread
12//!   merges the pieces' chaining values in order as it hands back the
13//!   digest.
14//! - The stream is its own source of tasks for the pool's workers
15//!   (`lanes::sources`): it writes each task into its source's ring and
16//!   publishes it with one store, and workers take tasks in order by
17//!   compare-and-swap, with no lock and no shared queue. The open task is
18//!   handed over when it fills, or when the program collects or finishes.
19//! - Each submission names where its digests go, in the program's memory;
20//!   the threads write them there (a long message's, merged on the
21//!   program's thread, too). The stream hands that memory back to the
22//!   stream's one handler in submission order, on the program's thread,
23//!   from a submission (when the stream is full), `collect` and `finish`.
24//!   A program waiting hashes tasks no thread has taken.
25//!
26//! The messages are borrowed for the scope of [`stream`], which waits for
27//! every task before it returns, also when its body panics: no thread
28//! reads a message after the scope ends.
29
30use crate::lanes::{MEMBERS, Member, OwnLine, TASK_LEN, Task};
31use crate::platform::Platform;
32use crate::{CHUNK_LEN, CVBytes, CVWords, Hash, Mode, OUT_LEN};
33use core::cell::UnsafeCell;
34use core::marker::PhantomData;
35use core::sync::atomic::{AtomicUsize, Ordering};
36
37/// The longest message hashed as one entry; a longer one goes in pieces
38/// this long (a task's subtree).
39pub(crate) const PIECE: usize = TASK_LEN;
40
41/// The bytes a stream keeps for messages and batches written in place:
42/// 2 MiB. With the digests in the program's places, 4 MiB made two
43/// programs streaming 4 MiB batches 18% slower (the ring, the places, and
44/// the programs' own buffers past the L3, i7-12700K); 2 MiB is level there
45/// and 2-4 points less fast than 4 MiB on some other cells.
46const RING_BYTES: usize = 2 << 20;
47
48/// The most entries (messages and pieces) in flight in one stream (1024:
49/// 4 KiB to 16 KiB messages 8-9% faster than at 8192, i7-12700K; 256 lost
50/// 26-130% at 64 and 256 B).
51const ENTRIES: usize = 1024;
52
53/// One message, one piece of a long one, or one part of a batch, from its
54/// submission to its delivery: what the program's thread keeps of it. Its
55/// task's threads write its digests, then count down the `left` of the
56/// task's first entry (Release, once a task); the program hands them back
57/// once that is zero (Acquire). Kept apart, so that the program's writes
58/// here share no line with the threads'.
59#[derive(Clone, Copy)]
60struct Entry {
61    len: usize,
62    kind: Kind,
63    /// The index of its task's first entry, whose `left` counts the task.
64    first: usize,
65    /// The stream's ring bytes reserved up to and including this entry
66    /// (its own, for a message written in place): free once it is handed
67    /// back.
68    ring_end: usize,
69}
70
71/// What an entry is. Each `out` is the program's, borrowed for 'env.
72#[derive(Clone, Copy)]
73enum Kind {
74    /// A whole message: its digest at `out`.
75    Message { out: *mut Hash },
76    /// A piece of a long message: its first chunk's index, whether it is
77    /// the message's last piece, and where the message's digest goes.
78    Piece { counter: u64, last: bool, out: *mut Hash },
79    /// A part of a batch of messages of `message_len` bytes laid out as
80    /// hash_many takes them (an empty batch: one part of none), its
81    /// digests written into the batch's space; the batch's last part holds
82    /// that whole space, handed back once it is done.
83    Batch { message_len: usize, whole: Option<(*mut Hash, usize)> },
84    /// The end of a long message whose reader failed after some pieces
85    /// ([`Stream::submit_reader`]): no digest; its pieces' chaining values
86    /// are dropped.
87    Abandoned,
88}
89
90/// What a task's threads write for a piece of a long message, in
91/// `pieces_out`: its chaining value (a piece at chunk 0, of two chunks or
92/// more, gives its two children instead), on a line of its own.
93#[repr(transparent)]
94struct Out<const N: usize>(UnsafeCell<[u8; N]>);
95
96/// Hash a stream of messages on this crate's threads, in place: `body`
97/// submits messages that live at least as long as the call
98/// ([`Stream::submit`]), each with the place its digest goes, in the
99/// program's memory (a batch's, the place for all its digests). The
100/// threads write the digests there, and `done` receives each place back,
101/// in submission order, once its digests are in it: from a submission
102/// (when the stream is full), [`collect`](Stream::collect) and
103/// [`finish`](Stream::finish), on this thread. `body` calls `finish` last
104/// (a scope that ends with places not handed back panics). A message of
105/// any length; this crate's threads read it where it is, and `stream`
106/// returns only after they are done, also when `body` panics.
107///
108/// The program calls [`crate::initialize_multithreaded`] first. A stream
109/// holds up to 1024 messages (a message over 64 KiB counts once per 64 KiB,
110/// a batch once per 64 KiB of messages) in flight; when it is full, a
111/// submission waits for room, handing places back and hashing a waiting
112/// task itself. A message stays unhashed in a task still open until the
113/// task fills or the program calls `collect` or `finish`, so a program
114/// that needs a digest soon calls one of them. State that `done` and
115/// `body` share goes in a [`Cell`](core::cell::Cell) or
116/// [`RefCell`](core::cell::RefCell).
117///
118/// Each message's place comes back alone, about 1-2 ns a message: for
119/// many short messages of one length, [`Stream::submit_batch`] hands back
120/// one place per batch and hashes them side by side (64-byte messages one
121/// at a time ran 4-8% slower than in the stream's earlier design, which
122/// handed back runs of digests from its own memory; batches of them,
123/// 5-13% faster; i7-12700K).
124///
125/// **Streams, then other calls.** While a stream is open, this crate's
126/// threads keep looking for its next message for a few microseconds before
127/// they sleep, which makes streams of messages 3-5% faster. A program that
128/// streams and then soon makes a one-shot multithreaded call pays for it:
129/// on an i7-12700K under Linux, [`crate::hash_many_multithreaded`] on 16384
130/// messages right after streams of 16-64 KiB messages ran about 5% slower.
131/// A program that keeps streaming does not pay it; one that alternates can
132/// stream its other work
133/// too ([`Stream::submit_batch`] for a batch, a stream per round with
134/// [`finish`](Stream::finish) between rounds that depend on each other).
135///
136/// ```
137/// # blake3_servil::initialize_multithreaded(None);
138/// use blake3_servil::{Hash, Mode};
139/// let messages: Vec<Vec<u8>> = (0..1000u32).map(|i| i.to_le_bytes().repeat(16)).collect();
140/// let mut digests = vec![Hash::from_bytes([0; 32]); messages.len()];
141/// let mut handed_back = 0;
142/// blake3_servil::stream(Mode::Hash, |done: &mut [Hash]| handed_back += done.len(), |s| {
143///     for (message, digest) in messages.iter().zip(&mut digests) {
144///         s.submit(message, digest);
145///     }
146///     s.finish();
147/// });
148/// assert_eq!(handed_back, 1000);
149/// assert_eq!(digests[7], blake3_servil::hash(&messages[7]));
150/// ```
151pub fn stream<'env, D: FnMut(&'env mut [Hash]), T>(mode: Mode, done: D, body: impl for<'s> FnOnce(&'s mut Stream<'env, D>) -> T) -> T {
152    let mut stream = Stream::open(mode, done);
153    let result = body(&mut stream);
154    assert_eq!(stream.delivered, stream.next, "a stream's body calls finish last");
155    result
156}
157
158impl<'env, D: FnMut(&'env mut [Hash])> Stream<'env, D> {
159    /// A stream outside [`stream`]'s scope, for the C API (`capi/`): the
160    /// same contract, with the program holding the stream and ending it
161    /// (`finish`, then drop). Dropping a stream waits for its tasks. Rust
162    /// programs call [`stream`], whose scope holds the contract for them.
163    #[doc(hidden)]
164    pub fn open(mode: Mode, done: D) -> Self {
165        crate::lanes::require();
166        let (key, flags) = mode.key_and_flags();
167        let s = Storage::take();
168        // A slot, where some thread beside this one can take tasks (with one
169        // CPU none can: then every task is hashed here as it is handed over,
170        // as when every slot is taken).
171        let slot = if crate::lanes::splits() { s.source.register() } else { None };
172        crate::lanes::sources::opened();
173        Stream {
174            key,
175            flags,
176            s,
177            slot,
178            done,
179            open_first: 0,
180            next: 0,
181            delivered: 0,
182            head: 0,
183            tail: 0,
184            open: None,
185            stack: arrayvec::ArrayVec::new(),
186            pieces: 0,
187            env: PhantomData,
188        }
189    }
190}
191
192/// A stream's entries, and room for their tasks in the pool's list: kept
193/// for the thread's next stream (a stream of one message up to 4 KiB
194/// after a 1 ms pause: 55 us with 248 KiB allocated and faulted in each
195/// time, 4-5 us with the spare; one stream kept open, 2.6-4 us. i7-12700K).
196struct Storage {
197    entries: Box<[Entry]>,
198    /// RING_BYTES for messages and batches written in place
199    /// ([`Stream::submit_with`]), each at a 64-byte boundary, in the order
200    /// of their entries: an allocation of its own, reached by pointer
201    /// alone (the program writes some bytes while threads read others).
202    ring: *mut u8,
203    pieces_out: Box<[OwnLine<Out<64>>]>,
204    /// For a task's first entry, the count of the task's entries still
205    /// unhashed: on lines of their own (128 bytes, as the adjacent-line
206    /// prefetcher pairs lines), so that the program polling one shares no
207    /// line with the threads writing digests. Zero between streams.
208    lefts: Box<[OwnLine<AtomicUsize>]>,
209    /// The workers' view of this storage's tasks: shared with them, so
210    /// apart from the stream's own (`&mut`) storage, and never freed.
211    source: &'static crate::lanes::sources::Source,
212}
213
214/// Streams' storage not in use: recycled, never freed (a worker may read a
215/// source's counters after its stream ended).
216static FREE: std::sync::Mutex<std::vec::Vec<FreeStorage>> = std::sync::Mutex::new(std::vec::Vec::new());
217
218struct FreeStorage(&'static mut Storage);
219
220// Sound: a storage in the free list is no stream's; the one that takes it
221// owns it alone.
222unsafe impl Send for FreeStorage {}
223
224impl Storage {
225    /// A storage from the free list, or a new one (kept for good).
226    fn take() -> &'static mut Storage {
227        match FREE.lock().unwrap().pop() {
228            Some(FreeStorage(s)) => s,
229            None => Box::leak(Box::new(Storage::new())),
230        }
231    }
232
233    fn new() -> Storage {
234        Storage {
235            source: Box::leak(Box::new(crate::lanes::sources::Source::new(ENTRIES))),
236            entries: std::vec![Entry { len: 0, kind: Kind::Abandoned, first: 0, ring_end: 0 }; ENTRIES].into_boxed_slice(),
237            ring: {
238                // Zeroed, untouched pages (only the bytes used are faulted
239                // in: calloc maps them lazily, where an aligned allocation
240                // would write all 2 MiB), starting at the first 64-byte
241                // boundary, as each room's offset is, so every room starts
242                // a cache line (glibc puts a 2 MiB block 16 bytes into
243                // one). Kept for good, as the storage is.
244                let block = Box::leak(std::vec![0u8; RING_BYTES + 64].into_boxed_slice());
245                let start = block.as_ptr().align_offset(64);
246                block[start..].as_mut_ptr()
247            },
248            pieces_out: (0..ENTRIES).map(|_| OwnLine(Out(UnsafeCell::new([0; 64])))).collect(),
249            lefts: (0..ENTRIES).map(|_| OwnLine(AtomicUsize::new(0))).collect(),
250        }
251    }
252
253    /// The ring's byte at `at`.
254    fn ring_at(&self, at: usize) -> *mut u8 {
255        debug_assert!(at <= RING_BYTES);
256        // Sound: `at` is in the ring.
257        unsafe { self.ring.add(at) }
258    }
259}
260
261/// Part `i` of a message in `pieces` parts, its digest at `out`: the
262/// message whole, or a piece.
263fn part_kind(i: usize, pieces: usize, out: *mut Hash) -> Kind {
264    if pieces == 1 { Kind::Message { out } } else { Kind::Piece { counter: (i * PIECE / CHUNK_LEN) as u64, last: i + 1 == pieces, out } }
265}
266
267/// The messages of one [`stream`] call, and the places for their digests,
268/// borrowed for its scope; `D` is the stream's handler.
269pub struct Stream<'env, D> {
270    key: CVWords,
271    flags: u8,
272    /// Its own until the stream ends (then the thread's spare).
273    s: &'static mut Storage,
274    /// The source's registration, or None (this thread hashes every task).
275    slot: Option<usize>,
276    /// The handler that takes each place back.
277    done: D,
278    /// The open task's first entry.
279    open_first: usize,
280    /// Entries submitted, and entries handed back (each counts on).
281    next: usize,
282    delivered: usize,
283    /// Ring bytes reserved, and freed (each counts on; the ring holds
284    /// head - tail).
285    head: usize,
286    tail: usize,
287    /// Short messages gathered, not yet handed to the pool.
288    open: Option<Task>,
289    /// A long message being handed back: its pieces' chaining values so
290    /// far (merged as the Hasher's stack merges), and how many.
291    stack: arrayvec::ArrayVec<CVBytes, { crate::MAX_DEPTH + 1 }>,
292    pieces: u64,
293    /// Invariant in 'env, as std::thread::Scope is: the messages' and the
294    /// places' lifetime.
295    env: PhantomData<&'env mut &'env ()>,
296}
297
298// Sound: the entries are reached by the program's thread through `&mut
299// self`, and by the pool's threads only for entries in tasks in flight,
300// across the Release/Acquire pair on each entry's `left`; the messages are
301// shared borrows and the places exclusive ones, alive for 'env, each place
302// written only by its task's thread before the Release and handed back
303// only after the Acquire.
304unsafe impl<D: Send> Send for Stream<'_, D> {}
305
306impl<'env, D: FnMut(&'env mut [Hash])> Stream<'env, D> {
307    /// Hash `message` where it is, its digest into `out`.
308    pub fn submit(&mut self, message: &'env [u8], out: &'env mut Hash) {
309        let out: *mut Hash = out;
310        let pieces = message.len().div_ceil(PIECE).max(1);
311        for i in 0..pieces {
312            let part = &message[i * PIECE..][..(message.len() - i * PIECE).min(PIECE)];
313            let at = self.head;
314            self.add_part(part.as_ptr(), part.len(), part_kind(i, pieces, out), at, out as *mut u8);
315        }
316    }
317
318    /// Hash a message of `len` bytes that `fill` writes in place, into the
319    /// stream's own bytes, in pieces of up to 64 KiB, its digest into
320    /// `out`: `fill(at, room)` writes the message's bytes from `at` into
321    /// all of `room` (a read from a file or socket straight into the
322    /// stream). Each `room` starts on a 64-byte boundary. The stream
323    /// reuses the bytes once `out` is handed back; a stream keeps 2 MiB of
324    /// them, and `submit_with` waits for room as `submit` does.
325    pub fn submit_with(&mut self, len: usize, mut fill: impl FnMut(usize, &mut [u8]), out: &'env mut Hash) {
326        let out: *mut Hash = out;
327        let pieces = len.div_ceil(PIECE).max(1);
328        for i in 0..pieces {
329            let part_len = (len - i * PIECE).min(PIECE);
330            let at = self.reserve(part_len);
331            // Sound: [at, at + part_len) is reserved and free (no entry in
332            // flight holds it), and `&mut self` keeps every other writer out.
333            let room = unsafe { core::slice::from_raw_parts_mut(self.s.ring_at(at), part_len) };
334            fill(i * PIECE, room);
335            let end = self.head;
336            self.add_part(room.as_ptr(), part_len, part_kind(i, pieces, out), end, out as *mut u8);
337        }
338    }
339
340    /// Hash the message `reader` yields, to its end, read straight into the
341    /// stream's own bytes in pieces of up to 64 KiB (a file or socket read
342    /// into the stream, overlapping the hashing of earlier pieces), its
343    /// digest into `out`: the message's length on success. On a read error
344    /// (other than an interruption, which reads again) the error returns,
345    /// `out` gets no digest and is not handed back, and the stream goes on
346    /// with the next message. The length need not be known: the stream
347    /// reads one piece ahead to tell the last.
348    ///
349    /// `size_hint` is the length the caller expects, where it knows one (a
350    /// file's size, a protocol's length field): a scheduling hint, never
351    /// part of the hash, so a wrong one costs nothing but speed. Today the
352    /// stream does not use it; it is here so that a stream that schedules
353    /// by it later changes no caller.
354    pub fn submit_reader(&mut self, mut reader: impl std::io::Read, size_hint: Option<u64>, out: &'env mut Hash) -> std::io::Result<u64> {
355        let _ = size_hint;
356        let out: *mut Hash = out;
357        let start = self.head;
358        let (mut at, mut end, mut n, mut read) = self.read_piece(&mut reader);
359        let mut i = 0;
360        loop {
361            let ahead = (n == PIECE && read.is_ok()).then(|| self.read_piece(&mut reader));
362            let failed = read.is_err() || ahead.as_ref().is_some_and(|a| a.3.is_err());
363            if failed {
364                let error = match (read, ahead) {
365                    (Err(e), _) => e,
366                    (_, Some((_, _, _, Err(e)))) => e,
367                    _ => unreachable!("a read failed"),
368                };
369                // The bytes not yet handed to the pool go; the pieces that
370                // were end in an entry that drops their chaining values.
371                self.head = if i == 0 { start } else { end - PIECE.div_ceil(64) * 64 };
372                if i > 0 {
373                    let head = self.head;
374                    self.add_entry(0, Kind::Abandoned, head);
375                }
376                return Err(error);
377            }
378            let last = match &ahead {
379                None => true,
380                // Exactly a piece, then the end: give the read-ahead room back.
381                Some((_, _, 0, _)) => {
382                    self.head = end;
383                    true
384                }
385                Some(_) => false,
386            };
387            let kind = if i == 0 && last { Kind::Message { out } } else { Kind::Piece { counter: (i * PIECE / CHUNK_LEN) as u64, last, out } };
388            // [at, at + n): this piece's reserved room, written.
389            let input = self.s.ring_at(at);
390            self.add_part(input, n, kind, end, out as *mut u8);
391            if last {
392                return Ok((i * PIECE + n) as u64);
393            }
394            (at, end, n, read) = ahead.unwrap();
395            i += 1;
396        }
397    }
398
399    /// A piece's room (PIECE bytes, reserved), read into from `reader` until
400    /// it is full, the reader ends, or a read fails: its offset, the ring's
401    /// head after its reservation, the bytes read, and the read's result.
402    fn read_piece(&mut self, reader: &mut impl std::io::Read) -> (usize, usize, usize, std::io::Result<()>) {
403        let at = self.reserve(PIECE);
404        let end = self.head;
405        // Sound: [at, at + PIECE) is reserved and free, and `&mut self`
406        // keeps every other writer out.
407        let room = unsafe { core::slice::from_raw_parts_mut(self.s.ring_at(at), PIECE) };
408        let mut n = 0;
409        while n < PIECE {
410            match reader.read(&mut room[n..]) {
411                Ok(0) => break,
412                Ok(k) => n += k,
413                Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {}
414                Err(e) => return (at, end, n, Err(e)),
415            }
416        }
417        (at, end, n, Ok(()))
418    }
419
420    /// Hash a batch of messages of `message_len` bytes where it is, laid
421    /// out as [`hash_many`](crate::hash_many) takes them (each in whole
422    /// blocks, zero after its bytes), a digest per message into `out`, in
423    /// the messages' order: `out` holds one per message. The batch goes in
424    /// parts of up to 64 KiB, each one entry; `out` comes back whole.
425    pub fn submit_batch(&mut self, batch: &'env [u8], message_len: usize, out: &'env mut [Hash]) {
426        let slot = crate::many::slot_len(message_len);
427        assert_eq!(Some(batch.len()), slot.checked_mul(out.len()), "a batch's digest space holds one digest per message");
428        crate::many::assert_slots(batch, message_len, out.len());
429        let whole = (out.as_mut_ptr(), out.len());
430        let per_part = (TASK_LEN / slot).max(1);
431        let parts = out.len().div_ceil(per_part).max(1);
432        for p in 0..parts {
433            let first = p * per_part;
434            let count = (out.len() - first).min(per_part);
435            let at = self.head;
436            let kind = Kind::Batch { message_len, whole: (p + 1 == parts).then_some(whole) };
437            // Sound: `first` is within the batch and its space (count may be 0).
438            let (input, output) = unsafe { (batch.as_ptr().add(first * slot), whole.0.add(first)) };
439            self.add_part(input, count * slot, kind, at, output as *mut u8);
440        }
441    }
442
443    /// Hash a batch of `out.len()` messages of `message_len` bytes that
444    /// `fill` writes in place, laid out as [`hash_many`](crate::hash_many)
445    /// takes them: `fill(at, room)` writes the batch's bytes from `at`
446    /// (padding included: each message in whole blocks, zero after its
447    /// bytes) into all of `room`, a part of up to 64 KiB at a time. As
448    /// [`submit_with`](Stream::submit_with) and [`submit_batch`](Stream::submit_batch).
449    pub fn submit_batch_with(&mut self, message_len: usize, mut fill: impl FnMut(usize, &mut [u8]), out: &'env mut [Hash]) {
450        let slot = crate::many::slot_len(message_len);
451        let whole = (out.as_mut_ptr(), out.len());
452        let per_part = (TASK_LEN / slot).max(1);
453        let parts = out.len().div_ceil(per_part).max(1);
454        for p in 0..parts {
455            let first = p * per_part;
456            let count = (out.len() - first).min(per_part);
457            let at = self.reserve(count * slot);
458            // Sound: as in submit_with.
459            let room = unsafe { core::slice::from_raw_parts_mut(self.s.ring_at(at), count * slot) };
460            fill(first * slot, room);
461            crate::many::assert_slots(room, message_len, count);
462            let end = self.head;
463            let kind = Kind::Batch { message_len, whole: (p + 1 == parts).then_some(whole) };
464            // Sound: `first` is within the batch's space.
465            let output = unsafe { whole.0.add(first) };
466            self.add_part(room.as_ptr(), room.len(), kind, end, output as *mut u8);
467        }
468    }
469
470    /// An entry of `kind`, `len` bytes at `input`, its digests at `out`,
471    /// its ring bytes reserved up to `ring_end`: a whole message, or a
472    /// batch's part, joins the open task (of its kind: one of another kind
473    /// is handed over first); a piece of a long message is a task of its
474    /// own, a whole subtree at its offset; an empty batch's one part is no
475    /// task (done as it is added).
476    fn add_part(&mut self, input: *const u8, len: usize, kind: Kind, ring_end: usize, out: *mut u8) {
477        let entry = self.add_entry(len, kind, ring_end);
478        let batch = match kind {
479            Kind::Message { .. } => None,
480            Kind::Abandoned => unreachable!("an abandoned message adds no part"),
481            Kind::Batch { .. } if len == 0 => return,
482            Kind::Batch { message_len, .. } => Some(message_len),
483            Kind::Piece { counter, .. } => {
484                self.s.lefts[entry].store(1, Ordering::Relaxed);
485                // Sound: the bytes stay in place until the entry is handed back.
486                let bytes = unsafe { core::slice::from_raw_parts(input, len) };
487                let task = Task {
488                    key: self.key,
489                    flags: self.flags,
490                    out: self.s.pieces_out[entry].0.0.get() as *mut u8,
491                    left: &*self.s.lefts[entry],
492                    ..Task::of(bytes, counter)
493                };
494                self.hand_over(task);
495                return;
496            }
497        };
498        if self.open.as_ref().is_some_and(|open| open.batch != batch) {
499            self.publish();
500        }
501        let (key, flags) = (self.key, self.flags);
502        let open = self.open.get_or_insert_with(|| Task::members(&key, flags, batch));
503        // The task's first member's `left` counts all of its members (set
504        // as the task is handed over).
505        if open.members == 0 {
506            self.open_first = entry;
507            open.left = &*self.s.lefts[entry];
508        }
509        self.s.entries[entry].first = self.open_first;
510        open.member[open.members] = Member { input, len, out };
511        open.members += 1;
512        open.len += len;
513        // Messages longer than a block hash one at a time: a task of them
514        // stops at a quarter of a task's bytes, to spread over more threads
515        // (spread over more threads); batches fill a task's bytes.
516        if open.members == MEMBERS || open.len >= if batch.is_some() { TASK_LEN } else { TASK_LEN / 4 } {
517            self.publish();
518        }
519    }
520
521    /// The ring offset of `len` free bytes at a 64-byte boundary (whole,
522    /// before the ring's end), reserved: waits, handing places back and
523    /// hashing tasks, until entries in flight free them.
524    fn reserve(&mut self, len: usize) -> usize {
525        let slot = len.div_ceil(64) * 64;
526        let mut at = self.head % RING_BYTES;
527        // A slot that would cross the end starts the next lap instead.
528        let skip = if at + slot > RING_BYTES { RING_BYTES - at } else { 0 };
529        while RING_BYTES - (self.head - self.tail) < skip + slot {
530            self.publish();
531            self.help();
532        }
533        if skip > 0 {
534            at = 0;
535        }
536        self.head += skip + slot;
537        at
538    }
539
540    /// Hand back every place whose digests are done, in order, without
541    /// waiting; and hand the open task to the pool, so what it holds
542    /// starts hashing.
543    pub fn collect(&mut self) {
544        self.publish();
545        // Nothing back: a worker for any task still waiting (the caller
546        // takes none here; in steady flow the workers are awake and this
547        // costs a comparison). Workers spin 5 us for a stream's next task
548        // and then park, and publish wakes one only for two waiting tasks,
549        // so without this a caller looping on collect waited forever where
550        // no SME2 thread takes stream tasks (tests/stream_collect_parked_workers).
551        if self.deliver() == 0 && self.slot.is_some() {
552            self.s.source.wake_for_waiting();
553        }
554    }
555
556    /// Hand back every place, in order, once its digests are done: this
557    /// thread hashes any task no thread has taken, and waits for the rest.
558    pub fn finish(&mut self) {
559        self.publish();
560        while self.delivered < self.next {
561            self.help();
562        }
563    }
564
565    /// Hand back what is done; with nothing done, hash a waiting task or
566    /// pause.
567    fn help(&mut self) {
568        if self.deliver() == 0 && !self.run_waiting() {
569            core::hint::spin_loop();
570        }
571    }
572
573    /// Hash one of the stream's tasks no thread has taken: whether there
574    /// was one.
575    fn run_waiting(&mut self) -> bool {
576        match self.s.source.take_task() {
577            Some(task) => {
578                crate::lanes::run_task(task);
579                true
580            }
581            None => false,
582        }
583    }
584
585    /// A new entry of `len` bytes, waiting for room when every entry is in
586    /// flight.
587    #[inline(always)]
588    fn add_entry(&mut self, len: usize, kind: Kind, ring_end: usize) -> usize {
589        if self.next - self.delivered == ENTRIES {
590            self.wait_for_room();
591        }
592        let index = self.next % ENTRIES;
593        self.s.entries[index] = Entry { len, kind, first: index, ring_end };
594        self.next += 1;
595        index
596    }
597
598    /// Hand back places, and hash tasks, until an entry is free (out of
599    /// line: a frame of its own, set up only when the stream is full).
600    #[cold]
601    #[inline(never)]
602    fn wait_for_room(&mut self) {
603        self.publish();
604        while self.next - self.delivered == ENTRIES {
605            self.help();
606        }
607    }
608
609    /// Hand the open task to the pool.
610    fn publish(&mut self) {
611        if let Some(task) = self.open.take() {
612            // Before the push, whose lock orders it before the threads' reads.
613            self.s.lefts[self.open_first].store(task.members, Ordering::Relaxed);
614            self.hand_over(task);
615        }
616    }
617
618    /// Give `task` to the workers through the stream's source; with no
619    /// slot, hash it on this thread now.
620    fn hand_over(&mut self, task: Task) {
621        match self.slot {
622            Some(_) => self.s.source.publish(task),
623            None => crate::lanes::run_task(task),
624        }
625    }
626
627    /// Hand back every finished entry's place from the oldest on (a
628    /// batch's once its last part is done); how many entries.
629    fn deliver(&mut self) -> usize {
630        let mut count = 0;
631        while self.delivered < self.next {
632            let index = self.delivered % ENTRIES;
633            let Entry { len, kind, first, ring_end } = self.s.entries[index];
634            // An entry of no task (an empty batch's part, an abandoned
635            // message) has its own `left`, zero.
636            if self.s.lefts[first].load(Ordering::Acquire) != 0 {
637                break;
638            }
639            self.delivered += 1;
640            self.tail = ring_end;
641            count += 1;
642            // Sound, each place: the program lent it for 'env and the stream
643            // hands it back once, after its task wrote it (the Acquire above).
644            match kind {
645                Kind::Message { out } => (self.done)(core::slice::from_mut(unsafe { &mut *out })),
646                Kind::Batch { whole: Some((out, n)), .. } => (self.done)(unsafe { core::slice::from_raw_parts_mut(out, n) }),
647                Kind::Batch { whole: None, .. } => {}
648                Kind::Abandoned => {
649                    self.stack.clear();
650                    self.pieces = 0;
651                }
652                Kind::Piece { counter, last, out } => {
653                    // Sound: the task wrote its out before counting `left` down.
654                    let piece = unsafe { *self.s.pieces_out[index].0.0.get() };
655                    let (key, flags, platform) = (self.key, self.flags, Platform::detect());
656                    let parent = |left: &CVBytes, right: &CVBytes| crate::hasher::parent_output(left, right, &key, flags, platform);
657                    let cv: CVBytes = if counter == 0 && len > CHUNK_LEN {
658                        parent(piece[..OUT_LEN].try_into().unwrap(), piece[OUT_LEN..].try_into().unwrap()).chaining_value()
659                    } else {
660                        piece[..OUT_LEN].try_into().unwrap()
661                    };
662                    // As the Hasher's stack: before pushing piece n, merge
663                    // down to as many values as n has 1 bits.
664                    while self.stack.len() > self.pieces.count_ones() as usize {
665                        let right = self.stack.pop().unwrap();
666                        let left = self.stack.pop().unwrap();
667                        self.stack.push(parent(&left, &right).chaining_value());
668                    }
669                    self.stack.push(cv);
670                    self.pieces += 1;
671                    if last {
672                        let mut right = self.stack.pop().unwrap();
673                        while self.stack.len() > 1 {
674                            right = parent(&self.stack.pop().unwrap(), &right).chaining_value();
675                        }
676                        let place = unsafe { &mut *out };
677                        *place = parent(&self.stack.pop().unwrap(), &right).root_hash();
678                        self.pieces = 0;
679                        (self.done)(core::slice::from_mut(place));
680                    }
681                }
682            }
683        }
684        count
685    }
686}
687
688impl<D> Drop for Stream<'_, D> {
689    /// Wait for every task still reading the messages and writing the
690    /// places (also when the body panicked), then keep the storage as the
691    /// thread's spare. A stream lives only inside [`stream`], which owns
692    /// it, so this runs before the scope ends.
693    fn drop(&mut self) {
694        if let Some(task) = self.open.take() {
695            // Handed over, so that the wait below ends: as publish.
696            self.s.lefts[self.open_first].store(task.members, Ordering::Relaxed);
697            match self.slot {
698                Some(_) => self.s.source.publish(task),
699                None => crate::lanes::run_task(task),
700            }
701        }
702        for i in self.delivered..self.next {
703            while self.s.lefts[self.s.entries[i % ENTRIES].first].load(Ordering::Acquire) != 0 {
704                let ran = match self.s.source.take_task() {
705                    Some(task) => {
706                        crate::lanes::run_task(task);
707                        true
708                    }
709                    None => false,
710                };
711                if !ran {
712                    core::hint::spin_loop();
713                }
714            }
715        }
716        if let Some(slot) = self.slot {
717            crate::lanes::sources::Source::unregister(slot);
718        }
719        crate::lanes::sources::closed();
720        // Sound: `s` is not used again; the next stream takes it whole.
721        let s: &'static mut Storage = unsafe { &mut *(self.s as *mut Storage) };
722        FREE.lock().unwrap().push(FreeStorage(s));
723    }
724}
725
726#[cfg(test)]
727mod test {
728    use super::*;
729
730    /// Places for `n` digests, each starting as zeros.
731    fn places(n: usize) -> std::vec::Vec<Hash> {
732        std::vec![Hash::from_bytes([0; 32]); n]
733    }
734
735    /// The next `n` places of `rest`, split off its front.
736    fn take<'a>(rest: &mut &'a mut [Hash], n: usize) -> &'a mut [Hash] {
737        let (now, after) = core::mem::take(rest).split_at_mut(n);
738        *rest = after;
739        now
740    }
741
742    /// Where each place handed back starts, and its length.
743    fn record(handed: &mut std::vec::Vec<(usize, usize)>) -> impl FnMut(&mut [Hash]) + '_ {
744        |d: &mut [Hash]| handed.push((d.as_ptr() as usize, d.len()))
745    }
746
747    /// Messages of every kind the stream handles (empty, short, one length
748    /// many times over, a whole piece, a piece and a byte, several pieces
749    /// and a partial one), borrowed and written in place, in each mode,
750    /// against hash_with, in order; far more entries and bytes than a
751    /// stream holds at once, so it fills, wraps its ring, and waits.
752    /// Keep: it detects, by hanging, mutants the clock then stops
753    /// (tools/mutants/timeouts-x86_64.txt: publish, take).
754    #[test]
755    fn test_stream_against_hash_with() {
756        let mut input = std::vec![0u8; (1 << 20) + 200];
757        crate::test::paint_test_input(&mut input);
758        let key = [5u8; 32];
759        let lens: std::vec::Vec<usize> =
760            (0..12000).map(|i| [0, 1, 63, 64, 64, 64, 100, 1024, 1024, 1500, 4096, PIECE, PIECE + 1, 200_000, (1 << 20) + 7, 64][i % 16]).collect();
761        let message = |i: usize| &input[i % 97..][..lens[i]];
762        for (mode, ways) in [(Mode::Hash, 0), (Mode::Keyed(&key), 1), (Mode::DeriveKey("stream test"), 2)] {
763            let mut got = places(lens.len());
764            let want_handed: std::vec::Vec<(usize, usize)> = got.iter().map(|h| (h as *const Hash as usize, 1)).collect();
765            let mut handed = std::vec::Vec::new();
766            stream(mode, record(&mut handed), |s| {
767                // With workers to hand tasks to, the stream has its slot
768                // among the pool's sources (a stream without one hashes
769                // every task on the caller: right, and slower).
770                assert_eq!(s.slot.is_some(), std::thread::available_parallelism().map_or(1, |n| n.get()) > 1);
771                for (i, out) in got.iter_mut().enumerate() {
772                    // Borrowed, written in place (in reads of up to 1000
773                    // bytes), or each in turn.
774                    if ways == 0 || (ways == 2 && i % 3 == 0) {
775                        s.submit(message(i), out);
776                    } else {
777                        s.submit_with(
778                            lens[i],
779                            |at, room| {
780                                for (j, part) in room.chunks_mut(1000).enumerate() {
781                                    part.copy_from_slice(&message(i)[at + j * 1000..][..part.len()]);
782                                }
783                            },
784                            out,
785                        );
786                    }
787                    if i % 500 == 499 {
788                        s.collect();
789                    }
790                }
791                s.finish();
792            });
793            assert!(handed == want_handed, "every place handed back once, in order");
794            for i in 0..lens.len() {
795                assert_eq!(got[i], crate::hash_with(mode, message(i)), "message {i}, {} bytes", lens[i]);
796            }
797        }
798    }
799
800    /// A body that ends without finishing panics (after the threads are
801    /// done with its messages), and so does one that panics itself.
802    #[test]
803    #[cfg_attr(not(panic = "unwind"), ignore = "catches panics, which this target aborts on")]
804    fn test_stream_panics() {
805        let input = std::vec![9u8; 300_000];
806        for unwinding in [false, true] {
807            let result = std::panic::catch_unwind(|| {
808                let mut got = places(4);
809                stream(
810                    Mode::Hash,
811                    |_: &mut [Hash]| {},
812                    |s| {
813                        for (len, out) in [10, 100_000, 64, 300_000].into_iter().zip(&mut got) {
814                            s.submit(&input[..len], out);
815                        }
816                        assert!(!unwinding, "the body panics");
817                    },
818                )
819            });
820            assert!(result.is_err());
821        }
822    }
823
824    /// A reader that yields `data` in reads of varying sizes (1 byte to
825    /// 9000), interrupted now and then, failing after `fail_at` bytes.
826    struct Uneven<'a> {
827        data: &'a [u8],
828        at: usize,
829        calls: usize,
830        fail_at: Option<usize>,
831    }
832
833    impl std::io::Read for Uneven<'_> {
834        fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
835            self.calls += 1;
836            if self.calls % 7 == 0 {
837                return Err(std::io::ErrorKind::Interrupted.into());
838            }
839            if self.fail_at.is_some_and(|f| self.at >= f) {
840                return Err(std::io::Error::other("the reader fails"));
841            }
842            let limit = self.fail_at.unwrap_or(self.data.len()).min(self.data.len());
843            let n = buf.len().min([1, 9000, 333, 65536, 4096][self.calls % 5]).min(limit - self.at);
844            buf[..n].copy_from_slice(&self.data[self.at..][..n]);
845            self.at += n;
846            Ok(n)
847        }
848    }
849
850    /// A few messages through a stream, small enough for Miri: the handover
851    /// of entries to the pool's threads and back, checked for data races.
852    /// Keep: it detects, by hanging, mutants the clock then stops
853    /// (tools/mutants/timeouts-x86_64.txt: Task::run, run_task).
854    #[test]
855    fn test_miri_stream() {
856        let input = std::vec![3u8; 3 * PIECE + 5];
857        let lens = [0, 64, 64, 1000, PIECE + 1, 3 * PIECE + 5];
858        let mut got = places(2 * lens.len() + 2 + 7);
859        let mut handed = std::vec::Vec::new();
860        let mut rest: &mut [Hash] = &mut got;
861        stream(Mode::Hash, record(&mut handed), |s| {
862            for len in lens {
863                s.submit(&input[..len], &mut take(&mut rest, 1)[0]);
864                s.submit_with(
865                    len,
866                    |at, room| {
867                        assert_eq!(room.as_ptr() as usize % 64, 0, "a room starts a cache line");
868                        room.copy_from_slice(&input[at..][..room.len()])
869                    },
870                    &mut take(&mut rest, 1)[0],
871                );
872            }
873            assert_eq!(s.submit_reader(&input[..2 * PIECE + 1], None, &mut take(&mut rest, 1)[0]).unwrap(), 2 * PIECE as u64 + 1);
874            assert!(s.submit_reader(Uneven { data: &input, at: 0, calls: 0, fail_at: Some(PIECE + 1) }, None, &mut take(&mut rest, 1)[0]).is_err());
875            s.submit_batch(&input[..4 * 64], 64, take(&mut rest, 4));
876            s.submit_batch_with(
877                64,
878                |at, room| {
879                    assert_eq!(room.as_ptr() as usize % 64, 0, "a room starts a cache line");
880                    room.copy_from_slice(&input[at..][..room.len()])
881                },
882                take(&mut rest, 3),
883            );
884            s.finish();
885        });
886        let mut want: std::vec::Vec<Hash> = lens.iter().flat_map(|&l| [crate::hash(&input[..l]); 2]).collect();
887        want.push(crate::hash(&input[..2 * PIECE + 1]));
888        want.push(Hash::from_bytes([0; 32]));
889        want.extend([crate::hash(&input[..64]); 7]);
890        assert_eq!(got, want);
891        assert_eq!(handed.len(), 2 * lens.len() + 1 + 2, "a place back per submission, none for the failed reader");
892    }
893}