Skip to main content

ferroui_base/utilities/
pooled_inline_list.rs

1use std::rc::Rc;
2
3/// A list of shared items that holds one item in place and more in a list.
4///
5/// Items are compared by reference, as in the original, whose items are
6/// reference types. The original rents the array of the list from the shared
7/// array pool; here the list owns a `Vec`.
8pub struct PooledInlineList<T: ?Sized> {
9    item: ItemState<T>,
10}
11
12/// The original's `object? _item`: nothing, the one item or the list.
13enum ItemState<T: ?Sized> {
14    Null,
15    Single(Rc<T>),
16    List(SimplePooledList<T>),
17}
18
19/// The state of a [`PooledInlineList`] that has given up its ownership (see
20/// [`PooledInlineList::transfer_raw_state`]).
21pub struct PooledInlineListRawState<T: ?Sized>(ItemState<T>);
22
23impl<T: ?Sized> Default for PooledInlineList<T> {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl<T: ?Sized> PooledInlineList<T> {
30    pub fn new() -> Self {
31        Self { item: ItemState::Null }
32    }
33
34    /// For compositor serialization purposes only, takes the ownership of previously transferred state
35    pub fn from_raw_state(raw_state: PooledInlineListRawState<T>) -> Self {
36        Self { item: raw_state.0 }
37    }
38
39    pub fn add(&mut self, item: Rc<T>) {
40        if matches!(self.item, ItemState::Null) {
41            self.item = ItemState::Single(item);
42        } else {
43            self.convert_to_list();
44            if let ItemState::List(list) = &mut self.item {
45                list.add(item);
46            }
47        }
48    }
49
50    /// Removes the item when it is the one the list holds in place.
51    ///
52    /// As in the original, which tests the argument rather than its own
53    /// state for being a list, an item of a list of several is not removed.
54    pub fn remove(&mut self, item: &Rc<T>) -> bool {
55        let same = matches!(&self.item, ItemState::Single(single) if Rc::ptr_eq(single, item));
56        if same {
57            self.item = ItemState::Null;
58            return true;
59        }
60
61        false
62    }
63
64    fn convert_to_list(&mut self) {
65        if matches!(self.item, ItemState::List(_)) {
66            return;
67        }
68        let mut list = SimplePooledList::new();
69        if let ItemState::Single(item) = std::mem::replace(&mut self.item, ItemState::Null) {
70            list.add(item);
71        }
72        self.item = ItemState::List(list);
73    }
74
75    pub fn ensure_capacity(&mut self, count: i32) {
76        if count < 2 {
77            return;
78        }
79        self.convert_to_list();
80        if let ItemState::List(list) = &mut self.item {
81            list.ensure_capacity(count as usize);
82        }
83    }
84
85    pub fn dispose(&mut self) {
86        if let ItemState::List(list) = &mut self.item {
87            list.dispose();
88        }
89        self.item = ItemState::Null;
90    }
91
92    pub fn count(&self) -> i32 {
93        match &self.item {
94            ItemState::Null => 0,
95            ItemState::List(list) => list.count() as i32,
96            ItemState::Single(_) => 1,
97        }
98    }
99
100    pub fn get_enumerator(&self) -> PooledInlineListEnumerator<'_, T> {
101        PooledInlineListEnumerator::new(&self.item)
102    }
103
104    /// For compositor serialization purposes only, gives up the ownership of the internal state and returns it
105    pub fn transfer_raw_state(&mut self) -> PooledInlineListRawState<T> {
106        PooledInlineListRawState(std::mem::replace(&mut self.item, ItemState::Null))
107    }
108}
109
110impl<'a, T: ?Sized> IntoIterator for &'a PooledInlineList<T> {
111    type Item = &'a Rc<T>;
112    type IntoIter = PooledInlineListEnumerator<'a, T>;
113
114    fn into_iter(self) -> Self::IntoIter {
115        self.get_enumerator()
116    }
117}
118
119struct SimplePooledList<T: ?Sized> {
120    items: Option<Vec<Rc<T>>>,
121}
122
123impl<T: ?Sized> SimplePooledList<T> {
124    fn new() -> Self {
125        Self { items: None }
126    }
127
128    fn count(&self) -> usize {
129        self.items.as_ref().map_or(0, Vec::len)
130    }
131
132    fn add(&mut self, item: Rc<T>) {
133        let items = self.items.get_or_insert_with(|| Vec::with_capacity(4));
134        if items.len() == items.capacity() {
135            let count = items.len();
136            items.reserve_exact(count);
137        }
138
139        items.push(item);
140    }
141
142    fn ensure_capacity(&mut self, count: usize) {
143        if self.items.is_none() {
144            self.items = Some(Vec::with_capacity(count));
145            return;
146        }
147        if let Some(items) = &mut self.items {
148            if items.capacity() < count {
149                items.reserve_exact(count - items.len());
150            }
151        }
152    }
153
154    fn dispose(&mut self) {
155        self.items = None;
156    }
157}
158
159/// The enumerator of a [`PooledInlineList`] (the original's nested
160/// `Enumerator`). It is an `Iterator` as well.
161pub struct PooledInlineListEnumerator<'a, T: ?Sized> {
162    single_item: Option<&'a Rc<T>>,
163    index: i32,
164    list: Option<&'a SimplePooledList<T>>,
165}
166
167impl<'a, T: ?Sized> PooledInlineListEnumerator<'a, T> {
168    fn new(item: &'a ItemState<T>) -> Self {
169        let mut result = Self { single_item: None, index: -1, list: None };
170        match item {
171            ItemState::List(list) => result.list = Some(list),
172            ItemState::Single(single) => result.single_item = Some(single),
173            ItemState::Null => {}
174        }
175        result
176    }
177
178    pub fn move_next(&mut self) -> bool {
179        if self.single_item.is_some() {
180            if self.index >= 0 {
181                return false;
182            }
183            self.index = 0;
184            return true;
185        }
186
187        if let Some(list) = self.list {
188            if self.index >= list.count() as i32 - 1 {
189                return false;
190            }
191            self.index += 1;
192            return true;
193        }
194
195        false
196    }
197
198    /// Not supported: panics.
199    pub fn reset(&mut self) {
200        panic!("Specified method is not supported.");
201    }
202
203    /// The current item.
204    ///
205    /// Panics when the enumerator is not positioned on an item.
206    pub fn current(&self) -> &'a Rc<T> {
207        if let Some(list) = self.list {
208            return match &list.items {
209                Some(items) => &items[self.index as usize],
210                None => panic!("Object reference not set to an instance of an object."),
211            };
212        }
213        match self.single_item {
214            Some(single_item) => single_item,
215            None => panic!("Object reference not set to an instance of an object."),
216        }
217    }
218
219    pub fn dispose(&mut self) {}
220}
221
222impl<'a, T: ?Sized> Iterator for PooledInlineListEnumerator<'a, T> {
223    type Item = &'a Rc<T>;
224
225    fn next(&mut self) -> Option<Self::Item> {
226        if self.move_next() {
227            Some(self.current())
228        } else {
229            None
230        }
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    // Not from upstream: the original has no tests of this type.
237    use super::*;
238
239    fn values(list: &PooledInlineList<i32>) -> Vec<i32> {
240        list.get_enumerator().map(|item| **item).collect()
241    }
242
243    #[test]
244    fn holds_one_item_in_place_and_more_in_a_list() {
245        let mut target = PooledInlineList::<i32>::new();
246        assert_eq!(0, target.count());
247        assert!(values(&target).is_empty());
248
249        let first = Rc::new(1);
250        target.add(first.clone());
251        assert_eq!(1, target.count());
252        assert_eq!(vec![1], values(&target));
253
254        for value in 2..=9 {
255            target.add(Rc::new(value));
256        }
257        assert_eq!(9, target.count());
258        assert_eq!((1..=9).collect::<Vec<_>>(), values(&target));
259
260        // An item of a list is not removed (see `remove`).
261        assert!(!target.remove(&first));
262        assert_eq!(9, target.count());
263
264        target.dispose();
265        assert_eq!(0, target.count());
266    }
267
268    #[test]
269    fn removes_the_item_held_in_place() {
270        let mut target = PooledInlineList::<i32>::new();
271        let item = Rc::new(1);
272        target.add(item.clone());
273
274        assert!(!target.remove(&Rc::new(1)));
275        assert!(target.remove(&item));
276        assert_eq!(0, target.count());
277        assert!(!target.remove(&item));
278    }
279
280    #[test]
281    fn ensure_capacity_converts_to_a_list_for_two_or_more_items() {
282        let mut target = PooledInlineList::<i32>::new();
283        target.ensure_capacity(1);
284        target.add(Rc::new(1));
285        assert_eq!(1, target.count());
286
287        target.ensure_capacity(8);
288        assert_eq!(1, target.count());
289        assert_eq!(vec![1], values(&target));
290    }
291
292    #[test]
293    fn transfers_its_state() {
294        let mut target = PooledInlineList::<i32>::new();
295        target.add(Rc::new(1));
296        target.add(Rc::new(2));
297
298        let state = target.transfer_raw_state();
299        assert_eq!(0, target.count());
300
301        let restored = PooledInlineList::from_raw_state(state);
302        assert_eq!(vec![1, 2], values(&restored));
303    }
304}