ferroui_base/media/text_formatting/
formatting_object_pool.rs1use 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
7pub 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
20pub type RentedList<T> = Vec<T>;
22
23impl FormattingObjectPool {
24 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 #[cfg_attr(not(test), allow(dead_code))] 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
45pub struct ListPool<T> {
47 lists: RefCell<Vec<Vec<T>>>,
48 pending_return_count: Cell<i32>,
49}
50
51impl<T> ListPool<T> {
52 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 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 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 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 #[cfg_attr(not(test), allow(dead_code))] 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}