From eb1f7ade66c4cceea4e7c767809403b558bff770 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 6 Nov 2024 16:12:54 +0100 Subject: [PATCH 01/17] [#504] RelocatableVec has PointerType as additional generic argument --- iceoryx2-bb/container/src/vec.rs | 508 ++++++++++++++--------- iceoryx2-bb/container/tests/vec_tests.rs | 38 +- 2 files changed, 344 insertions(+), 202 deletions(-) diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index 6bbe634ea..bd1b0fed6 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -85,6 +85,7 @@ use std::{ use iceoryx2_bb_elementary::{ math::{align_to, unaligned_mem_size}, + owning_pointer::OwningPointer, placement_default::PlacementDefault, pointer_trait::PointerTrait, relocatable_container::RelocatableContainer, @@ -94,258 +95,377 @@ use iceoryx2_bb_log::{fail, fatal_panic}; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicBool; 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>>; + /// **Non-movable** relocatable vector with runtime fixed size capacity. -#[repr(C)] -#[derive(Debug)] -pub struct RelocatableVec { - data_ptr: RelocatablePointer>, - capacity: usize, - len: usize, - is_initialized: IoxAtomicBool, -} +pub type RelocatableVec = details::RelocatableVec>>; -unsafe impl Send for RelocatableVec {} +#[doc(hidden)] +pub mod details { + use iceoryx2_bb_elementary::owning_pointer::OwningPointer; -impl Drop for RelocatableVec { - fn drop(&mut self) { - unsafe { self.clear() }; + use super::*; + + /// **Non-movable** relocatable vector with runtime fixed size capacity. + #[repr(C)] + #[derive(Debug)] + pub struct RelocatableVec>> { + data_ptr: PointerType, + capacity: usize, + len: usize, + is_initialized: IoxAtomicBool, + _phantom_data: PhantomData, } -} -impl RelocatableContainer for RelocatableVec { - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - len: 0, - is_initialized: IoxAtomicBool::new(true), - } + unsafe impl>> Send + for RelocatableVec + { } - unsafe fn new_uninit(capacity: usize) -> Self { - Self { - data_ptr: RelocatablePointer::new_uninit(), - capacity, - len: 0, - is_initialized: IoxAtomicBool::new(false), + impl>> Drop for RelocatableVec { + fn drop(&mut self) { + self.clear_impl(); } } - unsafe fn init( - &self, - allocator: &Allocator, - ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { - if self.is_initialized.load(Ordering::Relaxed) { - fatal_panic!(from "Vec::init()", "Memory already initialized, Initializing it twice may lead to undefined behavior."); + impl RelocatableContainer for RelocatableVec>> { + unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { + Self { + data_ptr: RelocatablePointer::new(distance_to_data), + capacity, + len: 0, + is_initialized: IoxAtomicBool::new(true), + _phantom_data: PhantomData, + } } - self.data_ptr.init(fail!(from "Queue::init", when allocator - .allocate(Layout::from_size_align_unchecked( - std::mem::size_of::() * self.capacity, - std::mem::align_of::(), - )), "Failed to initialize queue since the allocation of the data memory failed." - )); - self.is_initialized - .store(true, std::sync::atomic::Ordering::Relaxed); + unsafe fn new_uninit(capacity: usize) -> Self { + Self { + data_ptr: RelocatablePointer::new_uninit(), + capacity, + len: 0, + is_initialized: IoxAtomicBool::new(false), + _phantom_data: PhantomData, + } + } - Ok(()) - } + unsafe fn init( + &self, + allocator: &Allocator, + ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { + if self.is_initialized.load(Ordering::Relaxed) { + fatal_panic!(from "Vec::init()", "Memory already initialized, Initializing it twice may lead to undefined behavior."); + } - fn memory_size(capacity: usize) -> usize { - Self::const_memory_size(capacity) + self.data_ptr.init(fail!(from "Queue::init", when allocator + .allocate(Layout::from_size_align_unchecked( + std::mem::size_of::() * self.capacity, + std::mem::align_of::(), + )), "Failed to initialize queue since the allocation of the data memory failed." + )); + self.is_initialized + .store(true, std::sync::atomic::Ordering::Relaxed); + + Ok(()) + } + + fn memory_size(capacity: usize) -> usize { + Self::const_memory_size(capacity) + } } -} -impl Deref for RelocatableVec { - type Target = [T]; + impl>> Deref for RelocatableVec { + type Target = [T]; - fn deref(&self) -> &Self::Target { - self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); - unsafe { core::slice::from_raw_parts((*self.data_ptr.as_ptr()).as_ptr(), self.len) } + fn deref(&self) -> &Self::Target { + self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); + unsafe { core::slice::from_raw_parts((*self.data_ptr.as_ptr()).as_ptr(), self.len) } + } } -} -impl DerefMut for RelocatableVec { - fn deref_mut(&mut self) -> &mut Self::Target { - self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); - unsafe { - core::slice::from_raw_parts_mut((*self.data_ptr.as_mut_ptr()).as_mut_ptr(), self.len) + impl>> DerefMut for RelocatableVec { + fn deref_mut(&mut self) -> &mut Self::Target { + self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); + unsafe { + core::slice::from_raw_parts_mut( + (*self.data_ptr.as_mut_ptr()).as_mut_ptr(), + self.len, + ) + } } } -} -impl PartialEq for RelocatableVec { - fn eq(&self, other: &Self) -> bool { - if other.len() != self.len() { - return false; + impl>> PartialEq + for RelocatableVec + { + fn eq(&self, other: &Self) -> bool { + if other.len() != self.len() { + return false; + } + + for i in 0..self.len() { + if other[i] != self[i] { + return false; + } + } + + true + } + } + + impl>> Eq for RelocatableVec {} + + impl>> RelocatableVec { + #[inline(always)] + fn verify_init(&self, source: &str) { + debug_assert!( + self.is_initialized + .load(std::sync::atomic::Ordering::Relaxed), + "From: {}, Undefined behavior - the object was not initialized with 'init' before.", + source + ); + } + + /// 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) + } + + /// Returns the capacity of the vector + pub fn capacity(&self) -> usize { + self.capacity + } + + /// Returns the number of elements stored inside the vector + pub fn len(&self) -> usize { + self.len + } + + /// Returns true if the vector is empty, otherwise false + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns true if the vector is full, otherwise false + pub fn is_full(&self) -> bool { + self.len == self.capacity } - for i in 0..self.len() { - if other[i] != self[i] { + fn push_impl(&mut self, value: T) -> bool { + if self.is_full() { return false; } + + self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); + self.push_unchecked(value); + true } - true - } -} + fn fill_impl(&mut self, value: T) + where + T: Clone, + { + for _ in self.len..self.capacity { + self.push_unchecked(value.clone()); + } + } -impl Eq for RelocatableVec {} + fn push_unchecked(&mut self, value: T) { + unsafe { + self.data_ptr + .as_mut_ptr() + .add(self.len) + .write(MaybeUninit::new(value)) + }; -impl RelocatableVec { - #[inline(always)] - fn verify_init(&self, source: &str) { - debug_assert!( - self.is_initialized - .load(std::sync::atomic::Ordering::Relaxed), - "From: {}, Undefined behavior - the object was not initialized with 'init' before.", - source - ); - } + self.len += 1; + } - /// 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) - } + fn extend_from_slice_impl(&mut self, other: &[T]) -> bool + where + T: Clone, + { + if self.capacity < self.len + other.len() { + return false; + } - /// Returns the capacity of the vector - pub fn capacity(&self) -> usize { - self.capacity - } + for element in other { + self.push_unchecked(element.clone()); + } - /// Returns the number of elements stored inside the vector - pub fn len(&self) -> usize { - self.len - } + true + } - /// Returns true if the vector is empty, otherwise false - pub fn is_empty(&self) -> bool { - self.len == 0 - } + fn pop_impl(&mut self) -> Option { + if self.is_empty() { + return None; + } - /// Returns true if the vector is full, otherwise false - pub fn is_full(&self) -> bool { - self.len == self.capacity - } + self.verify_init(&format!("Vec<{}>::pop()", std::any::type_name::())); + Some(self.pop_unchecked()) + } - /// 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. - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn push(&mut self, value: T) -> bool { - if self.is_full() { - return false; + fn clear_impl(&mut self) { + for _ in 0..self.len { + self.pop_unchecked(); + } } - self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); - self.push_unchecked(value); - true - } + fn pop_unchecked(&mut self) -> T { + let value = std::mem::replace( + unsafe { &mut *self.data_ptr.as_mut_ptr().offset(self.len as isize - 1) }, + MaybeUninit::uninit(), + ); + self.len -= 1; - /// Fill the remaining space of the vector with value. - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn fill(&mut self, value: T) - where - T: Clone, - { - for _ in self.len..self.capacity { - self.push_unchecked(value.clone()); + unsafe { value.assume_init() } } - } - unsafe fn push_unchecked(&mut self, value: T) { - self.data_ptr - .as_mut_ptr() - .add(self.len) - .write(MaybeUninit::new(value)); + fn as_slice_impl(&self) -> &[T] { + unsafe { core::slice::from_raw_parts(self.data_ptr.as_ptr().cast(), self.len) } + } - self.len += 1; + 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) } + } } - /// Append all elements from other via [`Clone`]. - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn extend_from_slice(&mut self, other: &[T]) -> bool - where - T: Clone, - { - if self.capacity < self.len + other.len() { - return false; + impl RelocatableVec>> { + /// Creates a new [`Queue`] with the provided capacity + pub fn new(capacity: usize) -> Self { + Self { + data_ptr: OwningPointer::>::new_with_alloc(capacity), + capacity, + len: 0, + is_initialized: IoxAtomicBool::new(true), + _phantom_data: PhantomData, + } } - for element in other { - self.push_unchecked(element.clone()); + /// 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) } - true - } + /// Fill the remaining space of the vector with value. + pub fn fill(&mut self, value: T) + where + T: Clone, + { + self.fill_impl(value) + } - /// Removes the last element of the vector and returns it to the user. If the vector is empty - /// it returns [`None`]. - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn pop(&mut self) -> Option { - if self.is_empty() { - return None; + /// Append all elements from other via [`Clone`]. + pub fn extend_from_slice(&mut self, other: &[T]) -> bool + where + T: Clone, + { + self.extend_from_slice_impl(other) } - self.verify_init(&format!("Vec<{}>::pop()", std::any::type_name::())); - Some(self.pop_unchecked()) - } + /// 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() + } - /// Removes all elements from the vector - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn clear(&mut self) { - for _ in 0..self.len { - self.pop_unchecked(); + /// Removes all elements from the vector + pub fn clear(&mut self) { + self.clear_impl() } - } - unsafe fn pop_unchecked(&mut self) -> T { - let value = std::mem::replace( - &mut *self.data_ptr.as_mut_ptr().offset(self.len as isize - 1), - MaybeUninit::uninit(), - ); - self.len -= 1; + /// Returns a slice to the contents of the vector + pub fn as_slice(&self) -> &[T] { + self.as_slice_impl() + } - value.assume_init() + /// Returns a mutable slice to the contents of the vector + pub fn as_mut_slice(&mut self) -> &mut [T] { + self.as_mut_slice_impl() + } } - /// Returns a slice to the contents of the vector - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn as_slice(&self) -> &[T] { - core::slice::from_raw_parts(self.data_ptr.as_ptr().cast(), self.len) - } + impl RelocatableVec>> { + /// 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. + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn push(&mut self, value: T) -> bool { + self.push_impl(value) + } - /// Returns a mutable slice to the contents of the vector - /// - /// # Safety - /// - /// * [`RelocatableVec::init()`] must be called once before - /// - pub unsafe fn as_mut_slice(&mut self) -> &mut [T] { - core::slice::from_raw_parts_mut(self.data_ptr.as_mut_ptr().cast(), self.len) + /// Fill the remaining space of the vector with value. + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn fill(&mut self, value: T) + where + T: Clone, + { + self.fill_impl(value) + } + + /// Append all elements from other via [`Clone`]. + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn extend_from_slice(&mut self, other: &[T]) -> bool + where + T: Clone, + { + 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`]. + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn pop(&mut self) -> Option { + self.pop_impl() + } + + /// Removes all elements from the vector + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn clear(&mut self) { + self.clear_impl() + } + + /// Returns a slice to the contents of the vector + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn as_slice(&self) -> &[T] { + self.as_slice_impl() + } + + /// Returns a mutable slice to the contents of the vector + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn as_mut_slice(&mut self) -> &mut [T] { + self.as_mut_slice_impl() + } } } diff --git a/iceoryx2-bb/container/tests/vec_tests.rs b/iceoryx2-bb/container/tests/vec_tests.rs index 81277727a..116d84c5f 100644 --- a/iceoryx2-bb/container/tests/vec_tests.rs +++ b/iceoryx2-bb/container/tests/vec_tests.rs @@ -10,15 +10,17 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +use iceoryx2_bb_container::vec::*; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; +use iceoryx2_bb_elementary::placement_default::PlacementDefault; +use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; +use iceoryx2_bb_testing::assert_that; +use iceoryx2_bb_testing::lifetime_tracker::LifetimeTracker; +use iceoryx2_bb_testing::memory::RawMemory; +use serde_test::{assert_tokens, Token}; + mod fixed_size_vec { - use iceoryx2_bb_container::vec::*; - use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; - use iceoryx2_bb_elementary::placement_default::PlacementDefault; - use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; - use iceoryx2_bb_testing::assert_that; - use iceoryx2_bb_testing::lifetime_tracker::LifetimeTracker; - use iceoryx2_bb_testing::memory::RawMemory; - use serde_test::{assert_tokens, Token}; + use super::*; const SUT_CAPACITY: usize = 128; type Sut = FixedSizeVec; @@ -312,3 +314,23 @@ mod fixed_size_vec { ); } } + +mod vec { + use super::*; + + #[test] + fn push_and_pop_element_works() { + const CAPACITY: usize = 12; + const TEST_VALUE: usize = 89123; + let mut sut = Vec::::new(CAPACITY); + assert_that!(sut.capacity(), eq CAPACITY); + assert_that!(sut, len 0); + + sut.push(TEST_VALUE); + + assert_that!(sut, len 1); + assert_that!(sut[0], eq TEST_VALUE); + assert_that!(sut.pop(), eq Some(TEST_VALUE)); + assert_that!(sut, len 0); + } +} From 9edea42d35801345679a81c64933b08a7124e490 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 6 Nov 2024 20:30:01 +0100 Subject: [PATCH 02/17] [#504] Introduce SlotMap with first test --- iceoryx2-bb/container/src/lib.rs | 2 + iceoryx2-bb/container/src/queue.rs | 10 +- iceoryx2-bb/container/src/slotmap.rs | 220 +++++++++++++++++++ iceoryx2-bb/container/src/vec.rs | 40 +++- iceoryx2-bb/container/tests/slotmap_tests.rs | 31 +++ iceoryx2-bb/container/tests/vec_tests.rs | 2 +- 6 files changed, 291 insertions(+), 14 deletions(-) create mode 100644 iceoryx2-bb/container/src/slotmap.rs create mode 100644 iceoryx2-bb/container/tests/slotmap_tests.rs diff --git a/iceoryx2-bb/container/src/lib.rs b/iceoryx2-bb/container/src/lib.rs index e5f258652..85ad33752 100644 --- a/iceoryx2-bb/container/src/lib.rs +++ b/iceoryx2-bb/container/src/lib.rs @@ -89,6 +89,8 @@ pub mod byte_string; /// A queue similar to [`std::collections::VecDeque`] pub mod queue; +/// A container with persistent unique keys to access values. +pub mod slotmap; /// Extends the [ByteString](crate::byte_string) so that custom string types with a semantic /// ruleset on their content can be realized. #[macro_use] diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index 3f95cfd02..693e8a6d0 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -261,6 +261,11 @@ pub mod details { } impl Queue>> { + /// 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) + } + /// Removes all elements from the queue /// /// # Safety @@ -336,11 +341,6 @@ pub mod details { ); } - /// 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) - } - /// Returns true if the queue is empty, otherwise false pub fn is_empty(&self) -> bool { self.len == 0 diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs new file mode 100644 index 000000000..5fc2c1dd0 --- /dev/null +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -0,0 +1,220 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::queue::details::Queue; +use crate::vec::details::RelocatableVec; +use iceoryx2_bb_elementary::owning_pointer::OwningPointer; +use iceoryx2_bb_elementary::pointer_trait::PointerTrait; +use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; +use std::mem::MaybeUninit; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct SlotMapKey(usize); + +pub type SlotMap = details::RelocatableSlotMap< + T, + OwningPointer>>, + OwningPointer>, +>; +pub type RelocatableSlotMap = details::RelocatableSlotMap< + T, + RelocatablePointer>>, + RelocatablePointer>, +>; + +const INVALID_KEY: usize = usize::MAX; + +#[doc(hidden)] +pub mod details { + use super::*; + + pub struct Iter< + 'slotmap, + T, + DataPtrType: PointerTrait>>, + IdxPtrType: PointerTrait>, + > { + slotmap: &'slotmap RelocatableSlotMap, + key: SlotMapKey, + } + + impl< + 'slotmap, + T, + DataPtrType: PointerTrait>>, + IdxPtrType: PointerTrait>, + > Iterator for Iter<'slotmap, T, DataPtrType, IdxPtrType> + { + type Item = (SlotMapKey, &'slotmap T); + + fn next<'this>(&'this mut self) -> Option { + if let Some((key, value)) = self.slotmap.next(self.key) { + self.key.0 = key.0 + 1; + Some((key, value)) + } else { + None + } + } + } + + #[repr(C)] + #[derive(Debug)] + pub struct RelocatableSlotMap< + T, + DataPtrType: PointerTrait>>, + IdxPtrType: PointerTrait>, + > { + idx_to_data: RelocatableVec, + idx_to_data_next_free_index: Queue, + data: RelocatableVec, DataPtrType>, + data_next_free_index: Queue, + } + + impl< + T, + DataPtrType: PointerTrait>>, + IdxPtrType: PointerTrait>, + > RelocatableSlotMap + { + fn next(&self, start: SlotMapKey) -> Option<(SlotMapKey, &T)> { + let idx_to_data = &self.idx_to_data; + + for n in start.0..idx_to_data.len() { + let data_idx = self.idx_to_data[n]; + if data_idx != INVALID_KEY { + return Some(( + SlotMapKey(n), + self.data[data_idx].as_ref().expect( + "By contract, data contains a value when idx_to_data contains a value", + ), + )); + } + } + + 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>> + { + Iter { + slotmap: self, + key: SlotMapKey(0), + } + } + + pub fn get(&self, key: SlotMapKey) -> Option<&T> { + match self.idx_to_data[key.0] { + INVALID_KEY => None, + n => Some(self.data[n].as_ref().expect( + "data and idx_to_data correspond and this value must be always available.", + )), + } + } + + pub fn get_mut(&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( + "data and idx_to_data correspond and this value must be always available.", + )), + } + } + + pub fn insert(&mut self, value: T) -> Option { + match self.idx_to_data_next_free_index.pop() { + None => None, + Some(key) => { + let key = SlotMapKey(key); + self.insert_at(key, value); + Some(key) + } + } + } + + pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { + if key.0 > self.capacity() { + return false; + } + + let data_idx = self.idx_to_data[key.0]; + if data_idx != INVALID_KEY { + 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."); + self.idx_to_data[key.0] = n; + self.data[n] = Some(value); + false + } + } + + pub fn remove(&mut self, key: SlotMapKey) -> bool { + if key.0 > self.idx_to_data.len() { + return false; + } + + 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.idx_to_data[key.0] = INVALID_KEY; + true + } else { + false + } + } + + pub fn len(&self) -> usize { + self.capacity() - self.data_next_free_index.len() + } + + pub fn capacity(&self) -> usize { + self.idx_to_data.capacity() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn is_full(&self) -> bool { + self.len() == self.capacity() + } + } +} diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index bd1b0fed6..9de10a684 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Contributors to the Eclipse Foundation +// Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation // // See the NOTICE file(s) distributed with this work for additional // information regarding copyright ownership. @@ -104,8 +104,6 @@ pub type RelocatableVec = details::RelocatableVec usize { - unaligned_mem_size::(capacity) - } - /// Returns the capacity of the vector pub fn capacity(&self) -> usize { self.capacity @@ -272,6 +265,12 @@ pub mod details { } } + fn fill_with_impl T>(&mut self, mut f: F) { + for _ in self.len..self.capacity { + self.push_unchecked(f()); + } + } + fn push_unchecked(&mut self, value: T) { unsafe { self.data_ptr @@ -358,6 +357,11 @@ pub mod details { 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) + } + /// Append all elements from other via [`Clone`]. pub fn extend_from_slice(&mut self, other: &[T]) -> bool where @@ -389,6 +393,11 @@ pub mod details { } impl RelocatableVec>> { + /// 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) + } + /// 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. /// @@ -413,6 +422,16 @@ pub mod details { self.fill_impl(value) } + /// Fill the remaining space of the vector by calling the provided closure repeatedly + /// + /// # Safety + /// + /// * [`RelocatableVec::init()`] must be called once before + /// + pub unsafe fn fill_with T>(&mut self, f: F) { + self.fill_with(f) + } + /// Append all elements from other via [`Clone`]. /// /// # Safety @@ -636,6 +655,11 @@ impl FixedSizeVec { unsafe { self.state.fill(value) } } + /// Fill the remaining space of the vector with value. + pub fn fill_with T>(&mut self, f: F) { + unsafe { self.state.fill_with(f) } + } + /// Append all elements from other via [`Clone`]. pub fn extend_from_slice(&mut self, other: &[T]) -> bool where diff --git a/iceoryx2-bb/container/tests/slotmap_tests.rs b/iceoryx2-bb/container/tests/slotmap_tests.rs new file mode 100644 index 000000000..254d72dc4 --- /dev/null +++ b/iceoryx2-bb/container/tests/slotmap_tests.rs @@ -0,0 +1,31 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use iceoryx2_bb_container::slotmap::SlotMap; +use iceoryx2_bb_testing::assert_that; + +mod slot_map { + + use super::*; + + const SUT_CAPACITY: usize = 128; + type Sut = SlotMap; + + #[test] + fn new_slotmap_is_empty() { + let sut = Sut::new(SUT_CAPACITY); + + assert_that!(sut, is_empty); + assert_that!(sut.is_full(), eq false); + assert_that!(sut, len 0); + } +} diff --git a/iceoryx2-bb/container/tests/vec_tests.rs b/iceoryx2-bb/container/tests/vec_tests.rs index 116d84c5f..6c1ef391f 100644 --- a/iceoryx2-bb/container/tests/vec_tests.rs +++ b/iceoryx2-bb/container/tests/vec_tests.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2023 Contributors to the Eclipse Foundation +// Copyright (c) 2023 - 2024 Contributors to the Eclipse Foundation // // See the NOTICE file(s) distributed with this work for additional // information regarding copyright ownership. From 175a54d8ed266c05a6a22d6173401b5451d0607e Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 14:40:42 +0100 Subject: [PATCH 03/17] [#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 693e8a6d0..c96ff1416 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 5fc2c1dd0..7bb487655 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 9de10a684..f56f6beb3 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`]. From 6aaacd53486306a710b56bbc651872312feedebb Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 17:58:01 +0100 Subject: [PATCH 04/17] [#504] FixedSizeSlot initialization --- iceoryx2-bb/container/src/slotmap.rs | 54 ++++++++++++-------- iceoryx2-bb/container/tests/slotmap_tests.rs | 12 +++++ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 7bb487655..192d17942 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -13,6 +13,7 @@ use crate::queue::details::MetaQueue; use crate::vec::details::MetaVec; use crate::{queue::RelocatableQueue, vec::RelocatableVec}; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::owning_pointer::OwningPointer; use iceoryx2_bb_elementary::pointer_trait::PointerTrait; @@ -107,6 +108,15 @@ pub mod details { None } + pub(crate) unsafe fn initialize_data_structures(&mut self) { + self.idx_to_data.fill(INVALID_KEY); + self.data.fill_with(|| None); + for n in 0..self.capacity_impl() { + self.idx_to_data_next_free_index.push_impl(n); + self.data_next_free_index.push_impl(n); + } + } + pub(crate) unsafe fn iter_impl(&self) -> Iter { Iter { slotmap: self, @@ -178,7 +188,7 @@ pub mod details { } pub(crate) unsafe fn len_impl(&self) -> usize { - self.capacity_impl() - self.data_next_free_index.len() + self.capacity_impl() - self.idx_to_data_next_free_index.len() } pub(crate) unsafe fn capacity_impl(&self) -> usize { @@ -202,7 +212,7 @@ pub mod details { > { unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { + let mut new_self = Self { idx_to_data: RelocatableVec::new(capacity, distance_to_data), idx_to_data_next_free_index: RelocatableQueue::new( capacity, @@ -222,7 +232,9 @@ pub mod details { + RelocatableQueue::::const_memory_size(capacity) as isize + RelocatableVec::>::const_memory_size(capacity) as isize, ), - } + }; + new_self.initialize_data_structures(); + new_self } unsafe fn new_uninit(capacity: usize) -> Self { @@ -283,13 +295,7 @@ pub mod details { 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); - } + unsafe { new_self.initialize_data_structures() }; new_self } @@ -404,24 +410,30 @@ pub struct FixedSizeSlotMap { impl Default for FixedSizeSlotMap { fn default() -> Self { - Self { - state: Self::initialize_state(), + let mut new_self = Self { _idx_to_data: core::array::from_fn(|_| INVALID_KEY), - _idx_to_data_next_free_index: core::array::from_fn(|i| i), + _idx_to_data_next_free_index: core::array::from_fn(|_| 0), _data: core::array::from_fn(|_| None), - _data_next_free_index: core::array::from_fn(|_| INVALID_KEY), - } + _data_next_free_index: core::array::from_fn(|_| 0), + state: Self::initialize_state(), + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self._idx_to_data) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + unsafe { new_self.state.initialize_data_structures() }; + + new_self } } impl FixedSizeSlotMap { fn initialize_state() -> RelocatableSlotMap { - unsafe { - RelocatableSlotMap::new( - CAPACITY, - align_to::>(std::mem::size_of::>()) as isize, - ) - } + unsafe { RelocatableSlotMap::new_uninit(CAPACITY) } } pub fn new() -> Self { diff --git a/iceoryx2-bb/container/tests/slotmap_tests.rs b/iceoryx2-bb/container/tests/slotmap_tests.rs index 254d72dc4..75358c8c4 100644 --- a/iceoryx2-bb/container/tests/slotmap_tests.rs +++ b/iceoryx2-bb/container/tests/slotmap_tests.rs @@ -15,17 +15,29 @@ use iceoryx2_bb_testing::assert_that; mod slot_map { + use iceoryx2_bb_container::slotmap::FixedSizeSlotMap; + use super::*; const SUT_CAPACITY: usize = 128; type Sut = SlotMap; + type FixedSizeSut = FixedSizeSlotMap; #[test] fn new_slotmap_is_empty() { let sut = Sut::new(SUT_CAPACITY); + assert_that!(sut, len 0); assert_that!(sut, is_empty); assert_that!(sut.is_full(), eq false); + } + + #[test] + fn new_fixed_size_slotmap_is_empty() { + let sut = FixedSizeSut::new(); + assert_that!(sut, len 0); + assert_that!(sut, is_empty); + assert_that!(sut.is_full(), eq false); } } From 0aa10d2f888c853e7c5e0ff56468107929fb534c Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 18:14:28 +0100 Subject: [PATCH 05/17] [#504] RelocatableContainer init is mutable --- iceoryx2-bb/container/src/queue.rs | 2 +- iceoryx2-bb/container/src/slotmap.rs | 4 ++-- iceoryx2-bb/container/src/vec.rs | 2 +- iceoryx2-bb/elementary/src/relocatable_container.rs | 2 +- iceoryx2-bb/lock-free/src/mpmc/bit_set.rs | 2 +- iceoryx2-bb/lock-free/src/mpmc/container.rs | 2 +- iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs | 2 +- iceoryx2-bb/lock-free/src/spsc/index_queue.rs | 2 +- .../src/spsc/safely_overflowing_index_queue.rs | 2 +- iceoryx2-bb/lock-free/tests/mpmc_container_tests.rs | 2 +- .../lock-free/tests/mpmc_unique_index_set_tests.rs | 2 +- iceoryx2-bb/memory/src/pool_allocator.rs | 2 +- iceoryx2-bb/memory/tests/pool_allocator_tests.rs | 2 +- .../trait-tests/tests/relocatable_container_tests.rs | 6 +++--- iceoryx2-cal/src/shared_memory/common.rs | 2 +- iceoryx2-cal/src/shm_allocator/bump_allocator.rs | 2 +- iceoryx2-cal/src/shm_allocator/mod.rs | 2 +- iceoryx2-cal/src/shm_allocator/pool_allocator.rs | 2 +- .../src/zero_copy_connection/used_chunk_list.rs | 2 +- iceoryx2-cal/tests/event_id_tracker_tests.rs | 12 ++++++------ .../tests/shm_allocator_pool_allocator_tests.rs | 2 +- iceoryx2-cal/tests/shm_allocator_trait_tests.rs | 2 +- iceoryx2/src/service/dynamic_config/event.rs | 2 +- iceoryx2/src/service/dynamic_config/mod.rs | 8 ++++---- .../src/service/dynamic_config/publish_subscribe.rs | 2 +- 25 files changed, 36 insertions(+), 36 deletions(-) diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index c96ff1416..18b15b7c1 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -230,7 +230,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &Allocator, ) -> Result<(), AllocationError> { if self diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 192d17942..c822c8cba 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -247,7 +247,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &Allocator, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { let msg = "Unable to initialize RelocatableSlotMap"; @@ -264,6 +264,7 @@ pub mod details { when self.data_next_free_index.init(allocator), "{msg} since the underlying data_next_free_index queue could not be initialized."); + self.initialize_data_structures(); Ok(()) } @@ -425,7 +426,6 @@ impl Default for FixedSizeSlotMap { .init(&allocator) .expect("All required memory is preallocated.") }; - unsafe { new_self.state.initialize_data_structures() }; new_self } diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index f56f6beb3..3385d0cc4 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -152,7 +152,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &Allocator, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { if self.is_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/elementary/src/relocatable_container.rs b/iceoryx2-bb/elementary/src/relocatable_container.rs index d989683b3..58ba2dde3 100644 --- a/iceoryx2-bb/elementary/src/relocatable_container.rs +++ b/iceoryx2-bb/elementary/src/relocatable_container.rs @@ -51,7 +51,7 @@ pub trait RelocatableContainer { /// * Shall be only used when the [`RelocatableContainer`] was created with /// [`RelocatableContainer::new_uninit()`] /// - unsafe fn init(&self, allocator: &T) -> Result<(), AllocationError>; + unsafe fn init(&mut self, allocator: &T) -> Result<(), AllocationError>; /// Returns the amount of memory the object requires. The whole memory consumption is /// `std::mem::size_of::() + RelocatableContainer::memory_size()`. diff --git a/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs b/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs index 3a14966f4..96b40112d 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs @@ -125,7 +125,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &T, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/lock-free/src/mpmc/container.rs b/iceoryx2-bb/lock-free/src/mpmc/container.rs index 5ba86939e..4353ec69f 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/container.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/container.rs @@ -198,7 +198,7 @@ impl RelocatableContainer for Container { } unsafe fn init( - &self, + &mut self, allocator: &Allocator, ) -> Result<(), AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs index 68cd0d391..364b404cc 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs @@ -277,7 +277,7 @@ impl RelocatableContainer for UniqueIndexSet { } } - unsafe fn init(&self, allocator: &T) -> Result<(), AllocationError> { + unsafe fn init(&mut self, allocator: &T) -> Result<(), AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { fatal_panic!(from self, "Memory already initialized. Initializing it twice may lead to undefined behavior."); } diff --git a/iceoryx2-bb/lock-free/src/spsc/index_queue.rs b/iceoryx2-bb/lock-free/src/spsc/index_queue.rs index 50804c4ff..04c879ce1 100644 --- a/iceoryx2-bb/lock-free/src/spsc/index_queue.rs +++ b/iceoryx2-bb/lock-free/src/spsc/index_queue.rs @@ -152,7 +152,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &T, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs b/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs index a57baa44e..43d84315f 100644 --- a/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs +++ b/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs @@ -165,7 +165,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &T, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/lock-free/tests/mpmc_container_tests.rs b/iceoryx2-bb/lock-free/tests/mpmc_container_tests.rs index 1ae91a1b7..d8f3a1170 100644 --- a/iceoryx2-bb/lock-free/tests/mpmc_container_tests.rs +++ b/iceoryx2-bb/lock-free/tests/mpmc_container_tests.rs @@ -131,7 +131,7 @@ mod mpmc_container { // case - hack required since `T` cannot be used in const operations let mut memory = [0u8; Container::::const_memory_size(129_usize)]; let allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); - let sut = unsafe { Container::::new_uninit(CAPACITY) }; + let mut sut = unsafe { Container::::new_uninit(CAPACITY) }; unsafe { assert_that!(sut.init(&allocator), is_ok) }; let mut stored_indices = vec![]; diff --git a/iceoryx2-bb/lock-free/tests/mpmc_unique_index_set_tests.rs b/iceoryx2-bb/lock-free/tests/mpmc_unique_index_set_tests.rs index 12a36a012..05c996e29 100644 --- a/iceoryx2-bb/lock-free/tests/mpmc_unique_index_set_tests.rs +++ b/iceoryx2-bb/lock-free/tests/mpmc_unique_index_set_tests.rs @@ -137,7 +137,7 @@ fn mpmc_unique_index_set_borrowed_indices_works() { fn mpmc_unique_index_set_acquire_and_release_works_with_uninitialized_memory() { let mut memory = [0u8; UniqueIndexSet::const_memory_size(128)]; let allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); - let sut = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; + let mut sut = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; unsafe { assert_that!(sut.init(&allocator), is_ok) }; let mut ids = vec![]; diff --git a/iceoryx2-bb/memory/src/pool_allocator.rs b/iceoryx2-bb/memory/src/pool_allocator.rs index 9d0a6d978..282080a80 100644 --- a/iceoryx2-bb/memory/src/pool_allocator.rs +++ b/iceoryx2-bb/memory/src/pool_allocator.rs @@ -125,7 +125,7 @@ impl PoolAllocator { /// * must be called exactly once before any other method can be called /// pub unsafe fn init( - &self, + &mut self, allocator: &Allocator, ) -> Result<(), AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-bb/memory/tests/pool_allocator_tests.rs b/iceoryx2-bb/memory/tests/pool_allocator_tests.rs index 1434cd063..9ad02aee3 100644 --- a/iceoryx2-bb/memory/tests/pool_allocator_tests.rs +++ b/iceoryx2-bb/memory/tests/pool_allocator_tests.rs @@ -488,7 +488,7 @@ fn pool_allocator_relocatable_acquire_all_memory_works() { const CHUNK_SIZE: usize = 100; let start_ptr = NonNull::new(test.get_mut_memory()).unwrap(); - let sut = + let mut sut = unsafe { PoolAllocator::new_uninit(BUCKET_LAYOUT, start_ptr, TestFixture::memory_size()) }; let mgmt_memory_size = PoolAllocator::memory_size(BUCKET_LAYOUT, TestFixture::memory_size()); diff --git a/iceoryx2-bb/trait-tests/tests/relocatable_container_tests.rs b/iceoryx2-bb/trait-tests/tests/relocatable_container_tests.rs index c28c4a15c..3c0a3e2f4 100644 --- a/iceoryx2-bb/trait-tests/tests/relocatable_container_tests.rs +++ b/iceoryx2-bb/trait-tests/tests/relocatable_container_tests.rs @@ -47,7 +47,7 @@ mod relocatable_container { let mut memory = memory(); let allocator = allocator(&mut *memory); - let sut = unsafe { T::new_uninit(capacity) }; + let mut sut = unsafe { T::new_uninit(capacity) }; let require_memory_size = T::memory_size(capacity); assert_that!(unsafe { sut.init(&allocator) }, is_ok); @@ -66,7 +66,7 @@ mod relocatable_container { let mut current_size = 0; for capacity in 1..MAX_CAPACITY { - let sut = unsafe { T::new_uninit(capacity) }; + let mut sut = unsafe { T::new_uninit(capacity) }; let require_memory_size = T::memory_size(capacity); assert_that!(unsafe { sut.init(&allocator) }, is_ok); @@ -83,7 +83,7 @@ mod relocatable_container { let mut memory = memory(); let allocator = allocator(&mut *memory); - let sut = unsafe { T::new_uninit(MAX_CAPACITY) }; + let mut sut = unsafe { T::new_uninit(MAX_CAPACITY) }; assert_that!(unsafe { sut.init(&allocator) }, is_ok); diff --git a/iceoryx2-cal/src/shared_memory/common.rs b/iceoryx2-cal/src/shared_memory/common.rs index df19d6182..38545aff8 100644 --- a/iceoryx2-cal/src/shared_memory/common.rs +++ b/iceoryx2-cal/src/shared_memory/common.rs @@ -187,7 +187,7 @@ pub mod details { Allocator::new_uninit(SystemInfo::PageSize.value(), memory, allocator_config) }); - if let Err(e) = unsafe { details.allocator.assume_init_ref().init(init_allocator) } { + if let Err(e) = unsafe { details.allocator.assume_init_mut().init(init_allocator) } { debug!(from self, "{} since the management memory for the allocator could not be initialized ({:?}).", msg, e); false } else { diff --git a/iceoryx2-cal/src/shm_allocator/bump_allocator.rs b/iceoryx2-cal/src/shm_allocator/bump_allocator.rs index 3f1e00cda..a8c5d7813 100644 --- a/iceoryx2-cal/src/shm_allocator/bump_allocator.rs +++ b/iceoryx2-cal/src/shm_allocator/bump_allocator.rs @@ -56,7 +56,7 @@ impl ShmAllocator for BumpAllocator { } unsafe fn init( - &self, + &mut self, _mgmt_allocator: &Allocator, ) -> Result<(), ShmAllocatorInitError> { let msg = "Unable to initialize allocator"; diff --git a/iceoryx2-cal/src/shm_allocator/mod.rs b/iceoryx2-cal/src/shm_allocator/mod.rs index 106e3b294..fa803a5af 100644 --- a/iceoryx2-cal/src/shm_allocator/mod.rs +++ b/iceoryx2-cal/src/shm_allocator/mod.rs @@ -76,7 +76,7 @@ pub trait ShmAllocator: Send + Sync + 'static { /// * must be called before any other method is called /// unsafe fn init( - &self, + &mut self, mgmt_allocator: &Allocator, ) -> Result<(), ShmAllocatorInitError>; diff --git a/iceoryx2-cal/src/shm_allocator/pool_allocator.rs b/iceoryx2-cal/src/shm_allocator/pool_allocator.rs index 20850e390..96f42abb0 100644 --- a/iceoryx2-cal/src/shm_allocator/pool_allocator.rs +++ b/iceoryx2-cal/src/shm_allocator/pool_allocator.rs @@ -88,7 +88,7 @@ impl ShmAllocator for PoolAllocator { } unsafe fn init( - &self, + &mut self, mgmt_allocator: &Allocator, ) -> Result<(), ShmAllocatorInitError> { let msg = "Unable to initialize allocator"; diff --git a/iceoryx2-cal/src/zero_copy_connection/used_chunk_list.rs b/iceoryx2-cal/src/zero_copy_connection/used_chunk_list.rs index 736193202..e9fb7a7a4 100644 --- a/iceoryx2-cal/src/zero_copy_connection/used_chunk_list.rs +++ b/iceoryx2-cal/src/zero_copy_connection/used_chunk_list.rs @@ -72,7 +72,7 @@ pub mod details { } unsafe fn init( - &self, + &mut self, allocator: &T, ) -> Result<(), iceoryx2_bb_elementary::allocator::AllocationError> { if self.is_memory_initialized.load(Ordering::Relaxed) { diff --git a/iceoryx2-cal/tests/event_id_tracker_tests.rs b/iceoryx2-cal/tests/event_id_tracker_tests.rs index d7e977863..5a8f840bd 100644 --- a/iceoryx2-cal/tests/event_id_tracker_tests.rs +++ b/iceoryx2-cal/tests/event_id_tracker_tests.rs @@ -39,7 +39,7 @@ mod event_id_tracker { const CAPACITY: usize = 5234; let mut memory = memory(); - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); assert_that!(sut.trigger_id_max().as_value(), lt CAPACITY); } @@ -49,7 +49,7 @@ mod event_id_tracker { let mut memory = memory(); const CAPACITY: usize = 1234; - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); assert_that!(unsafe { sut.acquire() }, eq None); @@ -66,7 +66,7 @@ mod event_id_tracker { let mut memory = memory(); const CAPACITY: usize = 1234; - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); for i in 0..CAPACITY { @@ -89,7 +89,7 @@ mod event_id_tracker { let mut memory = memory(); const CAPACITY: usize = 3234; - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); for i in 0..CAPACITY { @@ -117,7 +117,7 @@ mod event_id_tracker { let mut memory = memory(); const CAPACITY: usize = 234; - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); for i in 0..CAPACITY { @@ -147,7 +147,7 @@ mod event_id_tracker { let mut memory = memory(); const CAPACITY: usize = 1234; - let sut = unsafe { Sut::new_uninit(CAPACITY) }; + let mut sut = unsafe { Sut::new_uninit(CAPACITY) }; assert_that!(unsafe { sut.init(&allocator(&mut *memory)) }, is_ok); assert_that!(unsafe { sut.acquire() }, eq None); diff --git a/iceoryx2-cal/tests/shm_allocator_pool_allocator_tests.rs b/iceoryx2-cal/tests/shm_allocator_pool_allocator_tests.rs index 44cc93569..90e2fba65 100644 --- a/iceoryx2-cal/tests/shm_allocator_pool_allocator_tests.rs +++ b/iceoryx2-cal/tests/shm_allocator_pool_allocator_tests.rs @@ -42,7 +42,7 @@ mod shm_allocator_pool_allocator { MEM_SIZE, ); let config = &Config { bucket_layout }; - let sut = Box::new(unsafe { + let mut sut = Box::new(unsafe { PoolAllocator::new_uninit(MAX_SUPPORTED_ALIGNMENT, base_address, config) }); diff --git a/iceoryx2-cal/tests/shm_allocator_trait_tests.rs b/iceoryx2-cal/tests/shm_allocator_trait_tests.rs index 225179d12..ddb2489ee 100644 --- a/iceoryx2-cal/tests/shm_allocator_trait_tests.rs +++ b/iceoryx2-cal/tests/shm_allocator_trait_tests.rs @@ -149,7 +149,7 @@ mod shm_allocator { let mut test = TestFixture::::new(); test.prepare(); - let sut = unsafe { + let mut sut = unsafe { Sut::new_uninit( 1, NonNull::new_unchecked(test.memory.as_mut_slice()), diff --git a/iceoryx2/src/service/dynamic_config/event.rs b/iceoryx2/src/service/dynamic_config/event.rs index 41a295e57..0da479fa8 100644 --- a/iceoryx2/src/service/dynamic_config/event.rs +++ b/iceoryx2/src/service/dynamic_config/event.rs @@ -72,7 +72,7 @@ impl DynamicConfig { } } - pub(crate) unsafe fn init(&self, allocator: &BumpAllocator) { + pub(crate) unsafe fn init(&mut self, allocator: &BumpAllocator) { fatal_panic!(from "event::DynamicConfig::init", when self.listeners.init(allocator), "This should never happen! Unable to initialize listener port id container."); diff --git a/iceoryx2/src/service/dynamic_config/mod.rs b/iceoryx2/src/service/dynamic_config/mod.rs index 576bd4051..42943b51a 100644 --- a/iceoryx2/src/service/dynamic_config/mod.rs +++ b/iceoryx2/src/service/dynamic_config/mod.rs @@ -92,12 +92,12 @@ impl DynamicConfig { Container::::memory_size(max_number_of_nodes) } - pub(crate) unsafe fn init(&self, allocator: &BumpAllocator) { + pub(crate) unsafe fn init(&mut self, allocator: &BumpAllocator) { fatal_panic!(from self, when self.nodes.init(allocator), "This should never happen! Unable to initialize NodeId container."); - match &self.messaging_pattern { - MessagingPattern::PublishSubscribe(ref v) => v.init(allocator), - MessagingPattern::Event(ref v) => v.init(allocator), + match &mut self.messaging_pattern { + MessagingPattern::PublishSubscribe(ref mut v) => v.init(allocator), + MessagingPattern::Event(ref mut v) => v.init(allocator), } } diff --git a/iceoryx2/src/service/dynamic_config/publish_subscribe.rs b/iceoryx2/src/service/dynamic_config/publish_subscribe.rs index 52db35567..9fae84e35 100644 --- a/iceoryx2/src/service/dynamic_config/publish_subscribe.rs +++ b/iceoryx2/src/service/dynamic_config/publish_subscribe.rs @@ -75,7 +75,7 @@ impl DynamicConfig { } } - pub(crate) unsafe fn init(&self, allocator: &BumpAllocator) { + pub(crate) unsafe fn init(&mut self, allocator: &BumpAllocator) { fatal_panic!(from self, when self.subscribers.init(allocator), "This should never happen! Unable to initialize subscriber port id container."); From e4ac8e70ed8a99df25a748d7a623920bee4e5d14 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 18:52:39 +0100 Subject: [PATCH 06/17] [#504] RelocatableContainer new with distance removed --- iceoryx2-bb/container/src/queue.rs | 46 ++++++++--------- iceoryx2-bb/container/src/slotmap.rs | 38 +++----------- iceoryx2-bb/container/src/vec.rs | 50 ++++++++----------- iceoryx2-bb/elementary/src/bump_allocator.rs | 5 +- .../elementary/src/relocatable_container.rs | 13 ----- iceoryx2-bb/lock-free/src/mpmc/bit_set.rs | 34 ++++++------- iceoryx2-bb/lock-free/src/mpmc/container.rs | 46 ++++++----------- .../lock-free/src/mpmc/unique_index_set.rs | 43 ++++++---------- iceoryx2-bb/lock-free/src/spsc/index_queue.rs | 36 ++++++------- .../spsc/safely_overflowing_index_queue.rs | 37 ++++++-------- iceoryx2-bb/memory/src/pool_allocator.rs | 23 ++++++--- .../zero_copy_connection/used_chunk_list.rs | 30 +++++------ 12 files changed, 152 insertions(+), 249 deletions(-) diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index 18b15b7c1..582bb3a66 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -96,7 +96,7 @@ //! ``` //! use iceoryx2_bb_elementary::allocator::{AllocationError, BaseAllocator}; -use iceoryx2_bb_elementary::math::align_to; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::math::unaligned_mem_size; use iceoryx2_bb_elementary::owning_pointer::OwningPointer; use iceoryx2_bb_elementary::placement_default::PlacementDefault; @@ -207,17 +207,6 @@ pub mod details { } impl RelocatableContainer for MetaQueue>> { - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - start: 0, - len: 0, - capacity, - is_initialized: IoxAtomicBool::new(true), - _phantom_data: PhantomData, - } - } - unsafe fn new_uninit(capacity: usize) -> Self { Self { data_ptr: RelocatablePointer::new_uninit(), @@ -467,31 +456,38 @@ pub struct FixedSizeQueue { impl PlacementDefault for FixedSizeQueue { unsafe fn placement_default(ptr: *mut Self) { let state_ptr = core::ptr::addr_of_mut!((*ptr).state); - state_ptr.write(Self::initialize_state()); + state_ptr.write(RelocatableQueue::new_uninit(CAPACITY)); + + let allocator = BumpAllocator::new(core::ptr::addr_of!((*ptr)._data) as usize); + (*ptr) + .state + .init(&allocator) + .expect("All required memory is preallocated."); } } impl Default for FixedSizeQueue { fn default() -> Self { - Self { - state: Self::initialize_state(), + let mut new_self = Self { + state: unsafe { RelocatableQueue::new_uninit(CAPACITY) }, _data: unsafe { MaybeUninit::uninit().assume_init() }, - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self._data) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } } unsafe impl Send for FixedSizeQueue {} impl FixedSizeQueue { - fn initialize_state() -> RelocatableQueue { - unsafe { - RelocatableQueue::new( - CAPACITY, - align_to::>(std::mem::size_of::>()) as isize, - ) - } - } - /// Creates a new queue. pub fn new() -> Self { Self::default() diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index c822c8cba..2ba964714 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -14,8 +14,8 @@ use crate::queue::details::MetaQueue; use crate::vec::details::MetaVec; use crate::{queue::RelocatableQueue, vec::RelocatableVec}; use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; -use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::owning_pointer::OwningPointer; +use iceoryx2_bb_elementary::placement_default::PlacementDefault; use iceoryx2_bb_elementary::pointer_trait::PointerTrait; use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; @@ -211,32 +211,6 @@ pub mod details { RelocatablePointer>, > { - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - let mut new_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, - ), - }; - new_self.initialize_data_structures(); - new_self - } - unsafe fn new_uninit(capacity: usize) -> Self { Self { idx_to_data: RelocatableVec::new_uninit(capacity), @@ -409,6 +383,10 @@ pub struct FixedSizeSlotMap { _data_next_free_index: [usize; CAPACITY], } +impl PlacementDefault for FixedSizeSlotMap { + unsafe fn placement_default(ptr: *mut Self) {} +} + impl Default for FixedSizeSlotMap { fn default() -> Self { let mut new_self = Self { @@ -416,7 +394,7 @@ impl Default for FixedSizeSlotMap { _idx_to_data_next_free_index: core::array::from_fn(|_| 0), _data: core::array::from_fn(|_| None), _data_next_free_index: core::array::from_fn(|_| 0), - state: Self::initialize_state(), + state: unsafe { RelocatableSlotMap::new_uninit(CAPACITY) }, }; let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self._idx_to_data) as usize); @@ -432,10 +410,6 @@ impl Default for FixedSizeSlotMap { } impl FixedSizeSlotMap { - fn initialize_state() -> RelocatableSlotMap { - unsafe { RelocatableSlotMap::new_uninit(CAPACITY) } - } - pub fn new() -> Self { Self::default() } diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index 3385d0cc4..41b7989f5 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -83,12 +83,10 @@ use std::{ sync::atomic::Ordering, }; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::{ - math::{align_to, unaligned_mem_size}, - owning_pointer::OwningPointer, - placement_default::PlacementDefault, - pointer_trait::PointerTrait, - relocatable_container::RelocatableContainer, + math::unaligned_mem_size, owning_pointer::OwningPointer, placement_default::PlacementDefault, + pointer_trait::PointerTrait, relocatable_container::RelocatableContainer, relocatable_ptr::RelocatablePointer, }; use iceoryx2_bb_log::{fail, fatal_panic}; @@ -131,16 +129,6 @@ pub mod details { } impl RelocatableContainer for MetaVec>> { - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - len: 0, - is_initialized: IoxAtomicBool::new(true), - _phantom_data: PhantomData, - } - } - unsafe fn new_uninit(capacity: usize) -> Self { Self { data_ptr: RelocatablePointer::new_uninit(), @@ -563,16 +551,31 @@ impl<'de, T: Deserialize<'de>, const CAPACITY: usize> Deserialize<'de> impl PlacementDefault for FixedSizeVec { unsafe fn placement_default(ptr: *mut Self) { let state_ptr = core::ptr::addr_of_mut!((*ptr).state); - state_ptr.write(Self::initialize_state()) + state_ptr.write(unsafe { RelocatableVec::new_uninit(CAPACITY) }); + let allocator = BumpAllocator::new(core::ptr::addr_of!((*ptr)._data) as usize); + (*ptr) + .state + .init(&allocator) + .expect("All required memory is preallocated."); } } impl Default for FixedSizeVec { fn default() -> Self { - Self { - state: Self::initialize_state(), + let mut new_self = Self { + state: unsafe { RelocatableVec::new_uninit(CAPACITY) }, _data: core::array::from_fn(|_| MaybeUninit::uninit()), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self._data) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } } @@ -609,15 +612,6 @@ impl Clone for FixedSizeVec { unsafe impl Send for FixedSizeVec {} impl FixedSizeVec { - fn initialize_state() -> RelocatableVec { - unsafe { - RelocatableVec::new( - CAPACITY, - align_to::>(std::mem::size_of::>()) as isize, - ) - } - } - /// Creates a new vector. pub fn new() -> Self { Self::default() diff --git a/iceoryx2-bb/elementary/src/bump_allocator.rs b/iceoryx2-bb/elementary/src/bump_allocator.rs index 9bdd0d846..10ba93024 100644 --- a/iceoryx2-bb/elementary/src/bump_allocator.rs +++ b/iceoryx2-bb/elementary/src/bump_allocator.rs @@ -14,15 +14,14 @@ use crate::{allocator::BaseAllocator, math::align}; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicUsize; use std::sync::atomic::Ordering; -/// Simple BumpAllocator for testing purposes. Do not use this in production. If you are looking -/// for a production ready BumpAllocator use the one from iceoryx2_bb_memory::bump_allocator -#[doc(hidden)] +/// A minimalistic [`BumpAllocator`]. pub struct BumpAllocator { start: usize, pos: IoxAtomicUsize, } impl BumpAllocator { + /// Creates a new [`BumpAllocator`] that manages the memory starting at `start`. pub fn new(start: usize) -> Self { Self { start, diff --git a/iceoryx2-bb/elementary/src/relocatable_container.rs b/iceoryx2-bb/elementary/src/relocatable_container.rs index 58ba2dde3..35792666c 100644 --- a/iceoryx2-bb/elementary/src/relocatable_container.rs +++ b/iceoryx2-bb/elementary/src/relocatable_container.rs @@ -18,19 +18,6 @@ use crate::{allocator::AllocationError, allocator::BaseAllocator}; /// mapped at a different virtual memory position the underlying constructs must be relocatable in /// the sense that they should not rely on absolut memory positions. pub trait RelocatableContainer { - /// Creates a new RelocatableContainer. It assumes that the memory of size - /// [`RelocatableContainer::memory_size()`] has the position self + distance_to_data. - /// This approach requires that the object itself and the data of the object are placed in - /// the same shared memory object. - /// - /// # Safety - /// - /// * `distance_to_data` is the offset to the data. The offset refers to the pointer value of - /// the [`RelocatableContainer`] - memory position. - /// * the provided memory must have the size of [`RelocatableContainer::memory_size()`] - /// - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self; - /// Creates a new uninitialized RelocatableContainer. Before the container can be used the method /// [`RelocatableContainer::init()`] must be called. /// diff --git a/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs b/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs index 96b40112d..80057a461 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/bit_set.rs @@ -37,7 +37,7 @@ //! ``` use iceoryx2_bb_elementary::{ - math::align_to, + bump_allocator::BumpAllocator, math::unaligned_mem_size, owning_pointer::OwningPointer, relocatable_container::RelocatableContainer, @@ -160,16 +160,6 @@ pub mod details { fn memory_size(capacity: usize) -> usize { Self::const_memory_size(capacity) } - - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - array_capacity: Self::array_capacity(capacity), - is_memory_initialized: IoxAtomicBool::new(true), - reset_position: IoxAtomicUsize::new(0), - } - } } impl + Debug> BitSet { @@ -313,16 +303,20 @@ unsafe impl Sync for FixedSizeBitSet {} impl Default for FixedSizeBitSet { fn default() -> Self { - Self { - bitset: unsafe { - RelocatableBitSet::new( - CAPACITY, - align_to::(std::mem::size_of::()) - as _, - ) - }, + let mut new_self = Self { + bitset: unsafe { RelocatableBitSet::new_uninit(CAPACITY) }, data: core::array::from_fn(|_| details::BitsetElement::new(0)), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.data) as usize); + unsafe { + new_self + .bitset + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } } diff --git a/iceoryx2-bb/lock-free/src/mpmc/container.rs b/iceoryx2-bb/lock-free/src/mpmc/container.rs index 4353ec69f..9f0790adb 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/container.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/container.rs @@ -54,6 +54,7 @@ //! ``` pub use crate::mpmc::unique_index_set::ReleaseMode; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; pub use iceoryx2_bb_elementary::CallbackProgression; use iceoryx2_bb_elementary::allocator::AllocationError; @@ -234,30 +235,6 @@ impl RelocatableContainer for Container { Ok(()) } - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - let unique_index_set_distance = distance_to_data - - align_to::(std::mem::size_of::>()) as isize - + align_to::(std::mem::size_of::()) as isize; - - let distance_to_active_index = align_to::( - distance_to_data as usize + (std::mem::size_of::() * (capacity + 1)), - ) as isize; - let distance_to_container_data = align_to::>>( - distance_to_active_index as usize + (std::mem::size_of::() * capacity), - ) as isize - - std::mem::size_of::>() as isize; - - Self { - container_id: UniqueId::new(), - active_index_ptr: RelocatablePointer::new(distance_to_active_index), - data_ptr: RelocatablePointer::new(distance_to_container_data), - capacity, - change_counter: IoxAtomicU64::new(0), - index_set: UniqueIndexSet::new(capacity, unique_index_set_distance), - is_memory_initialized: IoxAtomicBool::new(true), - } - } - fn memory_size(capacity: usize) -> usize { Self::const_memory_size(capacity) } @@ -472,18 +449,23 @@ pub struct FixedSizeContainer { impl Default for FixedSizeContainer { fn default() -> Self { - Self { - container: unsafe { - Container::new( - CAPACITY, - align_to::(std::mem::size_of::>()) as isize, - ) - }, + let mut new_self = Self { + container: unsafe { Container::new_uninit(CAPACITY) }, next_free_index: core::array::from_fn(|i| UnsafeCell::new(i as u32 + 1)), next_free_index_plus_one: UnsafeCell::new(CAPACITY as u32 + 1), active_index: core::array::from_fn(|_| IoxAtomicU64::new(0)), data: core::array::from_fn(|_| UnsafeCell::new(MaybeUninit::uninit())), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.next_free_index) as usize); + unsafe { + new_self + .container + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } } diff --git a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs index 364b404cc..78a49b29b 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs @@ -82,8 +82,8 @@ //! ``` use iceoryx2_bb_elementary::allocator::{AllocationError, BaseAllocator}; +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::enum_gen; -use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::pointer_trait::PointerTrait; use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; @@ -299,15 +299,6 @@ impl RelocatableContainer for UniqueIndexSet { Ok(()) } - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity: capacity as u32, - head: IoxAtomicU64::new(0), - is_memory_initialized: IoxAtomicBool::new(true), - } - } - fn memory_size(capacity: usize) -> usize { Self::const_memory_size(capacity) } @@ -529,16 +520,7 @@ pub struct FixedSizeUniqueIndexSet { impl Default for FixedSizeUniqueIndexSet { fn default() -> Self { - Self { - state: unsafe { - UniqueIndexSet::new( - CAPACITY, - align_to::>(std::mem::size_of::()) as isize, - ) - }, - next_free_index: core::array::from_fn(|i| UnsafeCell::new(i as u32 + 1)), - next_free_index_plus_one: UnsafeCell::new(CAPACITY as u32 + 1), - } + Self::new_with_reduced_capacity(CAPACITY).expect("Does not exceed supported capacity.") } } @@ -565,16 +547,21 @@ impl FixedSizeUniqueIndexSet { "Provided value of capacity is zero."); } - Ok(Self { - state: unsafe { - UniqueIndexSet::new( - capacity, - align_to::>(std::mem::size_of::()) as isize, - ) - }, + let mut new_self = Self { + state: unsafe { UniqueIndexSet::new_uninit(capacity) }, next_free_index: core::array::from_fn(|i| UnsafeCell::new(i as u32 + 1)), next_free_index_plus_one: UnsafeCell::new(capacity as u32 + 1), - }) + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.next_free_index) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + Ok(new_self) } /// See [`UniqueIndexSet::acquire()`] diff --git a/iceoryx2-bb/lock-free/src/spsc/index_queue.rs b/iceoryx2-bb/lock-free/src/spsc/index_queue.rs index 04c879ce1..b79561852 100644 --- a/iceoryx2-bb/lock-free/src/spsc/index_queue.rs +++ b/iceoryx2-bb/lock-free/src/spsc/index_queue.rs @@ -45,7 +45,7 @@ use std::{alloc::Layout, cell::UnsafeCell, fmt::Debug, sync::atomic::Ordering}; use iceoryx2_bb_elementary::{ - math::align_to, owning_pointer::OwningPointer, pointer_trait::PointerTrait, + bump_allocator::BumpAllocator, owning_pointer::OwningPointer, pointer_trait::PointerTrait, relocatable_container::RelocatableContainer, relocatable_ptr::RelocatablePointer, }; use iceoryx2_bb_log::{fail, fatal_panic}; @@ -175,18 +175,6 @@ pub mod details { Ok(()) } - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - write_position: IoxAtomicUsize::new(0), - read_position: IoxAtomicUsize::new(0), - has_producer: IoxAtomicBool::new(true), - has_consumer: IoxAtomicBool::new(true), - is_memory_initialized: IoxAtomicBool::new(true), - } - } - fn memory_size(capacity: usize) -> usize { Self::const_memory_size(capacity) } @@ -390,16 +378,20 @@ impl Default for FixedSizeIndexQueue { impl FixedSizeIndexQueue { /// Creates a new empty [`FixedSizeIndexQueue`]. pub fn new() -> Self { - Self { - state: unsafe { - RelocatableIndexQueue::new( - CAPACITY, - align_to::>(std::mem::size_of::()) - as isize, - ) - }, + let mut new_self = Self { + state: unsafe { RelocatableIndexQueue::new_uninit(CAPACITY) }, data: core::array::from_fn(|_| UnsafeCell::new(0)), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.data) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } /// See [`IndexQueue::acquire_producer()`] diff --git a/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs b/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs index 43d84315f..976dd1afc 100644 --- a/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs +++ b/iceoryx2-bb/lock-free/src/spsc/safely_overflowing_index_queue.rs @@ -47,7 +47,7 @@ use iceoryx2_pal_concurrency_sync::iox_atomic::{IoxAtomicBool, IoxAtomicUsize}; use std::{alloc::Layout, cell::UnsafeCell, fmt::Debug, sync::atomic::Ordering}; use iceoryx2_bb_elementary::{ - math::align_to, owning_pointer::OwningPointer, pointer_trait::PointerTrait, + bump_allocator::BumpAllocator, owning_pointer::OwningPointer, pointer_trait::PointerTrait, relocatable_container::RelocatableContainer, relocatable_ptr::RelocatablePointer, }; use iceoryx2_bb_log::{fail, fatal_panic}; @@ -188,18 +188,6 @@ pub mod details { Ok(()) } - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - write_position: IoxAtomicUsize::new(0), - read_position: IoxAtomicUsize::new(0), - has_producer: IoxAtomicBool::new(true), - has_consumer: IoxAtomicBool::new(true), - is_memory_initialized: IoxAtomicBool::new(true), - } - } - fn memory_size(capacity: usize) -> usize { Self::const_memory_size(capacity) } @@ -445,18 +433,21 @@ impl Default for FixedSizeSafelyOverflowingIndexQueue FixedSizeSafelyOverflowingIndexQueue { /// Creates a new empty [`FixedSizeSafelyOverflowingIndexQueue`]. pub fn new() -> Self { - Self { - state: unsafe { - RelocatableSafelyOverflowingIndexQueue::new( - CAPACITY, - align_to::>(std::mem::size_of::< - RelocatableSafelyOverflowingIndexQueue, - >()) as isize, - ) - }, + let mut new_self = Self { + state: unsafe { RelocatableSafelyOverflowingIndexQueue::new_uninit(CAPACITY) }, data: core::array::from_fn(|_| UnsafeCell::new(0)), data_plus_one: UnsafeCell::new(0), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.data) as usize); + unsafe { + new_self + .state + .init(&allocator) + .expect("All required memory is preallocated.") + }; + + new_self } /// See [`SafelyOverflowingIndexQueue::acquire_producer()`] diff --git a/iceoryx2-bb/memory/src/pool_allocator.rs b/iceoryx2-bb/memory/src/pool_allocator.rs index 282080a80..9a4a5e519 100644 --- a/iceoryx2-bb/memory/src/pool_allocator.rs +++ b/iceoryx2-bb/memory/src/pool_allocator.rs @@ -47,6 +47,7 @@ //! Layout::from_size_align_unchecked(32, 4))}; //! ``` +use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::math::align; use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::relocatable_container::*; @@ -315,13 +316,13 @@ impl FixedSizePoolAllocator>(std::mem::size_of::()) as isize, - ) + UniqueIndexSet::new_uninit(std::cmp::min( + number_of_buckets, + MAX_NUMBER_OF_BUCKETS, + )) }, bucket_size: bucket_layout.size(), bucket_alignment: bucket_layout.align(), @@ -331,7 +332,17 @@ impl FixedSizePoolAllocator usize { Self::const_memory_size(capacity) } - - unsafe fn new(capacity: usize, distance_to_data: isize) -> Self { - Self { - data_ptr: RelocatablePointer::new(distance_to_data), - capacity, - is_memory_initialized: IoxAtomicBool::new(true), - } - } } impl + Debug> UsedChunkList { @@ -175,15 +167,19 @@ pub struct FixedSizeUsedChunkList { impl Default for FixedSizeUsedChunkList { fn default() -> Self { - Self { - list: unsafe { - RelocatableUsedChunkList::new( - CAPACITY, - align_to::(std::mem::size_of::()) as _, - ) - }, + let mut new_self = Self { + list: unsafe { RelocatableUsedChunkList::new_uninit(CAPACITY) }, data: core::array::from_fn(|_| IoxAtomicBool::new(false)), - } + }; + + let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.data) as usize); + unsafe { + new_self + .list + .init(&allocator) + .expect("All required memory is preallocated.") + }; + new_self } } From 32caf056bf3241feac423388b73f5a3d5c489197 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 20:42:32 +0100 Subject: [PATCH 07/17] [#504] Finish SlotMap tests --- iceoryx2-bb/container/src/slotmap.rs | 208 +++++++++++++++---- iceoryx2-bb/container/src/vec.rs | 2 +- iceoryx2-bb/container/tests/slotmap_tests.rs | 97 ++++++++- iceoryx2-bb/memory/src/pool_allocator.rs | 1 - 4 files changed, 265 insertions(+), 43 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 2ba964714..3f66e8106 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -22,14 +22,32 @@ use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; use iceoryx2_bb_log::fail; use std::mem::MaybeUninit; +/// A key of a [`SlotMap`], [`RelocatableSlotMap`] or [`FixedSizeSlotMap`] that identifies a +/// value. #[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct SlotMapKey(usize); +impl SlotMapKey { + /// Creates a new [`SlotMapKey`] with the specified value. + pub fn new(value: usize) -> Self { + Self(value) + } + + /// Returns the underlying value of the [`SlotMapKey`]. + pub fn value(&self) -> usize { + self.0 + } +} + +/// A runtime fixed-size, non-shared memory compatible [`SlotMap`]. The [`SlotMap`]s memory resides +/// in the heap. pub type SlotMap = details::MetaSlotMap< T, OwningPointer>>, OwningPointer>, >; + +/// A runtime fixed-size, shared-memory compatible [`RelocatableSlotMap`]. pub type RelocatableSlotMap = details::MetaSlotMap< T, RelocatablePointer>>, @@ -42,6 +60,7 @@ const INVALID_KEY: usize = usize::MAX; pub mod details { use super::*; + /// The iterator of a [`SlotMap`], [`RelocatableSlotMap`] or [`FixedSizeSlotMap`]. pub struct Iter< 'slotmap, T, @@ -52,6 +71,15 @@ pub mod details { key: SlotMapKey, } + pub type OwningIter<'slotmap, T> = + Iter<'slotmap, T, OwningPointer>>, OwningPointer>>; + pub type RelocatableIter<'slotmap, T> = Iter< + 'slotmap, + T, + RelocatablePointer>>, + RelocatablePointer>, + >; + impl< 'slotmap, T, @@ -61,7 +89,7 @@ pub mod details { { type Item = (SlotMapKey, &'slotmap T); - fn next<'this>(&'this mut self) -> Option { + fn next(&mut self) -> Option { if let Some((key, value)) = self.slotmap.next(self.key) { self.key.0 = key.0 + 1; Some((key, value)) @@ -109,9 +137,9 @@ pub mod details { } pub(crate) unsafe fn initialize_data_structures(&mut self) { - self.idx_to_data.fill(INVALID_KEY); - self.data.fill_with(|| None); for n in 0..self.capacity_impl() { + self.idx_to_data.push_impl(INVALID_KEY); + self.data.push_impl(None); self.idx_to_data_next_free_index.push_impl(n); self.data_next_free_index.push_impl(n); } @@ -124,6 +152,10 @@ pub mod details { } } + pub(crate) unsafe fn contains_impl(&self, key: SlotMapKey) -> bool { + self.idx_to_data[key.0] != INVALID_KEY + } + pub(crate) unsafe fn get_impl(&self, key: SlotMapKey) -> Option<&T> { match self.idx_to_data[key.0] { INVALID_KEY => None, @@ -161,13 +193,13 @@ pub mod details { let data_idx = self.idx_to_data[key.0]; if data_idx != INVALID_KEY { self.data[data_idx] = Some(value); - true } else { 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 } + + true } pub(crate) unsafe fn remove_impl(&mut self, key: SlotMapKey) -> bool { @@ -187,19 +219,19 @@ pub mod details { } } - pub(crate) unsafe fn len_impl(&self) -> usize { + pub(crate) fn len_impl(&self) -> usize { self.capacity_impl() - self.idx_to_data_next_free_index.len() } - pub(crate) unsafe fn capacity_impl(&self) -> usize { + pub(crate) fn capacity_impl(&self) -> usize { self.idx_to_data.capacity() } - pub(crate) unsafe fn is_empty_impl(&self) -> bool { + pub(crate) fn is_empty_impl(&self) -> bool { self.len_impl() == 0 } - pub(crate) unsafe fn is_full_impl(&self) -> bool { + pub(crate) fn is_full_impl(&self) -> bool { self.len_impl() == self.capacity_impl() } } @@ -254,6 +286,8 @@ pub mod details { RelocatablePointer>, > { + /// Returns how many memory the [`RelocatableSlotMap`] will allocate from the allocator + /// in [`RelocatableSlotMap::init()`]. pub const fn const_memory_size(capacity: usize) -> usize { RelocatableVec::::const_memory_size(capacity) + RelocatableQueue::::const_memory_size(capacity) @@ -263,6 +297,7 @@ pub mod details { } impl MetaSlotMap>>, OwningPointer>> { + /// Creates a new runtime-fixed size [`SlotMap`] on the heap with the given capacity. pub fn new(capacity: usize) -> Self { let mut new_self = Self { idx_to_data: MetaVec::new(capacity), @@ -274,47 +309,65 @@ pub mod details { new_self } - pub fn iter( - &self, - ) -> Iter>>, OwningPointer>> - { + /// Returns the [`Iter`]ator to iterate over all entries. + pub fn iter(&self) -> OwningIter { unsafe { self.iter_impl() } } + /// Returns `true` if the provided `key` is contained, otherwise `false`. + pub fn contains(&self, key: SlotMapKey) -> bool { + unsafe { self.contains_impl(key) } + } + + /// Returns a reference to the value stored under the given key. If there is no such key, + /// [`None`] is returned. pub fn get(&self, key: SlotMapKey) -> Option<&T> { unsafe { self.get_impl(key) } } + /// Returns a mutable reference to the value stored under the given key. If there is no + /// such key, [`None`] is returned. pub fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { unsafe { self.get_mut_impl(key) } } + /// Insert a value and returns the corresponding [`SlotMapKey`]. If the container is full + /// [`None`] is returned. pub fn insert(&mut self, value: T) -> Option { unsafe { self.insert_impl(value) } } + /// Insert a value at the specified [`SlotMapKey`] and returns true. If the provided key + /// is out-of-bounds it returns `false` and adds nothing. If there is already a value + /// stored at the `key`s index, the value is overridden with the provided value. pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { unsafe { self.insert_at_impl(key, value) } } + /// Removes a value at the specified [`SlotMapKey`]. If there was no value corresponding + /// to the [`SlotMapKey`] it returns false, otherwise true. pub fn remove(&mut self, key: SlotMapKey) -> bool { unsafe { self.remove_impl(key) } } + /// Returns the number of stored values. pub fn len(&self) -> usize { - unsafe { self.len_impl() } + self.len_impl() } + /// Returns the capacity. pub fn capacity(&self) -> usize { - unsafe { self.capacity_impl() } + self.capacity_impl() } + /// Returns true if the container is empty, otherwise false. pub fn is_empty(&self) -> bool { - unsafe { self.is_empty_impl() } + self.is_empty_impl() } + /// Returns true if the container is full, otherwise false. pub fn is_full(&self) -> bool { - unsafe { self.is_full_impl() } + self.is_full_impl() } } @@ -325,54 +378,105 @@ pub mod details { RelocatablePointer>, > { - pub unsafe fn iter( - &self, - ) -> Iter< - T, - RelocatablePointer>>, - RelocatablePointer>, - > { + /// Returns the [`Iter`]ator to iterate over all entries. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// + pub unsafe fn iter(&self) -> RelocatableIter { self.iter_impl() } + /// Returns `true` if the provided `key` is contained, otherwise `false`. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// + pub unsafe fn contains(&self, key: SlotMapKey) -> bool { + self.contains_impl(key) + } + + /// Returns a reference to the value stored under the given key. If there is no such key, + /// [`None`] is returned. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// pub unsafe fn get(&self, key: SlotMapKey) -> Option<&T> { self.get_impl(key) } + /// Returns a mutable reference to the value stored under the given key. If there is no + /// such key, [`None`] is returned. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// pub unsafe fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { self.get_mut_impl(key) } + /// Insert a value and returns the corresponding [`SlotMapKey`]. If the container is full + /// [`None`] is returned. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// pub unsafe fn insert(&mut self, value: T) -> Option { self.insert_impl(value) } + /// Insert a value at the specified [`SlotMapKey`] and returns true. If the provided key + /// is out-of-bounds it returns `false` and adds nothing. If there is already a value + /// stored at the `key`s index, the value is overridden with the provided value. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// pub unsafe fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { self.insert_at_impl(key, value) } + /// Removes a value at the specified [`SlotMapKey`]. If there was no value corresponding + /// to the [`SlotMapKey`] it returns false, otherwise true. + /// + /// # Safety + /// + /// * [`RelocatableSlotMap::init()`] must be called once before + /// pub unsafe fn remove(&mut self, key: SlotMapKey) -> bool { self.remove_impl(key) } - pub unsafe fn len(&self) -> usize { + /// Returns the number of stored values. + pub fn len(&self) -> usize { self.len_impl() } - pub unsafe fn capacity(&self) -> usize { + /// Returns the capacity. + pub fn capacity(&self) -> usize { self.capacity_impl() } - pub unsafe fn is_empty(&self) -> bool { + /// Returns true if the container is empty, otherwise false. + pub fn is_empty(&self) -> bool { self.is_empty_impl() } - pub unsafe fn is_full(&self) -> bool { + /// Returns true if the container is full, otherwise false. + pub fn is_full(&self) -> bool { self.is_full_impl() } } } +/// A compile-time fixed-size, shared memory compatible [`FixedSizeSlotMap`]. #[repr(C)] #[derive(Debug)] pub struct FixedSizeSlotMap { @@ -384,7 +488,15 @@ pub struct FixedSizeSlotMap { } impl PlacementDefault for FixedSizeSlotMap { - unsafe fn placement_default(ptr: *mut Self) {} + unsafe fn placement_default(ptr: *mut Self) { + let state_ptr = core::ptr::addr_of_mut!((*ptr).state); + state_ptr.write(unsafe { RelocatableSlotMap::new_uninit(CAPACITY) }); + let allocator = BumpAllocator::new(core::ptr::addr_of!((*ptr)._data) as usize); + (*ptr) + .state + .init(&allocator) + .expect("All required memory is preallocated."); + } } impl Default for FixedSizeSlotMap { @@ -410,53 +522,69 @@ impl Default for FixedSizeSlotMap { } impl FixedSizeSlotMap { + /// Creates a new empty [`FixedSizeSlotMap`]. pub fn new() -> Self { Self::default() } - pub fn iter( - &self, - ) -> details::Iter< - T, - RelocatablePointer>>, - RelocatablePointer>, - > { + /// Returns the [`Iter`]ator to iterate over all entries. + pub fn iter(&self) -> details::RelocatableIter { unsafe { self.state.iter_impl() } } + /// Returns `true` if the provided `key` is contained, otherwise `false`. + pub fn contains(&self, key: SlotMapKey) -> bool { + unsafe { self.state.contains_impl(key) } + } + + /// Returns a reference to the value stored under the given key. If there is no such key, + /// [`None`] is returned. pub fn get(&self, key: SlotMapKey) -> Option<&T> { unsafe { self.state.get_impl(key) } } + /// Returns a mutable reference to the value stored under the given key. If there is no + /// such key, [`None`] is returned. pub fn get_mut(&mut self, key: SlotMapKey) -> Option<&mut T> { unsafe { self.state.get_mut_impl(key) } } + /// Insert a value and returns the corresponding [`SlotMapKey`]. If the container is full + /// [`None`] is returned. pub fn insert(&mut self, value: T) -> Option { unsafe { self.state.insert_impl(value) } } + /// Insert a value at the specified [`SlotMapKey`] and returns true. If the provided key + /// is out-of-bounds it returns `false` and adds nothing. If there is already a value + /// stored at the `key`s index, the value is overridden with the provided value. pub fn insert_at(&mut self, key: SlotMapKey, value: T) -> bool { unsafe { self.state.insert_at_impl(key, value) } } + /// Removes a value at the specified [`SlotMapKey`]. If there was no value corresponding + /// to the [`SlotMapKey`] it returns false, otherwise true. pub fn remove(&mut self, key: SlotMapKey) -> bool { unsafe { self.state.remove_impl(key) } } + /// Returns the number of stored values. pub fn len(&self) -> usize { - unsafe { self.state.len_impl() } + self.state.len_impl() } + /// Returns the capacity. pub fn capacity(&self) -> usize { - unsafe { self.state.capacity_impl() } + self.state.capacity_impl() } + /// Returns true if the container is empty, otherwise false. pub fn is_empty(&self) -> bool { - unsafe { self.state.is_empty_impl() } + self.state.is_empty_impl() } + /// Returns true if the container is full, otherwise false. pub fn is_full(&self) -> bool { - unsafe { self.state.is_full_impl() } + self.state.is_full_impl() } } diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index 41b7989f5..cedacec9b 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -236,7 +236,7 @@ pub mod details { self.len == self.capacity } - unsafe fn push_impl(&mut self, value: T) -> bool { + pub(crate) unsafe fn push_impl(&mut self, value: T) -> bool { if self.is_full() { return false; } diff --git a/iceoryx2-bb/container/tests/slotmap_tests.rs b/iceoryx2-bb/container/tests/slotmap_tests.rs index 75358c8c4..2612cd5c9 100644 --- a/iceoryx2-bb/container/tests/slotmap_tests.rs +++ b/iceoryx2-bb/container/tests/slotmap_tests.rs @@ -15,7 +15,7 @@ use iceoryx2_bb_testing::assert_that; mod slot_map { - use iceoryx2_bb_container::slotmap::FixedSizeSlotMap; + use iceoryx2_bb_container::slotmap::{FixedSizeSlotMap, SlotMapKey}; use super::*; @@ -30,6 +30,7 @@ mod slot_map { assert_that!(sut, len 0); assert_that!(sut, is_empty); assert_that!(sut.is_full(), eq false); + assert_that!(sut.capacity(), eq SUT_CAPACITY); } #[test] @@ -39,5 +40,99 @@ mod slot_map { assert_that!(sut, len 0); assert_that!(sut, is_empty); assert_that!(sut.is_full(), eq false); + assert_that!(sut.capacity(), eq SUT_CAPACITY); + } + + #[test] + fn inserting_elements_works() { + let mut sut = FixedSizeSut::new(); + + for i in 0..SUT_CAPACITY { + assert_that!(sut.is_full(), eq false); + let key = sut.insert(i).unwrap(); + *sut.get_mut(key).unwrap() += i; + assert_that!(*sut.get(key).unwrap(), eq 2 * i); + assert_that!(sut, len i + 1); + assert_that!(sut.is_empty(), eq false); + } + + assert_that!(sut.is_full(), eq true); + assert_that!(sut.insert(123), is_none); + } + + #[test] + fn insert_when_full_fails() { + let mut sut = FixedSizeSut::new(); + + for i in 0..SUT_CAPACITY { + assert_that!(sut.insert(i), is_some); + } + + assert_that!(sut.insert(34), is_none); + } + + #[test] + fn removing_elements_works() { + let mut sut = FixedSizeSut::new(); + let mut keys = vec![]; + + for i in 0..SUT_CAPACITY { + keys.push(sut.insert(i).unwrap()); + } + + for (n, key) in keys.iter().enumerate() { + assert_that!(sut.len(), eq sut.capacity() - n); + assert_that!(sut.is_empty(), eq false); + assert_that!(sut.contains(*key), eq true); + assert_that!(sut.remove(*key), eq true); + assert_that!(sut.remove(*key), eq false); + assert_that!(sut.contains(*key), eq false); + assert_that!(sut.is_full(), eq false); + + assert_that!(sut.get(*key), is_none); + assert_that!(sut.get_mut(*key), is_none); + } + + assert_that!(sut.is_empty(), eq true); + } + + #[test] + fn removing_out_of_bounds_key_returns_false() { + let mut sut = FixedSizeSut::new(); + + assert_that!(sut.remove(SlotMapKey::new(SUT_CAPACITY + 1)), eq false); + } + + #[test] + fn insert_at_works() { + let mut sut = FixedSizeSut::new(); + + let key = SlotMapKey::new(5); + let value = 71823; + assert_that!(sut.insert_at(key, 781), eq true); + assert_that!(sut.insert_at(key, value), eq true); + + assert_that!(*sut.get(key).unwrap(), eq value); + } + + #[test] + fn insert_at_out_of_bounds_key_returns_false() { + let mut sut = FixedSizeSut::new(); + let key = SlotMapKey::new(SUT_CAPACITY + 1); + assert_that!(sut.insert_at(key, 781), eq false); + } + + #[test] + fn iterating_works() { + let mut sut = FixedSizeSut::new(); + let mut keys = vec![]; + + for i in 0..SUT_CAPACITY { + keys.push(sut.insert(5 * i + 3).unwrap()); + } + + for (key, value) in sut.iter() { + assert_that!(*value, eq 5 * key.value() + 3); + } } } diff --git a/iceoryx2-bb/memory/src/pool_allocator.rs b/iceoryx2-bb/memory/src/pool_allocator.rs index 9a4a5e519..c9f4c6e5b 100644 --- a/iceoryx2-bb/memory/src/pool_allocator.rs +++ b/iceoryx2-bb/memory/src/pool_allocator.rs @@ -49,7 +49,6 @@ use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::math::align; -use iceoryx2_bb_elementary::math::align_to; use iceoryx2_bb_elementary::relocatable_container::*; use iceoryx2_bb_lock_free::mpmc::unique_index_set::*; From af66bc6b885e697045196f453a57e9d3890a19da Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 20:51:26 +0100 Subject: [PATCH 08/17] [#504] Add release notes --- doc/release-notes/iceoryx2-unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/release-notes/iceoryx2-unreleased.md b/doc/release-notes/iceoryx2-unreleased.md index 811dfc59a..a2babd171 100644 --- a/doc/release-notes/iceoryx2-unreleased.md +++ b/doc/release-notes/iceoryx2-unreleased.md @@ -16,6 +16,7 @@ * Developer permissions for resources [#460](https://github.com/eclipse-iceoryx/iceoryx2/issues/460) * Add `--send-copy` flag to Benchmark to consider mem operations [#483](https://github.com/eclipse-iceoryx/iceoryx2/issues/483) * Support for slices in the C++ bindings [#490](https://github.com/eclipse-iceoryx/iceoryx2/issues/490) +* Add relocatable `SlotMap` [#504](https://github.com/eclipse-iceoryx/iceoryx2/issues/504) ### Bugfixes From 364d62d908897c6b5a34d03cfbdf0f62f5b33f0d Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Thu, 7 Nov 2024 21:28:25 +0100 Subject: [PATCH 09/17] [#504] Add documentation example --- iceoryx2-bb/container/src/queue.rs | 16 ++++++---- iceoryx2-bb/container/src/slotmap.rs | 26 ++++++++++++++++- iceoryx2-bb/container/src/vec.rs | 20 +++++++++---- iceoryx2-bb/lock-free/src/mpmc/container.rs | 12 +++----- .../lock-free/src/mpmc/unique_index_set.rs | 29 ++++++++++--------- 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index 582bb3a66..b8d57e208 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -57,6 +57,7 @@ //! ``` //! use iceoryx2_bb_container::queue::RelocatableQueue; //! use iceoryx2_bb_elementary::math::align_to; +//! use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; //! use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; //! use core::mem::MaybeUninit; //! @@ -68,11 +69,16 @@ //! //! impl MyConstruct { //! pub fn new() -> Self { -//! Self { -//! queue: unsafe { RelocatableQueue::new(QUEUE_CAPACITY, -//! align_to::>(std::mem::size_of::>()) as isize) }, +//! let mut new_self = Self { +//! queue: unsafe { RelocatableQueue::new_uninit(QUEUE_CAPACITY) }, //! queue_memory: core::array::from_fn(|_| MaybeUninit::uninit()), -//! } +//! }; +//! +//! let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.queue_memory) as usize); +//! unsafe { +//! new_self.queue.init(&allocator).expect("Enough memory provided.") +//! }; +//! new_self //! } //! } //! ``` @@ -91,7 +97,7 @@ //! //! let bump_allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); //! -//! let queue = unsafe { RelocatableQueue::::new_uninit(QUEUE_CAPACITY) }; +//! let mut queue = unsafe { RelocatableQueue::::new_uninit(QUEUE_CAPACITY) }; //! unsafe { queue.init(&bump_allocator).expect("queue init failed") }; //! ``` //! diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 3f66e8106..5c29a80e8 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -10,6 +10,30 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +//! A SlotMap is a container that has a static unique key for every stored value. Adding or +//! removing values to the SlotMap do not change the unique key of the remaining values. +//! Multiple variationes of that container are available. +//! +//! * [`SlotMap`](crate::slotmap::SlotMap), run-time fixed-size slotmap that is not shared-memory +//! compatible since the memory resides in the heap. +//! * [`FixedSizeSlotMap`](crate::slotmap::FixedSizeSlotMap), compile-time fixed-size slotmap that +//! is self-contained and shared-memory compatible. +//! * [`RelocatableSlotMap`](crate::slotmap::RelocatableSlotMap), run-time fixed-size slotmap that +//! is shared-memory compatible. +//! +//! # User Examples +//! +//! ``` +//! use iceoryx2_bb_container::slotmap::FixedSizeSlotMap; +//! +//! const CAPACITY: usize = 123; +//! let mut slotmap = FixedSizeSlotMap::::new(); +//! +//! let key = slotmap.insert(78181).unwrap(); +//! +//! println!("value: {:?}", slotmap.get(key)); +//! ``` + use crate::queue::details::MetaQueue; use crate::vec::details::MetaVec; use crate::{queue::RelocatableQueue, vec::RelocatableVec}; @@ -527,7 +551,7 @@ impl FixedSizeSlotMap { Self::default() } - /// Returns the [`Iter`]ator to iterate over all entries. + /// Returns the [`details::RelocatableIter`]ator to iterate over all entries. pub fn iter(&self) -> details::RelocatableIter { unsafe { self.state.iter_impl() } } diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index cedacec9b..75493493c 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -10,8 +10,10 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Contains two vector variations that are similar to [`std::vec::Vec`]. +//! Contains vector variations that are similar to [`std::vec::Vec`]. //! +//! * [`Vec`](crate::vec::Vec), run-time fixed-size vector that is not shared-memory compatible +//! since the memory resides in the heap. //! * [`FixedSizeVec`](crate::vec::FixedSizeVec), compile-time fixed size vector that is //! self-contained. //! * [`RelocatableVec`](crate::vec::RelocatableVec), run-time fixed size vector that uses by default heap memory. @@ -37,6 +39,7 @@ //! ``` //! use iceoryx2_bb_container::vec::RelocatableVec; //! use iceoryx2_bb_elementary::math::align_to; +//! use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; //! use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; //! use core::mem::MaybeUninit; //! @@ -48,11 +51,16 @@ //! //! impl MyConstruct { //! pub fn new() -> Self { -//! Self { -//! vec: unsafe { RelocatableVec::new(VEC_CAPACITY, -//! align_to::>(std::mem::size_of::>()) as isize) }, +//! let mut new_self = Self { +//! vec: unsafe { RelocatableVec::new_uninit(VEC_CAPACITY) }, //! vec_memory: core::array::from_fn(|_| MaybeUninit::uninit()), -//! } +//! }; +//! +//! let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.vec_memory) as usize); +//! unsafe { +//! new_self.vec.init(&allocator).expect("Enough memory provided.") +//! }; +//! new_self //! } //! } //! ``` @@ -71,7 +79,7 @@ //! //! let bump_allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); //! -//! let vec = unsafe { RelocatableVec::::new_uninit(VEC_CAPACITY) }; +//! let mut vec = unsafe { RelocatableVec::::new_uninit(VEC_CAPACITY) }; //! unsafe { vec.init(&bump_allocator).expect("vec init failed") }; //! ``` diff --git a/iceoryx2-bb/lock-free/src/mpmc/container.rs b/iceoryx2-bb/lock-free/src/mpmc/container.rs index 9f0790adb..90cb8f253 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/container.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/container.rs @@ -282,8 +282,7 @@ impl Container { /// /// # Safety /// - /// * Ensure that the either [`Container::new()`] was used or [`Container::init()`] was used - /// before calling this method + /// * Ensure that [`Container::init()`] was called before calling this method /// * Use [`Container::remove()`] to release the acquired index again. Otherwise, the /// element will leak. /// @@ -314,8 +313,7 @@ impl Container { /// /// # Safety /// - /// * Ensure that the either [`Container::new()`] was used or [`Container::init()`] was used - /// before calling this method + /// * Ensure that [`Container::init()`] was called before calling this method /// * Ensure that no one else possesses the [`UniqueIndex`] and the index was unrecoverable /// lost /// * Ensure that the `handle` was acquired by the same [`Container`] @@ -345,8 +343,7 @@ impl Container { /// /// # Safety /// - /// * Ensure that the either [`Container::new()`] was used or [`Container::init()`] was used - /// before calling this method + /// * Ensure that [`Container::init()`] was called before calling this method /// pub unsafe fn get_state(&self) -> ContainerState { self.verify_memory_initialization("get_state"); @@ -361,8 +358,7 @@ impl Container { /// /// # Safety /// - /// * Ensure that the either [`Container::new()`] was used or [`Container::init()`] was used - /// before calling this method + /// * Ensure that [`Container::init()`] was called before calling this method /// * Ensure that the input argument `previous_state` was acquired by the same [`Container`] /// with [`Container::get_state()`], otherwise the method will panic. /// diff --git a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs index 78a49b29b..ceb194a35 100644 --- a/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs +++ b/iceoryx2-bb/lock-free/src/mpmc/unique_index_set.rs @@ -27,7 +27,7 @@ //! let mut memory = [0u8; UniqueIndexSet::const_memory_size(CAPACITY)]; //! let allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); //! -//! let index_set = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; +//! let mut index_set = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; //! unsafe { index_set.init(&allocator) }.expect("failed to allocate enough memory"); //! //! let new_index = match unsafe { index_set.acquire() } { @@ -187,7 +187,7 @@ impl Drop for UniqueIndex<'_> { /// let mut memory = [0u8; UniqueIndexSet::const_memory_size(CAPACITY)]; /// let allocator = BumpAllocator::new(memory.as_mut_ptr() as usize); /// -/// let index_set = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; +/// let mut index_set = unsafe { UniqueIndexSet::new_uninit(CAPACITY) }; /// unsafe { index_set.init(&allocator) }.expect("failed to allocate enough memory"); /// /// let new_index = match unsafe { index_set.acquire() } { @@ -200,6 +200,7 @@ impl Drop for UniqueIndex<'_> { /// ``` /// use iceoryx2_bb_lock_free::mpmc::unique_index_set::*; /// use iceoryx2_bb_elementary::relocatable_container::*; +/// use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; /// use std::mem::MaybeUninit; /// /// const CAPACITY: usize = 128; @@ -214,15 +215,16 @@ impl Drop for UniqueIndex<'_> { /// /// impl FixedSizeSet { /// pub fn new() -> Self { -/// FixedSizeSet { -/// set: unsafe { -/// UniqueIndexSet::new(CAPACITY, -/// // distance to data beginning from the start of the set (UniqueIndexSet) -/// // member start -/// std::mem::size_of::() as isize) -/// }, +/// let mut new_self = FixedSizeSet { +/// set: unsafe { UniqueIndexSet::new_uninit(CAPACITY) }, /// data: [MaybeUninit::uninit(); UniqueIndexSet::const_memory_size(CAPACITY)] -/// } +/// }; +/// +/// let allocator = BumpAllocator::new(core::ptr::addr_of!(new_self.data) as usize); +/// unsafe { +/// new_self.set.init(&allocator).expect("Enough memory provided.") +/// }; +/// new_self /// } /// } /// ``` @@ -324,8 +326,7 @@ impl UniqueIndexSet { /// /// # Safety /// - /// * Ensure that either the [`UniqueIndexSet`] was created with [`UniqueIndexSet::new()`] or - /// [`UniqueIndexSet::init()`] was called. + /// * Ensure that [`UniqueIndexSet::init()`] was called once. /// pub unsafe fn acquire(&self) -> Result, UniqueIndexSetAcquireFailure> { self.verify_init("acquire"); @@ -383,8 +384,7 @@ impl UniqueIndexSet { /// /// # Safety /// - /// * Ensure that either the [`UniqueIndexSet`] was created with [`UniqueIndexSet::new()`] or - /// [`UniqueIndexSet::init()`] was called. + /// * Ensure that [`UniqueIndexSet::init()`] was called once. /// * The index must be manually released with [`UniqueIndexSet::release_raw_index()`] /// otherwise the index is leaked. pub unsafe fn acquire_raw_index(&self) -> Result { @@ -437,6 +437,7 @@ impl UniqueIndexSet { /// /// # Safety /// + /// * Ensure that [`UniqueIndexSet::init()`] was called once. /// * It must be ensured that the index was acquired before and is not released twice. /// * Shall be only used when the index was acquired with /// [`UniqueIndexSet::acquire_raw_index()`] From e883eb0fbf7e30fda12f796291fcf56fb029f0c3 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 12 Nov 2024 18:37:29 +0100 Subject: [PATCH 10/17] [#504] Introduce freelist in slotmap --- iceoryx2-bb/container/src/slotmap.rs | 62 ++++++++++++++++---- iceoryx2-bb/container/tests/slotmap_tests.rs | 36 ++++++++++++ 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 5c29a80e8..0759bff29 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -63,6 +63,11 @@ impl SlotMapKey { } } +struct FreeListEntry { + previous: usize, + next: usize, +} + /// A runtime fixed-size, non-shared memory compatible [`SlotMap`]. The [`SlotMap`]s memory resides /// in the heap. pub type SlotMap = details::MetaSlotMap< @@ -131,9 +136,12 @@ pub mod details { IdxPtrType: PointerTrait>, > { idx_to_data: MetaVec, - idx_to_data_next_free_index: MetaQueue, + idx_to_data_free_list: MetaVec, data: MetaVec, DataPtrType>, data_next_free_index: MetaQueue, + + idx_to_data_free_list_head: usize, + len: usize, } impl< @@ -164,7 +172,7 @@ pub mod details { for n in 0..self.capacity_impl() { self.idx_to_data.push_impl(INVALID_KEY); self.data.push_impl(None); - self.idx_to_data_next_free_index.push_impl(n); + self.idx_to_data_free_list.push_impl(n + 1); self.data_next_free_index.push_impl(n); } } @@ -198,18 +206,42 @@ pub mod details { } } + unsafe fn acquire_next_free_index(&mut self) -> Option { + if self.idx_to_data_free_list_head == self.capacity_impl() { + return None; + } + + let free_idx = self.idx_to_data_free_list_head; + self.idx_to_data_free_list_head = + self.idx_to_data_free_list[self.idx_to_data_free_list_head]; + Some(free_idx) + } + + unsafe fn reserve_index(&mut self) -> Option { + todo!() + } + + unsafe fn release_free_index(&mut self, idx: usize) { + self.idx_to_data_free_list[idx] = self.idx_to_data_free_list_head; + self.idx_to_data_free_list_head = idx; + } + pub(crate) unsafe fn insert_impl(&mut self, value: T) -> Option { - match self.idx_to_data_next_free_index.pop_impl() { + match self.acquire_next_free_index() { None => None, Some(key) => { let key = SlotMapKey(key); - self.insert_at_impl(key, value); + self.store_value(key, value); Some(key) } } } pub(crate) unsafe fn insert_at_impl(&mut self, key: SlotMapKey, value: T) -> bool { + self.store_value(key, value) + } + + pub(crate) unsafe fn store_value(&mut self, key: SlotMapKey, value: T) -> bool { if key.0 > self.capacity_impl() { return false; } @@ -221,6 +253,7 @@ pub mod details { 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); + self.len += 1; } true @@ -234,9 +267,10 @@ 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_impl(data_idx); - self.idx_to_data_next_free_index.push_impl(key.0); + debug_assert!(self.data_next_free_index.push_impl(data_idx)); + self.release_free_index(key.0); self.idx_to_data[key.0] = INVALID_KEY; + self.len -= 1; true } else { false @@ -244,7 +278,7 @@ pub mod details { } pub(crate) fn len_impl(&self) -> usize { - self.capacity_impl() - self.idx_to_data_next_free_index.len() + self.len } pub(crate) fn capacity_impl(&self) -> usize { @@ -269,8 +303,10 @@ pub mod details { { unsafe fn new_uninit(capacity: usize) -> Self { Self { + len: 0, + idx_to_data_free_list_head: 0, idx_to_data: RelocatableVec::new_uninit(capacity), - idx_to_data_next_free_index: RelocatableQueue::new_uninit(capacity), + idx_to_data_free_list: RelocatableVec::new_uninit(capacity), data: RelocatableVec::new_uninit(capacity), data_next_free_index: RelocatableQueue::new_uninit(capacity), } @@ -285,8 +321,8 @@ pub mod details { 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."); + when self.idx_to_data_free_list.init(allocator), + "{msg} since the underlying idx_to_data_free_list vec could not be initialized."); fail!(from "RelocatableSlotMap::init()", when self.data.init(allocator), "{msg} since the underlying data vector could not be initialized."); @@ -314,7 +350,7 @@ pub mod details { /// in [`RelocatableSlotMap::init()`]. pub const fn const_memory_size(capacity: usize) -> usize { RelocatableVec::::const_memory_size(capacity) - + RelocatableQueue::::const_memory_size(capacity) + + RelocatableVec::::const_memory_size(capacity) + RelocatableVec::>::const_memory_size(capacity) + RelocatableQueue::::const_memory_size(capacity) } @@ -324,8 +360,10 @@ pub mod details { /// Creates a new runtime-fixed size [`SlotMap`] on the heap with the given capacity. pub fn new(capacity: usize) -> Self { let mut new_self = Self { + len: 0, + idx_to_data_free_list_head: 0, idx_to_data: MetaVec::new(capacity), - idx_to_data_next_free_index: MetaQueue::new(capacity), + idx_to_data_free_list: MetaVec::new(capacity), data: MetaVec::new(capacity), data_next_free_index: MetaQueue::new(capacity), }; diff --git a/iceoryx2-bb/container/tests/slotmap_tests.rs b/iceoryx2-bb/container/tests/slotmap_tests.rs index 2612cd5c9..99dbcc635 100644 --- a/iceoryx2-bb/container/tests/slotmap_tests.rs +++ b/iceoryx2-bb/container/tests/slotmap_tests.rs @@ -115,6 +115,42 @@ mod slot_map { assert_that!(*sut.get(key).unwrap(), eq value); } + #[test] + fn insert_at_and_remove_adjust_map_len_correctly() { + let mut sut = FixedSizeSut::new(); + + for n in 0..SUT_CAPACITY { + let key = SlotMapKey::new(n); + assert_that!(sut.len(), eq n); + assert_that!(sut.insert_at(key, 0), eq true); + } + assert_that!(sut.len(), eq SUT_CAPACITY); + + for n in (0..SUT_CAPACITY).rev() { + let key = SlotMapKey::new(n); + assert_that!(sut.remove(key), eq true); + assert_that!(sut.remove(key), eq false); + assert_that!(sut.len(), eq n); + } + assert_that!(sut.len(), eq 0); + } + + #[test] + fn insert_does_not_use_insert_at_indices() { + let mut sut = FixedSizeSut::new(); + + for n in 0..SUT_CAPACITY / 2 { + let key = SlotMapKey::new(2 * n + 1); + assert_that!(sut.insert_at(key, 0), eq true); + } + + for _ in 0..SUT_CAPACITY / 2 { + let key = sut.insert(0); + assert_that!(key, is_some); + assert_that!(key.unwrap().value() % 2, eq 0); + } + } + #[test] fn insert_at_out_of_bounds_key_returns_false() { let mut sut = FixedSizeSut::new(); From 2b3c766d0b775986e84c7e6609477612d24cab1b Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 12 Nov 2024 18:48:02 +0100 Subject: [PATCH 11/17] [#504] Introduce generic pointer --- iceoryx2-bb/elementary/src/generic_pointer.rs | 19 +++++++++++++++++++ iceoryx2-bb/elementary/src/lib.rs | 1 + iceoryx2-bb/elementary/src/owning_pointer.rs | 7 +++++++ iceoryx2-bb/elementary/src/relocatable_ptr.rs | 7 +++++++ 4 files changed, 34 insertions(+) create mode 100644 iceoryx2-bb/elementary/src/generic_pointer.rs diff --git a/iceoryx2-bb/elementary/src/generic_pointer.rs b/iceoryx2-bb/elementary/src/generic_pointer.rs new file mode 100644 index 000000000..7d166e16c --- /dev/null +++ b/iceoryx2-bb/elementary/src/generic_pointer.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +use crate::pointer_trait::PointerTrait; + +/// Trait that allows to use typed pointers as generic arguments for structs. +pub trait GenericPointer { + /// The underlying pointer type. + type Type: PointerTrait; +} diff --git a/iceoryx2-bb/elementary/src/lib.rs b/iceoryx2-bb/elementary/src/lib.rs index 3b1bfb0df..e55db7c28 100644 --- a/iceoryx2-bb/elementary/src/lib.rs +++ b/iceoryx2-bb/elementary/src/lib.rs @@ -19,6 +19,7 @@ pub mod enum_gen; pub mod alignment; pub mod allocator; pub mod bump_allocator; +pub mod generic_pointer; pub mod lazy_singleton; pub mod math; pub mod owning_pointer; diff --git a/iceoryx2-bb/elementary/src/owning_pointer.rs b/iceoryx2-bb/elementary/src/owning_pointer.rs index 5194cd0b3..f1f6f5f13 100644 --- a/iceoryx2-bb/elementary/src/owning_pointer.rs +++ b/iceoryx2-bb/elementary/src/owning_pointer.rs @@ -16,8 +16,11 @@ use std::alloc::Layout; use std::alloc::{alloc, dealloc}; +use crate::generic_pointer::GenericPointer; use crate::pointer_trait::PointerTrait; +pub struct GenericOwningPointer; + /// Representation of a pointer which owns its memory. #[repr(C)] #[derive(Debug)] @@ -63,3 +66,7 @@ impl PointerTrait for OwningPointer { self.ptr } } + +impl GenericPointer for GenericOwningPointer { + type Type = OwningPointer; +} diff --git a/iceoryx2-bb/elementary/src/relocatable_ptr.rs b/iceoryx2-bb/elementary/src/relocatable_ptr.rs index 146a0a9c9..d4e48d6a4 100644 --- a/iceoryx2-bb/elementary/src/relocatable_ptr.rs +++ b/iceoryx2-bb/elementary/src/relocatable_ptr.rs @@ -64,10 +64,13 @@ //! } //! ``` +use crate::generic_pointer::GenericPointer; pub use crate::pointer_trait::PointerTrait; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicIsize; use std::{marker::PhantomData, ptr::NonNull}; +pub struct GenericRelocatablePointer; + /// A [`RelocatablePointer`] stores only the distance from its memory starting position to the /// memory location it is pointing to. When the [`RelocatablePointer`] is now shared between /// processes its virtual memory starting position changes but the distance to the object it is @@ -138,3 +141,7 @@ impl PointerTrait for RelocatablePointer { self.as_ptr() as *mut T } } + +impl GenericPointer for GenericRelocatablePointer { + type Type = RelocatablePointer; +} From d4321bcf21bc33c2bd3ce23a2679529c375d14ff Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 12 Nov 2024 19:14:29 +0100 Subject: [PATCH 12/17] [#504] Integrate GenericPointer --- iceoryx2-bb/container/src/queue.rs | 28 +++-- iceoryx2-bb/container/src/slotmap.rs | 119 +++++------------- iceoryx2-bb/container/src/vec.rs | 37 +++--- iceoryx2-bb/elementary/src/generic_pointer.rs | 4 +- iceoryx2-bb/elementary/src/owning_pointer.rs | 4 +- iceoryx2-bb/elementary/src/relocatable_ptr.rs | 5 +- 6 files changed, 78 insertions(+), 119 deletions(-) diff --git a/iceoryx2-bb/container/src/queue.rs b/iceoryx2-bb/container/src/queue.rs index b8d57e208..29efa6757 100644 --- a/iceoryx2-bb/container/src/queue.rs +++ b/iceoryx2-bb/container/src/queue.rs @@ -104,11 +104,11 @@ use iceoryx2_bb_elementary::allocator::{AllocationError, BaseAllocator}; use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; use iceoryx2_bb_elementary::math::unaligned_mem_size; -use iceoryx2_bb_elementary::owning_pointer::OwningPointer; +use iceoryx2_bb_elementary::owning_pointer::{GenericOwningPointer, OwningPointer}; use iceoryx2_bb_elementary::placement_default::PlacementDefault; use iceoryx2_bb_elementary::pointer_trait::PointerTrait; pub use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; -use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; +use iceoryx2_bb_elementary::relocatable_ptr::{GenericRelocatablePointer, RelocatablePointer}; use iceoryx2_bb_log::{fail, fatal_panic}; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicBool; use std::marker::PhantomData; @@ -116,18 +116,20 @@ 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::MetaQueue>>; +pub type Queue = details::MetaQueue; /// **Non-movable** relocatable queue with runtime fixed size capacity. -pub type RelocatableQueue = details::MetaQueue>>; +pub type RelocatableQueue = details::MetaQueue; #[doc(hidden)] pub mod details { + use iceoryx2_bb_elementary::generic_pointer::GenericPointer; + use super::*; /// **Non-movable** relocatable queue with runtime fixed size capacity. #[repr(C)] #[derive(Debug)] - pub struct MetaQueue>> { - data_ptr: PointerType, + pub struct MetaQueue { + data_ptr: Ptr::Type>, start: usize, len: usize, capacity: usize, @@ -135,9 +137,9 @@ pub mod details { _phantom_data: PhantomData, } - unsafe impl>> Send for MetaQueue {} + unsafe impl Send for MetaQueue {} - impl MetaQueue>> { + impl MetaQueue { /// Creates a new [`Queue`] with the provided capacity pub fn new(capacity: usize) -> Self { Self { @@ -184,7 +186,7 @@ pub mod details { } } - impl> + Debug> MetaQueue { + impl MetaQueue { /// Returns a copy of the element stored at index. The index is starting by 0 for the first /// element until [`Queue::len()`]. /// @@ -212,7 +214,7 @@ pub mod details { } } - impl RelocatableContainer for MetaQueue>> { + impl RelocatableContainer for MetaQueue { unsafe fn new_uninit(capacity: usize) -> Self { Self { data_ptr: RelocatablePointer::new_uninit(), @@ -255,7 +257,7 @@ pub mod details { } } - impl MetaQueue>> { + 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) @@ -325,7 +327,7 @@ pub mod details { } } - impl>> MetaQueue { + impl MetaQueue { #[inline(always)] fn verify_init(&self, source: &str) { debug_assert!( @@ -438,7 +440,7 @@ pub mod details { } } - impl>> Drop for MetaQueue { + impl Drop for MetaQueue { fn drop(&mut self) { if self .is_initialized diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 0759bff29..970a91547 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -38,13 +38,11 @@ use crate::queue::details::MetaQueue; use crate::vec::details::MetaVec; use crate::{queue::RelocatableQueue, vec::RelocatableVec}; use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; -use iceoryx2_bb_elementary::owning_pointer::OwningPointer; +use iceoryx2_bb_elementary::owning_pointer::GenericOwningPointer; use iceoryx2_bb_elementary::placement_default::PlacementDefault; -use iceoryx2_bb_elementary::pointer_trait::PointerTrait; use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; -use iceoryx2_bb_elementary::relocatable_ptr::RelocatablePointer; +use iceoryx2_bb_elementary::relocatable_ptr::GenericRelocatablePointer; use iceoryx2_bb_log::fail; -use std::mem::MaybeUninit; /// A key of a [`SlotMap`], [`RelocatableSlotMap`] or [`FixedSizeSlotMap`] that identifies a /// value. @@ -70,52 +68,32 @@ struct FreeListEntry { /// A runtime fixed-size, non-shared memory compatible [`SlotMap`]. The [`SlotMap`]s memory resides /// in the heap. -pub type SlotMap = details::MetaSlotMap< - T, - OwningPointer>>, - OwningPointer>, ->; +pub type SlotMap = details::MetaSlotMap; /// A runtime fixed-size, shared-memory compatible [`RelocatableSlotMap`]. -pub type RelocatableSlotMap = details::MetaSlotMap< - T, - RelocatablePointer>>, - RelocatablePointer>, ->; +pub type RelocatableSlotMap = details::MetaSlotMap; const INVALID_KEY: usize = usize::MAX; #[doc(hidden)] pub mod details { + use iceoryx2_bb_elementary::{ + generic_pointer::GenericPointer, owning_pointer::GenericOwningPointer, + relocatable_ptr::GenericRelocatablePointer, + }; + use super::*; /// The iterator of a [`SlotMap`], [`RelocatableSlotMap`] or [`FixedSizeSlotMap`]. - pub struct Iter< - 'slotmap, - T, - DataPtrType: PointerTrait>>, - IdxPtrType: PointerTrait>, - > { - slotmap: &'slotmap MetaSlotMap, + pub struct Iter<'slotmap, T, Ptr: GenericPointer> { + slotmap: &'slotmap MetaSlotMap, key: SlotMapKey, } - pub type OwningIter<'slotmap, T> = - Iter<'slotmap, T, OwningPointer>>, OwningPointer>>; - pub type RelocatableIter<'slotmap, T> = Iter< - 'slotmap, - T, - RelocatablePointer>>, - RelocatablePointer>, - >; - - impl< - 'slotmap, - T, - DataPtrType: PointerTrait>>, - IdxPtrType: PointerTrait>, - > Iterator for Iter<'slotmap, T, DataPtrType, IdxPtrType> - { + pub type OwningIter<'slotmap, T> = Iter<'slotmap, T, GenericOwningPointer>; + pub type RelocatableIter<'slotmap, T> = Iter<'slotmap, T, GenericRelocatablePointer>; + + impl<'slotmap, T, Ptr: GenericPointer> Iterator for Iter<'slotmap, T, Ptr> { type Item = (SlotMapKey, &'slotmap T); fn next(&mut self) -> Option { @@ -130,26 +108,17 @@ pub mod details { #[repr(C)] #[derive(Debug)] - pub struct MetaSlotMap< - T, - DataPtrType: PointerTrait>>, - IdxPtrType: PointerTrait>, - > { - idx_to_data: MetaVec, - idx_to_data_free_list: MetaVec, - data: MetaVec, DataPtrType>, - data_next_free_index: MetaQueue, + pub struct MetaSlotMap { + idx_to_data: MetaVec, + idx_to_data_free_list: MetaVec, + data: MetaVec, Ptr>, + data_next_free_index: MetaQueue, idx_to_data_free_list_head: usize, len: usize, } - impl< - T, - DataPtrType: PointerTrait>>, - IdxPtrType: PointerTrait>, - > MetaSlotMap - { + impl MetaSlotMap { fn next(&self, start: SlotMapKey) -> Option<(SlotMapKey, &T)> { let idx_to_data = &self.idx_to_data; @@ -177,7 +146,7 @@ pub mod details { } } - pub(crate) unsafe fn iter_impl(&self) -> Iter { + pub(crate) unsafe fn iter_impl(&self) -> Iter { Iter { slotmap: self, key: SlotMapKey(0), @@ -294,13 +263,7 @@ pub mod details { } } - impl RelocatableContainer - for MetaSlotMap< - T, - RelocatablePointer>>, - RelocatablePointer>, - > - { + impl RelocatableContainer for MetaSlotMap { unsafe fn new_uninit(capacity: usize) -> Self { Self { len: 0, @@ -339,24 +302,7 @@ pub mod details { } } - impl - MetaSlotMap< - T, - RelocatablePointer>>, - RelocatablePointer>, - > - { - /// Returns how many memory the [`RelocatableSlotMap`] will allocate from the allocator - /// in [`RelocatableSlotMap::init()`]. - pub const fn const_memory_size(capacity: usize) -> usize { - RelocatableVec::::const_memory_size(capacity) - + RelocatableVec::::const_memory_size(capacity) - + RelocatableVec::>::const_memory_size(capacity) - + RelocatableQueue::::const_memory_size(capacity) - } - } - - impl MetaSlotMap>>, OwningPointer>> { + impl MetaSlotMap { /// Creates a new runtime-fixed size [`SlotMap`] on the heap with the given capacity. pub fn new(capacity: usize) -> Self { let mut new_self = Self { @@ -433,13 +379,16 @@ pub mod details { } } - impl - MetaSlotMap< - T, - RelocatablePointer>>, - RelocatablePointer>, - > - { + impl MetaSlotMap { + /// Returns how many memory the [`RelocatableSlotMap`] will allocate from the allocator + /// in [`RelocatableSlotMap::init()`]. + pub const fn const_memory_size(capacity: usize) -> usize { + RelocatableVec::::const_memory_size(capacity) + + RelocatableVec::::const_memory_size(capacity) + + RelocatableVec::>::const_memory_size(capacity) + + RelocatableQueue::::const_memory_size(capacity) + } + /// Returns the [`Iter`]ator to iterate over all entries. /// /// # Safety diff --git a/iceoryx2-bb/container/src/vec.rs b/iceoryx2-bb/container/src/vec.rs index 75493493c..13e1c92f6 100644 --- a/iceoryx2-bb/container/src/vec.rs +++ b/iceoryx2-bb/container/src/vec.rs @@ -91,22 +91,27 @@ use std::{ sync::atomic::Ordering, }; -use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; +use iceoryx2_bb_elementary::generic_pointer::GenericPointer; +use iceoryx2_bb_elementary::{ + bump_allocator::BumpAllocator, owning_pointer::GenericOwningPointer, + relocatable_ptr::GenericRelocatablePointer, +}; use iceoryx2_bb_elementary::{ math::unaligned_mem_size, owning_pointer::OwningPointer, placement_default::PlacementDefault, pointer_trait::PointerTrait, relocatable_container::RelocatableContainer, relocatable_ptr::RelocatablePointer, }; + use iceoryx2_bb_log::{fail, fatal_panic}; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicBool; 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::MetaVec>>; +pub type Vec = details::MetaVec; /// **Non-movable** relocatable vector with runtime fixed size capacity. -pub type RelocatableVec = details::MetaVec>>; +pub type RelocatableVec = details::MetaVec; #[doc(hidden)] pub mod details { @@ -115,17 +120,17 @@ pub mod details { /// **Non-movable** relocatable vector with runtime fixed size capacity. #[repr(C)] #[derive(Debug)] - pub struct MetaVec>> { - data_ptr: PointerType, + pub struct MetaVec { + data_ptr: Ptr::Type>, capacity: usize, len: usize, is_initialized: IoxAtomicBool, _phantom_data: PhantomData, } - unsafe impl>> Send for MetaVec {} + unsafe impl Send for MetaVec {} - impl>> Drop for MetaVec { + impl Drop for MetaVec { fn drop(&mut self) { if self .is_initialized @@ -136,7 +141,7 @@ pub mod details { } } - impl RelocatableContainer for MetaVec>> { + impl RelocatableContainer for MetaVec { unsafe fn new_uninit(capacity: usize) -> Self { Self { data_ptr: RelocatablePointer::new_uninit(), @@ -172,7 +177,7 @@ pub mod details { } } - impl>> Deref for MetaVec { + impl Deref for MetaVec { type Target = [T]; fn deref(&self) -> &Self::Target { @@ -181,7 +186,7 @@ pub mod details { } } - impl>> DerefMut for MetaVec { + impl DerefMut for MetaVec { fn deref_mut(&mut self) -> &mut Self::Target { self.verify_init(&format!("Vec<{}>::push()", std::any::type_name::())); unsafe { @@ -193,9 +198,7 @@ pub mod details { } } - impl>> PartialEq - for MetaVec - { + impl PartialEq for MetaVec { fn eq(&self, other: &Self) -> bool { if other.len() != self.len() { return false; @@ -211,9 +214,9 @@ pub mod details { } } - impl>> Eq for MetaVec {} + impl Eq for MetaVec {} - impl>> MetaVec { + impl MetaVec { #[inline(always)] fn verify_init(&self, source: &str) { debug_assert!( @@ -329,7 +332,7 @@ pub mod details { } } - impl MetaVec>> { + impl MetaVec { /// Creates a new [`Queue`] with the provided capacity pub fn new(capacity: usize) -> Self { Self { @@ -390,7 +393,7 @@ pub mod details { } } - impl MetaVec>> { + 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) diff --git a/iceoryx2-bb/elementary/src/generic_pointer.rs b/iceoryx2-bb/elementary/src/generic_pointer.rs index 7d166e16c..b24946b59 100644 --- a/iceoryx2-bb/elementary/src/generic_pointer.rs +++ b/iceoryx2-bb/elementary/src/generic_pointer.rs @@ -10,10 +10,12 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT +use std::fmt::Debug; + use crate::pointer_trait::PointerTrait; /// Trait that allows to use typed pointers as generic arguments for structs. pub trait GenericPointer { /// The underlying pointer type. - type Type: PointerTrait; + type Type: PointerTrait + Debug; } diff --git a/iceoryx2-bb/elementary/src/owning_pointer.rs b/iceoryx2-bb/elementary/src/owning_pointer.rs index f1f6f5f13..bc3966091 100644 --- a/iceoryx2-bb/elementary/src/owning_pointer.rs +++ b/iceoryx2-bb/elementary/src/owning_pointer.rs @@ -15,10 +15,12 @@ use std::alloc::Layout; use std::alloc::{alloc, dealloc}; +use std::fmt::Debug; use crate::generic_pointer::GenericPointer; use crate::pointer_trait::PointerTrait; +#[derive(Debug)] pub struct GenericOwningPointer; /// Representation of a pointer which owns its memory. @@ -68,5 +70,5 @@ impl PointerTrait for OwningPointer { } impl GenericPointer for GenericOwningPointer { - type Type = OwningPointer; + type Type = OwningPointer; } diff --git a/iceoryx2-bb/elementary/src/relocatable_ptr.rs b/iceoryx2-bb/elementary/src/relocatable_ptr.rs index d4e48d6a4..4529f613a 100644 --- a/iceoryx2-bb/elementary/src/relocatable_ptr.rs +++ b/iceoryx2-bb/elementary/src/relocatable_ptr.rs @@ -67,8 +67,9 @@ use crate::generic_pointer::GenericPointer; pub use crate::pointer_trait::PointerTrait; use iceoryx2_pal_concurrency_sync::iox_atomic::IoxAtomicIsize; -use std::{marker::PhantomData, ptr::NonNull}; +use std::{fmt::Debug, marker::PhantomData, ptr::NonNull}; +#[derive(Debug)] pub struct GenericRelocatablePointer; /// A [`RelocatablePointer`] stores only the distance from its memory starting position to the @@ -143,5 +144,5 @@ impl PointerTrait for RelocatablePointer { } impl GenericPointer for GenericRelocatablePointer { - type Type = RelocatablePointer; + type Type = RelocatablePointer; } From caf7250b80e2b745d11081e4e25eff2d599f3d11 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Tue, 12 Nov 2024 19:47:09 +0100 Subject: [PATCH 13/17] [#504] Use freelist in SlotMap instead of index queue --- iceoryx2-bb/container/src/slotmap.rs | 94 ++++++++++++++++++---------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 970a91547..98ee89beb 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -34,10 +34,13 @@ //! println!("value: {:?}", slotmap.get(key)); //! ``` +use std::mem::MaybeUninit; + use crate::queue::details::MetaQueue; use crate::vec::details::MetaVec; use crate::{queue::RelocatableQueue, vec::RelocatableVec}; use iceoryx2_bb_elementary::bump_allocator::BumpAllocator; +use iceoryx2_bb_elementary::generic_pointer::GenericPointer; use iceoryx2_bb_elementary::owning_pointer::GenericOwningPointer; use iceoryx2_bb_elementary::placement_default::PlacementDefault; use iceoryx2_bb_elementary::relocatable_container::RelocatableContainer; @@ -61,6 +64,7 @@ impl SlotMapKey { } } +#[derive(Debug, Clone, Copy)] struct FreeListEntry { previous: usize, next: usize, @@ -73,15 +77,10 @@ pub type SlotMap = details::MetaSlotMap; /// A runtime fixed-size, shared-memory compatible [`RelocatableSlotMap`]. pub type RelocatableSlotMap = details::MetaSlotMap; -const INVALID_KEY: usize = usize::MAX; +const INVALID: usize = usize::MAX; #[doc(hidden)] pub mod details { - use iceoryx2_bb_elementary::{ - generic_pointer::GenericPointer, owning_pointer::GenericOwningPointer, - relocatable_ptr::GenericRelocatablePointer, - }; - use super::*; /// The iterator of a [`SlotMap`], [`RelocatableSlotMap`] or [`FixedSizeSlotMap`]. @@ -110,10 +109,9 @@ pub mod details { #[derive(Debug)] pub struct MetaSlotMap { idx_to_data: MetaVec, - idx_to_data_free_list: MetaVec, + idx_to_data_free_list: MetaVec, data: MetaVec, Ptr>, data_next_free_index: MetaQueue, - idx_to_data_free_list_head: usize, len: usize, } @@ -124,7 +122,7 @@ pub mod details { for n in start.0..idx_to_data.len() { let data_idx = self.idx_to_data[n]; - if data_idx != INVALID_KEY { + if data_idx != INVALID { return Some(( SlotMapKey(n), self.data[data_idx].as_ref().expect( @@ -138,11 +136,16 @@ pub mod details { } pub(crate) unsafe fn initialize_data_structures(&mut self) { - for n in 0..self.capacity_impl() { - self.idx_to_data.push_impl(INVALID_KEY); + let capacity = self.capacity_impl(); + for n in 0..capacity { + self.idx_to_data.push_impl(INVALID); self.data.push_impl(None); - self.idx_to_data_free_list.push_impl(n + 1); self.data_next_free_index.push_impl(n); + + let previous = if n == 0 { INVALID } else { n - 1 }; + let next = if n < capacity - 1 { n + 1 } else { INVALID }; + self.idx_to_data_free_list + .push_impl(FreeListEntry { previous, next }); } } @@ -154,12 +157,12 @@ pub mod details { } pub(crate) unsafe fn contains_impl(&self, key: SlotMapKey) -> bool { - self.idx_to_data[key.0] != INVALID_KEY + self.idx_to_data[key.0] != INVALID } pub(crate) unsafe fn get_impl(&self, key: SlotMapKey) -> Option<&T> { match self.idx_to_data[key.0] { - INVALID_KEY => None, + INVALID => None, n => Some(self.data[n].as_ref().expect( "data and idx_to_data correspond and this value must be always available.", )), @@ -168,7 +171,7 @@ pub mod details { pub(crate) unsafe fn get_mut_impl(&mut self, key: SlotMapKey) -> Option<&mut T> { match self.idx_to_data[key.0] { - INVALID_KEY => None, + INVALID => None, n => Some(self.data[n].as_mut().expect( "data and idx_to_data correspond and this value must be always available.", )), @@ -176,22 +179,46 @@ pub mod details { } unsafe fn acquire_next_free_index(&mut self) -> Option { - if self.idx_to_data_free_list_head == self.capacity_impl() { + if self.idx_to_data_free_list_head == INVALID { return None; } let free_idx = self.idx_to_data_free_list_head; - self.idx_to_data_free_list_head = - self.idx_to_data_free_list[self.idx_to_data_free_list_head]; + let next = self.idx_to_data_free_list[free_idx].next; + + if next != INVALID { + self.idx_to_data_free_list[next].previous = INVALID; + } + self.idx_to_data_free_list_head = next; Some(free_idx) } - unsafe fn reserve_index(&mut self) -> Option { - todo!() + unsafe fn reserve_index(&mut self, idx: usize) { + if idx >= self.capacity_impl() { + return; + } + + let entry = self.idx_to_data_free_list[idx]; + if entry.previous != INVALID { + self.idx_to_data_free_list[entry.previous].next = entry.next; + } + if entry.next != INVALID { + self.idx_to_data_free_list[entry.next].previous = entry.previous; + } + self.idx_to_data_free_list[idx].next = INVALID; + self.idx_to_data_free_list[idx].previous = INVALID; } unsafe fn release_free_index(&mut self, idx: usize) { - self.idx_to_data_free_list[idx] = self.idx_to_data_free_list_head; + if self.idx_to_data_free_list_head != INVALID { + self.idx_to_data_free_list[self.idx_to_data_free_list_head].previous = idx; + } + + self.idx_to_data_free_list[idx] = FreeListEntry { + previous: INVALID, + next: self.idx_to_data_free_list_head, + }; + self.idx_to_data_free_list_head = idx; } @@ -207,6 +234,7 @@ pub mod details { } pub(crate) unsafe fn insert_at_impl(&mut self, key: SlotMapKey, value: T) -> bool { + self.reserve_index(key.value()); self.store_value(key, value) } @@ -216,7 +244,7 @@ pub mod details { } let data_idx = self.idx_to_data[key.0]; - if data_idx != INVALID_KEY { + if data_idx != INVALID { self.data[data_idx] = Some(value); } else { 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."); @@ -234,11 +262,11 @@ pub mod details { } let data_idx = self.idx_to_data[key.0]; - if data_idx != INVALID_KEY { + if data_idx != INVALID { self.data[data_idx].take(); debug_assert!(self.data_next_free_index.push_impl(data_idx)); self.release_free_index(key.0); - self.idx_to_data[key.0] = INVALID_KEY; + self.idx_to_data[key.0] = INVALID; self.len -= 1; true } else { @@ -384,7 +412,7 @@ pub mod details { /// in [`RelocatableSlotMap::init()`]. pub const fn const_memory_size(capacity: usize) -> usize { RelocatableVec::::const_memory_size(capacity) - + RelocatableVec::::const_memory_size(capacity) + + RelocatableVec::::const_memory_size(capacity) + RelocatableVec::>::const_memory_size(capacity) + RelocatableQueue::::const_memory_size(capacity) } @@ -492,10 +520,10 @@ pub mod details { #[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], + _idx_to_data: MaybeUninit<[usize; CAPACITY]>, + _idx_to_data_free_list: MaybeUninit<[FreeListEntry; CAPACITY]>, + _data: MaybeUninit<[Option; CAPACITY]>, + _data_next_free_index: MaybeUninit<[usize; CAPACITY]>, } impl PlacementDefault for FixedSizeSlotMap { @@ -513,10 +541,10 @@ impl PlacementDefault for FixedSizeSlotMap Default for FixedSizeSlotMap { fn default() -> Self { let mut new_self = Self { - _idx_to_data: core::array::from_fn(|_| INVALID_KEY), - _idx_to_data_next_free_index: core::array::from_fn(|_| 0), - _data: core::array::from_fn(|_| None), - _data_next_free_index: core::array::from_fn(|_| 0), + _idx_to_data: MaybeUninit::uninit(), + _idx_to_data_free_list: MaybeUninit::uninit(), + _data: MaybeUninit::uninit(), + _data_next_free_index: MaybeUninit::uninit(), state: unsafe { RelocatableSlotMap::new_uninit(CAPACITY) }, }; From 6d771161d1b7852be3fb1363bee3b97b890dae50 Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Wed, 13 Nov 2024 11:48:41 +0100 Subject: [PATCH 14/17] [#504] Add SlotMap requirements to the documentation --- iceoryx2-bb/container/src/slotmap.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 98ee89beb..08653b4d7 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -21,6 +21,16 @@ //! * [`RelocatableSlotMap`](crate::slotmap::RelocatableSlotMap), run-time fixed-size slotmap that //! is shared-memory compatible. //! +//! The SlotMap shall satisfy the following requirements: +//! +//! * A new element can be inserted with a max runtime of `O(1)` +//! * A new element can be inserted at a user-provided key with a max runtime of `O(1)` +//! * An element can be removed by providing the corresponding key with a max runtime of `O(1)` +//! * One can iterate over all elements of the SlotMap. +//! +//! The SlotMap is the perfect container when elements shall be added, removed and accesses quickly +//! but iteration is allowed to be slow. +//! //! # User Examples //! //! ``` From 5be0ce7e7a49c557bb2d9b3054b1b6e48ee264ac Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Mon, 18 Nov 2024 13:57:45 +0100 Subject: [PATCH 15/17] [#504] Add additional tests; rename internal functions; simplify code --- iceoryx2-bb/container/src/slotmap.rs | 25 ++++++++--------- iceoryx2-bb/container/tests/slotmap_tests.rs | 28 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 08653b4d7..70b2c7e61 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -106,9 +106,9 @@ pub mod details { type Item = (SlotMapKey, &'slotmap T); fn next(&mut self) -> Option { - if let Some((key, value)) = self.slotmap.next(self.key) { - self.key.0 = key.0 + 1; - Some((key, value)) + if let Some((next_key, value)) = self.slotmap.next_available_key_after(self.key) { + self.key.0 = next_key.0 + 1; + Some((next_key, value)) } else { None } @@ -127,7 +127,7 @@ pub mod details { } impl MetaSlotMap { - fn next(&self, start: SlotMapKey) -> Option<(SlotMapKey, &T)> { + fn next_available_key_after(&self, start: SlotMapKey) -> Option<(SlotMapKey, &T)> { let idx_to_data = &self.idx_to_data; for n in start.0..idx_to_data.len() { @@ -203,7 +203,7 @@ pub mod details { Some(free_idx) } - unsafe fn reserve_index(&mut self, idx: usize) { + unsafe fn claim_index(&mut self, idx: usize) { if idx >= self.capacity_impl() { return; } @@ -233,18 +233,15 @@ pub mod details { } pub(crate) unsafe fn insert_impl(&mut self, value: T) -> Option { - match self.acquire_next_free_index() { - None => None, - Some(key) => { - let key = SlotMapKey(key); - self.store_value(key, value); - Some(key) - } - } + self.acquire_next_free_index() + .map(|key| SlotMapKey(key)) + .inspect(|key| { + self.store_value(*key, value); + }) } pub(crate) unsafe fn insert_at_impl(&mut self, key: SlotMapKey, value: T) -> bool { - self.reserve_index(key.value()); + self.claim_index(key.value()); self.store_value(key, value) } diff --git a/iceoryx2-bb/container/tests/slotmap_tests.rs b/iceoryx2-bb/container/tests/slotmap_tests.rs index 99dbcc635..1ed75f42d 100644 --- a/iceoryx2-bb/container/tests/slotmap_tests.rs +++ b/iceoryx2-bb/container/tests/slotmap_tests.rs @@ -149,6 +149,8 @@ mod slot_map { assert_that!(key, is_some); assert_that!(key.unwrap().value() % 2, eq 0); } + + assert_that!(sut.insert(0), is_none); } #[test] @@ -171,4 +173,30 @@ mod slot_map { assert_that!(*value, eq 5 * key.value() + 3); } } + + #[test] + fn insert_remove_and_insert_works() { + let mut sut = FixedSizeSut::new(); + + for _ in 0..SUT_CAPACITY { + assert_that!(sut.insert(3), is_some); + } + + for n in 0..SUT_CAPACITY / 2 { + assert_that!(sut.remove(SlotMapKey::new(2 * n)), eq true); + } + + for _ in 0..SUT_CAPACITY / 2 { + let key = sut.insert(2); + assert_that!(key, is_some); + } + + for (key, value) in sut.iter() { + if key.value() % 2 == 0 { + assert_that!(*value, eq 2); + } else { + assert_that!(*value, eq 3); + } + } + } } From 3be6089f8f64f9516f17a1ed486d9ba35f86e35b Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Mon, 18 Nov 2024 18:39:26 +0100 Subject: [PATCH 16/17] [#504] Remove 'inspect' since it is not available in 1.75 --- iceoryx2-bb/container/src/slotmap.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 70b2c7e61..96f716ad2 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -233,11 +233,11 @@ pub mod details { } pub(crate) unsafe fn insert_impl(&mut self, value: T) -> Option { - self.acquire_next_free_index() - .map(|key| SlotMapKey(key)) - .inspect(|key| { - self.store_value(*key, value); - }) + self.acquire_next_free_index().map(|key| { + let key = SlotMapKey(key); + self.store_value(key, value); + key + }) } pub(crate) unsafe fn insert_at_impl(&mut self, key: SlotMapKey, value: T) -> bool { From a5f1e64a714b034aaafe360c404d15e96ffb71be Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Mon, 18 Nov 2024 20:01:45 +0100 Subject: [PATCH 17/17] [#504] Fix release build --- iceoryx2-bb/container/src/slotmap.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iceoryx2-bb/container/src/slotmap.rs b/iceoryx2-bb/container/src/slotmap.rs index 96f716ad2..663f77177 100644 --- a/iceoryx2-bb/container/src/slotmap.rs +++ b/iceoryx2-bb/container/src/slotmap.rs @@ -271,7 +271,8 @@ pub mod details { let data_idx = self.idx_to_data[key.0]; if data_idx != INVALID { self.data[data_idx].take(); - debug_assert!(self.data_next_free_index.push_impl(data_idx)); + let push_result = self.data_next_free_index.push_impl(data_idx); + debug_assert!(push_result); self.release_free_index(key.0); self.idx_to_data[key.0] = INVALID; self.len -= 1;