Verfahrensmakro für Bitfields, mit denen Bitfields als Strukturen angegeben werden können. Da diese Bibliothek ein prozedurales Makro bietet, verfügt sie über keine Laufzeitabhängigkeiten und funktioniert für no-std -Umgebungen.
Default , fmt::Debug oder defmt::Format Fügen Sie dies Ihrer Cargo.toml hinzu.
[ dependencies ]
bitfield-struct = " 0.10 " Beginnen wir mit einem einfachen Beispiel. Angenommen, wir möchten mehrere Daten in einem einzigen Byte speichern, wie unten gezeigt:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| P | Ebene | S | Art | ||||
Diese Kiste erzeugt einen schönen Wrapper -Typ, der es einfach macht, dies zu tun:
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 ( ) ) ; Darüber hinaus hat diese Kiste einige nützliche Funktionen, die hier ausführlicher angezeigt werden.
Das folgende Beispiel zeigt, wie Attribute übertragen werden und wie signierte Ganzzahlen, Polsterung und benutzerdefinierte Typen behandelt werden.
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 ) ;Das Makro generiert drei Accessor -Funktionen für jedes Feld. Jeder Accessor erbt auch die Dokumentation seines Feldes.
Die Unterschriften für int sind:
// 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...Tipp: Sie können die Rost-Analyzer "Makro erweitern" -Ataktion verwenden, um den generierten Code anzuzeigen.
Das Makro unterstützt alle Typen, die in den zugrunde liegenden Bitfield -Typ konvertierbar sind. Dies kann wie im folgenden Beispiel oder in einer anderen Strukturen wie im folgenden Beispiel sein.
Die Konvertierungs- und Standardwerte können mit den folgenden #[bits] -Parametern angegeben werden:
from : Funktion, die von Rohbits in den benutzerdefinierten Typ konvertiert wird, standardmäßig in <ty>::from_bitsinto : Funktion konvertieren vom benutzerdefinierten Typ in Rohbits, Standards in <ty>::into_bitsdefault : benutzerdefinierter Ausdruck, Standardeinstellungen zum Aufrufen von <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 ,
} Das optionale order -Makroargument bestimmt das Layout der Bits, wobei der Standard -LSB (am wenigsten signifikant) zuerst ist:
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 ) ;Das Makro erzeugt die umgekehrte Reihenfolge, wenn MSB (das bedeutendste Bit) angegeben ist:
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 ) ; Das Makro unterstützt benutzerdefinierte Typen für die Darstellung der Bitfield -Struktur. Dies kann ein endianerdefinierender Typ sein, wie in den folgenden Beispielen (von endian-num ) oder einer anderen Struktur, die in und vom Hauptbitfield-Typ konvertiert werden können.
Die Darstellung und ihre Konvertierungsfunktionen können mit den folgenden #[bitfield] -Parametern angegeben werden:
repr spezifiziert die Darstellung des Bitfield im Speicherfrom eine Konvertierungsfunktion von Repr an den Ganzzahltyp des Bitfield anzugebeninto um eine Konvertierungsfunktion aus dem Ganzzahltyp des Bitfield zu geben, um zu reprierenDieses Beispiel hat auch auf Big-Endian-Maschinen eine Byte-Byte-Byte-Bestellung:
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 ] ) ;Dieses Beispiel hat auch auf Little-Endian-Maschinen eine Big-Endian-Byte-Bestellung:
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 Standardmäßig leitet dieses Makro Clone und Copy ab. Sie können dies mit dem zusätzlichen clone deaktivieren, wenn die Semantik des Klonen Ihres Typs dies erfordert (z. B. der Typ enthält einen Zeiger auf Besitzdaten, die ebenfalls kloniert werden müssen). In diesem Fall können Sie Ihre eigenen Implementierungen für Clone und Copy bereitstellen.
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 Standardmäßig generiert es auch geeignete fmt::Debug und Default , ähnlich den für normalen Strukturen erstellten Strukturen von #[derive(Debug, Default)] . Sie können dies mit dem zusätzlichen debug und default deaktivieren.
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 Dieses Makro kann automatisch ein defmt::Format implementieren, das die Standard -Implementierung fmt::Debug widerspiegelt, indem das zusätzliche defmt -Argument bestanden wird. Diese Implementierung erfordert, dass die DEFMT -Kiste als defmt verfügbar ist, und verfügt über die gleichen Regeln und Vorbehalte wie #[derive(defmt::Format)] .
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , defmt = true ) ]
struct DefmtExample {
data : u64
}new / Clone / Debug / Default / defmt::Format Anstelle von Booleschen können Sie cfg(...) -attribute für new , clone , debug , default und defmt angeben:
use bitfield_struct :: bitfield ;
# [ bitfield ( u64 , debug = cfg ( test ) , default = cfg ( feature = "foo" ) ) ]
struct CustomDebug {
data : u64
}