Skip to main content

ferroui_base/media/text_formatting/
formatting_object_pool.rs

1use std::cell::{Cell, RefCell};
2use std::rc::Rc;
3
4use crate::media::text_formatting::formatting_buffer_helper::FormattingBufferHelper;
5use crate::media::text_formatting::{TextLine, TextRun, UnshapedTextRun};
6
7/// Contains various list pools that are commonly used during text formatting.
8///
9/// This class provides an instance per thread.
10///
11/// In most applications, there'll be only one instance: on the UI thread,
12/// which is responsible for layout.
13// Internal upstream; public so the Skia unit tests reach it.
14pub struct FormattingObjectPool {
15    pub text_run_lists: ListPool<Rc<dyn TextRun>>,
16    pub unshaped_text_run_lists: ListPool<Rc<UnshapedTextRun>>,
17    pub text_lines: ListPool<Rc<dyn TextLine>>,
18}
19
20/// A list rented from a [`ListPool`]. Give it back with [`ListPool::return_list`].
21pub type RentedList<T> = Vec<T>;
22
23impl FormattingObjectPool {
24    /// The pool of the current thread.
25    pub fn instance() -> &'static FormattingObjectPool {
26        crate::ferro_thread_local! {
27            static INSTANCE: &'static FormattingObjectPool = Box::leak(Box::new(FormattingObjectPool {
28                text_run_lists: ListPool::new(),
29                unshaped_text_run_lists: ListPool::new(),
30                text_lines: ListPool::new(),
31            }));
32        }
33        INSTANCE.with(|instance| *instance)
34    }
35
36    /// Checks (in debug builds) that every rented list was returned.
37    #[cfg_attr(not(test), allow(dead_code))] // called by tests, as upstream's test base does
38    pub fn verify_all_returned(&self) {
39        self.text_run_lists.verify_all_returned();
40        self.unshaped_text_run_lists.verify_all_returned();
41        self.text_lines.verify_all_returned();
42    }
43}
44
45/// A pool of reusable lists.
46pub struct ListPool<T> {
47    lists: RefCell<Vec<Vec<T>>>,
48    pending_return_count: Cell<i32>,
49}
50
51impl<T> ListPool<T> {
52    /// We don't need a big number here, these are for temporary usages only
53    /// which should quickly be returned.
54    const MAX_SIZE: usize = 16;
55
56    fn new() -> Self {
57        Self { lists: RefCell::new(Vec::with_capacity(Self::MAX_SIZE)), pending_return_count: Cell::new(0) }
58    }
59
60    /// Takes an empty list out of the pool.
61    pub fn rent(&self) -> RentedList<T> {
62        let list = self.lists.borrow_mut().pop().unwrap_or_default();
63
64        debug_assert!(list.is_empty(), "A RentedList has been used after being returned!");
65
66        self.pending_return_count.set(self.pending_return_count.get() + 1);
67
68        list
69    }
70
71    /// Gives a list back to the pool. The items are dropped; the storage is kept.
72    pub fn return_list(&self, mut rented_list: RentedList<T>) {
73        self.pending_return_count.set(self.pending_return_count.get() - 1);
74
75        FormattingBufferHelper::clear_then_reset_if_too_large(&mut rented_list);
76
77        let mut lists = self.lists.borrow_mut();
78
79        if lists.len() < Self::MAX_SIZE {
80            lists.push(rented_list);
81        }
82    }
83
84    /// Gives an optional list back to the pool (upstream accepts null).
85    pub fn return_optional(&self, rented_list: Option<RentedList<T>>) {
86        if let Some(rented_list) = rented_list {
87            self.return_list(rented_list);
88        }
89    }
90
91    /// Checks (in debug builds) that every rented list was returned.
92    #[cfg_attr(not(test), allow(dead_code))] // see FormattingObjectPool::verify_all_returned
93    pub fn verify_all_returned(&self) {
94        if cfg!(debug_assertions) {
95            let pending_return_count = self.pending_return_count.replace(0);
96
97            if pending_return_count > 0 {
98                panic!(
99                    "{pending_return_count} RentedList<{}> haven't been returned to the pool!",
100                    std::any::type_name::<T>()
101                );
102            }
103
104            if pending_return_count < 0 {
105                panic!(
106                    "{} RentedList<{}> extra lists have been returned to the pool!",
107                    -pending_return_count,
108                    std::any::type_name::<T>()
109                );
110            }
111        }
112    }
113}