use crate::{MemReplicaError, Slice, is, unwrap, whilst};
#[doc = crate::_tags!(mem lifetime)]
#[doc = crate::_doc_meta!{
location("sys/mem/view"),
#[cfg(target_pointer_width = "32")]
test_size_of(__: MemReplicaSlice<'_, [u8; 1024], 2> = 32|256),
#[cfg(target_pointer_width = "64")]
test_size_of(__: MemReplicaSlice<'_, [u8; 1024], 2> = 64|512),
}]
#[derive(Debug, PartialEq)]
pub struct MemReplicaSlice<'a, T, const N: usize> {
storage: &'a mut [T],
channel_offset_bytes: usize,
num_channels: usize,
len: usize,
capacity: usize,
elems_per_chunk: usize,
stride_elems: usize,
}
impl<'a, T, const N: usize> MemReplicaSlice<'a, T, N> {
pub const fn new(
storage: &'a mut [T],
channel_offset_bytes: usize,
num_channels: usize,
) -> Result<Self, MemReplicaError> {
use MemReplicaError as E;
is! { num_channels == 0, return Err(E::ZeroChannels) }
is! { N == 0, return Err(E::ZeroReplicas) }
is! { N > num_channels, return Err(E::TooManyReplicas) }
let elem_size = size_of::<T>();
is! { elem_size == 0, return Err(E::ZeroSizedType) }
is! { channel_offset_bytes < elem_size, return Err(E::OffsetTooSmall) }
is! { !channel_offset_bytes.is_multiple_of(elem_size), return Err(E::MisalignedOffset) }
let elems_per_chunk = channel_offset_bytes / elem_size;
let stride_elems = num_channels * elems_per_chunk;
is! { stride_elems == 0, return Err(E::StorageTooSmall) }
let full_strides = storage.len() / stride_elems;
let capacity = full_strides * elems_per_chunk;
is! { capacity == 0, return Err(E::StorageTooSmall) }
Ok(Self {
storage,
channel_offset_bytes,
num_channels,
len: 0,
capacity,
elems_per_chunk,
stride_elems,
})
}
pub const fn channel_offset_bytes(self) -> usize {
self.channel_offset_bytes
}
pub const fn num_channels(self) -> usize {
self.num_channels
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub const fn capacity(&self) -> usize {
self.capacity
}
pub const fn set_len(&mut self, new_len: usize) -> Result<(), MemReplicaError> {
is! { new_len > self.capacity, return Err(MemReplicaError::OutOfBounds) }
self.len = new_len;
Ok(())
}
pub const fn get(&self, logical_index: usize) -> Option<&T> {
self.get_replica(0, logical_index)
}
pub const fn get_replica(&self, replica: usize, logical_index: usize) -> Option<&T> {
is! { replica >= N || logical_index >= self.len, return None }
let idx = unwrap![some? self.physical_index(replica, logical_index)];
Slice::get(self.storage, idx)
}
pub const fn get_replica_mut(
&mut self,
replica: usize,
logical_index: usize,
) -> Option<&mut T> {
is! { replica >= N || logical_index >= self.len, return None }
let idx = unwrap![some? self.physical_index(replica, logical_index)];
Slice::get_mut(self.storage, idx)
}
pub const fn replicas(&self, logical_index: usize) -> Option<[&T; N]> {
is! { logical_index >= self.len, return None }
let first = unwrap![some? self.get_replica(0, logical_index)];
let mut out = [first; N];
whilst! { replica in 1..N; {
out[replica] = unwrap![some? self.get_replica(replica, logical_index)];
}}
Some(out)
}
pub const fn physical_index(&self, replica: usize, logical_index: usize) -> Option<usize> {
is! { replica >= N || logical_index >= self.capacity, return None }
let chunk_idx = logical_index / self.elems_per_chunk;
let offset_in_chunk = logical_index % self.elems_per_chunk;
Some(chunk_idx * self.stride_elems + replica * self.elems_per_chunk + offset_in_chunk)
}
}
impl<'a, T: Copy, const N: usize> MemReplicaSlice<'a, T, N> {
pub const fn insert(&mut self, value: T) -> Result<(), MemReplicaError> {
is! { self.len >= self.capacity, return Err(MemReplicaError::Full) }
unwrap![ok? self.set(self.len, value)];
self.len += 1;
Ok(())
}
pub const fn set(&mut self, logical_index: usize, value: T) -> Result<(), MemReplicaError> {
is! { logical_index >= self.capacity, return Err(MemReplicaError::OutOfBounds) }
whilst! { replica in 0..N; {
let idx = unwrap![some_ok_or?
self.physical_index(replica, logical_index),
MemReplicaError::OutOfBounds];
self.storage[idx] = value;
}}
Ok(())
}
}