44//! Generic font families.
55
66use super :: FamilyId ;
7+ use bytemuck:: { checked:: CheckedBitPattern , Contiguous , NoUninit , Zeroable } ;
78use core:: fmt;
89use smallvec:: SmallVec ;
910
@@ -122,7 +123,31 @@ impl fmt::Display for GenericFamily {
122123 }
123124}
124125
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 ;
126151
127152/// Maps generic families to family identifiers.
128153#[ derive( Clone , Default , Debug ) ]
@@ -148,3 +173,78 @@ impl GenericFamilyMap {
148173 self . map [ generic as usize ] . extend ( families) ;
149174 }
150175}
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