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}