6个版本
0.1.5 | 2024年1月21日 |
---|---|
0.1.4 | 2023年11月28日 |
0.1.3 | 2023年7月23日 |
0.1.2 | 2022年7月25日 |
97 在 编码 中排名
每月 1,787 次下载
在 3 个crate中使用(通过 maitake-sync)
125KB
1.5K SLoC
mycelium-bitfield
🍄 位字段实用工具,由Mycelium提供。
这是什么?
此库提供在Rust中定义结构化位字段的实用工具。它包含一组用于定义可以打包和从整数值解包的范围的类型,以及一个用于使用打包类型自动生成位字段类型的宏 bitfield!
。这些组件是模块化的:可以使用打包规范类型手动编写宏 bitfield!
会生成的所有代码。
这个crate最初是为在 Mycelium操作系统 中使用而实现的,尽管它也可以用于其他项目,并且不依赖于任何Mycelium特定的库。
注意
这是一个兴趣项目。我在业余时间做这个,供自己使用。我很高兴与更广泛的Rust社区分享,并且始终欢迎 贡献 和 错误报告。然而,请记住,我是在 玩乐 中构建这个库,如果它不再有趣……好吧,你明白了。
无论如何,请随意使用并享受这个crate,并根据你的意愿进行尽可能多的贡献!
与其他crate的比较
还有其他几个crate在Rust中实现了位字段或与位字段相关的实用工具。这些crate提供与 mycelium-bitfield
不同,但有时重叠的功能。特别是,目前我所知道的最直接可比的crate是 modular-bitfield
和 bitflags
库。
注意 该软件包存在主要是因为我认为编写自己的位域软件包很有趣,而不是因为现有的库有缺陷。我对于“有趣”可能有一些扭曲的认识...
特别是,
modular-bitfield
软件包可以执行与mycelium-bitfield
相似的大部分操作。然而,这两个库之间有一些差异,可能值得考虑。
-
bitflags
:该bitflags
软件包提供了一种声明性宏,用于生成表示一组位标志的结构化类型。bitflags
的bitflags!
宏与mycelium-bitfield
的bitfield!
宏之间的关键区别是,bitflags
软件包仅实现了位标志,而不是位域。无法使用bitflags
定义多位的结构化范围;只能设置和清除单个位的标志。然而,
bitflags
软件包被广泛使用,存在很长时间,相对简单且轻量级。如果您只需要一组布尔型、单位的标志,它是一个非常好的选择。但是,如果您需要mycelium-bitfield
在处理多位范围方面的附加功能,请注意它也可以执行大多数bitflags
软件包可以执行的操作。 -
modular-bitfield
:该modular-bitfield
软件包提供了一种过程宏,用于生成带类型的结构化位域。modular-bitfield
实现的功能与mycelium-bitfield
非常相似——这两个库可以做大部分相同的事情。主要区别在于,
modular-bitfield
使用过程宏属性实现,而mycelium-bitfield
的bitfield!
宏是一个声明性宏。在我看来,这并不是在大多数情况下更喜欢mycelium-bitfield
而不是modular-bitfield
的理由。我决定尝试使用声明性宏来编写整个软件包,因为我认为这将是一个有趣的挑战,而不是因为它更好(实际上,使用过程宏实现位域类型生成可能会更容易)。然而,如果用户需要出于某些原因减少或避免过程宏,可能需要考虑出于这个原因选择mycelium-bitfield
。与
modular-bitfield
相比,mycelium-bitfield
的主要区别在于,它还提供了包含打包规范类型的pack
模块。这些类型可以用于“手动”构建位域类型,在需要与宏生成的代码不同的行为时使用。modular-bitfield
仅提供过程宏,并且没有与此低级接口等效的功能。另一方面,
modular-bitfield
提供了更友好的验证,以确保用作位域一部分的类型实际上适合该位域。mycelium-bitfield
目前无法进行此类编译时检查,而是依赖于用户提供的类型实现FromBits
特质的正确性。
使用方法
本软件包的API由三个主要组件组成,即打包规范类型、bitfield! 宏
和FromBits
特质。
打包规范类型
pack
模块定义了一组类型,可用于打包和解包各种大小整数的范围,例如,Pack64
用于打包和解包从u64
值得到的范围。
这些打包规范类型具有const fn
构造函数,允许它们相互关联地定义。例如
use mycelium_bitfield::Pack64;
// Defines a packing spec for the least-significant 12 bits of a 64-bit value.
const LOW: Pack64 = Pack64::least_significant(12);
// Defines a packing spec for the next 8 more-significant bits after `LOW`.
const MID: Pack64 = LOW.next(8);
// Defines a packing spec for the next 4 more-significant bits after `MID`.
const HIGH: Pack64 = MID.next(4);
// Wrap an integer value to pack it using method calls.
let coffee = Pack64::pack_in(0)
// pack the 12 bits of `0xfee` at the range specified by `LOW`.
.pack(0xfee, &LOW)
// pack the 4 bits `0xc` at the range specified by `HIGH`.
.pack(0xc, &HIGH)
// pack `0xf` in the 8 bits specified by `MID`.
.pack(0xf, &MID)
// unwrap the packing value back into a `u64`.
.bits();
assert_eq!(coffee, 0xc0ffee); // i want c0ffee
pack
模块中的大多数函数都是const fn
,允许在const上下文中使用打包规范。
有关详细信息,请参阅pack
模块的模块级文档。
bitfield!
宏
bitfield!
宏允许声明性地定义结构化位域类型。该宏将生成使用pack
模块的打包规范API来表示位域类型的代码。
例如
mycelium_bitfield::bitfield! {
/// Bitfield types can have doc comments.
#[derive(Eq, PartialEq)] // ...and attributes
pub struct MyBitfield<u16> {
/// Generates a packing spec named `HELLO` for the first 6
/// least-significant bits.
pub const HELLO = 6;
// Fields with names starting with `_` can be used to mark bits as
// reserved.
const _RESERVED = 4;
/// Generates a packing spec named `WORLD` for the next 3 bits.
pub const WORLD = 3;
/// A boolean value will generate a packing spec for a single bit.
pub const FLAG: bool;
}
}
// Bitfield types can be cheaply constructed from a raw numeric
// representation:
let bitfield = MyBitfield::from_bits(0b10100_0011_0101);
// `get` methods can be used to unpack fields from a bitfield type:
assert_eq!(bitfield.get(MyBitfield::HELLO), 0b11_0101);
assert_eq!(bitfield.get(MyBitfield::WORLD), 0b0101);
// `with` methods can be used to pack bits into a bitfield type by
// value:
let bitfield2 = MyBitfield::new()
.with(MyBitfield::HELLO, 0b11_0101)
.with(MyBitfield::WORLD, 0b0101);
assert_eq!(bitfield, bitfield2);
// `set` methods can be used to mutate a bitfield type in place:
let mut bitfield3 = MyBitfield::new();
bitfield3
.set(MyBitfield::HELLO, 0b011_0101)
.set(MyBitfield::WORLD, 0b0101);
assert_eq!(bitfield, bitfield3);
有关宏的使用和生成的代码的详细信息,请参阅bitfield!
宏的文档。
FromBits
特质
可以为用户定义的类型实现FromBits
特质,这些类型可以用作bitfield!
生成的结构化位域类型的子字段。此特质可以手动为任何具有定义位表示的用户定义类型实现,或者可以使用enum_from_bits!
宏自动为枚举类型生成。
例如
use mycelium_bitfield::{bitfield, enum_from_bits, FromBits};
// An enum type can implement the `FromBits` trait if it has a
// `#[repr(uN)]` attribute.
#[repr(u8)]
#[derive(Debug, Eq, PartialEq)]
enum MyEnum {
Foo = 0b00,
Bar = 0b01,
Baz = 0b10,
}
impl FromBits<u32> for MyEnum {
// Two bits can represent all possible `MyEnum` values.
const BITS: u32 = 2;
type Error = &'static str;
fn try_from_bits(bits: u32) -> Result<Self, Self::Error> {
match bits as u8 {
bits if bits == Self::Foo as u8 => Ok(Self::Foo),
bits if bits == Self::Bar as u8 => Ok(Self::Bar),
bits if bits == Self::Baz as u8 => Ok(Self::Baz),
_ => Err("expected one of 0b00, 0b01, or 0b10"),
}
}
fn into_bits(self) -> u32 {
self as u8 as u32
}
}
// Alternatively, the `enum_from_bits!` macro can be used to
// automatically generate a `FromBits` implementation for an
// enum type:
enum_from_bits! {
#[derive(Debug, Eq, PartialEq)]
pub enum MyGeneratedEnum<u8> {
/// Isn't this cool?
Wow = 0b1001,
/// It sure is! :D
Whoa = 0b0110,
}
}
bitfield! {
pub struct TypedBitfield<u32> {
/// Use the first two bits to represent a typed `MyEnum` value.
const ENUM_VALUE: MyEnum;
/// Typed values and untyped raw bit fields can be used in the
/// same bitfield type.
pub const SOME_BITS = 6;
/// The `FromBits` trait is also implemented for `bool`, which
/// can be used to implement bitflags.
pub const FLAG_1: bool;
pub const FLAG_2: bool;
/// `FromBits` is also implemented by (signed and unsigned) integer
/// types. This will allow the next 8 bits to be treated as a `u8`.
pub const A_BYTE: u8;
/// We can also use the automatically generated enum:
pub const OTHER_ENUM: MyGeneratedEnum;
}
}
// Unpacking a typed value with `get` will return that value, or panic if
// the bit pattern is invalid:
let my_bitfield = TypedBitfield::from_bits(0b0010_0100_0011_0101_1001_1110);
assert_eq!(my_bitfield.get(TypedBitfield::ENUM_VALUE), MyEnum::Baz);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_1), true);
assert_eq!(my_bitfield.get(TypedBitfield::FLAG_2), false);
assert_eq!(my_bitfield.get(TypedBitfield::OTHER_ENUM), MyGeneratedEnum::Wow);
// The `try_get` method will return an error rather than panicking if an
// invalid bit pattern is encountered:
let invalid = TypedBitfield::from_bits(0b0011);
// There is no `MyEnum` variant for 0b11.
assert!(invalid.try_get(TypedBitfield::ENUM_VALUE).is_err());
请参阅FromBits
特性文档以了解如何为用户定义的类型实现FromBits
。