#![allow(clippy::unusual_byte_groupings)]
use crate::geometry::Size;
use crate::style::AvailableSpace;
use crate::tree::{LayoutInput, LayoutOutput, RunMode};
use crate::RequestedAxis;
const CACHE_SIZE: usize = 9;
const INFINITY_BITS: u32 = 0b_0_11111111_00000000000000000000000_u32;
const NEG_INFINITY_BITS: u32 = 0b_1_11111111_00000000000000000000000_u32;
const A_ONE: u64 = 1u64 << 63;
const B_ONE: u64 = 1u64 << 31;
const CHECK_MASK: u64 = A_ONE | B_ONE;
const SET_MASK: u64 = !CHECK_MASK;
#[inline(always)]
fn option_cache_key(input: Option<f32>) -> u32 {
match input {
Some(value) => value.to_bits(),
None => INFINITY_BITS,
}
}
#[inline(always)]
fn size_option_cache_key(input: Size<Option<f32>>) -> u64 {
(option_cache_key(input.width) as u64) << 32 | option_cache_key(input.height) as u64
}
#[inline(always)]
fn available_space_cache_key(input: AvailableSpace) -> u32 {
match input {
AvailableSpace::Definite(value) => (-value).to_bits(),
AvailableSpace::MinContent => NEG_INFINITY_BITS,
AvailableSpace::MaxContent => INFINITY_BITS,
}
}
#[inline(always)]
#[allow(dead_code)]
fn size_available_space_cache_key(input: Size<AvailableSpace>) -> u64 {
(available_space_cache_key(input.width) as u64) << 32 | available_space_cache_key(input.height) as u64
}
#[inline(always)]
fn mixed_cache_key(kd: Option<f32>, avs: AvailableSpace) -> u32 {
kd.map(|kd| kd.to_bits()).unwrap_or_else(|| available_space_cache_key(avs))
}
#[inline(always)]
fn size_mixed_cache_key(kd: Size<Option<f32>>, avs: Size<AvailableSpace>) -> u64 {
(mixed_cache_key(kd.width, avs.width) as u64) << 32 | mixed_cache_key(kd.height, avs.height) as u64
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
struct CacheKey {
kd_available_space: u64,
parent_size: u64,
}
impl From<&LayoutInput> for CacheKey {
fn from(input: &LayoutInput) -> Self {
let extra_bits = match input.axis {
RequestedAxis::Horizontal => A_ONE,
RequestedAxis::Vertical => B_ONE,
RequestedAxis::Both => A_ONE | B_ONE,
};
Self {
kd_available_space: size_mixed_cache_key(input.known_dimensions, input.available_space),
parent_size: (size_option_cache_key(input.parent_size) & SET_MASK) | extra_bits,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub(crate) struct CacheEntry<T> {
key: CacheKey,
content: T,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Cache {
final_layout_entry: Option<CacheEntry<LayoutOutput>>,
measure_entries: [Option<CacheEntry<Size<f32>>>; CACHE_SIZE],
is_empty: bool,
}
impl Default for Cache {
fn default() -> Self {
Self::new()
}
}
impl Cache {
pub const fn new() -> Self {
Self { final_layout_entry: None, measure_entries: [None; CACHE_SIZE], is_empty: true }
}
#[inline]
fn compute_cache_slot(known_dimensions: Size<Option<f32>>, available_space: Size<AvailableSpace>) -> usize {
use AvailableSpace::{Definite, MaxContent, MinContent};
let has_known_width = known_dimensions.width.is_some();
let has_known_height = known_dimensions.height.is_some();
if has_known_width && has_known_height {
return 0;
}
if has_known_width && !has_known_height {
return 1 + (available_space.height == MinContent) as usize;
}
if has_known_height && !has_known_width {
return 3 + (available_space.width == MinContent) as usize;
}
match (available_space.width, available_space.height) {
(MaxContent | Definite(_), MaxContent | Definite(_)) => 5,
(MaxContent | Definite(_), MinContent) => 6,
(MinContent, MaxContent | Definite(_)) => 7,
(MinContent, MinContent) => 8,
}
}
#[inline]
pub fn get(&self, input: &LayoutInput) -> Option<LayoutOutput> {
let key = CacheKey::from(input);
match input.run_mode {
RunMode::PerformLayout => self.final_layout_entry.filter(|entry| entry.key == key).map(|e| e.content),
RunMode::ComputeSize => {
for entry in self.measure_entries.iter().flatten() {
if entry.key.kd_available_space == key.kd_available_space
&& (entry.key.parent_size & CHECK_MASK == key.parent_size & CHECK_MASK)
{
return Some(LayoutOutput::from_outer_size(entry.content));
}
}
None
}
RunMode::PerformHiddenLayout => None,
}
}
pub fn store(&mut self, input: &LayoutInput, layout_output: LayoutOutput) {
let key = CacheKey::from(input);
match input.run_mode {
RunMode::PerformLayout => {
self.is_empty = false;
self.final_layout_entry = Some(CacheEntry { key, content: layout_output })
}
RunMode::ComputeSize => {
self.is_empty = false;
let cache_slot = Self::compute_cache_slot(input.known_dimensions, input.available_space);
self.measure_entries[cache_slot] = Some(CacheEntry { key, content: layout_output.size });
}
RunMode::PerformHiddenLayout => {}
}
}
pub fn clear(&mut self) -> ClearState {
if self.is_empty {
return ClearState::AlreadyEmpty;
}
self.is_empty = true;
self.final_layout_entry = None;
self.measure_entries = [None; CACHE_SIZE];
ClearState::Cleared
}
pub fn is_empty(&self) -> bool {
self.final_layout_entry.is_none() && !self.measure_entries.iter().any(|entry| entry.is_some())
}
}
pub enum ClearState {
Cleared,
AlreadyEmpty,
}