Bit fields in Rust
| use core::marker::PhantomData; | |
| #[derive(Clone, Copy, Debug)] | |
| pub struct BitField<T>(u32, PhantomData<T>); | |
| impl<T> BitField<T> { | |
| #[cfg(test)] | |
| pub fn new(bit_field: u32) -> Self { | |
| BitField(bit_field, PhantomData) | |
| } | |
| pub fn get<M>(&self) -> Option<M::Value> | |
| where M: Member<BitField=T, Read=R> | |
| { | |
| let mask = M::mask(); | |
| let shift = M::shift(); | |
| M::Value::from_u32((self.0 & mask) >> shift) | |
| } | |
| pub fn set<M>(self, value: M::Value) -> Self | |
| where M: Member<BitField=T, Write=W> | |
| { | |
| let mask = M::mask(); | |
| let shift = M::shift(); | |
| let bit_field = self.0 & !mask; | |
| let value = (value.to_u32() << shift) & mask; | |
| BitField(bit_field | value, PhantomData) | |
| } | |
| } | |
| #[cfg(test)] | |
| impl<T> Default for BitField<T> { | |
| fn default() -> Self { | |
| BitField::new(0) | |
| } | |
| } | |
| pub struct R; | |
| pub struct W; | |
| pub trait Member { | |
| type BitField; | |
| type Value: Value; | |
| type Read; | |
| type Write; | |
| // This should be an associated constants, but those are not stable yet. | |
| fn mask() -> u32; | |
| /// Provides a default implementation that computes the shift from the mask | |
| /// | |
| /// Since this computation only uses simple operations on values known at | |
| /// compile-time, a sufficiently smart compiler should be able to optimize | |
| /// it out completely. And indeed, this is what seems to be happening. | |
| /// | |
| /// In case this optimization doesn't work for you, you can override this | |
| /// method and return the shift value manually. | |
| fn shift() -> u32 { | |
| let mut mask = Self::mask(); | |
| let mut shift = 0; | |
| while mask & 0b1 == 0 { | |
| mask >>= 1; | |
| shift += 1; | |
| if shift >= 31 { | |
| return 0; | |
| } | |
| } | |
| shift | |
| } | |
| } | |
| pub trait Value : Sized { | |
| fn from_u32(value: u32) -> Option<Self>; | |
| fn to_u32(self) -> u32; | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::BitField; | |
| #[derive(Clone, Copy)] | |
| pub struct MyBitField; | |
| member!(rw MyMember for MyBitField: 0b00111100, MyValue); | |
| pub struct MyMember; | |
| bit_field_value!( | |
| #[derive(Debug)] | |
| value MyValue { | |
| Value1010 = 0b1010, | |
| } | |
| ); | |
| #[test] | |
| fn it_should_read_a_value() { | |
| let bit_field = BitField::new(0b01101001); | |
| assert_eq!(Some(MyValue::Value1010), bit_field.get::<MyMember>()); | |
| } | |
| #[test] | |
| fn it_should_write_a_value() { | |
| let bit_field = BitField::new(0b01010101); | |
| let bit_field = bit_field.set::<MyMember>(MyValue::Value1010); | |
| assert_eq!(Some(MyValue::Value1010), bit_field.get::<MyMember>()); | |
| } | |
| } |
| /// Defines a bit field member | |
| /// Designed to be used within the `bit_field!` macro. | |
| macro_rules! member { | |
| (rw $member:tt for $bit_field:ty: $mask:expr, $value:ty) => { | |
| impl $crate::bit_field::Member for $member { | |
| type BitField = $bit_field; | |
| type Value = $value; | |
| type Read = $crate::bit_field::R; | |
| type Write = $crate::bit_field::W; | |
| fn mask() -> u32 { $mask } | |
| } | |
| }; | |
| (ro $member:tt for $bit_field:ty: $mask:expr, $value:ty) => { | |
| impl $crate::bit_field::Member for $member { | |
| type BitField = $bit_field; | |
| type Value = $value; | |
| type Read = $crate::bit_field::R; | |
| type Write = (); | |
| fn mask() -> u32 { $mask } | |
| } | |
| }; | |
| (wo $member:tt for $bit_field:ty: $mask:expr, $value:ty) => { | |
| impl $crate::bit_field::Member for $member { | |
| type BitField = $bit_field; | |
| type Value = $value; | |
| type Read = (); | |
| type Write = $crate::bit_field::W; | |
| fn mask() -> u32 { $mask } | |
| } | |
| }; | |
| } | |
| /// Implements an enum designed to be used as a value in the `bit_field` module | |
| macro_rules! bit_field_value { | |
| { | |
| $(#[$attr:meta])* | |
| value $name:ident { | |
| $($variant_name:ident = $variant_value:expr,)* | |
| } | |
| } => { | |
| #[derive(Eq, PartialEq)] | |
| $(#[$attr])* | |
| pub enum $name { | |
| $($variant_name = $variant_value,)* | |
| } | |
| impl $crate::bit_field::Value for $name { | |
| fn from_u32(value: u32) -> Option<Self> { | |
| match value { | |
| $($variant_value => Some($name::$variant_name),)* | |
| _ => None, | |
| } | |
| } | |
| fn to_u32(self) -> u32 { | |
| self as u32 | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
JinShil commentedNov 26, 2016
•
edited
I'm looking forward to formal documentation; I'm a little new to Rust, so I'm having a little bit of trouble fully understanding what's going on here. Thanks for sharing, though. Here's my take on a similar thing: https://gist.github.com/JinShil/c0fdd4afde144f3697982fd04cbb2705