1 | use alloc::vec::Vec; |
2 | use core::cmp::{self, Ordering}; |
3 | use core::ops::RangeInclusive; |
4 | |
5 | use ttf_parser::GlyphId; |
6 | |
7 | /// A set of glyphs. |
8 | /// |
9 | /// Performs best when the glyphs are in consecutive ranges. |
10 | #[derive (Clone, Debug)] |
11 | pub struct GlyphSet { |
12 | ranges: Vec<RangeInclusive<GlyphId>>, |
13 | } |
14 | |
15 | impl GlyphSet { |
16 | /// Create a new glyph set builder. |
17 | pub fn builder() -> GlyphSetBuilder { |
18 | GlyphSetBuilder { ranges: Vec::new() } |
19 | } |
20 | |
21 | /// Check whether the glyph is contained in the set. |
22 | pub fn contains(&self, glyph: GlyphId) -> bool { |
23 | self.ranges |
24 | .binary_search_by(|range: &RangeInclusive| { |
25 | if glyph < *range.start() { |
26 | Ordering::Greater |
27 | } else if glyph <= *range.end() { |
28 | Ordering::Equal |
29 | } else { |
30 | Ordering::Less |
31 | } |
32 | }) |
33 | .is_ok() |
34 | } |
35 | } |
36 | |
37 | /// A builder for a [`GlyphSet`]. |
38 | #[derive (Clone, Debug)] |
39 | pub struct GlyphSetBuilder { |
40 | ranges: Vec<RangeInclusive<GlyphId>>, |
41 | } |
42 | |
43 | impl GlyphSetBuilder { |
44 | /// Insert a single glyph. |
45 | pub fn insert(&mut self, glyph: GlyphId) { |
46 | self.ranges.push(glyph..=glyph); |
47 | } |
48 | |
49 | /// Insert a range of glyphs. |
50 | pub fn insert_range(&mut self, range: RangeInclusive<GlyphId>) { |
51 | self.ranges.push(range); |
52 | } |
53 | |
54 | /// Finish the set building. |
55 | pub fn finish(self) -> GlyphSet { |
56 | let mut ranges = self.ranges; |
57 | |
58 | // Sort because we want to use binary search in `GlyphSet::contains`. |
59 | ranges.sort_by_key(|range| *range.start()); |
60 | |
61 | // The visited and merged ranges are in `ranges[..=left]` and the |
62 | // unvisited ranges in `ranges[right..]`. |
63 | let mut left = 0; |
64 | let mut right = 1; |
65 | |
66 | // Merge touching and overlapping adjacent ranges. |
67 | // |
68 | // The cloning is cheap, it's just needed because `RangeInclusive<T>` |
69 | // does not implement `Copy`. |
70 | while let Some(next) = ranges.get(right).cloned() { |
71 | right += 1; |
72 | |
73 | if let Some(prev) = ranges.get_mut(left) { |
74 | // Detect whether the ranges can be merged. |
75 | // |
76 | // We add one to `prev.end` because we want to merge touching ranges |
77 | // like `1..=3` and `4..=5`. We have to be careful with overflow, |
78 | // hence `saturating_add`. |
79 | if next.start().0 <= prev.end().0.saturating_add(1) { |
80 | *prev = *prev.start()..=cmp::max(*prev.end(), *next.end()); |
81 | continue; |
82 | } |
83 | } |
84 | |
85 | left += 1; |
86 | ranges[left] = next.clone(); |
87 | } |
88 | |
89 | // Can't overflow because `left < ranges.len() <= isize::MAX`. |
90 | ranges.truncate(left + 1); |
91 | |
92 | GlyphSet { ranges } |
93 | } |
94 | } |
95 | |
96 | #[cfg (test)] |
97 | mod tests { |
98 | use super::*; |
99 | use alloc::vec; |
100 | |
101 | #[test ] |
102 | fn test_empty() { |
103 | assert!(GlyphSet::builder().finish().ranges.is_empty()); |
104 | } |
105 | |
106 | #[test ] |
107 | fn test_contains() { |
108 | let mut builder = GlyphSet::builder(); |
109 | builder.insert(GlyphId(1)); |
110 | builder.insert_range(GlyphId(3)..=GlyphId(5)); |
111 | let set = builder.finish(); |
112 | assert!(set.contains(GlyphId(1))); |
113 | assert!(!set.contains(GlyphId(2))); |
114 | assert!(set.contains(GlyphId(3))); |
115 | assert!(set.contains(GlyphId(4))); |
116 | assert!(set.contains(GlyphId(5))); |
117 | assert!(!set.contains(GlyphId(6))); |
118 | } |
119 | |
120 | #[test ] |
121 | fn test_merge_ranges() { |
122 | let mut builder = GlyphSet::builder(); |
123 | builder.insert(GlyphId(0)); |
124 | builder.insert_range(GlyphId(2)..=GlyphId(6)); |
125 | builder.insert_range(GlyphId(3)..=GlyphId(7)); |
126 | builder.insert_range(GlyphId(9)..=GlyphId(10)); |
127 | builder.insert(GlyphId(9)); |
128 | builder.insert_range(GlyphId(18)..=GlyphId(21)); |
129 | builder.insert_range(GlyphId(11)..=GlyphId(14)); |
130 | assert_eq!( |
131 | builder.finish().ranges, |
132 | vec![ |
133 | GlyphId(0)..=GlyphId(0), |
134 | GlyphId(2)..=GlyphId(7), |
135 | GlyphId(9)..=GlyphId(14), |
136 | GlyphId(18)..=GlyphId(21), |
137 | ] |
138 | ) |
139 | } |
140 | |
141 | #[test ] |
142 | fn test_merge_ranges_at_numeric_boundaries() { |
143 | let mut builder = GlyphSet::builder(); |
144 | builder.insert_range(GlyphId(3)..=GlyphId(u16::MAX)); |
145 | builder.insert(GlyphId(u16::MAX - 1)); |
146 | builder.insert(GlyphId(2)); |
147 | assert_eq!( |
148 | builder.finish().ranges, |
149 | vec![GlyphId(2)..=GlyphId(u16::MAX)] |
150 | ); |
151 | } |
152 | } |
153 | |