taitank/
cache.rs

1// 布局缓存实现
2
3use crate::flex::*;
4use crate::util::*;
5
6const MAX_MEASURES_COUNT: usize = 6;
7
8#[derive(Debug, Clone)]
9pub struct MeasureResult {
10    pub available_size: TaitankSize,
11    pub result_size: TaitankSize,
12    pub width_measure_mode: MeasureMode,
13    pub height_measure_mode: MeasureMode,
14    pub layout_action: FlexLayoutAction,
15}
16
17#[derive(Debug)]
18pub struct TaitankLayoutCache {
19    cached_layout: Option<MeasureResult>,
20    cached_measures: Vec<MeasureResult>,
21    next_measure_index: usize,
22}
23
24impl Default for TaitankLayoutCache {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl TaitankLayoutCache {
31    pub fn new() -> Self {
32        let mut cache = Self {
33            cached_layout: None,
34            cached_measures: Vec::with_capacity(MAX_MEASURES_COUNT),
35            next_measure_index: 0,
36        };
37        cache.init_cache();
38        cache
39    }
40
41    fn init_cache(&mut self) {
42        self.cached_layout = None;
43        self.cached_measures.clear();
44        self.cached_measures.resize(
45            MAX_MEASURES_COUNT,
46            MeasureResult {
47                available_size: TaitankSize::new(VALUE_UNDEFINED, VALUE_UNDEFINED),
48                result_size: TaitankSize::new(VALUE_UNDEFINED, VALUE_UNDEFINED),
49                width_measure_mode: MeasureMode::Undefined,
50                height_measure_mode: MeasureMode::Undefined,
51                layout_action: FlexLayoutAction::Layout,
52            },
53        );
54        self.next_measure_index = 0;
55    }
56
57    pub fn cache_result(
58        &mut self,
59        available_size: TaitankSize,
60        result_size: TaitankSize,
61        measure_mode: TaitankSizeMode,
62        layout_action: FlexLayoutAction,
63    ) {
64        if layout_action == FlexLayoutAction::Layout {
65            self.cached_layout = Some(MeasureResult {
66                available_size,
67                result_size,
68                width_measure_mode: measure_mode.width_measure_mode,
69                height_measure_mode: measure_mode.height_measure_mode,
70                layout_action,
71            });
72        } else {
73            if self.next_measure_index < self.cached_measures.len() {
74                self.cached_measures[self.next_measure_index] = MeasureResult {
75                    available_size,
76                    result_size,
77                    width_measure_mode: measure_mode.width_measure_mode,
78                    height_measure_mode: measure_mode.height_measure_mode,
79                    layout_action,
80                };
81            } else {
82                self.cached_measures.push(MeasureResult {
83                    available_size,
84                    result_size,
85                    width_measure_mode: measure_mode.width_measure_mode,
86                    height_measure_mode: measure_mode.height_measure_mode,
87                    layout_action,
88                });
89            }
90            self.next_measure_index = (self.next_measure_index + 1) % MAX_MEASURES_COUNT;
91        }
92    }
93
94    fn size_is_exact_and_matches_old_measured_size(
95        size_mode: MeasureMode,
96        size: f32,
97        last_result_size: f32,
98    ) -> bool {
99        size_mode == MeasureMode::Exactly && float_is_equal(size, last_result_size)
100    }
101
102    fn old_size_is_undefined_and_still_fits(
103        size_mode: MeasureMode,
104        size: f32,
105        last_size_mode: MeasureMode,
106        last_result_size: f32,
107    ) -> bool {
108        size_mode == MeasureMode::AtMost
109            && last_size_mode == MeasureMode::Undefined
110            && (size >= last_result_size || float_is_equal(size, last_result_size))
111    }
112
113    fn new_measure_size_is_stricter_and_still_valid(
114        size_mode: MeasureMode,
115        size: f32,
116        last_size_mode: MeasureMode,
117        last_size: f32,
118        last_result_size: f32,
119    ) -> bool {
120        last_size_mode == MeasureMode::AtMost
121            && size_mode == MeasureMode::AtMost
122            && last_size > size
123            && (last_result_size <= size || float_is_equal(size, last_result_size))
124    }
125
126    fn use_measure_cache_if_possible(
127        &self,
128        available_size: TaitankSize,
129        measure_mode: TaitankSizeMode,
130        layout_action: FlexLayoutAction,
131        is_measure_node: bool,
132    ) -> Option<&MeasureResult> {
133        if self.next_measure_index == 0 {
134            return None;
135        }
136
137        let check_count = self.next_measure_index.min(self.cached_measures.len());
138        for i in 0..check_count {
139            let cache_measure = &self.cached_measures[i];
140            if layout_action != cache_measure.layout_action && !is_measure_node {
141                continue;
142            }
143
144            let mut width_can_use = cache_measure.width_measure_mode
145                == measure_mode.width_measure_mode
146                && float_is_equal(cache_measure.available_size.width, available_size.width);
147
148            if is_measure_node {
149                if !width_can_use {
150                    width_can_use = Self::size_is_exact_and_matches_old_measured_size(
151                        measure_mode.width_measure_mode,
152                        available_size.width,
153                        cache_measure.result_size.width,
154                    );
155                }
156                if !width_can_use {
157                    width_can_use = Self::old_size_is_undefined_and_still_fits(
158                        measure_mode.width_measure_mode,
159                        available_size.width,
160                        cache_measure.width_measure_mode,
161                        cache_measure.result_size.width,
162                    );
163                }
164                if !width_can_use {
165                    width_can_use = Self::new_measure_size_is_stricter_and_still_valid(
166                        measure_mode.width_measure_mode,
167                        available_size.width,
168                        cache_measure.width_measure_mode,
169                        cache_measure.available_size.width,
170                        cache_measure.result_size.width,
171                    );
172                }
173            }
174
175            let mut height_can_use = cache_measure.height_measure_mode
176                == measure_mode.height_measure_mode
177                && float_is_equal(cache_measure.available_size.height, available_size.height);
178
179            if is_measure_node {
180                if !height_can_use {
181                    height_can_use = Self::size_is_exact_and_matches_old_measured_size(
182                        measure_mode.height_measure_mode,
183                        available_size.height,
184                        cache_measure.result_size.height,
185                    );
186                }
187                if !height_can_use {
188                    height_can_use = Self::old_size_is_undefined_and_still_fits(
189                        measure_mode.height_measure_mode,
190                        available_size.height,
191                        cache_measure.height_measure_mode,
192                        cache_measure.result_size.height,
193                    );
194                }
195                if !height_can_use {
196                    height_can_use = Self::new_measure_size_is_stricter_and_still_valid(
197                        measure_mode.height_measure_mode,
198                        available_size.height,
199                        cache_measure.height_measure_mode,
200                        cache_measure.available_size.height,
201                        cache_measure.result_size.height,
202                    );
203                }
204            }
205
206            if width_can_use && height_can_use {
207                return Some(cache_measure);
208            }
209        }
210
211        None
212    }
213
214    fn use_layout_cache_if_possible(
215        &self,
216        available_size: TaitankSize,
217        measure_mode: TaitankSizeMode,
218    ) -> Option<&MeasureResult> {
219        if let Some(ref cached) = self.cached_layout {
220            if is_undefined(cached.available_size.width)
221                || is_undefined(cached.available_size.height)
222            {
223                return None;
224            }
225            if size_is_equal(cached.available_size, available_size)
226                && cached.width_measure_mode == measure_mode.width_measure_mode
227                && cached.height_measure_mode == measure_mode.height_measure_mode
228            {
229                return Some(cached);
230            }
231        }
232        None
233    }
234
235    pub fn get_cached_measure_result(
236        &self,
237        available_size: TaitankSize,
238        measure_mode: TaitankSizeMode,
239        layout_action: FlexLayoutAction,
240        is_measure_node: bool,
241    ) -> Option<&MeasureResult> {
242        if is_measure_node {
243            if let Some(result) = self.use_layout_cache_if_possible(available_size, measure_mode) {
244                return Some(result);
245            }
246            self.use_measure_cache_if_possible(
247                available_size,
248                measure_mode,
249                layout_action,
250                is_measure_node,
251            )
252        } else if layout_action == FlexLayoutAction::Layout {
253            self.use_layout_cache_if_possible(available_size, measure_mode)
254        } else {
255            self.use_measure_cache_if_possible(
256                available_size,
257                measure_mode,
258                layout_action,
259                is_measure_node,
260            )
261        }
262    }
263
264    pub fn get_cached_layout(&self) -> Option<&MeasureResult> {
265        self.cached_layout.as_ref()
266    }
267
268    pub fn clear_cache(&mut self) {
269        self.init_cache();
270    }
271}