Процедурный макрос для BitFields, который позволяет определять битфилды в качестве структур. Поскольку эта библиотека обеспечивает процедурный макрос, у нее нет зависимостей времени выполнения и работает в средах no-std .
Default , fmt::Debug или defmt::Format Добавьте это в свой Cargo.toml :
[ dependencies ]
bitfield-struct = " 0.10 " Начнем с простого примера. Предположим, мы хотим сохранить несколько данных внутри одного байта, как показано ниже:
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| П | Уровень | С | Добрый | ||||
Этот ящик генерирует хороший тип обертки, который позволяет легко сделать это:
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 «развернуть макросмерки» для просмотра сгенерированного кода.
Макро поддерживает любые типы, которые конвертируются в базовый тип Bitfield. Это может быть перечисленным в следующем примере или любой другой структуре.
Значения преобразования и по умолчанию могут быть указаны со следующими параметрами #[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] :
repr указывает представление Bitfield в памятиfrom указания функции преобразования из PERP в целочисленный тип битфилдаinto указание функции преобразования из целочисленного типа Bitfield в репрессиюЭтот пример имеет малооносный байтовый порядок даже на биг-эндианских машинах:
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 , который отражает реализацию fmt::Debug передавая дополнительный аргумент 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
}