1use fret_core::{Corners, Px, Rect, TextStyle};
2use std::collections::hash_map::DefaultHasher;
3use std::hash::Hash;
4use std::hash::Hasher;
5
6pub struct CacheKeyBuilder {
13 hasher: DefaultHasher,
14}
15
16impl CacheKeyBuilder {
17 pub fn new() -> Self {
18 Self {
19 hasher: DefaultHasher::new(),
20 }
21 }
22
23 pub fn write_u64(&mut self, value: u64) {
24 self.hasher.write_u64(value);
25 }
26
27 pub fn write_u32(&mut self, value: u32) {
28 self.hasher.write_u32(value);
29 }
30
31 pub fn write_bool(&mut self, value: bool) {
32 self.hasher.write_u8(if value { 1 } else { 0 });
33 }
34
35 pub fn write_px(&mut self, value: Px) {
36 self.hasher.write_u32(value.0.to_bits());
37 }
38
39 pub fn write_rect(&mut self, rect: Rect) {
40 self.write_px(rect.origin.x);
41 self.write_px(rect.origin.y);
42 self.write_px(rect.size.width);
43 self.write_px(rect.size.height);
44 }
45
46 pub fn write_corners(&mut self, corners: Corners) {
47 self.write_px(corners.top_left);
48 self.write_px(corners.top_right);
49 self.write_px(corners.bottom_right);
50 self.write_px(corners.bottom_left);
51 }
52
53 pub fn write_text_style(&mut self, style: &TextStyle) {
54 style.font.hash(&mut self.hasher);
57 style.weight.hash(&mut self.hasher);
58 style.slant.hash(&mut self.hasher);
59
60 self.hasher.write_u32(style.size.0.to_bits());
61 self.hasher
62 .write_u32(style.line_height.map(|h| h.0.to_bits()).unwrap_or(0));
63 self.hasher
64 .write_u32(style.letter_spacing_em.map(f32::to_bits).unwrap_or(0));
65 }
66
67 pub fn finish(self) -> u64 {
68 self.hasher.finish()
69 }
70}
71
72impl Default for CacheKeyBuilder {
73 fn default() -> Self {
74 Self::new()
75 }
76}
77
78pub fn mix(seed: u64, value: u64) -> u64 {
79 let mut b = CacheKeyBuilder::new();
80 b.write_u64(seed);
81 b.write_u64(value);
82 b.finish()
83}
84
85pub fn rect_key(rect: Rect) -> u64 {
86 let mut b = CacheKeyBuilder::new();
87 b.write_rect(rect);
88 b.finish()
89}
90
91pub fn corners_key(corners: Corners) -> u64 {
92 let mut b = CacheKeyBuilder::new();
93 b.write_corners(corners);
94 b.finish()
95}
96
97pub fn text_style_key(style: &TextStyle) -> u64 {
98 let mut b = CacheKeyBuilder::new();
99 b.write_text_style(style);
100 b.finish()
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use fret_core::FontId;
107 use fret_core::text::FontWeight;
108 use fret_core::text::TextSlant;
109
110 #[test]
111 fn text_style_key_changes_when_style_changes() {
112 let style = TextStyle {
113 font: FontId::default(),
114 size: Px(13.0),
115 weight: FontWeight::NORMAL,
116 slant: TextSlant::Normal,
117 line_height: None,
118 line_height_em: None,
119 line_height_policy: Default::default(),
120 letter_spacing_em: None,
121 features: Vec::new(),
122 axes: Vec::new(),
123 vertical_placement: fret_core::TextVerticalPlacement::CenterMetricsBox,
124 leading_distribution: Default::default(),
125 strut_style: None,
126 };
127 let a = text_style_key(&style);
128 let b = text_style_key(&TextStyle {
129 size: Px(14.0),
130 ..style.clone()
131 });
132 assert_ne!(a, b);
133 }
134
135 #[test]
136 fn mix_is_stable_for_same_inputs() {
137 assert_eq!(mix(1, 2), mix(1, 2));
138 assert_ne!(mix(1, 2), mix(2, 1));
139 }
140}