#位域 # #过程宏 # #调试 #默认值

无需std 位域结构体

位域的类似结构体的过程宏

28个版本

0.8.0 2024年6月20日
0.6.2 2024年5月23日
0.6.0 2024年2月16日
0.5.6 2023年12月10日
0.1.1 2021年12月28日

#98 in 数据结构

Download history 7552/week @ 2024-05-03 7353/week @ 2024-05-10 7867/week @ 2024-05-17 7720/week @ 2024-05-24 7860/week @ 2024-05-31 7447/week @ 2024-06-07 9587/week @ 2024-06-14 9690/week @ 2024-06-21 9264/week @ 2024-06-28 9534/week @ 2024-07-05 10744/week @ 2024-07-12 10428/week @ 2024-07-19 10602/week @ 2024-07-26 11629/week @ 2024-08-02 11912/week @ 2024-08-09 11165/week @ 2024-08-16

47,093 个月的下载量
65 个crate中使用 (35个直接使用)

MIT 许可证

44KB
836

位域结构体

Crate API

允许将位域指定为结构体的过程宏。因为这个库提供了一个过程宏,所以它没有运行时依赖项,并且适用于 no-std 环境。

  • 非常适合驱动程序/操作系统/嵌入式开发(定义硬件寄存器/结构)
  • 支持布尔标志、整数以及可转换为整数的自定义类型(结构体/枚举)
  • 生成最小化、纯、安全的Rust函数
  • 编译时检查类型和字段大小
  • Rust-analyzer/docrs友好(将文档传递给访问器函数)
  • 将字段偏移和大小作为常量导出(对const断言很有用)
  • 生成 Defaultfmt::Debugdefmt::Format 特性
  • 自定义内部表示(字节序)

用法

将此添加到您的 Cargo.toml

[dependencies]
bitfield-struct = "0.7"

基础

让我们从一个简单的例子开始。假设我们想在单个字节中存储多个数据,如下所示

7 6 5 4 3 2 1 0
P 级别 S 种类

此crate生成一个很好的包装类型,使得这样做变得容易

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());

特性

此外,此crate还有一些有用的特性,以下将详细介绍。

以下示例显示了如何传递属性以及如何处理有符号整数、填充和自定义类型。

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 的签名是

use std::fmt::{Debug, Formatter, Result};

// 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 with_int(self, value: u16) -> Self { todo!() }
    const fn int(&self) -> u16 { todo!() }
    fn set_int(&mut self, value: u16) { todo!() }

    // other field ...
}
// Also generates From<u64>, Into<u64>, Default, and Debug implementations...

提示:您可以使用rust-analyzer的“递归展开宏”操作来查看生成的代码。

自定义类型

此宏支持任何可以转换为底层位域类型的类型。这可以是以下示例中的枚举或其他任何结构体。

可以使用以下 #[bits] 参数来指定转换和默认值:

  • from:将原始位转换为自定义类型的函数,默认为 <ty>::from_bits
  • into:将自定义类型转换为原始位的函数,默认为 <ty>::into_bits
  • default:自定义表达式,默认为调用 <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);

自定义表示和字节序

该宏支持用于表示位域结构的自定义类型。这可以是一个字节序定义类型,如以下示例所示(来自 endian-num)或任何可以转换为和从主位域类型转换的任何其他结构。

可以使用以下 #[bitfield] 参数来指定表示及其转换函数:

  • repr 指定位域在内存中的表示
  • from 指定一个从表示到位域整型的转换函数
  • into 指定一个从位域整型到表示的转换函数

此示例即使在大端机器上也有小端字节序

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]);

自动实现特质

此宏自动创建一个合适的 fmt::DebugDefault 实现,类似于由 #[derive(Debug, Default)] 为普通结构创建的实现。您可以通过额外的 debugdefault 参数来禁用此功能。

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 参数自动实现一个 defmt::Format,该实现类似于通过传递额外的 defmt 参数传递的默认 fmt::Debug 实现。此实现需要 defmt crate 以 defmt 的形式可用,并具有与 #[derive(defmt::Format)] 相同的规则和注意事项。

use bitfield_struct::bitfield;

#[bitfield(u64, defmt = true)]
struct DefmtExample {
    data: u64
}

有条件地启用 Debug/Default/defmt::Format

您可以为 debugdefaultdefmt 指定 cfg(...) 属性,而不是布尔值。

use bitfield_struct::bitfield;

#[bitfield(u64, debug = cfg(test), default = cfg(feature = "foo"))]
struct CustomDebug {
    data: u64
}

依赖项

~265–710KB
~17K SLoC