makepad_platform/
id_pool.rs

1use std::{
2    cell::RefCell,
3    rc::Rc,
4    ops::Deref,
5    ops::DerefMut,
6};
7
8#[derive(Clone, Default, Debug, PartialEq)]
9pub struct IdPoolFree(Rc<RefCell<Vec<usize >> >);
10
11#[derive(Default, Debug)]
12pub struct IdPool<T> where T: Default {
13    pub pool: Vec<IdPoolItem<T >>,
14    pub free: IdPoolFree
15}
16
17#[derive(Debug, PartialEq)]
18pub struct IdPoolItem<T> {
19    pub item: T,
20    pub generation: u64,
21}
22
23impl<T> Deref for IdPoolItem<T> {
24    type Target = T;
25    fn deref(&self) -> &Self::Target {&self.item}
26}
27
28impl<T> DerefMut for IdPoolItem<T> {
29    fn deref_mut(&mut self) -> &mut Self::Target {&mut self.item}
30}
31
32#[derive(Debug, PartialEq)]
33pub struct PoolId {
34    pub id: usize,
35    pub generation: u64,
36    pub free: IdPoolFree
37}
38
39impl Drop for PoolId {
40    fn drop(&mut self) {
41        self.free.0.borrow_mut().push(self.id)
42    }
43}
44
45impl<T> IdPool<T> where T: Default {
46    pub fn alloc(&mut self) -> PoolId {
47        let last_from_free_pool = self.free.0.borrow_mut().pop();
48        if let Some(id) = last_from_free_pool {
49            self.pool[id].generation += 1;
50            PoolId {
51                id,
52                generation: self.pool[id].generation,
53                free: self.free.clone()
54            }
55        }
56        else {
57            self.alloc_new(None)
58        }
59    }
60
61    pub fn alloc_new(&mut self, item: Option<T>) -> PoolId {
62        let id = self.pool.len();
63        self.pool.push(IdPoolItem {
64            generation: 0,
65            item: item.unwrap_or_else(|| T::default())
66        });
67        PoolId {
68            id,
69            generation: 0,
70            free: self.free.clone()
71        }
72    }
73
74    /// Allocates an item in the pool, potentially reusing an existing slot.
75    ///
76    /// This method attempts to find a reusable slot in the pool that satisfies the given filter.
77    /// If a suitable slot is found, it's reused; otherwise, a new slot is allocated.
78    ///
79    /// # Arguments
80    /// * `filter` - A closure that determines if an existing item can be reused.
81    /// * `item` - The new item to be stored in the pool.
82    ///
83    /// # Returns
84    /// A tuple containing:
85    /// - `PoolId`: The ID of the allocated or reused slot.
86    /// - `Option<T>`: The previous item if a slot was reused, or None if a new slot was allocated.
87    pub fn alloc_with_reuse_filter<F>(&mut self, mut filter: F, item: T) -> (PoolId, Option<T>)
88    where F: FnMut(&IdPoolItem<T>) -> bool {
89        let maybe_free_id = self.free.0.borrow_mut()
90            .iter()
91            .enumerate()
92            .find_map(|(index, &id)| {
93                if filter(&self.pool[id]) {
94                    Some((index, id))
95                } else {
96                    None
97                }
98            });
99    
100        if let Some((index, id)) = maybe_free_id {
101            self.free.0.borrow_mut().remove(index);
102            self.pool[id].generation += 1;
103            let old_item = std::mem::replace(&mut self.pool[id].item, item);
104
105            let pool_id = PoolId {
106                id,
107                generation: self.pool[id].generation,
108                free: self.free.clone()
109            };
110            (pool_id, Some(old_item))
111        } else {
112            (self.alloc_new(Some(item)), None)
113        }
114    }
115}