use core::alloc::Layout;
use iceoryx2_bb_concurrency::atomic::AtomicBool;
use iceoryx2_bb_concurrency::atomic::Ordering;
use iceoryx2_bb_elementary::{
bump_allocator::BumpAllocator,
relocatable_ptr::{PointerTrait, RelocatablePointer},
};
use iceoryx2_bb_elementary_traits::{
owning_pointer::OwningPointer, relocatable_container::RelocatableContainer,
};
use iceoryx2_log::{fail, fatal_panic};
pub type UsedChunkList = details::UsedChunkList<OwningPointer<AtomicBool>>;
pub type RelocatableUsedChunkList = details::UsedChunkList<RelocatablePointer<AtomicBool>>;
pub mod details {
use core::fmt::Debug;
use iceoryx2_bb_elementary::math::unaligned_mem_size;
use iceoryx2_bb_elementary_traits::owning_pointer::OwningPointer;
use super::*;
#[derive(Debug)]
#[repr(C)]
pub struct UsedChunkList<PointerType: PointerTrait<AtomicBool>> {
data_ptr: PointerType,
capacity: usize,
is_memory_initialized: AtomicBool,
}
unsafe impl<PointerType: PointerTrait<AtomicBool>> Send for UsedChunkList<PointerType> {}
unsafe impl<PointerType: PointerTrait<AtomicBool>> Sync for UsedChunkList<PointerType> {}
impl UsedChunkList<OwningPointer<AtomicBool>> {
pub fn new(capacity: usize) -> Self {
let mut data_ptr = OwningPointer::<AtomicBool>::new_with_alloc(capacity);
for i in 0..capacity {
unsafe { data_ptr.as_mut_ptr().add(i).write(AtomicBool::new(false)) };
}
Self {
data_ptr,
capacity,
is_memory_initialized: AtomicBool::new(true),
}
}
}
impl RelocatableContainer for UsedChunkList<RelocatablePointer<AtomicBool>> {
unsafe fn new_uninit(capacity: usize) -> Self {
Self {
data_ptr: unsafe { RelocatablePointer::new_uninit() },
capacity,
is_memory_initialized: AtomicBool::new(false),
}
}
unsafe fn init<T: iceoryx2_bb_elementary_traits::allocator::BaseAllocator>(
&mut self,
allocator: &T,
) -> Result<(), iceoryx2_bb_elementary_traits::allocator::AllocationError> {
if self.is_memory_initialized.load(Ordering::Relaxed) {
fatal_panic!(from self,
"Memory already initialized. Initializing it twice may lead to undefined behavior.");
}
let memory = fail!(from self, when allocator
.allocate(unsafe {
Layout::from_size_align_unchecked(
core::mem::size_of::<AtomicBool>() * self.capacity,
core::mem::align_of::<AtomicBool>())
}),
"Failed to initialize since the allocation of the data memory failed.");
unsafe { self.data_ptr.init(memory) };
for i in 0..self.capacity {
unsafe {
(self.data_ptr.as_ptr() as *mut AtomicBool)
.add(i)
.write(AtomicBool::new(false))
}
}
self.is_memory_initialized.store(true, Ordering::Relaxed);
Ok(())
}
fn memory_size(capacity: usize) -> usize {
Self::const_memory_size(capacity)
}
}
impl<PointerType: PointerTrait<AtomicBool> + Debug> UsedChunkList<PointerType> {
pub const fn const_memory_size(capacity: usize) -> usize {
unaligned_mem_size::<AtomicBool>(capacity)
}
pub fn capacity(&self) -> usize {
self.capacity
}
#[inline(always)]
fn verify_init(&self, source: &str) {
debug_assert!(
self.is_memory_initialized.load(Ordering::Relaxed),
"Undefined behavior when calling \"{source}\" and the object is not initialized."
);
}
fn set(&self, idx: usize, value: bool) -> bool {
self.verify_init("set");
debug_assert!(
idx < self.capacity,
"This should never happen. Out of bounds access with index {idx}."
);
unsafe { (*self.data_ptr.as_ptr().add(idx)).swap(value, Ordering::Relaxed) }
}
pub fn insert(&self, value: usize) -> bool {
!self.set(value, true)
}
pub fn remove(&self, value: usize) -> bool {
self.set(value, false)
}
pub fn remove_all<F: FnMut(usize)>(&self, mut callback: F) {
self.verify_init("pop");
for i in 0..self.capacity {
if unsafe { (*self.data_ptr.as_ptr().add(i)).swap(false, Ordering::Relaxed) } {
callback(i);
}
}
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct FixedSizeUsedChunkList<const CAPACITY: usize> {
list: RelocatableUsedChunkList,
data: [AtomicBool; CAPACITY],
}
impl<const CAPACITY: usize> Default for FixedSizeUsedChunkList<CAPACITY> {
fn default() -> Self {
let mut new_self = Self {
list: unsafe { RelocatableUsedChunkList::new_uninit(CAPACITY) },
data: [const { AtomicBool::new(false) }; CAPACITY],
};
let allocator = BumpAllocator::new(new_self.data.as_mut_ptr().cast());
unsafe {
new_self
.list
.init(&allocator)
.expect("All required memory is preallocated.")
};
new_self
}
}
impl<const CAPACITY: usize> FixedSizeUsedChunkList<CAPACITY> {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&self, value: usize) -> bool {
self.list.insert(value)
}
pub fn remove_all<F: FnMut(usize)>(&mut self, callback: F) {
self.list.remove_all(callback)
}
pub const fn capacity(&self) -> usize {
CAPACITY
}
pub fn remove(&self, value: usize) -> bool {
self.list.remove(value)
}
}