mycelium_bitfield/
from_bits.rs

1use core::{convert::Infallible, fmt};
2
3/// Trait implemented by values which can be converted to and from raw bits.
4///
5/// This trait is [implemented by default] for all signed and unsigned integer
6/// types, as well as for `bool`s. It can be implemented manually for any
7/// user-defined type which has a well-defined bit-pattern representation. For
8/// `enum` types with unsigned integer `repr`s, it may also be implemented
9/// automatically using the [`enum_from_bits!`] macro.
10///
11/// [implemented by default]: #foreign-impls
12/// [`enum_from_bits!`]: crate::enum_from_bits!
13pub trait FromBits<B>: Sized {
14    /// The error type returned by [`Self::try_from_bits`] when an invalid bit
15    /// pattern is encountered.
16    ///
17    /// If all bit patterns possible in [`Self::BITS`] bits are valid bit
18    /// patterns for a `Self`-typed value, this should generally be
19    /// [`core::convert::Infallible`].
20    type Error: fmt::Display;
21
22    /// The number of bits required to represent a value of this type.
23    const BITS: u32;
24
25    /// Attempt to convert `bits` into a value of this type.
26    ///
27    /// # Returns
28    ///
29    /// - `Ok(Self)` if `bits` contained a valid bit pattern for a value of this
30    ///   type.
31    /// - `Err(Self::Error)` if `bits` is an invalid bit pattern for a value of
32    ///   this type.
33    fn try_from_bits(bits: B) -> Result<Self, Self::Error>;
34
35    /// Convert `self` into a raw bit representation.
36    ///
37    /// In general, this will be a low-cost conversion (e.g., for `enum`s, this
38    /// is generally an `as` cast).
39    fn into_bits(self) -> B;
40}
41
42/// Generates automatic [`FromBits`] and [`core::convert::TryFrom`]
43/// implementations for an `enum` type of [`repr(uN)`], where `uN` is one of
44/// [`u8`], [`u16`], [`u32`], [`u64`], or [`u128`].
45///
46/// This allows an `enum` type to be used with the
47/// [`bitfield!`](crate::bitfield!) macro without requiring a manual [`FromBits`]
48/// implementation. Essentially, this macro can be thought of as being analogous
49/// to `#[derive(FromBits, TryFrom)]`.[^1]
50///
51/// # Generated Implementations
52///
53/// This macro will automatically generate a [`FromBits`]`<uN>` and a
54/// [`core::convert::TryFrom`]`<uN>` implementation for the defined `enum` type.
55/// In addition, [`FromBits`] and [`core::convert::TryFrom`] implementations for
56/// each unsized integer type *larger* than `uN` are also automatically
57/// generated. The [`Copy`] and [`Clone`] traits are also derived for the
58/// generated `enum`, as they are required by the [`FromBits`] implementation..
59///
60/// Generated `enum` types are [`repr(uN)]`].
61///
62/// Additional traits may be derived for the `enum` type, such as
63/// [`PartialEq`], [`Eq`], and [`Default`]. These traits are not automatically
64/// derived, as custom implementations may also be desired, depending on the
65/// use-case. For example, the `Default` value for am `enum` may _not_ be all
66/// zeroes.
67///
68/// # Examples
69///
70/// Basic usage:
71///
72/// ```rust
73/// use mycelium_bitfield::FromBits;
74/// use core::convert::TryFrom;
75///
76/// mycelium_bitfield::enum_from_bits! {
77///    /// Doc comments can be added to generated enum types.
78///    #[derive(Debug, PartialEq, Eq)] // additional `derive` attributes can be added
79///     enum Example<u8> { // generate an enum represented by a u8
80///         Foo = 0b0000,
81///         Bar = 0b0001,
82///         Baz = 0b1000,
83///         Qux = 0b0111,
84///     }
85/// }
86///
87/// // the generated enum will implement the `FromBits` trait:
88/// assert_eq!(Example::try_from_bits(0b1u8), Ok(Example::Bar));
89/// assert_eq!(FromBits::<u8>::into_bits(Example::Foo), 0);
90///
91/// // `core::convert::TryFrom` implementations are also generated:
92/// assert_eq!(Example::try_from(0b1000u8), Ok(Example::Baz));
93/// assert_eq!(0b0111u32.try_into(), Ok(Example::Qux));
94///
95/// // invalid bit-patterns return an error:
96/// assert!(Example::try_from_bits(0b1001u8).is_err()); // invalid bit pattern
97/// assert!(Example::try_from_bits(0b1000_0000u8).is_err()); // too many bits
98/// ```
99///
100/// Only `u8`, `u16`, `u32`, `u64`, and `u128` may be used as `repr`s for
101/// generated enums:
102///
103/// ```rust,compile_fail
104/// mycelium_bitfield::enum_from_bits! {
105///     /// This won't work. Don't do this.
106///     enum InvalidRepr<i32> {
107///         This = 0b01,
108///         Wont = 0b10,
109///         Work = 0b11,
110///     }
111/// }
112/// ```
113///
114/// [^1]: **Why Not `#[derive(FromBits)]`?** Some readers may be curious about why
115///     this is a declarative macro, rather than a procedural `#[derive]` macro.
116///     The answer is..."because I felt like it lol". This probably *should* be
117///     a proc-macro, since it's essentially just  deriving a trait
118///     implementation. However, one of my goals for `mycelium-bitfield` was to
119///     see how far I could go using only `macro_rules!` macros. This isn't
120///     because I dislike procedural macros, or that I'm concerned about
121///     proc-macro compile times --- I just thought it would be a fun challenge
122///     to do everything declaratively, if it was possible. And, if you *do*
123///     care about the potential build time impact of proc-macro dependencies,
124///     this should help. :)
125///
126/// [`repr(uN)`]:
127///     https://doc.rust-lang.org/reference/type-layout.html#primitive-representations
128#[macro_export]
129macro_rules! enum_from_bits {
130    (
131        $(#[$meta:meta])* $vis:vis enum $Type:ident<$uN:ident> {
132            $(#[$var1_meta:meta])*
133            $Variant1:ident = $value1:expr,
134            $(
135                $(#[$var_meta:meta])*
136                $Variant:ident = $value:expr
137            ),* $(,)?
138        }
139    ) => {
140        $(#[$meta])*
141        #[repr($uN)]
142        #[derive(Copy, Clone)]
143        $vis enum $Type {
144            $(#[$var1_meta])*
145            $Variant1 = $value1,
146            $(
147                $(#[$var_meta])*
148                $Variant = $value
149            ),*
150        }
151
152        impl $Type {
153            const VARIANTS: &'static [Self] = &[
154                Self::$Variant1,
155                $(
156                    Self::$Variant,
157                )*
158            ];
159
160            const MAX_VARIANT: Self = {
161                // crappy while loop because `for` and iterator adapters don't
162                // work in const-eval...
163                let mut max = Self::VARIANTS[0];
164                let mut i = 0;
165                while i < Self::VARIANTS.len() {
166                    if Self::VARIANTS[i] as $uN > max as $uN {
167                        max = Self::VARIANTS[i];
168                    }
169                    i += 1;
170                }
171                max
172            };
173
174            const MIN_VARIANT: Self = {
175                let mut min = Self::VARIANTS[0];
176                let mut i = 0;
177                while i < Self::VARIANTS.len() {
178                    if (Self::VARIANTS[i] as $uN) < min as $uN {
179                        min = Self::VARIANTS[i];
180                    }
181                    i += 1;
182                }
183                min
184            };
185
186            const NEEDED_BITS: u32 = {
187                // we need at least (bit position of `MAX_VARIANT`'s MSB) bits
188                // to represent a value of this type.
189                let max = Self::MAX_VARIANT as $uN;
190                <$uN>::BITS - max.leading_zeros()
191            };
192
193            const ERROR: &'static str = concat!(
194                "invalid value for ",
195                stringify!($Type),
196                ": expected one of [",
197                stringify!($value1),
198                $(
199                    ", ", stringify!($value),
200                )*
201                "]"
202            );
203        }
204
205        #[automatically_derived]
206        impl core::convert::TryFrom<$uN> for $Type {
207            type Error = &'static str;
208
209            #[inline]
210            fn try_from(value: $uN) -> Result<Self, Self::Error> {
211                match value {
212                    $value1 => Ok(Self::$Variant1),
213                    $(
214                        $value => Ok(Self::$Variant),
215                    )*
216                    _ => Err(Self::ERROR),
217                }
218            }
219        }
220
221        #[automatically_derived]
222        impl $crate::FromBits<$uN> for $Type {
223            type Error = &'static str;
224            const BITS: u32 = Self::NEEDED_BITS;
225
226            #[inline]
227            fn try_from_bits(u: $uN) -> Result<Self, Self::Error> {
228                Self::try_from(u)
229            }
230
231            #[inline]
232            fn into_bits(self) -> $uN {
233                self as $uN
234            }
235        }
236
237        $crate::enum_from_bits!(@bigger $uN, $Type);
238    };
239
240    (@bigger u8, $Type:ty) =>   {
241        $crate::enum_from_bits! { @impl u8, $Type, u16, u32, u64, u128, usize }
242    };
243    (@bigger u16, $Type:ty) => {
244        $crate::enum_from_bits! { @impl u16, $Type, u32, u64, u128, usize }
245    };
246    (@bigger u32, $Type:ty) => {
247        $crate::enum_from_bits! { @impl u32, $Type, u64, u128, usize }
248    };
249    (@bigger u64, $Type:ty) => {
250        $crate::enum_from_bits! { @impl u128 }
251    };
252    (@bigger $uN:ty, $Type:ty) => {
253        compile_error!(
254            concat!(
255                "repr for ",
256                stringify!($Type),
257                " must be one of u8, u16, u32, u64, or u128 (got ",
258                stringify!($uN),
259                ")",
260            ));
261    };
262    (@impl $uN:ty, $Type:ty, $($bigger:ty),+) => {
263        $(
264
265            #[automatically_derived]
266            impl $crate::FromBits<$bigger> for $Type {
267                type Error = &'static str;
268                const BITS: u32 = Self::NEEDED_BITS;
269
270                #[inline]
271                fn try_from_bits(u: $bigger) -> Result<Self, Self::Error> {
272                    Self::try_from(u as $uN)
273                }
274
275                #[inline]
276                fn into_bits(self) -> $bigger {
277                    self as $bigger
278                }
279            }
280
281            #[automatically_derived]
282            impl core::convert::TryFrom<$bigger> for $Type {
283                type Error = &'static str;
284                #[inline]
285                fn try_from(u: $bigger) -> Result<Self, Self::Error> {
286                    Self::try_from(u as $uN)
287                }
288            }
289        )+
290    };
291
292}
293
294macro_rules! impl_frombits_for_ty {
295   ($(impl FromBits<$($F:ty),+> for $T:ty {})+) => {
296        $(
297
298            $(
299                impl FromBits<$F> for $T {
300                    const BITS: u32 = <$T>::BITS;
301                    type Error = Infallible;
302
303                    fn try_from_bits(f: $F) -> Result<Self, Self::Error> {
304                        Ok(f as $T)
305                    }
306
307                    fn into_bits(self) -> $F {
308                        self as $F
309                    }
310                }
311            )*
312        )+
313    }
314}
315
316macro_rules! impl_frombits_for_bool {
317    (impl FromBits<$($F:ty),+> for bool {}) => {
318        $(
319            impl FromBits<$F> for bool {
320                const BITS: u32 = 1;
321                type Error = Infallible;
322
323                fn try_from_bits(f: $F) -> Result<Self, Self::Error> {
324                    Ok(if f == 0 { false } else { true })
325                }
326
327                fn into_bits(self) -> $F {
328                    if self {
329                        1
330                    } else {
331                        0
332                    }
333                }
334            }
335        )+
336    }
337}
338
339impl_frombits_for_bool! {
340    impl FromBits<u8, u16, u32, u64, u128, usize> for bool {}
341}
342
343impl_frombits_for_ty! {
344    impl FromBits<u8, u16, u32, u64, u128> for u8 {}
345    impl FromBits<u16, u32, u64, u128> for u16 {}
346    impl FromBits<u32, u64, u128> for u32 {}
347    impl FromBits<u64, u128> for u64 {}
348    impl FromBits<u128> for u128 {}
349
350    impl FromBits<u8, u16, u32, u64, u128> for i8 {}
351    impl FromBits<u16, u32, u64, u128> for i16 {}
352    impl FromBits<u32, u64, u128> for i32 {}
353    impl FromBits<u64, u128> for i64 {}
354
355    // Rust doesn't support 8 bit targets, so {u,i}size are always at least 16 bit wide,
356    // source: https://doc.rust-lang.org/1.45.2/src/core/convert/num.rs.html#134-139
357    //
358    // This allows the following impls to be supported on all platforms.
359    // Impls for {u,i}32 and {u,i}64 however need to be restricted (see below).
360    impl FromBits<usize> for u8 {}
361    impl FromBits<usize> for i8 {}
362    impl FromBits<usize> for u16 {}
363    impl FromBits<usize> for i16 {}
364
365    impl FromBits<usize> for usize {}
366    impl FromBits<usize> for isize {}
367    impl FromBits<u128> for usize {}
368}
369
370#[cfg(target_pointer_width = "16")]
371impl_frombits_for_ty! {
372    impl FromBits<u16, u32, u64> for usize {}
373    impl FromBits<u16, u32, u64> for isize {}
374}
375
376#[cfg(target_pointer_width = "32")]
377impl_frombits_for_ty! {
378    impl FromBits<u32, u64> for usize {}
379    impl FromBits<u32, u64> for isize {}
380
381    impl FromBits<usize> for u32 {}
382    impl FromBits<usize> for i32 {}
383}
384
385#[cfg(target_pointer_width = "64")]
386impl_frombits_for_ty! {
387    impl FromBits<u64> for usize {}
388    impl FromBits<u64> for isize {}
389
390    impl FromBits<usize> for u32 {}
391    impl FromBits<usize> for i32 {}
392    impl FromBits<usize> for u64 {}
393    impl FromBits<usize> for i64 {}
394}
395
396#[cfg(test)]
397mod tests {
398    use super::*;
399
400    enum_from_bits! {
401        #[derive(Debug, PartialEq, Eq)]
402        enum Test<u8> {
403            Foo = 0b0000,
404            Bar = 0b0001,
405            Baz = 0b1000,
406            Qux = 0b0111,
407        }
408    }
409
410    #[test]
411    fn enum_max_variant() {
412        assert_eq!(Test::MAX_VARIANT, Test::Baz);
413    }
414
415    #[test]
416    fn enum_min_variant() {
417        assert_eq!(Test::MIN_VARIANT, Test::Foo);
418    }
419
420    #[test]
421    fn enum_needed_bits() {
422        assert_eq!(Test::NEEDED_BITS, 4);
423    }
424
425    #[test]
426    fn enum_roundtrips() {
427        for variant in [Test::Foo, Test::Bar, Test::Baz, Test::Qux] {
428            let bits = dbg!(variant as u8);
429            assert_eq!(dbg!(Test::try_from_bits(bits)), Ok(variant));
430            assert_eq!(dbg!(Test::try_from_bits(bits as u16)), Ok(variant));
431            assert_eq!(dbg!(Test::try_from_bits(bits as u32)), Ok(variant));
432            assert_eq!(dbg!(Test::try_from_bits(bits as u64)), Ok(variant));
433            assert_eq!(dbg!(Test::try_from_bits(bits as u128)), Ok(variant));
434        }
435    }
436
437    #[test]
438    fn enum_invalid() {
439        for value in [0b1001u8, 0b1000_0000u8, 0b1000_0001u8, 0b1111u8] {
440            dbg!(value);
441            assert!(dbg!(Test::try_from_bits(value)).is_err());
442        }
443    }
444}