#![warn(clippy::pedantic)]
#![no_std]
mod guard;
use core::mem::{self, MaybeUninit};
use alloc::boxed::Box;
use crate::guard::Guard;
extern crate alloc;
#[must_use]
pub fn new_copied<T, const N: usize>(initial: &T) -> Box<[T; N]>
where
T: Copy,
{
let mut array = uninit();
for v in &mut *array {
v.write(*initial);
}
unsafe { assume_init(array) }
}
#[must_use]
pub fn new_cloned<T, const N: usize>(initial: &T) -> Box<[T; N]>
where
T: Clone,
{
let mut array = uninit();
let mut guard = Guard::new(&mut array);
for _ in 0..N {
unsafe { guard.push_unchecked(initial.clone()) };
}
unsafe { guard.finalize() };
unsafe { assume_init(array) }
}
#[must_use]
pub fn new_default<T, const N: usize>() -> Box<[T; N]>
where
T: Default,
{
let mut array = uninit();
let mut guard = Guard::new(&mut array);
for _ in 0..N {
unsafe { guard.push_unchecked(T::default()) };
}
unsafe { guard.finalize() };
unsafe { assume_init(array) }
}
#[must_use]
pub fn from_fn<T, const N: usize, F>(mut f: F) -> Box<[T; N]>
where
F: FnMut(usize) -> T,
{
let mut array = uninit();
let mut guard = Guard::new(&mut array);
for i in 0..N {
unsafe { guard.push_unchecked(f(i)) };
}
unsafe { guard.finalize() };
unsafe { assume_init(array) }
}
#[allow(clippy::missing_errors_doc)]
pub fn try_from_fn<T, E, const N: usize, F>(mut f: F) -> Result<Box<[T; N]>, E>
where
F: FnMut(usize) -> Result<T, E>,
{
let mut array = uninit();
let mut guard = Guard::new(&mut array);
for i in 0..N {
let v = f(i)?;
unsafe { guard.push_unchecked(v) };
}
unsafe { guard.finalize() };
unsafe { Ok(assume_init(array)) }
}
#[must_use]
pub fn uninit<T, const N: usize>() -> Box<[MaybeUninit<T>; N]> {
unsafe { Box::new_uninit().assume_init() }
}
#[allow(clippy::unnecessary_box_returns)]
#[must_use]
pub unsafe fn assume_init<T, const N: usize>(arr: Box<[MaybeUninit<T>; N]>) -> Box<[T; N]> {
unsafe { mem::transmute(arr) }
}
#[cfg(test)]
mod tests {
use core::sync::atomic::{AtomicUsize, Ordering};
#[test]
#[cfg(miri)]
fn new_cloned() {
const LEN: usize = 1024;
let arr = super::new_cloned::<Option<bool>, LEN>(&Some(true));
assert_eq!(arr.len(), LEN);
assert_eq!(arr[LEN - 1], Some(true));
}
#[test]
#[cfg(not(miri))]
fn new_cloned() {
const LEN: usize = 1024 * 1024 * 1024;
let arr = super::new_copied::<Option<bool>, LEN>(&Some(true));
assert_eq!(arr.len(), LEN);
assert_eq!(arr[LEN - 1], Some(true));
}
#[test]
fn default() {
let arr = super::new_default::<Option<bool>, 1000>();
assert_eq!(arr.len(), 1000);
assert_eq!(arr[999], None);
}
#[test]
fn from_fn() {
let arr = super::from_fn::<u64, 1000, _>(|i| i as u64);
assert_eq!(arr.len(), 1000);
assert_eq!(arr[999], 999);
}
#[test]
fn try_from_fn_partial_drop_cleanup() {
struct DropCounter {
counter: &'static AtomicUsize,
}
impl Drop for DropCounter {
fn drop(&mut self) {
self.counter.fetch_add(1, Ordering::SeqCst);
}
}
static DROPS: AtomicUsize = AtomicUsize::new(0);
let result = crate::try_from_fn::<_, _, 1024, _>(|i| {
if i < 2 {
Ok(DropCounter { counter: &DROPS })
} else {
Err(())
}
});
assert!(result.is_err());
assert_eq!(DROPS.load(Ordering::SeqCst), 2);
}
#[test]
fn uninit() {
const LEN: usize = 1024 * 1024 * 1024; let arr = super::uninit::<u64, LEN>();
core::hint::black_box(arr);
}
}