#![no_std]
use core::mem::MaybeUninit;
mod util;
pub struct Cursor<'a, T, const N: usize> {
slice: &'a mut [MaybeUninit<T>; N],
}
impl<'a, T, const N: usize> Cursor<'a, T, N> {
pub fn new(slice: &'a mut [MaybeUninit<T>; N]) -> Self {
Self { slice }
}
fn write_impl(&mut self, value: [T; N]) {
*self.slice = value.map(|v| MaybeUninit::new(v));
}
pub fn finish(mut self, value: [T; N]) {
self.write_impl(value);
core::mem::forget(self);
}
pub fn write<const L: usize, const R: usize>(self, value: [T; L]) -> Cursor<'a, T, R> {
let (l, r) = self.split::<L, R>();
l.finish(value);
r
}
fn into_buf(self) -> &'a mut [MaybeUninit<T>; N] {
unsafe { core::mem::transmute(self) }
}
pub fn split<const L: usize, const R: usize>(self) -> (Cursor<'a, T, L>, Cursor<'a, T, R>) {
let buf = self.into_buf();
let (l, r) = crate::util::split_mut::<_, N, L, R>(buf);
(Cursor { slice: l }, Cursor { slice: r })
}
pub fn assert_size<const M: usize>(self) -> Cursor<'a, T, M> {
let (l, _) = self.split::<M, 0>();
l
}
}
impl<T, const N: usize> Drop for Cursor<'_, T, N> {
fn drop(&mut self) {
if N > 0 {
panic!("Cursor still has uninitialized bytes");
}
}
}
#[cfg(test)]
mod tests {
use core::sync::atomic::AtomicU8;
use super::*;
#[test]
fn test_drop() {
struct DropCounter<'a>(&'a AtomicU8);
impl core::ops::Drop for DropCounter<'_> {
fn drop(&mut self) {
self.0.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
}
}
let value = AtomicU8::new(0);
{
let mut data: [MaybeUninit<DropCounter<'_>>; 1] = [MaybeUninit::uninit()];
Cursor::new(&mut data).finish([DropCounter(&value)]);
}
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 0);
let value = AtomicU8::new(0);
{
let mut data: [MaybeUninit<DropCounter<'_>>; 2] =
[MaybeUninit::uninit(), MaybeUninit::uninit()];
Cursor::new(&mut data).finish([DropCounter(&value), DropCounter(&value)]);
}
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 0);
let value = AtomicU8::new(0);
{
let mut data: [MaybeUninit<DropCounter<'_>>; 1] = [MaybeUninit::uninit()];
Cursor::new(&mut data).finish([DropCounter(&value)]);
let [value] = data;
unsafe { value.assume_init() };
}
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 1);
let value = AtomicU8::new(0);
{
let mut data: [MaybeUninit<DropCounter<'_>>; 2] =
[MaybeUninit::uninit(), MaybeUninit::uninit()];
Cursor::new(&mut data).finish([DropCounter(&value), DropCounter(&value)]);
let [value0, value1] = data;
unsafe { value0.assume_init() };
unsafe { value1.assume_init() };
}
assert_eq!(value.load(core::sync::atomic::Ordering::SeqCst), 2);
}
#[test]
fn test_initalized() {
let mut data: [MaybeUninit<u8>; 4] = [MaybeUninit::new(0); 4];
Cursor::new(&mut data).write([1, 2]).finish([3, 4]);
assert_eq!(data.map(|d| unsafe { d.assume_init() }), [1, 2, 3, 4]);
}
}