microscope_memory/parallel.rs
1//! Parallel iteration where the target has threads, sequential where it does not.
2//!
3//! `rayon` is a native-only dependency: Cargo.toml already keeps it under
4//! `cfg(not(target_arch = "wasm32"))`. The browser build compiles for wasm32,
5//! where there is no thread pool, so the fifteen `par_iter` and `into_par_iter`
6//! call sites resolve to the sequential stand-in below instead.
7//!
8//! Everything downstream of those two calls is unchanged: the chains are `map`,
9//! `filter`, `enumerate` and `collect`, all of which behave identically on a
10//! `std` iterator. The wasm32 build therefore computes the same answer, serially.
11//! The hash folds inside the `map` closures were already plain iterator folds,
12//! so nothing about the results changes.
13
14#[cfg(not(target_arch = "wasm32"))]
15pub use rayon::prelude::*;
16
17/// wasm32 stand-in for the two rayon entry points. Not a general adapter: only
18/// the receivers this crate actually uses are implemented.
19///
20/// The two methods yield different things -- `par_iter` borrows, `into_par_iter`
21/// owns -- so they have separate iterator types and no shared item type. The
22/// lifetime is on the trait because `par_iter` hands back a borrowing iterator,
23/// which an associated type cannot express with `'_`.
24#[cfg(target_arch = "wasm32")]
25pub trait SequentialParallel<'a> {
26 type Iter: Iterator + 'a;
27 type IntoIter: Iterator;
28
29 fn par_iter(&'a self) -> Self::Iter;
30 fn into_par_iter(self) -> Self::IntoIter;
31}
32
33#[cfg(target_arch = "wasm32")]
34impl<'a, T: 'a> SequentialParallel<'a> for Vec<T> {
35 type Iter = std::slice::Iter<'a, T>;
36 type IntoIter = std::vec::IntoIter<T>;
37
38 fn par_iter(&'a self) -> Self::Iter {
39 self.iter()
40 }
41 fn into_par_iter(self) -> Self::IntoIter {
42 self.into_iter()
43 }
44}
45
46// `Clone` only here, because copying a slice into an iterator needs it; nothing
47// on the Vec path does.
48#[cfg(target_arch = "wasm32")]
49impl<'a, T: Clone + 'a> SequentialParallel<'a> for &'a [T] {
50 type Iter = std::slice::Iter<'a, T>;
51 type IntoIter = std::vec::IntoIter<T>;
52
53 fn par_iter(&'a self) -> Self::Iter {
54 self.iter()
55 }
56 fn into_par_iter(self) -> Self::IntoIter {
57 self.to_vec().into_iter()
58 }
59}
60
61#[cfg(target_arch = "wasm32")]
62impl SequentialParallel<'static> for std::ops::Range<usize> {
63 type Iter = std::ops::Range<usize>;
64 type IntoIter = std::ops::Range<usize>;
65
66 fn par_iter(&'static self) -> Self::Iter {
67 self.clone()
68 }
69 fn into_par_iter(self) -> Self::IntoIter {
70 self
71 }
72}