Skip to main content

iceoryx2_cal/bag/
mod.rs

1// Copyright (c) 2026 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13//! Offers an interface to a container to concurrently add, remove and access
14//! values with a fixed position for the lifetime of the data. The data in the
15//! container is not necessarily ordered.
16//!
17//! # Example
18//!
19//! ```
20//! use core::ptr::NonNull;
21//!
22//! use iceoryx2_bb_container::queue::RelocatableContainer;
23//! use iceoryx2_bb_elementary_traits::allocator::{Allocate, AllocationError};
24//! use iceoryx2_cal::bag::*;
25//!
26//! fn create_bag<B: BagFamily, T: BagType, A: Allocate<NonNull<u8>>>(
27//!     capacity: usize,
28//!     allocator: A,
29//! ) -> Result<B::Bag<T>, AllocationError> {
30//!     let mut bag = unsafe { B::Bag::<T>::new_uninit(capacity) };
31//!     unsafe {
32//!         bag.init(&allocator)?;
33//!     }
34//!     Ok(bag)
35//! }
36//! ```
37
38pub mod lock_free_bag;
39pub mod recommended;
40
41use core::fmt::Debug;
42
43use iceoryx2_bb_container::queue::RelocatableContainer;
44use iceoryx2_bb_elementary::CallbackProgression;
45use iceoryx2_bb_elementary_traits::zero_copy_send::ZeroCopySend;
46
47use iceoryx2_bb_lock_free::mpmc::robust_unique_index_set::OwnerId;
48use iceoryx2_bb_lock_free::mpmc::unique_index_set_enums::{ReleaseMode, ReleaseState};
49
50/// States the reason why an element could not be added to the [`Bag`]
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum BagAddFailure {
53    /// The new element would exceed the maximum [`Bag::capacity()`].
54    OutOfSpace,
55    /// The last element was removed from the [`Bag`] with the option
56    /// [`ReleaseMode::LockIfLastIndex`] which prevents adding new elements when the [`Bag`]
57    /// reached the [`Bag::is_empty()`] state.
58    IsLocked,
59}
60
61/// States the reason why a [`BagHandleFamily`] handle could not be removed to the [`Bag`]
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum BagRemoveError {
64    /// The [`BagHandleFamily`] handle is not part of the bag. Either it is a double remove, belongs to
65    /// a different [`Bag`] or it was forcefully removed with [`Bag::recover()`].
66    HandleNotOwnedByInstance,
67}
68
69/// The [`BagHandleFamily`] trait provides access to a handle of the concrete implementation of a bag
70pub trait BagHandleFamily: Debug + Clone + Copy + PartialEq + Eq + PartialOrd + Ord + Send {
71    fn index(&self) -> usize;
72}
73
74/// A super trait defining the trait bounds of a [`Bag`] type
75pub trait BagType: Copy + Debug + Send + ZeroCopySend {}
76impl<T: Copy + Debug + Send + ZeroCopySend> BagType for T {}
77
78/// The [`BagStateFamily`] trait provides access to a state of the concrete implementation of a bag
79pub trait BagStateFamily<T: BagType>: Debug + Send {
80    /// Iterates over all elements and calls the callback for each of them, providing the
81    /// index of the element and a reference to the underlying value.
82    /// **Note:** The index of a value never changes as long as it is stored inside the bag.
83    fn for_each<F: FnMut(usize, &T) -> CallbackProgression>(&self, callback: F);
84
85    /// Returns the element at the given index, if occupied
86    fn get(&self, index: usize) -> Option<&T>;
87}
88
89/// The [`BagFamily`] provides the associated type for the concrete type implementing the concept
90pub trait BagFamily: Debug + 'static {
91    type BagHandle: BagHandleFamily;
92    type BagState<T: BagType>: BagStateFamily<T>;
93    type Bag<T: BagType>: Debug
94        + Send
95        + Sync
96        + ZeroCopySend
97        + RelocatableContainer
98        + Bag<T, BagHandle = Self::BagHandle, BagState<T> = Self::BagState<T>>;
99}
100
101/// The [`Bag`] trait provides access to an unordered container with fix position for the data
102/// during its lifetime
103pub trait Bag<T: BagType>: Debug {
104    type BagHandle: BagHandleFamily;
105    type BagState<TT: BagType>: BagStateFamily<T>;
106
107    /// Returns the capacity of the bag.
108    fn capacity(&self) -> usize;
109
110    /// Returns the current len of the bag
111    fn len(&self) -> usize;
112
113    /// Returns true if the container is empty, otherwise false
114    fn is_empty(&self) -> bool;
115
116    /// Adds a new element to the [`Bag`]. If there is no more space available it returns
117    /// [`None`], otherwise [`Some`] containing the the index value to the underlying element.
118    ///
119    /// Must be released with [`Bag::remove()`].
120    ///
121    /// # Safety
122    ///
123    ///  * Ensure that [`Bag::init()`](RelocatableContainer::init()) was called before calling this method
124    ///
125    unsafe fn add(
126        &self,
127        value: T,
128        owner_id: OwnerId,
129    ) -> Result<(*const T, Self::BagHandle), BagAddFailure>
130    where
131        T: PartialEq;
132
133    /// Useful in IPC context when an application holding the UniqueIndex has died.
134    ///
135    /// # Safety
136    ///
137    ///  * Ensure that [`Bag::init()`](RelocatableContainer::init()) was called before calling this method
138    ///  * Ensure that no one else possesses the [`BagHandleFamily`] handle and the index was unrecoverable
139    ///    lost
140    ///  * Ensure that the `handle` was acquired by the same [`Bag`]
141    ///    with [`Bag::add()`], otherwise the method will panic.
142    ///
143    /// **Important:** If the [`BagHandleFamily`] handle still exists it causes double frees or freeing an index
144    /// which was allocated afterwards
145    ///
146    unsafe fn remove(
147        &self,
148        handle: Self::BagHandle,
149        mode: ReleaseMode,
150    ) -> Result<ReleaseState, BagRemoveError>;
151
152    /// Returns [`BagStateFamily`] state which contains all elements of this bag. Be aware that
153    /// this state can be out of date as soon as it is returned from this function.
154    ///
155    /// # Safety
156    ///
157    ///  * Ensure that [`Bag::init()`](RelocatableContainer::init()) was called before calling this method
158    ///
159    unsafe fn get_state(&self) -> Self::BagState<T>;
160
161    /// Recovers and releases all entries the dead [`OwnerId`] owned. It assumes that the dead owner
162    /// maybe died while adding some entry, therefore it removes all entries where the
163    /// [`OwnerId`] does not contain any data or where there was data and the provided predicate
164    /// returned [`true`].
165    ///
166    /// # Safety
167    ///
168    ///  * Ensure that [`Bag::init()`](RelocatableContainer::init()) was called before calling this method
169    ///  * All existing [`BagHandleFamily`] handle that belong to the [`OwnerId`] must never be removed with
170    ///    [`Bag::remove()`] otherwise we corrupt the state.
171    ///
172    unsafe fn recover<F: FnMut(T) -> bool>(
173        &self,
174        dead_owner_id: OwnerId,
175        predicate: F,
176        mode: ReleaseMode,
177    ) -> ReleaseState;
178
179    /// Syncs the [`BagStateFamily`] state with the current state of the [`Bag`]. If the state has
180    /// changed it returns true, otherwise false.
181    ///
182    /// # Safety
183    ///
184    ///  * Ensure that [`Bag::init()`](RelocatableContainer::init()) was called before calling this method
185    ///  * Ensure that the input argument `previous_state` was acquired by the same [`Bag`]
186    ///    with [`Bag::get_state()`], otherwise the method will panic.
187    ///
188    unsafe fn update_state(&self, previous_state: &mut Self::BagState<T>) -> bool;
189}