Skip to main content

peniko/
impl_bytemuck.rs

1// Copyright 2024 the Peniko Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4#![allow(unsafe_code, reason = "unsafe is required for bytemuck unsafe impls")]
5
6use crate::{Compose, Extend, Fill, ImageAlphaType, ImageFormat, ImageQuality, Mix};
7
8// Safety: The enum is `repr(u8)` and has only fieldless variants.
9unsafe impl bytemuck::NoUninit for Compose {}
10
11// Safety: The enum is `repr(u8)` and `0` is a valid value.
12unsafe impl bytemuck::Zeroable for Compose {}
13
14// Safety: The enum is `repr(u8)`.
15unsafe impl bytemuck::checked::CheckedBitPattern for Compose {
16    type Bits = u8;
17
18    fn is_valid_bit_pattern(bits: &u8) -> bool {
19        use bytemuck::Contiguous;
20        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
21        *bits <= Self::MAX_VALUE
22    }
23}
24
25// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
26// the min and max values.
27unsafe impl bytemuck::Contiguous for Compose {
28    type Int = u8;
29    const MIN_VALUE: u8 = Self::Clear as u8;
30    const MAX_VALUE: u8 = Self::PlusLighter as u8;
31}
32
33// Safety: The enum is `repr(u8)` and has only fieldless variants.
34unsafe impl bytemuck::NoUninit for Extend {}
35
36// Safety: The enum is `repr(u8)` and `0` is a valid value.
37unsafe impl bytemuck::Zeroable for Extend {}
38
39// Safety: The enum is `repr(u8)`.
40unsafe impl bytemuck::checked::CheckedBitPattern for Extend {
41    type Bits = u8;
42
43    fn is_valid_bit_pattern(bits: &u8) -> bool {
44        use bytemuck::Contiguous;
45        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
46        *bits <= Self::MAX_VALUE
47    }
48}
49
50// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
51// the min and max values.
52unsafe impl bytemuck::Contiguous for Extend {
53    type Int = u8;
54    const MIN_VALUE: u8 = Self::Pad as u8;
55    const MAX_VALUE: u8 = Self::Reflect as u8;
56}
57
58// Safety: The enum is `repr(u8)` and has only fieldless variants.
59unsafe impl bytemuck::NoUninit for Fill {}
60
61// Safety: The enum is `repr(u8)` and `0` is a valid value.
62unsafe impl bytemuck::Zeroable for Fill {}
63
64// Safety: The enum is `repr(u8)`.
65unsafe impl bytemuck::checked::CheckedBitPattern for Fill {
66    type Bits = u8;
67
68    fn is_valid_bit_pattern(bits: &u8) -> bool {
69        use bytemuck::Contiguous;
70        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
71        *bits <= Self::MAX_VALUE
72    }
73}
74
75// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
76// the min and max values.
77unsafe impl bytemuck::Contiguous for Fill {
78    type Int = u8;
79    const MIN_VALUE: u8 = Self::NonZero as u8;
80    const MAX_VALUE: u8 = Self::EvenOdd as u8;
81}
82
83// Safety: The enum is `repr(u8)` and has only fieldless variants.
84unsafe impl bytemuck::NoUninit for ImageAlphaType {}
85
86// Safety: The enum is `repr(u8)` and `0` is a valid value.
87unsafe impl bytemuck::Zeroable for ImageAlphaType {}
88
89// Safety: The enum is `repr(u8)`.
90unsafe impl bytemuck::checked::CheckedBitPattern for ImageAlphaType {
91    type Bits = u8;
92
93    fn is_valid_bit_pattern(bits: &u8) -> bool {
94        use bytemuck::Contiguous;
95        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
96        *bits <= Self::MAX_VALUE
97    }
98}
99
100// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
101// the min and max values.
102unsafe impl bytemuck::Contiguous for ImageAlphaType {
103    type Int = u8;
104    const MIN_VALUE: u8 = Self::Alpha as u8;
105    const MAX_VALUE: u8 = Self::AlphaPremultiplied as u8;
106}
107
108// Safety: The enum is `repr(u8)` and has only fieldless variants.
109unsafe impl bytemuck::NoUninit for ImageFormat {}
110
111// Safety: The enum is `repr(u8)` and `0` is a valid value.
112unsafe impl bytemuck::Zeroable for ImageFormat {}
113
114// Safety: The enum is `repr(u8)`.
115unsafe impl bytemuck::checked::CheckedBitPattern for ImageFormat {
116    type Bits = u8;
117
118    fn is_valid_bit_pattern(bits: &u8) -> bool {
119        use bytemuck::Contiguous;
120        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
121        *bits <= Self::MAX_VALUE
122    }
123}
124
125// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
126// the min and max values.
127unsafe impl bytemuck::Contiguous for ImageFormat {
128    type Int = u8;
129    const MIN_VALUE: u8 = Self::Rgba8 as u8;
130    const MAX_VALUE: u8 = Self::Bgra8 as u8;
131}
132
133// Safety: The enum is `repr(u8)` and has only fieldless variants.
134unsafe impl bytemuck::NoUninit for ImageQuality {}
135
136// Safety: The enum is `repr(u8)` and `0` is a valid value.
137unsafe impl bytemuck::Zeroable for ImageQuality {}
138
139// Safety: The enum is `repr(u8)`.
140unsafe impl bytemuck::checked::CheckedBitPattern for ImageQuality {
141    type Bits = u8;
142
143    fn is_valid_bit_pattern(bits: &u8) -> bool {
144        use bytemuck::Contiguous;
145        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
146        *bits <= Self::MAX_VALUE
147    }
148}
149
150// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
151// the min and max values.
152unsafe impl bytemuck::Contiguous for ImageQuality {
153    type Int = u8;
154    const MIN_VALUE: u8 = Self::Low as u8;
155    const MAX_VALUE: u8 = Self::High as u8;
156}
157
158// Safety: The enum is `repr(u8)` and has only fieldless variants.
159unsafe impl bytemuck::NoUninit for Mix {}
160
161// Safety: The enum is `repr(u8)` and `0` is a valid value.
162unsafe impl bytemuck::Zeroable for Mix {}
163
164// Safety: The enum is `repr(u8)`.
165unsafe impl bytemuck::checked::CheckedBitPattern for Mix {
166    type Bits = u8;
167
168    fn is_valid_bit_pattern(bits: &u8) -> bool {
169        use bytemuck::Contiguous;
170        // Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
171        *bits <= Self::MAX_VALUE
172    }
173}
174
175// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
176// the min and max values.
177unsafe impl bytemuck::Contiguous for Mix {
178    type Int = u8;
179    const MIN_VALUE: u8 = Self::Normal as u8;
180    const MAX_VALUE: u8 = Self::Luminosity as u8;
181}
182
183#[cfg(test)]
184mod tests {
185    use crate::{Compose, Extend, Fill, ImageAlphaType, ImageFormat, ImageQuality, Mix};
186    use bytemuck::{Contiguous, Zeroable, checked::try_from_bytes};
187    use core::ptr;
188
189    #[test]
190    fn checked_bit_pattern() {
191        let valid_zero = bytemuck::bytes_of(&0_u8);
192        let valid_one = bytemuck::bytes_of(&1_u8);
193        let invalid = bytemuck::bytes_of(&200_u8);
194
195        assert_eq!(Ok(&Compose::Copy), try_from_bytes::<Compose>(valid_one));
196        assert!(try_from_bytes::<Compose>(invalid).is_err());
197
198        assert_eq!(Ok(&Extend::Repeat), try_from_bytes::<Extend>(valid_one));
199        assert!(try_from_bytes::<Extend>(invalid).is_err());
200
201        assert_eq!(Ok(&Fill::EvenOdd), try_from_bytes::<Fill>(valid_one));
202        assert!(try_from_bytes::<Fill>(invalid).is_err());
203
204        assert_eq!(
205            Ok(&ImageAlphaType::Alpha),
206            try_from_bytes::<ImageAlphaType>(valid_zero)
207        );
208        assert_eq!(
209            Ok(&ImageAlphaType::AlphaPremultiplied),
210            try_from_bytes::<ImageAlphaType>(valid_one)
211        );
212        assert!(try_from_bytes::<ImageAlphaType>(invalid).is_err());
213
214        assert_eq!(
215            Ok(&ImageFormat::Rgba8),
216            try_from_bytes::<ImageFormat>(valid_zero)
217        );
218        assert_eq!(
219            Ok(&ImageFormat::Bgra8),
220            try_from_bytes::<ImageFormat>(valid_one)
221        );
222        assert!(try_from_bytes::<ImageFormat>(invalid).is_err());
223
224        assert_eq!(
225            Ok(&ImageQuality::Medium),
226            try_from_bytes::<ImageQuality>(valid_one)
227        );
228        assert!(try_from_bytes::<ImageQuality>(invalid).is_err());
229
230        assert_eq!(Ok(&Mix::Multiply), try_from_bytes::<Mix>(valid_one));
231        assert!(try_from_bytes::<Mix>(invalid).is_err());
232    }
233
234    #[test]
235    fn contiguous() {
236        let compose1 = Compose::PlusLighter;
237        let compose2 = Compose::from_integer(compose1.into_integer());
238        assert_eq!(Some(compose1), compose2);
239
240        assert_eq!(None, Compose::from_integer(255));
241
242        let extend1 = Extend::Repeat;
243        let extend2 = Extend::from_integer(extend1.into_integer());
244        assert_eq!(Some(extend1), extend2);
245
246        assert_eq!(None, Extend::from_integer(255));
247
248        let fill1 = Fill::EvenOdd;
249        let fill2 = Fill::from_integer(fill1.into_integer());
250        assert_eq!(Some(fill1), fill2);
251
252        assert_eq!(None, Fill::from_integer(255));
253
254        let image_alpha_type_1 = ImageAlphaType::Alpha;
255        let image_alpha_type_2 = ImageAlphaType::from_integer(image_alpha_type_1.into_integer());
256        assert_eq!(Some(image_alpha_type_1), image_alpha_type_2);
257
258        assert_eq!(None, ImageAlphaType::from_integer(255));
259
260        let image_format_1 = ImageFormat::Rgba8;
261        let image_format_2 = ImageFormat::from_integer(image_format_1.into_integer());
262        assert_eq!(Some(image_format_1), image_format_2);
263
264        assert_eq!(None, ImageFormat::from_integer(255));
265
266        let image_quality_1 = ImageQuality::Low;
267        let image_quality_2 = ImageQuality::from_integer(image_quality_1.into_integer());
268        assert_eq!(Some(image_quality_1), image_quality_2);
269
270        assert_eq!(None, ImageQuality::from_integer(255));
271
272        let mix_1 = Mix::Multiply;
273        let mix_2 = Mix::from_integer(mix_1.into_integer());
274        assert_eq!(Some(mix_1), mix_2);
275
276        assert_eq!(None, Mix::from_integer(255));
277    }
278
279    #[test]
280    fn zeroable() {
281        let compose = Compose::zeroed();
282        assert_eq!(compose, Compose::Clear);
283
284        let extend = Extend::zeroed();
285        assert_eq!(extend, Extend::Pad);
286
287        let fill = Fill::zeroed();
288        assert_eq!(fill, Fill::NonZero);
289
290        let image_alpha_type = ImageAlphaType::zeroed();
291        assert_eq!(image_alpha_type, ImageAlphaType::Alpha);
292
293        let image_format = ImageFormat::zeroed();
294        assert_eq!(image_format, ImageFormat::Rgba8);
295
296        let image_quality = ImageQuality::zeroed();
297        assert_eq!(image_quality, ImageQuality::Low);
298
299        let mix = Mix::zeroed();
300        assert_eq!(mix, Mix::Normal);
301    }
302
303    /// Tests that the [`Contiguous`] impl for [`Compose`] is not trivially incorrect.
304    const _: () = {
305        let mut value = 0;
306        while value <= Compose::MAX_VALUE {
307            // Safety: In a const context, therefore if this makes an invalid Compose, that will be detected.
308            let it: Compose = unsafe { ptr::read((&raw const value).cast()) };
309            // Evaluate the enum value to ensure it actually has a valid tag
310            if it as u8 != value {
311                unreachable!();
312            }
313            value += 1;
314        }
315    };
316
317    /// Tests that the [`Contiguous`] impl for [`Extend`] is not trivially incorrect.
318    const _: () = {
319        let mut value = 0;
320        while value <= Extend::MAX_VALUE {
321            // Safety: In a const context, therefore if this makes an invalid Extend, that will be detected.
322            let it: Extend = unsafe { ptr::read((&raw const value).cast()) };
323            // Evaluate the enum value to ensure it actually has a valid tag
324            if it as u8 != value {
325                unreachable!();
326            }
327            value += 1;
328        }
329    };
330
331    /// Tests that the [`Contiguous`] impl for [`Fill`] is not trivially incorrect.
332    const _: () = {
333        let mut value = 0;
334        while value <= Fill::MAX_VALUE {
335            // Safety: In a const context, therefore if this makes an invalid Fill, that will be detected.
336            let it: Fill = unsafe { ptr::read((&raw const value).cast()) };
337            // Evaluate the enum value to ensure it actually has a valid tag
338            if it as u8 != value {
339                unreachable!();
340            }
341            value += 1;
342        }
343    };
344
345    /// Tests that the [`Contiguous`] impl for [`ImageAlphaType`] is not trivially incorrect.
346    const _: () = {
347        let mut value = 0;
348        while value <= ImageAlphaType::MAX_VALUE {
349            // Safety: In a const context, therefore if this makes an invalid ImageFormat, that will be detected.
350            let it: ImageAlphaType = unsafe { ptr::read((&raw const value).cast()) };
351            // Evaluate the enum value to ensure it actually has a valid tag
352            if it as u8 != value {
353                unreachable!();
354            }
355            value += 1;
356        }
357    };
358
359    /// Tests that the [`Contiguous`] impl for [`ImageFormat`] is not trivially incorrect.
360    const _: () = {
361        let mut value = 0;
362        while value <= ImageFormat::MAX_VALUE {
363            // Safety: In a const context, therefore if this makes an invalid ImageFormat, that will be detected.
364            let it: ImageFormat = unsafe { ptr::read((&raw const value).cast()) };
365            // Evaluate the enum value to ensure it actually has a valid tag
366            if it as u8 != value {
367                unreachable!();
368            }
369            value += 1;
370        }
371    };
372
373    /// Tests that the [`Contiguous`] impl for [`ImageQuality`] is not trivially incorrect.
374    const _: () = {
375        let mut value = 0;
376        while value <= ImageQuality::MAX_VALUE {
377            // Safety: In a const context, therefore if this makes an invalid ImageQuality, that will be detected.
378            let it: ImageQuality = unsafe { ptr::read((&raw const value).cast()) };
379            // Evaluate the enum value to ensure it actually has a valid tag
380            if it as u8 != value {
381                unreachable!();
382            }
383            value += 1;
384        }
385    };
386
387    /// Tests that the [`Contiguous`] impl for [`Mix`] is not trivially incorrect.
388    const _: () = {
389        let mut value = 0;
390        while value <= ImageQuality::MAX_VALUE {
391            // Safety: In a const context, therefore if this makes an invalid ImageQuality, that will be detected.
392            let it: ImageQuality = unsafe { ptr::read((&raw const value).cast()) };
393            // Evaluate the enum value to ensure it actually has a valid tag
394            if it as u8 != value {
395                unreachable!();
396            }
397            value += 1;
398        }
399    };
400}
401
402#[cfg(doctest)]
403/// Doctests aren't collected under `cfg(test)`; we can use `cfg(doctest)` instead
404mod doctests {
405    /// Validates that any new variants in `Compose` has led to a change in the `Contiguous` impl.
406    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
407    /// We make the assumption that all new variants will maintain contiguousness.
408    ///
409    /// ```compile_fail,E0080
410    /// use bytemuck::Contiguous;
411    /// use peniko::Compose;
412    /// const {
413    ///     let value = Compose::MAX_VALUE + 1;
414    ///     // Safety: In a const context, therefore if this makes an invalid Compose, that will be detected.
415    ///     // (Indeed, we rely upon that)
416    ///     let it: Compose = unsafe { core::ptr::read((&raw const value).cast()) };
417    ///     // Evaluate the enum value to ensure it actually has an invalid tag
418    ///     if it as u8 != value {
419    ///         unreachable!();
420    ///     }
421    /// }
422    /// ```
423    const _COMPOSE: () = {};
424
425    /// Validates that any new variants in `Extend` has led to a change in the `Contiguous` impl.
426    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
427    /// We make the assumption that all new variants will maintain contiguousness.
428    ///
429    /// ```compile_fail,E0080
430    /// use bytemuck::Contiguous;
431    /// use peniko::Extend;
432    /// const {
433    ///     let value = Extend::MAX_VALUE + 1;
434    ///     let it: Extend = unsafe { core::ptr::read((&raw const value).cast()) };
435    ///     // Evaluate the enum value to ensure it actually has an invalid tag
436    ///     if it as u8 != value {
437    ///         unreachable!();
438    ///     }
439    /// }
440    /// ```
441    const _EXTEND: () = {};
442
443    /// Validates that any new variants in `Fill` has led to a change in the `Contiguous` impl.
444    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
445    /// We make the assumption that all new variants will maintain contiguousness.
446    ///
447    /// ```compile_fail,E0080
448    /// use bytemuck::Contiguous;
449    /// use peniko::Fill;
450    /// const {
451    ///     let value = Fill::MAX_VALUE + 1;
452    ///     let it: Fill = unsafe { core::ptr::read((&raw const value).cast()) };
453    ///     // Evaluate the enum value to ensure it actually has an invalid tag
454    ///     if it as u8 != value {
455    ///         unreachable!();
456    ///     }
457    /// }
458    /// ```
459    const _FILL: () = {};
460
461    /// Validates that any new variants in `ImageAlphaType` has led to a change in the `Contiguous` impl.
462    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
463    /// We make the assumption that all new variants will maintain contiguousness.
464    ///
465    /// ```compile_fail,E0080
466    /// use bytemuck::Contiguous;
467    /// use peniko::ImageAlphaType;
468    /// const {
469    ///     let value = ImageAlphaType::MAX_VALUE + 1;
470    ///     let it: ImageAlphaType = unsafe { core::ptr::read((&raw const value).cast()) };
471    ///     // Evaluate the enum value to ensure it actually has an invalid tag
472    ///     if it as u8 != value {
473    ///         unreachable!();
474    ///     }
475    /// }
476    /// ```
477    const _IMAGE_ALPHA_TYPE: () = {};
478
479    /// Validates that any new variants in `ImageFormat` has led to a change in the `Contiguous` impl.
480    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
481    /// We make the assumption that all new variants will maintain contiguousness.
482    ///
483    /// ```compile_fail,E0080
484    /// use bytemuck::Contiguous;
485    /// use peniko::ImageFormat;
486    /// const {
487    ///     let value = ImageFormat::MAX_VALUE + 1;
488    ///     let it: ImageFormat = unsafe { core::ptr::read((&raw const value).cast()) };
489    ///     // Evaluate the enum value to ensure it actually has an invalid tag
490    ///     if it as u8 != value {
491    ///         unreachable!();
492    ///     }
493    /// }
494    /// ```
495    const _IMAGE_FORMAT: () = {};
496
497    /// Validates that any new variants in `ImageQuality` has led to a change in the `Contiguous` impl.
498    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
499    /// We make the assumption that all new variants will maintain contiguousness.
500    ///
501    /// ```compile_fail,E0080
502    /// use bytemuck::Contiguous;
503    /// use peniko::ImageQuality;
504    /// const {
505    ///     let value = ImageQuality::MAX_VALUE + 1;
506    ///     let it: ImageQuality = unsafe { core::ptr::read((&raw const value).cast()) };
507    ///     // Evaluate the enum value to ensure it actually has an invalid tag
508    ///     if it as u8 != value {
509    ///         unreachable!();
510    ///     }
511    /// }
512    /// ```
513    const _IMAGE_QUALITY: () = {};
514
515    /// Validates that any new variants in `Mix` has led to a change in the `Contiguous` impl.
516    /// Note that to test this robustly, we'd need 256 tests, which is impractical.
517    /// We make the assumption that all new variants will maintain contiguousness.
518    ///
519    /// ```compile_fail,E0080
520    /// use bytemuck::Contiguous;
521    /// use peniko::Mix;
522    /// const {
523    ///     let value = Mix::MAX_VALUE + 1;
524    ///     let it: Mix = unsafe { core::ptr::read((&raw const value).cast()) };
525    ///     // Evaluate the enum value to ensure it actually has an invalid tag
526    ///     if it as u8 != value {
527    ///         unreachable!();
528    ///     }
529    /// }
530    /// ```
531    const _MIX: () = {};
532}