Skip to main content

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}