orx_concurrent_iter/implementations/jagged_arrays/owned/raw_jagged.rs
1use super::{raw_vec::RawVec, slice::RawJaggedSlice};
2use crate::implementations::{
3 jagged_arrays::{
4 as_raw_slice::{AsOwningSlice, AsRawSlice},
5 index::JaggedIndex,
6 indexer::JaggedIndexer,
7 },
8 ptr_utils::take,
9};
10use alloc::vec::Vec;
11use core::cmp::Ordering;
12
13/// Raw representation of a jagged array.
14/// Internally, the jagged array is stored as a vector of `RawVec<T>`.
15///
16/// Further, jagged has an indexer which maps a flat-element-index to a
17/// two-dimensional index where the first is the index of the array and
18/// the second is the position of the element within this array.
19///
20/// Once dropped, the owned raw jagged array will drop the elements and
21/// allocation of its raw vectors.
22pub struct RawJagged<T, X>
23where
24 X: JaggedIndexer,
25{
26 arrays: Vec<RawVec<T>>,
27 len: usize,
28 indexer: X,
29 num_taken: Option<usize>,
30}
31
32impl<T, X> RawJagged<T, X>
33where
34 X: JaggedIndexer,
35{
36 /// Creates the raw jagged array for the given `arrays` and `indexer`.
37 ///
38 /// If the total number of elements in all `arrays` is known, it can be passed in as `total_len`,
39 /// which will be assumed to be correct.
40 /// If `None` is passed as the total length, it will be computed as sum of all vectors.
41 ///
42 /// Once the jagged array is dropped, the elements and allocation of the vectors
43 /// will also be dropped.
44 pub fn new(arrays: Vec<RawVec<T>>, indexer: X, total_len: Option<usize>) -> Self {
45 let len = total_len.unwrap_or_else(|| arrays.iter().map(|v| v.length()).sum());
46 Self {
47 arrays,
48 len,
49 indexer,
50 num_taken: Some(0),
51 }
52 }
53
54 /// Leaves this jagged array empty without anything to drop.
55 ///
56 /// The remaining elements with respect to `num_taken` together with the allocation, and hence,
57 /// the responsibility to drop, are transferred to the returned raw jagged array.
58 pub(super) fn move_into_new(&mut self, num_taken: usize) -> Self {
59 let arrays = core::mem::take(&mut self.arrays);
60
61 let jagged_to_drop = Self {
62 arrays,
63 len: self.len,
64 indexer: self.indexer.clone(),
65 num_taken: Some(num_taken),
66 };
67
68 self.len = 0;
69 self.num_taken = None;
70
71 jagged_to_drop
72 }
73
74 /// Total number of elements in the jagged array (`O(1)`).
75 pub(super) fn len(&self) -> usize {
76 self.len
77 }
78
79 /// Returns number of arrays of the jagged array.
80 pub(super) fn num_arrays(&self) -> usize {
81 self.arrays.len()
82 }
83
84 /// Returns the [`JaggedIndex`] of the element at the given `flat_index` position of the flattened
85 /// jagged array.
86 ///
87 /// It returns `None` when `flat_index > self.len()`.
88 /// Importantly note that it returns `Some` when `flat_index == self.len()` which is the exclusive bound
89 /// of the collection.
90 ///
91 /// Returns:
92 ///
93 /// * `Some([f, i])` if `flat_index < self.len()` such that the element is located at the `f`-th array's
94 /// `i`-th position.
95 /// * `Some([f*, i*])` if `flat_index = self.len()` such that `f* = self.len() - 1` and `i* = self.arrays()[f*].len()`.
96 /// In other words, this is the exclusive end of the jagged index range of the jagged array.
97 /// * `None` if `flat_index > self.len()`.
98 pub(super) fn jagged_index(&self, flat_index: usize) -> Option<JaggedIndex> {
99 match flat_index.cmp(&self.len) {
100 Ordering::Less => Some(unsafe {
101 // SAFETY: flat_index is within bounds
102 self.indexer
103 .jagged_index_unchecked_from_slice(&self.arrays, flat_index)
104 }),
105 Ordering::Equal => match self.arrays.is_empty() {
106 true => None,
107 false => {
108 let f = self.arrays.len() - 1;
109 let i = self.arrays[f].length();
110 Some(JaggedIndex::new(f, i))
111 }
112 },
113 Ordering::Greater => None,
114 }
115 }
116
117 /// Jagged array when created with `new_as_owned` is responsible for dropping its elements and clear the
118 /// allocation of the vectors. However, it also allows that some of the elements are taken out before the
119 /// jagged array is dropped, see the [`take`] method. This is tracked by `num_taken` which is `Some(0)`
120 /// on construction.
121 ///
122 /// This method overwrites `num_taken`:
123 ///
124 /// * when `Some(n)`, the first `n` elements of the jagged array will not be dropped when
125 /// this `RawJagged` is dropped. The remaining elements and the allocations will be dropped.
126 /// * when `None`, neither any of the elements nor the allocations will be dropped.
127 ///
128 /// [`take`]: Self::take
129 ///
130 /// # Safety
131 ///
132 /// This method is unsafe due to the following use cases which would lead to undefined behavior.
133 ///
134 /// If the jagged array is created as owned:
135 /// * we intend to drop the vectors when we drop this jagged array,
136 /// * in this case, calling this method with `None` would cause the memory to leak.
137 ///
138 /// If the jagged array is Created as owned, and if we call this method with `Some(n)`:
139 /// * we must make sure that all elements with flat indices within range `0..n` must be taken manually
140 /// by the [`take`] method; and hence, will be dropped externally;
141 /// * if an element within `0..n` is not taken, the corresponding element will leak,
142 /// * if an element outside `0..n` is taken, there will be two attempts to drop the same element.
143 ///
144 /// Note that it is not required to call `set_num_taken` immediately after calling `take`.
145 /// The following sequence of events is perfectly safe:
146 ///
147 /// * `jagged.take(2)`
148 /// * `jagged.take(0)`
149 /// * `jagged.take(1)`
150 /// * `jagged.set_num_taken(3)`
151 /// * `drop(jagged)`
152 pub(super) unsafe fn set_num_taken(&mut self, num_taken: Option<usize>) {
153 self.num_taken = num_taken;
154 }
155
156 /// Jagged array when created with `new_as_owned` is responsible for dropping its elements and clear the
157 /// allocation of the vectors. However, it also allows that some of the elements are taken out before the
158 /// jagged array is dropped. This is tracked by `num_taken` which is `Some(0)` on construction.
159 ///
160 /// See [`set_num_taken`] to update the number of manually taken elements in order to avoid dropping the
161 /// same element twice.
162 ///
163 /// This method returns currently value of `num_taken`.
164 ///
165 /// [`set_num_taken`]: Self::set_num_taken
166 pub(super) fn num_taken(&self) -> Option<usize> {
167 self.num_taken
168 }
169
170 /// Takes the element at the `flat-index`-th position of the flattened jagged array.
171 ///
172 /// # Safety
173 ///
174 /// This method safely takes out the element; however, leaves the `flat-index`-th position uninitialized.
175 ///
176 /// Therefore, jagged array must not attempt to drop the element at this position.
177 /// This can be controlled using the [`set_num_taken`] method.
178 ///
179 /// [`set_num_taken`]: Self::set_num_taken
180 pub(super) unsafe fn take(&self, flat_index: usize) -> Option<T> {
181 self.jagged_index(flat_index).map(|idx| {
182 let vec = &self.arrays[idx.f];
183 let ptr = unsafe { vec.ptr_at(idx.i) as *mut T }; // index is in bounds
184 unsafe { take(ptr) }
185 })
186 }
187
188 /// Returns a reference to the `f`-th slice, returns None iF `f >= self.num_arrays()`.
189 pub(super) fn get(&self, f: usize) -> Option<&RawVec<T>> {
190 self.arrays.get(f)
191 }
192
193 /// Returns a reference to the `f`-th slice, without bounds checking.
194 ///
195 /// # SAFETY
196 ///
197 /// `f` must be within bounds; i.e., `f < self.num_arrays()`.
198 pub(super) unsafe fn get_unchecked(&self, f: usize) -> &RawVec<T> {
199 debug_assert!(f < self.arrays.len());
200 unsafe { self.arrays.get_unchecked(f) }
201 }
202
203 /// Returns the raw jagged array slice containing all elements having positions in range `flat_begin..flat_end`
204 /// of the flattened jagged array.
205 ///
206 /// Returns an empty slice if any of the indices are out of bounds or if `flat_end <= flat_begin`.
207 pub(super) fn slice(&self, flat_begin: usize, flat_end: usize) -> RawJaggedSlice<'_, T> {
208 match flat_end.saturating_sub(flat_begin) {
209 0 => Default::default(),
210 len => {
211 let [begin, end] = [flat_begin, flat_end].map(|i| self.jagged_index(i));
212 match (begin, end) {
213 (Some(begin), Some(end)) => RawJaggedSlice::new(&self.arrays, begin, end, len),
214 _ => Default::default(),
215 }
216 }
217 }
218 }
219
220 /// Returns the raw jagged array slice for the flattened positions within range `flat_begin..self.len()`.
221 pub(super) fn slice_from(&self, flat_begin: usize) -> RawJaggedSlice<'_, T> {
222 self.slice(flat_begin, self.len)
223 }
224}
225
226impl<T, X> Drop for RawJagged<T, X>
227where
228 X: JaggedIndexer,
229{
230 fn drop(&mut self) {
231 if let Some(num_taken) = self.num_taken {
232 // drop elements
233 if let Some(begin) = self.jagged_index(num_taken) {
234 let s = &self.arrays[begin.f];
235 for p in begin.i..s.length() {
236 unsafe { s.drop_in_place(p) };
237 }
238
239 for f in (begin.f + 1)..self.arrays.len() {
240 let s = &self.arrays[f];
241 for p in 0..s.length() {
242 unsafe { s.drop_in_place(p) };
243 }
244 }
245 }
246
247 // drop allocation
248 for vec in &self.arrays {
249 unsafe { vec.drop_allocation() };
250 }
251 }
252 }
253}