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