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}