pub unsafe trait Copyable: Copy {}
pub use zerocopy::IntoBytes;
unsafe impl<T: Copy + IntoBytes> Copyable for T {}
use crate::loom::sync::atomic::{AtomicUsize, Ordering, Ordering::Relaxed, fence};
use std::marker::PhantomData;
use std::mem::{MaybeUninit, size_of};
pub struct SlotItem<T> {
cell: AtomicUsize,
_t: PhantomData<T>,
}
pub type Slot<T> = [SlotItem<T>];
fn allocate<T>(n_items: usize) -> Box<[SlotItem<T>]> {
std::iter::repeat_with(|| SlotItem {
cell: AtomicUsize::new(0),
_t: PhantomData,
})
.take(n_items)
.collect::<Vec<_>>()
.into_boxed_slice()
}
const fn required_items<T>() -> usize {
size_of::<T>().div_ceil(size_of::<usize>())
}
pub struct AtomicSlotable;
use super::Slotable;
impl<T: Copyable> Slotable<T> for AtomicSlotable {
type Slot = Slot<T>;
type SlotArrayItem = SlotItem<T>;
fn boxed_uninit_single() -> Box<Self::Slot> {
allocate::<T>(required_items::<T>())
}
fn read(slot: &Self::Slot, ordering: Ordering) -> std::mem::MaybeUninit<T> {
let n = size_of::<T>() / size_of::<usize>();
let mut value = MaybeUninit::<T>::uninit();
let tgt = value.as_mut_ptr() as *mut usize;
for (i, src) in slot[0..n].iter().enumerate() {
let x = src.cell.load(Relaxed);
unsafe {
tgt.add(i).write_unaligned(x);
}
}
let remain = size_of::<T>() % size_of::<usize>();
if remain != 0 {
let part = slot[n].cell.load(Relaxed).to_ne_bytes();
unsafe {
tgt.add(n)
.cast::<u8>()
.copy_from_nonoverlapping(part.as_ptr(), remain);
}
}
if ordering != Relaxed {
fence(ordering);
}
value
}
fn write(slot: &Self::Slot, value: T, ordering: Ordering) {
if ordering != Relaxed {
fence(ordering);
}
let n = size_of::<T>() / size_of::<usize>();
let src = &value as *const T as *const usize;
for (i, tgt) in slot[0..n].iter().enumerate() {
let x = unsafe { src.add(i).read_unaligned() };
tgt.cell.store(x, Relaxed);
}
let remain = size_of::<T>() % size_of::<usize>();
if remain != 0 {
let mut part = [0u8; size_of::<usize>()];
unsafe {
part.as_mut_ptr()
.copy_from_nonoverlapping(src.add(n) as *const u8, remain);
}
slot[n].cell.store(usize::from_ne_bytes(part), Relaxed);
}
}
fn boxed_uninit_multiple(n: usize) -> Box<[Self::SlotArrayItem]> {
let req = required_items::<T>() * n;
allocate(req)
}
fn index_in_array(items: &[Self::SlotArrayItem], index: usize) -> &Self::Slot {
let n = required_items::<T>();
&items[index * n..(index + 1) * n]
}
}
crate::impl_channels!(crate::atomic::AtomicSlotable, crate::atomic::Copyable);
#[cfg(test)]
mod tests {
use super::*;
fn test_rw<T: Copyable + PartialEq + std::fmt::Debug>(value: T) {
let slot = AtomicSlotable::boxed_uninit_single();
AtomicSlotable::write(&slot, value, Ordering::Relaxed);
let readback: T = unsafe { AtomicSlotable::read(&slot, Ordering::Relaxed).assume_init() };
assert_eq!(readback, value);
}
#[test]
fn read_write_8() {
#[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
struct V(u64, u64, u32, u32);
test_rw(V(1, 2, 3, 4));
}
#[test]
fn read_write_4() {
#[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
struct V(u32, u32, u16, u16);
test_rw(V(1, 2, 3, 4));
}
#[test]
fn read_write_1() {
#[derive(Clone, Copy, PartialEq, Debug, IntoBytes)]
struct V(u8, u8);
test_rw(V(1, 2));
}
}