Create helper crate with tools for working with bitfields

This commit is contained in:
BlueTheDuck 2024-11-02 20:06:49 -03:00
parent 1428872322
commit 1f424c06f1
No known key found for this signature in database
GPG Key ID: 256464D859A672F3
5 changed files with 93 additions and 1 deletions

View File

@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["nds-sys", "nds-proc-macros", "nds-rt", "nds-rs", "picolibc"]
members = ["bitfield-tools", "nds-sys", "nds-proc-macros", "nds-rt", "nds-rs", "picolibc"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -0,0 +1,7 @@
[package]
name = "bitfield-tools"
version = "0.1.0"
edition = "2021"
[dependencies]

View File

@ -0,0 +1,44 @@
#[macro_export]
/// Macro to create bitfield getters and setters
///
/// # Usage
/// ```rust
/// #[derive(Clone, Copy)]
/// struct Tile(u16)
/// impl Tile {
/// field!([index, with_index, set_index] with INDEX_MASK: u16 = 0b00000011_11111111);
/// field!([hflip, with_hflip, set_hflip] with HFLIP_MASK: u16 = 0b00000100_00000000);
/// field!([vflip, with_vflip, set_vflip] with VFLIP_MASK: u16 = 0b00001000_00000000);
/// field!([pal, with_pal, set_pal ] with PAL_MASK: u16 = 0b11110000_00000000);
/// }
/// ```
macro_rules! field {
(@getter $vis:vis $name:ident for MASK: $ty:ty = $mask:literal) => {
$vis const fn $name(Self(this): Self) -> $ty {
const OFFSET: u32 = ($mask as $ty).trailing_zeros();
(this & $mask) >> OFFSET
}
};
(@builder $vis:vis $name:ident for MASK: $ty:ty = $mask:literal) => {
$vis const fn $name(Self(this): Self, value: $ty) -> Self {
const OFFSET: u32 = ($mask as $ty).trailing_zeros();
let data = this & !($mask);
let value = (value << OFFSET) & $mask;
Self(data | value)
}
};
(@setter $vis:vis $name:ident for MASK: $ty:ty = $mask:literal) => {
$vis fn $name(Self(this): &mut Self, value: $ty) {
const OFFSET: u32 = ($mask as $ty).trailing_zeros();
let data = *this & !($mask);
let value = (value << OFFSET) & $mask;
*this = data | value;
}
};
($vis:vis [$getter:ident, $builder:ident, $setter:ident] for MASK: $ty:ty = $mask:literal) => {
field!(@getter $vis $getter for MASK: $ty = $mask);
field!(@builder $vis $builder for MASK: $ty = $mask);
field!(@setter $vis $setter for MASK: $ty = $mask);
}
}

View File

@ -0,0 +1,6 @@
#![no_std]
#![allow(warnings)]
mod field;
mod masks;
pub use masks::{s, short, w, word};

View File

@ -0,0 +1,35 @@
macro_rules! impl_const_mask_funcs {
($t:ty) => {
use core::ops::RangeInclusive;
const WIDTH: usize = size_of::<$t>() * 8;
pub const fn bit(n: $t) -> $t {
debug_assert!(n as usize <= WIDTH);
match (1 as $t).checked_shl(n as _) {
Some(m) => m,
None => 0,
}
}
pub const fn bits(r: RangeInclusive<$t>) -> $t {
let end = *r.end();
let start = *r.start();
debug_assert!(start <= end);
(bit(end + 1).wrapping_sub(1)) & !(bit(start).wrapping_sub(1))
}
pub const fn extract(value: $t, r: RangeInclusive<$t>) -> $t {
let start = *r.start();
(value & bits(r)) >> start
}
};
}
pub mod word {
impl_const_mask_funcs!(u32);
}
pub use word as w;
pub mod short {
impl_const_mask_funcs!(u16);
}
pub use short as s;