4
4
//! Generic font families.
5
5
6
6
use super :: FamilyId ;
7
+ use bytemuck:: { checked:: CheckedBitPattern , Contiguous , NoUninit , Zeroable } ;
7
8
use core:: fmt;
8
9
use smallvec:: SmallVec ;
9
10
@@ -50,6 +51,7 @@ pub enum GenericFamily {
50
51
/// Song and cursive-style Kai forms. This style is often used for
51
52
/// government documents.
52
53
FangSong = 12 ,
54
+ // NOTICE: If a new value is added, be sure to add modify `MAX_VALUE` in the bytemuck impl.
53
55
}
54
56
55
57
impl GenericFamily {
@@ -122,7 +124,31 @@ impl fmt::Display for GenericFamily {
122
124
}
123
125
}
124
126
125
- const COUNT : usize = GenericFamily :: FangSong as usize + 1 ;
127
+ // Safety: The enum is `repr(u8)` and has only fieldless variants.
128
+ unsafe impl NoUninit for GenericFamily { }
129
+
130
+ // Safety: The enum is `repr(u8)` and `0` is a valid value.
131
+ unsafe impl Zeroable for GenericFamily { }
132
+
133
+ // Safety: The enum is `repr(u8)`.
134
+ unsafe impl CheckedBitPattern for GenericFamily {
135
+ type Bits = u8 ;
136
+
137
+ fn is_valid_bit_pattern ( bits : & u8 ) -> bool {
138
+ // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
139
+ * bits <= Self :: MAX_VALUE
140
+ }
141
+ }
142
+
143
+ // Safety: The enum is `repr(u8)`. All values are `u8` and fall within
144
+ // the min and max values.
145
+ unsafe impl Contiguous for GenericFamily {
146
+ type Int = u8 ;
147
+ const MIN_VALUE : u8 = Self :: Serif as u8 ;
148
+ const MAX_VALUE : u8 = Self :: FangSong as u8 ;
149
+ }
150
+
151
+ const COUNT : usize = GenericFamily :: MAX_VALUE as usize + 1 ;
126
152
127
153
/// Maps generic families to family identifiers.
128
154
#[ derive( Clone , Default , Debug ) ]
@@ -148,3 +174,78 @@ impl GenericFamilyMap {
148
174
self . map [ generic as usize ] . extend ( families) ;
149
175
}
150
176
}
177
+
178
+ #[ cfg( test) ]
179
+ mod tests {
180
+ use crate :: GenericFamily ;
181
+ use bytemuck:: { checked:: try_from_bytes, Contiguous , Zeroable } ;
182
+ use core:: ptr;
183
+
184
+ #[ test]
185
+ fn checked_bit_pattern ( ) {
186
+ let valid = bytemuck:: bytes_of ( & 2_u8 ) ;
187
+ let invalid = bytemuck:: bytes_of ( & 200_u8 ) ;
188
+
189
+ assert_eq ! (
190
+ Ok ( & GenericFamily :: Monospace ) ,
191
+ try_from_bytes:: <GenericFamily >( valid)
192
+ ) ;
193
+
194
+ assert ! ( try_from_bytes:: <GenericFamily >( invalid) . is_err( ) ) ;
195
+ }
196
+
197
+ #[ test]
198
+ fn contiguous ( ) {
199
+ let hd1 = GenericFamily :: SansSerif ;
200
+ let hd2 = GenericFamily :: from_integer ( hd1. into_integer ( ) ) ;
201
+ assert_eq ! ( Some ( hd1) , hd2) ;
202
+
203
+ assert_eq ! ( None , GenericFamily :: from_integer( 255 ) ) ;
204
+ }
205
+
206
+ #[ test]
207
+ fn zeroable ( ) {
208
+ let hd = GenericFamily :: zeroed ( ) ;
209
+ assert_eq ! ( hd, GenericFamily :: Serif ) ;
210
+ }
211
+
212
+ /// Tests that the [`Contiguous`] impl for [`GenericFamily`] is not trivially incorrect.
213
+ const _: ( ) = {
214
+ let mut value = 0 ;
215
+ while value <= GenericFamily :: MAX_VALUE {
216
+ // Safety: In a const context, therefore if this makes an invalid GenericFamily, that will be detected.
217
+ // When updating the MSRV to 1.82 or later, this can use `&raw const value` instead of the addr_of!
218
+ let it: GenericFamily = unsafe { ptr:: read ( ( core:: ptr:: addr_of!( value) ) . cast ( ) ) } ;
219
+ // Evaluate the enum value to ensure it actually has a valid tag
220
+ if it as u8 != value {
221
+ unreachable ! ( ) ;
222
+ }
223
+ value += 1 ;
224
+ }
225
+ } ;
226
+ }
227
+
228
+ #[ cfg( doctest) ]
229
+ /// Doctests aren't collected under `cfg(test)`; we can use `cfg(doctest)` instead
230
+ mod doctests {
231
+ /// Validates that any new variants in `GenericFamily` has led to a change in the `Contiguous` impl.
232
+ /// Note that to test this robustly, we'd need 256 tests, which is impractical.
233
+ /// We make the assumption that all new variants will maintain contiguousness.
234
+ ///
235
+ /// ```compile_fail,E0080
236
+ /// use bytemuck::Contiguous;
237
+ /// use fontique::GenericFamily;
238
+ /// const {
239
+ /// let value = GenericFamily::MAX_VALUE + 1;
240
+ /// // Safety: In a const context, therefore if this makes an invalid GenericFamily, that will be detected.
241
+ /// // (Indeed, we rely upon that)
242
+ /// // When updating the MSRV to 1.82 or later, this can use `&raw const value` instead of the addr_of!
243
+ /// let it: GenericFamily = unsafe { core::ptr::read((core::ptr::addr_of!(value)).cast()) };
244
+ /// // Evaluate the enum value to ensure it actually has an invalid tag
245
+ /// if it as u8 != value {
246
+ /// unreachable!();
247
+ /// }
248
+ /// }
249
+ /// ```
250
+ const _GENERIC_FAMILY: ( ) = { } ;
251
+ }
0 commit comments