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}