Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7807707

Browse files
committedDec 8, 2024
Impl bytemuck for GenericFamily.
We then use that to get the max value for the GenericFamilyMap in fontique.
1 parent 79ab65f commit 7807707

File tree

6 files changed

+109
-4
lines changed

6 files changed

+109
-4
lines changed
 

‎Cargo.lock

+4-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ clippy.wildcard_dependencies = "warn"
7979

8080
[workspace.dependencies]
8181
accesskit = "0.17"
82+
bytemuck = { version = "1.20.0", default-features = false }
8283
fontique = { version = "0.2.0", default-features = false, path = "fontique" }
8384
hashbrown = "0.15.2"
8485
parley = { version = "0.2.0", default-features = false, path = "parley" }

‎fontique/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ system = [
3030
]
3131

3232
[dependencies]
33+
bytemuck = { workspace = true }
3334
read-fonts = { workspace = true }
3435
peniko = { workspace = true }
3536
styled_text = { workspace = true }

‎fontique/src/generic.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
//! Generic font families.
55
66
use super::FamilyId;
7+
use bytemuck::Contiguous; // For GenericFamily::MAX_VALUE
78
use smallvec::SmallVec;
89
use styled_text::GenericFamily;
910

1011
type FamilyVec = SmallVec<[FamilyId; 2]>;
1112

12-
// FIXME(style): This should be done better.
13-
const COUNT: usize = GenericFamily::FangSong as usize + 1;
13+
const COUNT: usize = GenericFamily::MAX_VALUE as usize + 1;
1414

1515
/// Maps generic families to family identifiers.
1616
#[derive(Clone, Default, Debug)]

‎styled_text/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ libm = ["dep:core_maths"]
2121

2222
[dependencies]
2323
core_maths = { version = "0.1.0", optional = true }
24+
bytemuck = { workspace = true }

‎styled_text/src/generic_family.rs

+100
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,103 @@ impl fmt::Display for GenericFamily {
117117
write!(f, "{}", name)
118118
}
119119
}
120+
121+
// Safety: The enum is `repr(u8)` and has only fieldless variants.
122+
unsafe impl bytemuck::NoUninit for GenericFamily {}
123+
124+
// Safety: The enum is `repr(u8)` and `0` is a valid value.
125+
unsafe impl bytemuck::Zeroable for GenericFamily {}
126+
127+
// Safety: The enum is `repr(u8)`.
128+
unsafe impl bytemuck::checked::CheckedBitPattern for GenericFamily {
129+
type Bits = u8;
130+
131+
fn is_valid_bit_pattern(bits: &u8) -> bool {
132+
use bytemuck::Contiguous;
133+
// Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
134+
*bits <= Self::MAX_VALUE
135+
}
136+
}
137+
138+
// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
139+
// the min and max values.
140+
unsafe impl bytemuck::Contiguous for GenericFamily {
141+
type Int = u8;
142+
const MIN_VALUE: u8 = Self::Serif as u8;
143+
const MAX_VALUE: u8 = Self::FangSong as u8;
144+
}
145+
146+
#[cfg(test)]
147+
mod tests {
148+
use crate::GenericFamily;
149+
use bytemuck::{checked::try_from_bytes, Contiguous, Zeroable};
150+
use core::ptr;
151+
152+
#[test]
153+
fn checked_bit_pattern() {
154+
let valid = bytemuck::bytes_of(&2_u8);
155+
let invalid = bytemuck::bytes_of(&200_u8);
156+
157+
assert_eq!(
158+
Ok(&GenericFamily::Monospace),
159+
try_from_bytes::<GenericFamily>(valid)
160+
);
161+
162+
assert!(try_from_bytes::<GenericFamily>(invalid).is_err());
163+
}
164+
165+
#[test]
166+
fn contiguous() {
167+
let hd1 = GenericFamily::SansSerif;
168+
let hd2 = GenericFamily::from_integer(hd1.into_integer());
169+
assert_eq!(Some(hd1), hd2);
170+
171+
assert_eq!(None, GenericFamily::from_integer(255));
172+
}
173+
174+
#[test]
175+
fn zeroable() {
176+
let hd = GenericFamily::zeroed();
177+
assert_eq!(hd, GenericFamily::Serif);
178+
}
179+
180+
/// Tests that the [`Contiguous`] impl for [`GenericFamily`] is not trivially incorrect.
181+
const _: () = {
182+
let mut value = 0;
183+
while value <= GenericFamily::MAX_VALUE {
184+
// Safety: In a const context, therefore if this makes an invalid GenericFamily, that will be detected.
185+
// When updating the MSRV to 1.82 or later, this can use `&raw const value` instead of the addr_of!
186+
let it: GenericFamily = unsafe { ptr::read((core::ptr::addr_of!(value)).cast()) };
187+
// Evaluate the enum value to ensure it actually has a valid tag
188+
if it as u8 != value {
189+
unreachable!();
190+
}
191+
value += 1;
192+
}
193+
};
194+
}
195+
196+
#[cfg(doctest)]
197+
/// Doctests aren't collected under `cfg(test)`; we can use `cfg(doctest)` instead
198+
mod doctests {
199+
/// Validates that any new variants in `GenericFamily` has led to a change in the `Contiguous` impl.
200+
/// Note that to test this robustly, we'd need 256 tests, which is impractical.
201+
/// We make the assumption that all new variants will maintain contiguousness.
202+
///
203+
/// ```compile_fail,E0080
204+
/// use bytemuck::Contiguous;
205+
/// use styled_text::GenericFamily;
206+
/// const {
207+
/// let value = GenericFamily::MAX_VALUE + 1;
208+
/// // Safety: In a const context, therefore if this makes an invalid GenericFamily, that will be detected.
209+
/// // (Indeed, we rely upon that)
210+
/// // When updating the MSRV to 1.82 or later, this can use `&raw const value` instead of the addr_of!
211+
/// let it: GenericFamily = unsafe { core::ptr::read((core::ptr::addr_of!(value)).cast()) };
212+
/// // Evaluate the enum value to ensure it actually has an invalid tag
213+
/// if it as u8 != value {
214+
/// unreachable!();
215+
/// }
216+
/// }
217+
/// ```
218+
const _GENERIC_FAMILY: () = {};
219+
}

0 commit comments

Comments
 (0)
Please sign in to comment.