#[cfg(threads)]
use rayon::prelude::*;
#[cfg(all(threads, not(target_family = "wasm")))]
struct Pool;
#[cfg(all(threads, not(target_family = "wasm")))]
impl Pool {
fn install<R: Send>(&self, f: impl FnOnce() -> R + Send) -> R {
f()
}
fn threads(&self) -> usize {
rayon::current_num_threads()
}
}
#[cfg(all(threads, target_family = "wasm"))]
struct Pool(&'static rayon::ThreadPool);
#[cfg(all(threads, target_family = "wasm"))]
impl Pool {
fn install<R: Send>(&self, f: impl FnOnce() -> R + Send) -> R {
self.0.install(f)
}
fn threads(&self) -> usize {
self.0.current_num_threads()
}
}
#[cfg(all(threads, target_family = "wasm"))]
static POOL: std::sync::OnceLock<rayon::ThreadPool> = std::sync::OnceLock::new();
#[cfg(all(threads, target_family = "wasm"))]
pub fn set_thread_pool(pool: rayon::ThreadPool) -> Result<(), rayon::ThreadPool> {
POOL.set(pool)
}
#[cfg(threads)]
fn pool_for(coarse: bool) -> Option<Pool> {
#[cfg(not(target_family = "wasm"))]
{
let _ = coarse;
Some(Pool)
}
#[cfg(target_family = "wasm")]
POOL.get().filter(|_| coarse).map(Pool)
}
pub(crate) fn threads() -> usize {
#[cfg(threads)]
if let Some(p) = pool_for(true) {
return p.threads();
}
1
}
fn map_range_on<const COARSE: bool, R: Send>(n: usize, f: impl Fn(usize) -> R + Sync + Send) -> Vec<R> {
#[cfg(threads)]
if let Some(p) = pool_for(COARSE) {
return p.install(|| (0..n).into_par_iter().map(f).collect());
}
(0..n).map(f).collect()
}
#[cfg_attr(not(all(threads, target_family = "wasm")), allow(dead_code))]
pub(crate) fn map_init<T: Sync, S, R: Send>(
items: &[T],
init: impl Fn() -> S + Sync + Send,
f: impl Fn(&mut S, &T) -> R + Sync + Send,
) -> Vec<R> {
#[cfg(threads)]
if let Some(p) = pool_for(true) {
return p.install(|| items.par_iter().with_min_len(2).map_init(init, |s, t| f(s, t)).collect());
}
let mut s = init();
items.iter().map(|t| f(&mut s, t)).collect()
}
fn map_mut_on<const COARSE: bool, T: Send, R: Send>(items: &mut [T], f: impl Fn(&mut T) -> R + Sync + Send) -> Vec<R> {
#[cfg(threads)]
if let Some(p) = pool_for(COARSE) {
return p.install(|| items.par_iter_mut().map(f).collect());
}
items.iter_mut().map(f).collect()
}
fn for_each_owned_on<const COARSE: bool, T: Send>(items: Vec<T>, f: impl Fn(usize, T) + Sync + Send) {
#[cfg(threads)]
if let Some(p) = pool_for(COARSE) {
return p.install(|| items.into_par_iter().enumerate().for_each(|(i, t)| f(i, t)));
}
items.into_iter().enumerate().for_each(|(i, t)| f(i, t));
}
fn chunks_mut_on<const COARSE: bool, T: Send>(data: &mut [T], size: usize, f: impl Fn(usize, &mut [T]) + Sync + Send) {
let size = size.max(1);
#[cfg(threads)]
if let Some(p) = pool_for(COARSE) {
return p.install(|| data.par_chunks_mut(size).enumerate().for_each(|(i, c)| f(i, c)));
}
data.chunks_mut(size).enumerate().for_each(|(i, c)| f(i, c));
}
pub(crate) fn map_range<R: Send>(n: usize, f: impl Fn(usize) -> R + Sync + Send) -> Vec<R> {
map_range_on::<false, R>(n, f)
}
pub(crate) fn map_range_coarse<R: Send>(n: usize, f: impl Fn(usize) -> R + Sync + Send) -> Vec<R> {
map_range_on::<true, R>(n, f)
}
#[allow(dead_code)]
pub(crate) fn map_mut<T: Send, R: Send>(items: &mut [T], f: impl Fn(&mut T) -> R + Sync + Send) -> Vec<R> {
map_mut_on::<false, T, R>(items, f)
}
pub(crate) fn map_mut_coarse<T: Send, R: Send>(items: &mut [T], f: impl Fn(&mut T) -> R + Sync + Send) -> Vec<R> {
map_mut_on::<true, T, R>(items, f)
}
pub(crate) fn for_each_owned<T: Send>(items: Vec<T>, f: impl Fn(usize, T) + Sync + Send) {
for_each_owned_on::<false, T>(items, f)
}
pub(crate) fn for_each_owned_coarse<T: Send>(items: Vec<T>, f: impl Fn(usize, T) + Sync + Send) {
for_each_owned_on::<true, T>(items, f)
}
pub(crate) fn chunks_mut<T: Send>(data: &mut [T], size: usize, f: impl Fn(usize, &mut [T]) + Sync + Send) {
chunks_mut_on::<false, T>(data, size, f)
}
pub(crate) fn chunks_mut_coarse<T: Send>(data: &mut [T], size: usize, f: impl Fn(usize, &mut [T]) + Sync + Send) {
chunks_mut_on::<true, T>(data, size, f)
}
pub(crate) fn chunk_len(n: usize, min: usize) -> usize {
n.div_ceil(threads() * 4).max(min).max(1)
}
pub(crate) fn read_file(path: &std::path::Path) -> std::io::Result<Vec<u8>> {
#[cfg(all(unix, not(target_family = "wasm")))]
{
use std::os::unix::fs::FileExt;
const PART: usize = 1 << 21;
let file = std::fs::File::open(path)?;
let len = file.metadata()?.len() as usize;
if len >= 4 * PART && threads() > 1 {
let mut buf = vec![0u8; len];
let parts: Vec<&mut [u8]> = buf.chunks_mut(PART).collect();
let failed = std::sync::atomic::AtomicBool::new(false);
for_each_owned(parts, |i, part| {
if file.read_exact_at(part, (i * PART) as u64).is_err() {
failed.store(true, std::sync::atomic::Ordering::Relaxed);
}
});
if !failed.into_inner() && file.metadata()?.len() as usize == len {
return Ok(buf);
}
}
}
std::fs::read(path)
}