Bitfields的過程宏,允許將Bitfield指定為結構。由於該庫提供了一個程序上的宏,因此它沒有運行時依賴關係,並且適用於no-std環境。
Default , fmt::Debug或defmt::Format性狀的產生將其添加到您的Cargo.toml :
[ dependencies ]
bitfield-struct = " 0.10 " 讓我們從一個簡單的例子開始。假設我們要在單個字節中存儲多個數據,如下所示:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| p | 等級 | s | 種類 | ||||
該板條箱生成了一種不錯的包裝類型,使其很容易做到這一點:
use bitfield_struct :: bitfield ;
/// Define your type like this with the bitfield attribute
# [ bitfield ( u8 ) ]
struct MyByte {
/// The first field occupies the least significant bits
# [ bits ( 4 ) ]
kind : usize ,
/// Booleans are 1 bit large
system : bool ,
/// The bits attribute specifies the bit size of this field
# [ bits ( 2 ) ]
level : usize ,
/// The last field spans over the most significant bits
present : bool
}
// The macro creates three accessor functions for each field:
// <name>, with_<name> and set_<name>
let my_byte = MyByte :: new ( )
. with_kind ( 15 )
. with_system ( false )
. with_level ( 3 )
. with_present ( true ) ;
assert ! ( my_byte . present ( ) ) ; 此外,該板條箱具有一些有用的功能,此處將更詳細地顯示。
下面的示例顯示瞭如何攜帶屬性以及如何處理簽名的整數,填充和自定義類型。
use bitfield_struct :: bitfield ;
/// A test bitfield with documentation
# [ bitfield ( u64 ) ]
# [ derive ( PartialEq , Eq ) ] // <- Attributes after `bitfield` are carried over
struct MyBitfield {
/// Defaults to 16 bits for u16
int : u16 ,
/// Interpreted as 1 bit flag, with a custom default value
# [ bits ( default = true ) ]
flag : bool ,
/// Custom bit size
# [ bits ( 1 ) ]
tiny : u8 ,
/// Sign extend for signed integers
# [ bits ( 13 ) ]
negative : i16 ,
/// Supports any type with `into_bits`/`from_bits` functions
# [ bits ( 16 ) ]
custom : CustomEnum ,
/// Public field -> public accessor functions
# [ bits ( 10 ) ]
pub public : usize ,
/// Also supports read-only fields
# [ bits ( 1 , access = RO ) ]
read_only : bool ,
/// And write-only fields
# [ bits ( 1 , access = WO ) ]
write_only : bool ,
/// Padding
# [ bits ( 5 ) ]
__ : u8 ,
}
/// A custom enum
# [ derive ( Debug , PartialEq , Eq ) ]
# [ repr ( u16 ) ]
enum CustomEnum {
A = 0 ,
B = 1 ,
C = 2 ,
}
impl CustomEnum {
// This has to be a const fn
const fn into_bits ( self ) -> u16 {
self as _
}
const fn from_bits ( value : u16 ) -> Self {
match value {
0 => Self :: A ,
1 => Self :: B ,
_ => Self :: C ,
}
}
}
// Usage:
let mut val = MyBitfield :: new ( )
. with_int ( 3 << 15 )
. with_tiny ( 1 )
. with_negative ( - 3 )
. with_custom ( CustomEnum :: B )
. with_public ( 2 )
// .with_read_only(true) <- Would not compile
. with_write_only ( false ) ;
println ! ( "{val:?}" ) ;
let raw : u64 = val . into ( ) ;
println ! ( "{raw:b}" ) ;
assert_eq ! ( val . int ( ) , 3 << 15 ) ;
assert_eq ! ( val . flag ( ) , true ) ;
assert_eq ! ( val . negative ( ) , - 3 ) ;
assert_eq ! ( val . tiny ( ) , 1 ) ;
assert_eq ! ( val . custom ( ) , CustomEnum :: B ) ;
assert_eq ! ( val . public ( ) , 2 ) ;
assert_eq ! ( val . read_only ( ) , false ) ;
// const members
assert_eq ! ( MyBitfield :: FLAG_BITS , 1 ) ;
assert_eq ! ( MyBitfield :: FLAG_OFFSET , 16 ) ;
val . set_negative ( 1 ) ;
assert_eq ! ( val . negative ( ) , 1 ) ;宏為每個字段生成三個訪問器功能。每個訪問者還繼承其字段的文檔。
int的簽名是:
// generated struct
struct MyBitfield ( u64 ) ;
impl MyBitfield {
const fn new ( ) -> Self { Self ( 0 ) }
const INT_BITS : usize = 16 ;
const INT_OFFSET : usize = 0 ;
const fn int ( & self ) -> u16 { todo ! ( ) }
const fn with_int ( self , value : u16 ) -> Self { todo ! ( ) }
const fn with_int_checked ( self , value : u16 ) -> Result < Self , ( ) > { todo ! ( ) }
const fn set_int ( & mut self , value : u16 ) { todo ! ( ) }
const fn set_int_checked ( & mut self , value : u16 ) -> Result < ( ) , ( ) > { todo ! ( ) }
// other field ...
}
// Also generates From<u64>, Into<u64>, Default, and Debug implementations...提示:您可以使用Rust-Analyzer“遞歸擴展”操作來查看生成的代碼。
宏支持任何可轉換為基礎比特菲爾德類型的類型。這可以是枚舉,例如以下示例或任何其他結構。
轉換和默認值可以用以下#[bits]參數指定:
from :函數從原始位轉換為自定義類型,默認為<ty>::from_bitsinto :函數從自定義類型轉換為原始位,默認為<ty>::into_bitsdefault :自定義表達式,默認為調用<ty>::from_bits(0) use bitfield_struct :: bitfield ;
# [ bitfield ( u16 ) ]
# [ derive ( PartialEq , Eq ) ]
struct Bits {
/// Supports any convertible type
# [ bits ( 8 , default = CustomEnum :: B , from = CustomEnum :: my_from_bits ) ]
custom : CustomEnum ,
/// And nested bitfields
# [ bits ( 8 ) ]
nested : Nested ,
}
# [ derive ( Debug , PartialEq , Eq ) ]
# [ repr ( u8 ) ]
enum CustomEnum {
A = 0 ,
B = 1 ,
C = 2 ,
}
impl CustomEnum {
// This has to be a const fn
const fn into_bits ( self ) -> u8 {
self as _
}
const fn my_from_bits ( value : u8 ) -> Self {
match value {
0 => Self :: A ,
1 => Self :: B ,
_ => Self :: C ,
}
}
}
/// Bitfields implement the conversion functions automatically
# [ bitfield ( u8 ) ]
struct Nested {
# [ bits ( 4 ) ]
lo : u8 ,
# [ bits ( 4 ) ]
hi : u8 ,
} 可選order宏參數確定位的佈局,默認值是LSB(最不重要的位):
use bitfield_struct :: bitfield ;
# [ bitfield ( u8 , order = Lsb ) ]
struct MyLsbByte {
/// The first field occupies the *least* significant bits
# [ bits ( 4 ) ]
kind : usize ,
system : bool ,
# [ bits ( 2 ) ]
level : usize ,
present : bool
}
let my_byte_lsb = MyLsbByte :: new ( )
. with_kind ( 10 )
. with_system ( false )
. with_level ( 2 )
. with_present ( true ) ;
// .- present
// | .- level
// | | .- system
// | | | .- kind
assert_eq ! ( my_byte_lsb . 0 , 0b1_10_0_1010 ) ;指定MSB(最重要的位)時,宏會生成反向順序:
use bitfield_struct :: bitfield ;
# [ bitfield ( u8 , order = Msb ) ]
struct MyMsbByte {
/// The first field occupies the *most* significant bits
# [ bits ( 4 ) ]
kind : usize ,
system : bool ,
# [ bits ( 2 ) ]
level : usize ,
present : bool
}
let my_byte_msb = MyMsbByte :: new ( )
. with_kind ( 10 )
. with_system ( false )
. with_level ( 2 )
. with_present ( true ) ;
// .- kind
// | .- system
// | | .- level
// | | | .- present
assert_eq ! ( my_byte_msb . 0 , 0b1010_0_10_1 ) ; 宏支持Bitfield結構的表示自定義類型。這可以是以下示例(來自endian-num )或任何可以轉換為Bitfield類型的其他結構的eNDIAN定義類型。
可以使用以下#[bitfield]參數指定表示形式及其轉換功能:
repr在內存中指定比特菲爾德的表示from “從reper到Bitfield的整數類型”指定轉換函數into轉換函數到reper這個示例即使在大型機器上也有一個小的字節訂單:
use bitfield_struct :: bitfield ;
use endian_num :: le16 ;
# [ bitfield ( u16 , repr = le16 , from = le16 :: from_ne , into = le16 :: to_ne ) ]
struct MyLeBitfield {
# [ bits ( 4 ) ]
first_nibble : u8 ,
# [ bits ( 12 ) ]
other : u16 ,
}
let my_be_bitfield = MyLeBitfield :: new ( )
. with_first_nibble ( 0x1 )
. with_other ( 0x234 ) ;
assert_eq ! ( my_be_bitfield . into_bits ( ) . to_le_bytes ( ) , [ 0x41 , 0x23 ] ) ;這個示例即使在小型機器上也有大型字節訂單:
use bitfield_struct :: bitfield ;
use endian_num :: be16 ;
# [ bitfield ( u16 , repr = be16 , from = be16 :: from_ne , into = be16 :: to_ne ) ]
struct MyBeBitfield {
# [ bits ( 4 ) ]
first_nibble : u8 ,
# [ bits ( 12 ) ]
other : u16 ,
}
let my_be_bitfield = MyBeBitfield :: new ( )
. with_first_nibble ( 0x1 )
. with_other ( 0x234 ) ;
assert_eq ! ( my_be_bitfield . into_bits ( ) . to_be_bytes ( ) , [ 0x23 , 0x41 ] ) ; Clone , Copy默認情況下,此宏Clone並Copy 。如果克隆您的類型的語義需要它,則可以使用額外的clone參數禁用此功能(例如,該類型保留了一個必須克隆的數據指針)。在這種情況下,您可以為Clone和Copy提供自己的實現。
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , clone = false ) ]
struct CustomClone {
data : u64
}
impl Clone for CustomClone {
fn clone ( & self ) -> Self {
Self :: new ( ) . with_data ( self . data ( ) )
}
}
// optionally:
impl Copy for CustomClone { }fmt::Debug , Default默認情況下,它還生成合適的fmt::Debug和Default實現,類似於#[derive(Debug, Default)]為普通結構創建的實現。您可以使用額外的debug和default參數禁用此功能。
use std :: fmt :: { Debug , Formatter , Result } ;
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , debug = false , default = false ) ]
struct CustomDebug {
data : u64
}
impl Debug for CustomDebug {
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> Result {
write ! ( f , "0x{:x}" , self . data ( ) )
}
}
impl Default for CustomDebug {
fn default ( ) -> Self {
Self ( 123 )
}
}
let val = CustomDebug :: default ( ) ;
println ! ( "{val:?}" )defmt::Format該宏可以自動實現defmt::Format ,該格式通過傳遞額外的defmt參數來反映默認的fmt::Debug實現。此實現要求Defmt板條作為defmt可用,並且具有與#[derive(defmt::Format)]相同規則和警告。
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , defmt = true ) ]
struct DefmtExample {
data : u64
}new / Clone / Debug / Default / defmt::Format您可以為new , clone , debug , default和defmt指定cfg(...)屬性,而不是布爾值:
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , debug = cfg ( test ) , default = cfg ( feature = "foo" ) ) ]
struct CustomDebug {
data : u64
}