From 175a54d8ed266c05a6a22d6173401b5451d0607e Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 14:40:42 +0100 Subject: [PATCH] [#504] Split up SlotMap into Owning, Relocatable, FixedSize --- iceoryx2-bb/container/src/queue.rs | 28 +- iceoryx2-bb/container/src/slotmap.rs | 366 +++++++++++++++++++++++---- iceoryx2-bb/container/src/vec.rs | 70 ++--- 3 files changed, 363 insertions(+), 101 deletions(-) diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index 693e8a6d..c96ff141 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -110,9 +110,9 @@ use std::{alloc::Layout, fmt::Debug, mem::MaybeUninit}; /// Queue with run-time fixed size capacity. In contrast to its counterpart the /// [`RelocatableQueue`] it is movable but is not shared memory compatible. -pub type Queue = details::Queue>>; +pub type Queue = details::MetaQueue>>; /// **Non-movable** relocatable queue with runtime fixed size capacity. -pub type RelocatableQueue = details::Queue>>; +pub type RelocatableQueue = details::MetaQueue>>; #[doc(hidden)] pub mod details { @@ -120,7 +120,7 @@ pub mod details { /// **Non-movable** relocatable queue with runtime fixed size capacity. #[repr(C)] #[derive(Debug)] - pub struct Queue>> { + pub struct MetaQueue>> { data_ptr: PointerType, start: usize, len: usize, @@ -129,9 +129,9 @@ pub mod details { _phantom_data: PhantomData, } - unsafe impl>> Send for Queue {} + unsafe impl>> Send for MetaQueue {} - impl Queue>> { + impl MetaQueue>> { /// Creates a new [`Queue`] with the provided capacity pub fn new(capacity: usize) -> Self { Self { @@ -178,7 +178,7 @@ pub mod details { } } - impl> + Debug> Queue { + impl> + Debug> MetaQueue { /// Returns a copy of the element stored at index. The index is starting by 0 for the first /// element until [`Queue::len()`]. /// @@ -206,7 +206,7 @@ pub mod details { } } - impl RelocatableContainer for Queue>> { + impl RelocatableContainer for MetaQueue>> { unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { Self { data_ptr: RelocatablePointer::new(distance_to_data), @@ -260,7 +260,7 @@ pub mod details { } } - impl Queue>> { + impl MetaQueue>> { /// Returns the required memory size for a queue with a specified capacity pub const fn const_memory_size(capacity: usize) -> usize { unaligned_mem_size::(capacity) @@ -330,7 +330,7 @@ pub mod details { } } - impl>> Queue { + impl>> MetaQueue { #[inline(always)] fn verify_init(&self, source: &str) { debug_assert!( @@ -443,9 +443,14 @@ pub mod details { } } - impl>> Drop for Queue { + impl>> Drop for MetaQueue { fn drop(&mut self) { - unsafe { self.clear_impl() } + if self + .is_initialized + .load(std::sync::atomic::Ordering::Relaxed) + { + unsafe { self.clear_impl() } + } } } } @@ -476,7 +481,6 @@ impl Default for FixedSizeQueue { } unsafe impl Send for FixedSizeQueue {} -unsafe impl Sync for FixedSizeQueue {} impl FixedSizeQueue { fn initialize_state() -> RelocatableQueue { diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 5fc2c1dd..7bb48765 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -10,22 +10,26 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use crate::queue::details::Queue; -use crate::vec::details::RelocatableVec; +use crate::queue::details::MetaQueue; +use crate::vec::details::MetaVec; +use crate::{queue::RelocatableQueue, vec::RelocatableVec}; +use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::owning_pointer::OwningPointer; use iceoryx2_bb_elementary::pointer_trait::PointerTrait; +use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; +use iceoryx2_bb_log::fail; use std::mem::MaybeUninit; #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct SlotMapKey(usize); -pub type SlotMap = details::RelocatableSlotMap< +pub type SlotMap = details::MetaSlotMap< T, OwningPointer>>, OwningPointer>, >; -pub type RelocatableSlotMap = details::RelocatableSlotMap< +pub type RelocatableSlotMap = details::MetaSlotMap< T, RelocatablePointer>>, RelocatablePointer>, @@ -43,7 +47,7 @@ pub mod details { DataPtrType: PointerTrait>>, IdxPtrType: PointerTrait>, > { - slotmap: &'slotmap RelocatableSlotMap, + slotmap: &'slotmap MetaSlotMap, key: SlotMapKey, } @@ -68,22 +72,22 @@ pub mod details { #[repr(C)] #[derive(Debug)] - pub struct RelocatableSlotMap< + pub struct MetaSlotMap< T, DataPtrType: PointerTrait>>, IdxPtrType: PointerTrait>, > { - idx_to_data: RelocatableVec, - idx_to_data_next_free_index: Queue, - data: RelocatableVec, DataPtrType>, - data_next_free_index: Queue, + idx_to_data: MetaVec, + idx_to_data_next_free_index: MetaQueue, + data: MetaVec, DataPtrType>, + data_next_free_index: MetaQueue, } impl< T, DataPtrType: PointerTrait>>, IdxPtrType: PointerTrait>, - > RelocatableSlotMap + > MetaSlotMap { fn next(&self, start: SlotMapKey) -> Option<(SlotMapKey, &T)> { let idx_to_data = &self.idx_to_data; @@ -102,43 +106,15 @@ pub mod details { None } - } - - impl - RelocatableSlotMap< - T, - OwningPointer>>, - OwningPointer>, - > - { - pub fn new(capacity: usize) -> Self { - let mut new_self = Self { - idx_to_data: RelocatableVec::new(capacity), - idx_to_data_next_free_index: Queue::new(capacity), - data: RelocatableVec::new(capacity), - data_next_free_index: Queue::new(capacity), - }; - - new_self.idx_to_data.fill(INVALID_KEY); - new_self.data.fill_with(|| None); - for n in 0..capacity { - new_self.idx_to_data_next_free_index.push(n); - new_self.data_next_free_index.push(n); - } - new_self - } - pub fn iter( - &self, - ) -> Iter>>, OwningPointer>> - { + pub(crate) unsafe fn iter_impl(&self) -> Iter { Iter { slotmap: self, key: SlotMapKey(0), } } - pub fn get(&self, key: SlotMapKey) -> Option<&T> { + pub(crate) unsafe fn get_impl(&self, key: SlotMapKey) -> Option<&T> { match self.idx_to_data[key.0] { INVALID_KEY => None, n => Some(self.data[n].as_ref().expect( @@ -147,7 +123,7 @@ pub mod details { } } - pub fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { + pub(crate) unsafe fn get_mut_impl(&mut self, key: SlotMapKey) -> Option<&mut T> { match self.idx_to_data[key.0] { INVALID_KEY => None, n => Some(self.data[n].as_mut().expect( @@ -156,19 +132,19 @@ pub mod details { } } - pub fn insert(&mut self, value: T) -> Option { - match self.idx_to_data_next_free_index.pop() { + pub(crate) unsafe fn insert_impl(&mut self, value: T) -> Option { + match self.idx_to_data_next_free_index.pop_impl() { None => None, Some(key) => { let key = SlotMapKey(key); - self.insert_at(key, value); + self.insert_at_impl(key, value); Some(key) } } } - pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { - if key.0 > self.capacity() { + pub(crate) unsafe fn insert_at_impl(&mut self, key: SlotMapKey, value: T) -> bool { + if key.0 > self.capacity_impl() { return false; } @@ -177,14 +153,14 @@ pub mod details { self.data[data_idx] = Some(value); true } else { - let n = self.data_next_free_index.pop().expect("data and idx_to_data correspond and there must be always a free index available."); + let n = self.data_next_free_index.pop_impl().expect("data and idx_to_data correspond and there must be always a free index available."); self.idx_to_data[key.0] = n; self.data[n] = Some(value); false } } - pub fn remove(&mut self, key: SlotMapKey) -> bool { + pub(crate) unsafe fn remove_impl(&mut self, key: SlotMapKey) -> bool { if key.0 > self.idx_to_data.len() { return false; } @@ -192,8 +168,8 @@ pub mod details { let data_idx = self.idx_to_data[key.0]; if data_idx != INVALID_KEY { self.data[data_idx].take(); - self.data_next_free_index.push(data_idx); - self.idx_to_data_next_free_index.push(key.0); + self.data_next_free_index.push_impl(data_idx); + self.idx_to_data_next_free_index.push_impl(key.0); self.idx_to_data[key.0] = INVALID_KEY; true } else { @@ -201,20 +177,300 @@ pub mod details { } } + pub(crate) unsafe fn len_impl(&self) -> usize { + self.capacity_impl() - self.data_next_free_index.len() + } + + pub(crate) unsafe fn capacity_impl(&self) -> usize { + self.idx_to_data.capacity() + } + + pub(crate) unsafe fn is_empty_impl(&self) -> bool { + self.len_impl() == 0 + } + + pub(crate) unsafe fn is_full_impl(&self) -> bool { + self.len_impl() == self.capacity_impl() + } + } + + impl RelocatableContainer + for MetaSlotMap< + T, + RelocatablePointer>>, + RelocatablePointer>, + > + { + unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { + Self { + idx_to_data: RelocatableVec::new(capacity, distance_to_data), + idx_to_data_next_free_index: RelocatableQueue::new( + capacity, + distance_to_data + + RelocatableVec::::const_memory_size(capacity) as isize, + ), + data: RelocatableVec::new( + capacity, + distance_to_data + + RelocatableVec::::const_memory_size(capacity) as isize + + RelocatableQueue::::const_memory_size(capacity) as isize, + ), + data_next_free_index: RelocatableQueue::new( + capacity, + distance_to_data + + RelocatableVec::::const_memory_size(capacity) as isize + + RelocatableQueue::::const_memory_size(capacity) as isize + + RelocatableVec::>::const_memory_size(capacity) as isize, + ), + } + } + + unsafe fn new_uninit(capacity: usize) -> Self { + Self { + idx_to_data: RelocatableVec::new_uninit(capacity), + idx_to_data_next_free_index: RelocatableQueue::new_uninit(capacity), + data: RelocatableVec::new_uninit(capacity), + data_next_free_index: RelocatableQueue::new_uninit(capacity), + } + } + + unsafe fn init( + &self, + allocator: &Allocator, + ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { + let msg = "Unable to initialize RelocatableSlotMap"; + fail!(from "RelocatableSlotMap::init()", + when self.idx_to_data.init(allocator), + "{msg} since the underlying idx_to_data vector could not be initialized."); + fail!(from "RelocatableSlotMap::init()", + when self.idx_to_data_next_free_index.init(allocator), + "{msg} since the underlying idx_to_data_next_free_index queue could not be initialized."); + fail!(from "RelocatableSlotMap::init()", + when self.data.init(allocator), + "{msg} since the underlying data vector could not be initialized."); + fail!(from "RelocatableSlotMap::init()", + when self.data_next_free_index.init(allocator), + "{msg} since the underlying data_next_free_index queue could not be initialized."); + + Ok(()) + } + + fn memory_size(capacity: usize) -> usize { + Self::const_memory_size(capacity) + } + } + + impl + MetaSlotMap< + T, + RelocatablePointer>>, + RelocatablePointer>, + > + { + pub const fn const_memory_size(capacity: usize) -> usize { + RelocatableVec::::const_memory_size(capacity) + + RelocatableQueue::::const_memory_size(capacity) + + RelocatableVec::>::const_memory_size(capacity) + + RelocatableQueue::::const_memory_size(capacity) + } + } + + impl MetaSlotMap>>, OwningPointer>> { + pub fn new(capacity: usize) -> Self { + let mut new_self = Self { + idx_to_data: MetaVec::new(capacity), + idx_to_data_next_free_index: MetaQueue::new(capacity), + data: MetaVec::new(capacity), + data_next_free_index: MetaQueue::new(capacity), + }; + + new_self.idx_to_data.fill(INVALID_KEY); + new_self.data.fill_with(|| None); + for n in 0..capacity { + new_self.idx_to_data_next_free_index.push(n); + new_self.data_next_free_index.push(n); + } + new_self + } + + pub fn iter( + &self, + ) -> Iter>>, OwningPointer>> + { + unsafe { self.iter_impl() } + } + + pub fn get(&self, key: SlotMapKey) -> Option<&T> { + unsafe { self.get_impl(key) } + } + + pub fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { + unsafe { self.get_mut_impl(key) } + } + + pub fn insert(&mut self, value: T) -> Option { + unsafe { self.insert_impl(value) } + } + + pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { + unsafe { self.insert_at_impl(key, value) } + } + + pub fn remove(&mut self, key: SlotMapKey) -> bool { + unsafe { self.remove_impl(key) } + } + pub fn len(&self) -> usize { - self.capacity() - self.data_next_free_index.len() + unsafe { self.len_impl() } } pub fn capacity(&self) -> usize { - self.idx_to_data.capacity() + unsafe { self.capacity_impl() } } pub fn is_empty(&self) -> bool { - self.len() == 0 + unsafe { self.is_empty_impl() } } pub fn is_full(&self) -> bool { - self.len() == self.capacity() + unsafe { self.is_full_impl() } + } + } + + impl + MetaSlotMap< + T, + RelocatablePointer>>, + RelocatablePointer>, + > + { + pub unsafe fn iter( + &self, + ) -> Iter< + T, + RelocatablePointer>>, + RelocatablePointer>, + > { + self.iter_impl() + } + + pub unsafe fn get(&self, key: SlotMapKey) -> Option<&T> { + self.get_impl(key) + } + + pub unsafe fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { + self.get_mut_impl(key) + } + + pub unsafe fn insert(&mut self, value: T) -> Option { + self.insert_impl(value) + } + + pub unsafe fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { + self.insert_at_impl(key, value) + } + + pub unsafe fn remove(&mut self, key: SlotMapKey) -> bool { + self.remove_impl(key) + } + + pub unsafe fn len(&self) -> usize { + self.len_impl() + } + + pub unsafe fn capacity(&self) -> usize { + self.capacity_impl() + } + + pub unsafe fn is_empty(&self) -> bool { + self.is_empty_impl() + } + + pub unsafe fn is_full(&self) -> bool { + self.is_full_impl() + } + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct FixedSizeSlotMap { + state: RelocatableSlotMap, + _idx_to_data: [usize; CAPACITY], + _idx_to_data_next_free_index: [usize; CAPACITY], + _data: [Option; CAPACITY], + _data_next_free_index: [usize; CAPACITY], +} + +impl Default for FixedSizeSlotMap { + fn default() -> Self { + Self { + state: Self::initialize_state(), + _idx_to_data: core::array::from_fn(|_| INVALID_KEY), + _idx_to_data_next_free_index: core::array::from_fn(|i| i), + _data: core::array::from_fn(|_| None), + _data_next_free_index: core::array::from_fn(|_| INVALID_KEY), + } + } +} + +impl FixedSizeSlotMap { + fn initialize_state() -> RelocatableSlotMap { + unsafe { + RelocatableSlotMap::new( + CAPACITY, + align_to::>(std::mem::size_of::>()) as isize, + ) } } + + pub fn new() -> Self { + Self::default() + } + + pub fn iter( + &self, + ) -> details::Iter< + T, + RelocatablePointer>>, + RelocatablePointer>, + > { + unsafe { self.state.iter_impl() } + } + + pub fn get(&self, key: SlotMapKey) -> Option<&T> { + unsafe { self.state.get_impl(key) } + } + + pub fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { + unsafe { self.state.get_mut_impl(key) } + } + + pub fn insert(&mut self, value: T) -> Option { + unsafe { self.state.insert_impl(value) } + } + + pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { + unsafe { self.state.insert_at_impl(key, value) } + } + + pub fn remove(&mut self, key: SlotMapKey) -> bool { + unsafe { self.state.remove_impl(key) } + } + + pub fn len(&self) -> usize { + unsafe { self.state.len_impl() } + } + + pub fn capacity(&self) -> usize { + unsafe { self.state.capacity_impl() } + } + + pub fn is_empty(&self) -> bool { + unsafe { self.state.is_empty_impl() } + } + + pub fn is_full(&self) -> bool { + unsafe { self.state.is_full_impl() } + } } diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index 9de10a68..f56f6beb 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -97,10 +97,10 @@ use serde::{de::Visitor, Deserialize, Serialize}; /// Vector with run-time fixed size capacity. In contrast to its counterpart the /// [`RelocatableVec`] it is movable but is not shared memory compatible. -pub type Vec = details::RelocatableVec>>; +pub type Vec = details::MetaVec>>; /// **Non-movable** relocatable vector with runtime fixed size capacity. -pub type RelocatableVec = details::RelocatableVec>>; +pub type RelocatableVec = details::MetaVec>>; #[doc(hidden)] pub mod details { @@ -109,7 +109,7 @@ pub mod details { /// **Non-movable** relocatable vector with runtime fixed size capacity. #[repr(C)] #[derive(Debug)] - pub struct RelocatableVec>> { + pub struct MetaVec>> { data_ptr: PointerType, capacity: usize, len: usize, @@ -117,18 +117,20 @@ pub mod details { _phantom_data: PhantomData, } - unsafe impl>> Send - for RelocatableVec - { - } + unsafe impl>> Send for MetaVec {} - impl>> Drop for RelocatableVec { + impl>> Drop for MetaVec { fn drop(&mut self) { - self.clear_impl(); + if self + .is_initialized + .load(std::sync::atomic::Ordering::Relaxed) + { + unsafe { self.clear_impl() }; + } } } - impl RelocatableContainer for RelocatableVec>> { + impl RelocatableContainer for MetaVec>> { unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { Self { data_ptr: RelocatablePointer::new(distance_to_data), @@ -174,7 +176,7 @@ pub mod details { } } - impl>> Deref for RelocatableVec { + impl>> Deref for MetaVec { type Target = [T]; fn deref(&self) -> &Self::Target { @@ -183,7 +185,7 @@ pub mod details { } } - impl>> DerefMut for RelocatableVec { + impl>> DerefMut for MetaVec { fn deref_mut(&mut self) -> &mut Self::Target { self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); unsafe { @@ -196,7 +198,7 @@ pub mod details { } impl>> PartialEq - for RelocatableVec + for MetaVec { fn eq(&self, other: &Self) -> bool { if other.len() != self.len() { @@ -213,9 +215,9 @@ pub mod details { } } - impl>> Eq for RelocatableVec {} + impl>> Eq for MetaVec {} - impl>> RelocatableVec { + impl>> MetaVec { #[inline(always)] fn verify_init(&self, source: &str) { debug_assert!( @@ -246,7 +248,7 @@ pub mod details { self.len == self.capacity } - fn push_impl(&mut self, value: T) -> bool { + unsafe fn push_impl(&mut self, value: T) -> bool { if self.is_full() { return false; } @@ -256,7 +258,7 @@ pub mod details { true } - fn fill_impl(&mut self, value: T) + unsafe fn fill_impl(&mut self, value: T) where T: Clone, { @@ -265,7 +267,7 @@ pub mod details { } } - fn fill_with_impl T>(&mut self, mut f: F) { + unsafe fn fill_with_impl T>(&mut self, mut f: F) { for _ in self.len..self.capacity { self.push_unchecked(f()); } @@ -282,7 +284,7 @@ pub mod details { self.len += 1; } - fn extend_from_slice_impl(&mut self, other: &[T]) -> bool + unsafe fn extend_from_slice_impl(&mut self, other: &[T]) -> bool where T: Clone, { @@ -297,7 +299,7 @@ pub mod details { true } - fn pop_impl(&mut self) -> Option { + unsafe fn pop_impl(&mut self) -> Option { if self.is_empty() { return None; } @@ -306,7 +308,7 @@ pub mod details { Some(self.pop_unchecked()) } - fn clear_impl(&mut self) { + unsafe fn clear_impl(&mut self) { for _ in 0..self.len { self.pop_unchecked(); } @@ -322,16 +324,16 @@ pub mod details { unsafe { value.assume_init() } } - fn as_slice_impl(&self) -> &[T] { + unsafe fn as_slice_impl(&self) -> &[T] { unsafe { core::slice::from_raw_parts(self.data_ptr.as_ptr().cast(), self.len) } } - fn as_mut_slice_impl(&mut self) -> &mut [T] { + unsafe fn as_mut_slice_impl(&mut self) -> &mut [T] { unsafe { core::slice::from_raw_parts_mut(self.data_ptr.as_mut_ptr().cast(), self.len) } } } - impl RelocatableVec>> { + impl MetaVec>> { /// Creates a new [`Queue`] with the provided capacity pub fn new(capacity: usize) -> Self { Self { @@ -346,7 +348,7 @@ pub mod details { /// Adds an element at the end of the vector. If the vector is full and the element cannot be /// added it returns false, otherwise true. pub fn push(&mut self, value: T) -> bool { - self.push_impl(value) + unsafe { self.push_impl(value) } } /// Fill the remaining space of the vector with value. @@ -354,12 +356,12 @@ pub mod details { where T: Clone, { - self.fill_impl(value) + unsafe { self.fill_impl(value) } } /// Fill the remaining space of the vector by calling the provided closure repeatedly pub fn fill_with T>(&mut self, f: F) { - self.fill_with_impl(f) + unsafe { self.fill_with_impl(f) } } /// Append all elements from other via [`Clone`]. @@ -367,32 +369,32 @@ pub mod details { where T: Clone, { - self.extend_from_slice_impl(other) + unsafe { self.extend_from_slice_impl(other) } } /// Removes the last element of the vector and returns it to the user. If the vector is empty /// it returns [`None`]. pub fn pop(&mut self) -> Option { - self.pop_impl() + unsafe { self.pop_impl() } } /// Removes all elements from the vector pub fn clear(&mut self) { - self.clear_impl() + unsafe { self.clear_impl() } } /// Returns a slice to the contents of the vector pub fn as_slice(&self) -> &[T] { - self.as_slice_impl() + unsafe { self.as_slice_impl() } } /// Returns a mutable slice to the contents of the vector pub fn as_mut_slice(&mut self) -> &mut [T] { - self.as_mut_slice_impl() + unsafe { self.as_mut_slice_impl() } } } - impl RelocatableVec>> { + impl MetaVec>> { /// Returns the required memory size for a vec with a specified capacity pub const fn const_memory_size(capacity: usize) -> usize { unaligned_mem_size::(capacity) @@ -429,7 +431,7 @@ pub mod details { /// * [`RelocatableVec::init()`] must be called once before /// pub unsafe fn fill_with T>(&mut self, f: F) { - self.fill_with(f) + self.fill_with_impl(f) } /// Append all elements from other via [`Clone`].