font_types/
raw.rs

1//! types for working with raw big-endian bytes
2
3/// A trait for font scalars.
4///
5/// This is an internal trait for encoding and decoding big-endian bytes.
6///
7/// You do not need to implement this trait directly; it is an implementation
8/// detail of the [`BigEndian`] wrapper.
9pub trait Scalar: Sized {
10    /// The raw byte representation of this type.
11    type Raw: sealed::BeByteArray;
12
13    /// Create an instance of this type from raw big-endian bytes
14    fn from_raw(raw: Self::Raw) -> Self;
15
16    /// Encode this type as raw big-endian bytes
17    fn to_raw(self) -> Self::Raw;
18
19    /// Attempt to read a scalar from a slice.
20    ///
21    /// This will always succeed if `slice.len() == Self::RAW_BYTE_LEN`, and will
22    /// always return `None` otherwise.
23    fn read(slice: &[u8]) -> Option<Self> {
24        sealed::BeByteArray::from_slice(slice).map(Self::from_raw)
25    }
26}
27
28/// A trait for types that have a known, constant size.
29pub trait FixedSize: Sized {
30    /// The raw size of this type, in bytes.
31    ///
32    /// This is the size required to represent this type in a font file, which
33    /// may differ from the size of the native type:
34    ///
35    /// ```
36    /// # use font_types::{FixedSize, Offset24};
37    /// assert_eq!(std::mem::size_of::<u16>(), u16::RAW_BYTE_LEN);
38    /// assert_eq!(Offset24::RAW_BYTE_LEN, 3);
39    /// assert_eq!(std::mem::size_of::<Offset24>(), 4);
40    /// ```
41    const RAW_BYTE_LEN: usize;
42}
43
44/// we hide this trait; it isn't part of the public API, and this clarifies
45/// the guarantee that it is only implemented for [u8; N]
46mod sealed {
47    /// A trait representing any fixed-size big-endian byte array.
48    ///
49    /// This is only used in `Scalar`, as a way of expressing the condition that the
50    /// `Raw` type is always a fixed-size byte array.
51    #[cfg(not(feature = "bytemuck"))]
52    pub trait BeByteArray: Copy + AsRef<[u8]> {
53        /// Must always succeed for `[u8; N]` if `slice.len() == N`, must fail otherwise
54        fn from_slice(slice: &[u8]) -> Option<Self>;
55    }
56    #[cfg(feature = "bytemuck")]
57    pub trait BeByteArray:
58        Copy + AsRef<[u8]> + bytemuck::AnyBitPattern + bytemuck::Zeroable
59    {
60        /// Must always succeed for `[u8; N]` if `slice.len() == N`, must fail otherwise
61        fn from_slice(slice: &[u8]) -> Option<Self>;
62    }
63
64    impl<const N: usize> BeByteArray for [u8; N] {
65        fn from_slice(slice: &[u8]) -> Option<Self> {
66            slice.try_into().ok()
67        }
68    }
69}
70
71/// A wrapper around raw big-endian bytes for some type.
72#[derive(Clone, Copy, PartialEq, Eq, Hash)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74#[repr(transparent)]
75pub struct BigEndian<T: Scalar>(pub(crate) T::Raw);
76
77// # SAFETY:
78//
79// `BigEndian<T>` has the bound `T: Scalar`, and contains only a single value,
80// `<T as Scalar>::Raw` which is only ever a byte array.
81#[cfg(feature = "bytemuck")]
82unsafe impl<T> bytemuck::Zeroable for BigEndian<T> where T: Scalar + Copy {}
83#[cfg(feature = "bytemuck")]
84unsafe impl<T> bytemuck::AnyBitPattern for BigEndian<T> where T: Scalar + Copy + 'static {}
85
86impl<T: Scalar> BigEndian<T> {
87    /// construct a new `BigEndian<T>` from raw bytes
88    pub fn new(raw: T::Raw) -> BigEndian<T> {
89        BigEndian(raw)
90    }
91
92    /// Attempt to construct a new raw value from this slice.
93    ///
94    /// This will fail if `slice.len() != T::RAW_BYTE_LEN`.
95    pub fn from_slice(slice: &[u8]) -> Option<Self> {
96        sealed::BeByteArray::from_slice(slice).map(Self)
97    }
98
99    /// Convert this raw type to its native representation.
100    #[inline(always)]
101    pub fn get(&self) -> T {
102        T::from_raw(self.0)
103    }
104
105    /// Set the value, overwriting the bytes.
106    pub fn set(&mut self, value: T) {
107        self.0 = value.to_raw();
108    }
109
110    /// Get the raw big-endian bytes.
111    pub fn be_bytes(&self) -> &[u8] {
112        self.0.as_ref()
113    }
114}
115
116impl<T: Scalar> From<T> for BigEndian<T> {
117    #[inline]
118    fn from(val: T) -> Self {
119        BigEndian(val.to_raw())
120    }
121}
122
123impl<T: Scalar + Default> Default for BigEndian<T> {
124    fn default() -> Self {
125        Self::from(T::default())
126    }
127}
128
129//NOTE: do to the orphan rules, we cannot impl the inverse of this, e.g.
130// impl<T> PartialEq<BigEndian<T>> for T (<https://doc.rust-lang.org/error_codes/E0210.html>)
131impl<T: Scalar + Copy + PartialEq> PartialEq<T> for BigEndian<T> {
132    fn eq(&self, other: &T) -> bool {
133        self.get() == *other
134    }
135}
136
137impl<T: Scalar + Copy + PartialOrd + PartialEq> PartialOrd for BigEndian<T>
138where
139    <T as Scalar>::Raw: PartialEq,
140{
141    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
142        self.get().partial_cmp(&other.get())
143    }
144}
145
146impl<T: Scalar + Copy + Ord + Eq> Ord for BigEndian<T>
147where
148    <T as Scalar>::Raw: Eq,
149{
150    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
151        self.get().cmp(&other.get())
152    }
153}
154
155impl<T: Scalar> FixedSize for T {
156    const RAW_BYTE_LEN: usize = std::mem::size_of::<T::Raw>();
157}
158
159impl<T: Scalar> FixedSize for BigEndian<T> {
160    const RAW_BYTE_LEN: usize = T::RAW_BYTE_LEN;
161}
162
163/// An internal macro for implementing the `RawType` trait.
164#[macro_export]
165macro_rules! newtype_scalar {
166    ($ty:ident, $raw:ty) => {
167        impl $crate::raw::Scalar for $ty {
168            type Raw = $raw;
169            fn to_raw(self) -> $raw {
170                self.0.to_raw()
171            }
172
173            #[inline(always)]
174            fn from_raw(raw: $raw) -> Self {
175                Self($crate::raw::Scalar::from_raw(raw))
176            }
177        }
178    };
179}
180
181macro_rules! int_scalar {
182    ($ty:ty, $raw:ty) => {
183        impl crate::raw::Scalar for $ty {
184            type Raw = $raw;
185            fn to_raw(self) -> $raw {
186                self.to_be_bytes()
187            }
188
189            #[inline(always)]
190            fn from_raw(raw: $raw) -> $ty {
191                Self::from_be_bytes(raw)
192            }
193        }
194    };
195}
196
197int_scalar!(u8, [u8; 1]);
198int_scalar!(i8, [u8; 1]);
199int_scalar!(u16, [u8; 2]);
200int_scalar!(i16, [u8; 2]);
201int_scalar!(u32, [u8; 4]);
202int_scalar!(i32, [u8; 4]);
203int_scalar!(i64, [u8; 8]);
204int_scalar!(crate::Uint24, [u8; 3]);
205int_scalar!(crate::Int24, [u8; 3]);
206
207impl<T: std::fmt::Debug + Scalar + Copy> std::fmt::Debug for BigEndian<T> {
208    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
209        self.get().fmt(f)
210    }
211}
212
213impl<T: std::fmt::Display + Scalar + Copy> std::fmt::Display for BigEndian<T> {
214    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
215        self.get().fmt(f)
216    }
217}