#[cfg(feature = "rayon")]
use rayon::prelude::*;
const MIN_PARALLEL_BYTES: usize = 32 << 10;
pub fn map<T, R, S, F>(items: &[T], size: S, f: F) -> Vec<R>
where
T: Sync,
R: Send,
S: Fn(&T) -> usize,
F: Fn(&T) -> R + Send + Sync,
{
#[cfg(feature = "rayon")]
{
let total: usize = items.iter().map(&size).sum();
if total >= MIN_PARALLEL_BYTES {
return items.par_iter().map(f).collect();
}
}
#[cfg(not(feature = "rayon"))]
let _ = size;
items.iter().map(f).collect()
}
pub fn try_map<T, R, E, S, F>(items: &[T], size: S, f: F) -> Result<Vec<R>, E>
where
T: Sync,
R: Send,
E: Send,
S: Fn(&T) -> usize,
F: Fn(&T) -> Result<R, E> + Send + Sync,
{
#[cfg(feature = "rayon")]
{
let total: usize = items.iter().map(&size).sum();
if total >= MIN_PARALLEL_BYTES {
return items.par_iter().map(f).collect();
}
}
#[cfg(not(feature = "rayon"))]
let _ = size;
items.iter().map(f).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_and_large_batches_agree_with_a_serial_map() {
for size in [0usize, 1, 10, 500, 5_000] {
let items: Vec<String> = (0..size).map(|i| format!("item number {i}")).collect();
let got = map(&items, String::len, |s| s.len() * 2);
let want: Vec<usize> = items.iter().map(|s| s.len() * 2).collect();
assert_eq!(got, want, "batch of {size} disagrees with a serial map");
}
}
#[test]
fn results_stay_in_input_order_past_the_parallel_threshold() {
let items: Vec<String> = (0..4_000).map(|i| format!("{i:0>64}")).collect();
let total: usize = items.iter().map(String::len).sum();
assert!(
total > MIN_PARALLEL_BYTES,
"this test is only meaningful above the threshold"
);
let got = map(&items, String::len, |s| s.clone());
assert_eq!(got, items);
}
#[test]
fn try_map_short_circuits_and_reports_the_error() {
let items: Vec<String> = (0..4_000).map(|i| format!("{i:0>64}")).collect();
let out: Result<Vec<usize>, &str> = try_map(&items, String::len, |s| {
if s.ends_with("999") {
Err("boom")
} else {
Ok(s.len())
}
});
assert_eq!(out, Err("boom"));
}
#[test]
fn try_map_passes_a_clean_batch_through() {
let items: Vec<String> = (0..100).map(|i| format!("text {i}")).collect();
let out: Result<Vec<usize>, ()> = try_map(&items, String::len, |s| Ok(s.len()));
assert_eq!(
out.unwrap(),
items.iter().map(String::len).collect::<Vec<_>>()
);
}
}