use core::alloc::Layout;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use iceoryx2_bb_concurrency::atomic::{AtomicBool, AtomicU8, Ordering};
use iceoryx2_bb_elementary::relocatable_pointer::{Pointer, RelocatablePointer};
use iceoryx2_bb_elementary::static_assert_size_of;
use iceoryx2_bb_elementary_traits::allocator::{Allocate, AllocationError};
use iceoryx2_bb_elementary_traits::{atomic_copy::AtomicCopy, zero_copy_send::ZeroCopySend};
use iceoryx2_log::fail;
use iceoryx2_log::fatal_panic;
#[repr(transparent)]
pub struct MaybeTorn<T> {
inner: MaybeUninit<T>,
}
impl<T> MaybeTorn<T> {
pub fn new(inner: MaybeUninit<T>) -> Self {
Self { inner }
}
pub unsafe fn assume_consistent(self) -> T {
unsafe { self.inner.assume_init() }
}
}
pub struct RelocatableByteAtomic<T: AtomicCopy> {
data_ptr: RelocatablePointer<AtomicU8>,
capacity: usize,
is_initialized: AtomicBool,
_inner_type: PhantomData<T>,
}
unsafe impl<T: AtomicCopy + ZeroCopySend> ZeroCopySend for RelocatableByteAtomic<T> {}
impl<T: AtomicCopy> RelocatableByteAtomic<T> {
#[inline(always)]
fn verify_init(&self, source: &str) {
debug_assert!(
self.is_initialized.load(Ordering::Relaxed),
"From: RelocatableByteAtomic<{}>::{}, Undefined behavior - the object was not initialized with 'init' before.",
core::any::type_name::<T>(),
source,
);
}
pub unsafe fn new_uninit() -> Self {
Self {
data_ptr: unsafe { RelocatablePointer::new_uninit() },
capacity: size_of::<T>(),
is_initialized: AtomicBool::new(false),
_inner_type: PhantomData,
}
}
pub unsafe fn init<Allocator: Allocate<NonNull<u8>>>(
&mut self,
allocator: &Allocator,
value: T,
) -> Result<(), AllocationError> {
if self.is_initialized.load(Ordering::Relaxed) {
fatal_panic!(from "RelocatableByteAtomic::init()", "Memory already initialized.
Initializing it twice may lead to undefined behavior.");
}
unsafe {
self.data_ptr.init(fail!(from "RelocatableByteAtomic::init()", when allocator
.allocate(Layout::from_size_align_unchecked(self.capacity, 1)),
"Failed to initialize RelocatableByteAtomic since the allocation of the data memory failed."));
}
for i in 0..self.capacity {
unsafe {
self.data_ptr.as_mut_ptr().add(i).write(AtomicU8::new(0));
}
}
let value_ptr = (&raw const value).cast::<u8>();
value.for_each_field(0, &mut |offset, size| {
for i in offset..offset + size {
unsafe {
(*self.data_ptr.as_ptr().add(i)).store(*value_ptr.add(i), Ordering::Relaxed);
}
}
});
self.is_initialized.store(true, Ordering::Relaxed);
Ok(())
}
pub const fn const_memory_size() -> usize {
size_of::<T>()
}
pub fn read(&self) -> MaybeTorn<T> {
self.verify_init("read()");
read_impl(self.data_ptr.as_ptr())
}
pub fn write(&self, value: T) {
self.verify_init("write()");
write_impl(self.data_ptr.as_ptr(), value);
}
}
#[repr(C)]
pub struct FixedSizeByteAtomic<T: AtomicCopy, const SIZE: usize> {
data: [AtomicU8; SIZE],
_inner_type: PhantomData<T>,
}
unsafe impl<T: AtomicCopy + ZeroCopySend, const SIZE: usize> ZeroCopySend
for FixedSizeByteAtomic<T, SIZE>
{
}
impl<T: AtomicCopy, const SIZE: usize> FixedSizeByteAtomic<T, SIZE> {
pub fn new(value: T) -> Self {
static_assert_size_of!(T, SIZE);
let value_ptr = (&raw const value).cast::<u8>();
let mut bytes = [0u8; SIZE];
value.for_each_field(0, &mut |offset, size| {
for (i, byte) in bytes.iter_mut().enumerate().skip(offset).take(size) {
*byte = unsafe { *value_ptr.add(i) };
}
});
Self {
data: bytes.map(AtomicU8::new),
_inner_type: PhantomData,
}
}
pub fn read(&self) -> MaybeTorn<T> {
read_impl(self.data.as_ptr())
}
pub fn write(&self, value: T) {
write_impl(self.data.as_ptr(), value);
}
}
fn read_impl<T: AtomicCopy>(src_data_ptr: *const AtomicU8) -> MaybeTorn<T> {
let mut data: MaybeUninit<T> = MaybeUninit::uninit();
let dest_data_ptr = data.as_mut_ptr() as *mut u8;
for i in 0..size_of::<T>() {
unsafe {
*dest_data_ptr.add(i) = (*src_data_ptr.add(i)).load(Ordering::Relaxed);
}
}
MaybeTorn::new(data)
}
fn write_impl<T: AtomicCopy>(dest_data_ptr: *const AtomicU8, value: T) {
let value_ptr = (&raw const value).cast::<u8>();
value.for_each_field(0, &mut |offset, size| {
for i in offset..offset + size {
unsafe {
(*dest_data_ptr.add(i)).store(*value_ptr.add(i), Ordering::Relaxed);
}
}
});
}