มาโครขั้นตอนสำหรับ bitfields ที่อนุญาตให้ระบุ bitfields เป็น structs เนื่องจากไลบรารีนี้มีมาโครขั้นตอนจึงไม่มีการพึ่งพารันไทม์และทำงานสำหรับสภาพแวดล้อม 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 ) ;แมโครสร้างฟังก์ชั่น accessor สามฟังก์ชั่นสำหรับแต่ละฟิลด์ ผู้เข้าชมแต่ละคนยังสืบทอดเอกสารของฟิลด์
ลายเซ็นสำหรับ 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...คำแนะนำ: คุณสามารถใช้การกระทำที่เป็นสนิม "ขยายแมโครซ้ำ" การกระทำเพื่อดูรหัสที่สร้างขึ้น
แมโครรองรับทุกประเภทที่สามารถแปลงสภาพเป็นประเภท bitfield พื้นฐานได้ สิ่งนี้สามารถเป็น enums เช่นในตัวอย่างต่อไปนี้หรือโครงสร้างอื่น ๆ
สามารถระบุค่าการแปลงและค่าเริ่มต้นด้วยพารามิเตอร์ #[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 เช่นในตัวอย่างต่อไปนี้ (จาก endian-num ) หรือโครงสร้างอื่น ๆ ที่สามารถแปลงเป็นและจากประเภท bitfield หลัก
การแสดงและฟังก์ชั่นการแปลงสามารถระบุได้ด้วยพารามิเตอร์ #[bitfield] ต่อไปนี้:
repr ระบุการแสดงของ Bitfield ในหน่วยความจำfrom เพื่อระบุฟังก์ชั่นการแปลงจาก repr ไปยังประเภทจำนวนเต็มของ bitfieldinto ระบุฟังก์ชั่นการแปลงจากประเภทจำนวนเต็มของ bitfield เพื่อ reprตัวอย่างนี้มีคำสั่งไบต์น้อยกว่าแม้ในเครื่องจักรขนาดใหญ่-เอดิน:
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 คล้ายกับที่สร้างขึ้นสำหรับ structs ปกติโดย #[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 ที่สะท้อนการใช้งาน fmt::Debug เริ่มต้นโดยอัตโนมัติโดยผ่านอาร์กิวเมนต์ Extra defmt การใช้งานนี้ต้องใช้ลัง DEFMT ที่มีอยู่เป็น defmt และมีกฎและคำเตือนเช่นเดียวกับ #[derive(defmt::Format)]
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , defmt = true ) ]
struct DefmtExample {
data : u64
}new ตามเงื่อนไข / Clone / Debug / Default / defmt::Format แทนที่จะเป็นบูลีนคุณสามารถระบุแอตทริบิวต์ cfg(...) สำหรับ new , clone , debug , default และ defmt :
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , debug = cfg ( test ) , default = cfg ( feature = "foo" ) ) ]
struct CustomDebug {
data : u64
}