4 releases
0.3.0 | Apr 3, 2022 |
---|---|
0.1.2 | Apr 7, 2018 |
0.1.1 | Apr 6, 2018 |
0.1.0 | Apr 6, 2018 |
#43 in #compiler-plugin
Used in bitrange
11KB
262 lines
bitrange
getting started
To get started, add this to your Cargo.toml
:
[dependencies]
bitrange = { git = "https://github.com/trangar/bitrange" }
bitrange_plugin = { git = "https://github.com/trangar/bitrange" }
Then add the following code to your main.rs
or lib.rs
#[macro_use]
extern crate bitrange;
#[macro_use]
extern crate bitrange_plugin;
bitrange needs a nightly version of the compiler because it uses the feature proc_macro
which is not stabilized yet
quoted strings
Because of an openstanding RFC 2320, stringify!
can not be used in a proc-macro attribute. The code generated by this crate contains:
#[derive(Bitrange)]
#[BitrangeMask = $format]
#[BitrangeSize = $struct_size_string]
where $format
and $struct_size_string
are from the bitrange
macro.
In this instance we'd like to use stringify!($format)
and stringify($struct_size_string)
, however this does not work until RFC 2320 lands.
For this reason, the format needs to be quoted, and we need to annotate the type twice. e.g.
bitrange! {
Test: u8, "u8",
"aaaa_bbbb",
a: first,
b: second
}
instead of the desired
bitrange! {
Test: u8,
aaaa_bbbb,
a: first,
b: second
}
examples
Bitrange helps you map bit fields to proper getters and setters.
Say you're trying to make an IP parser. The rfc will give you this:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
If you wanted to parse this in Rust, you'd have to make the following mapping:
- the first 4 bits are mapped to
version
- The next 4 bits are mapped to
ihl
- The next 8 bits are mapped to
type_of_service
- The last 16 bits are mapped to
total_length
With bitrange, you can easily map bytes to fields. To parse this part of the protocol, simply write
#![feature(proc_macro)]
#[macro_use]
extern crate bitrange;
bitrange! {
IpHeader: u32, "u32", // struct name
"aaaa_bbbb_cccccccc_dddddddddddddddd", // pattern that we're matching against
a: version, // map character 'a' to field 'version'
b: ihl, // map character 'b' to field 'ihl'
c: type_of_service, // map character 'c' to field 'type_of_service'
d: total_length // map character 'd' to field 'total_length'
}
fn main() {
let header = IpHeader::from(0b0001_0010_00000011_0000000000000100);
assert_eq!(header.version(), 0b0001);
assert_eq!(header.ihl(), 0b0010);
assert_eq!(header.type_of_service(), 0b0011);
assert_eq!(header.total_length(), 0b0100);
}
If you wanted to make a field mutable, simply add a second ident to the field mapping, e.g.:
bitrange! {
IpHeader: u32, "u32", // struct name
"aaaa_bbbb_cccccccc_dddddddddddddddd", // pattern that we're matching against
a: version set_version, // map character 'a' to field 'version', and create setter 'set_version'
b: ihl, // map character 'b' to field 'ihl'
c: type_of_service, // map character 'c' to field 'type_of_service'
d: total_length // map character 'd' to field 'total_length'
}
fn main() {
let mut header = IpHeader::from(0b0001_0010_00000011_0000000000000100);
assert_eq!(header.version(), 0b0001);
assert_eq!(header.ihl(), 0b0010);
assert_eq!(header.type_of_service(), 0b0011);
assert_eq!(header.total_length(), 0b0100);
header.set_version(0b0100);
assert_eq!(header.version(), 0b0100);
}
In addition, you can define constraints to bits that have to always be 0 or 1
bitrange! {
Test: u8, "u8",
// from left (highest) to right (lowest)
// first 3 bits are mapped to a
// the next bit is always 1
// the next bit is always 0
// the last 3 bits are mapped to b
"aaa1_0bbb",
a: first,
b: second
}
fn main() {
// This panics at runtime
// Because the 4th highest bit should always be 1
// Test::from(0);
// The enum also implements Default, so you can simply do:
let _test = Test::default();
// And this will have value 0b0001_0000
}
Compile-time checks
bitrange will also check fields at compile time to see if they exist
bitrange! {
Test: u8, "u8",
"aaa1_0bbb",
a: first,
b: second,
c: third // this will panic with
// Token 'c' is not found in pattern "aaa10bbb"
}
However, this does not work for unmapped fields
bitrange! {
Test: u8, "u8",
"aaa1_0bbb",
a: first,
// b is not mapped
// Does not give a warning
}
Dependencies
~1.5MB
~37K SLoC