big updates v1 rc1
This commit is contained in:
parent
503f52b60a
commit
48b1ffeb99
11 changed files with 387 additions and 101 deletions
4
.gitignore
vendored
Executable file
4
.gitignore
vendored
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
||||||
|
macros/target
|
||||||
|
macros/Cargo.lock
|
3
Cargo.toml
Normal file → Executable file
3
Cargo.toml
Normal file → Executable file
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "binf"
|
name = "binf"
|
||||||
version = "0.2.3"
|
version = "1.0.0-rc1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "A crate that adds utilities for dealing with binary flags"
|
description = "A crate that adds utilities for dealing with binary flags"
|
||||||
|
@ -10,3 +10,4 @@ repository = "https://github.com/D0A1V2I3D/binf"
|
||||||
documentation = "https://docs.rs/binf/"
|
documentation = "https://docs.rs/binf/"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
binf_macros = { path = "macros" }
|
||||||
|
|
0
LICENSE
Normal file → Executable file
0
LICENSE
Normal file → Executable file
5
README.md
Normal file → Executable file
5
README.md
Normal file → Executable file
|
@ -1,3 +1,4 @@
|
||||||
documentation and better readme coming soon
|
# binf, Binary Flags
|
||||||
|
This crate aims to make working with binary/bit flags easier. It also provides a macro to add similar functionality to zig's packed structs with boolean fields.
|
||||||
|
|
||||||
any contribution (bug report, issue, feature request, pull request) is welcome
|
i suggest looking at tests.rs for examples
|
||||||
|
|
18
macros/Cargo.toml
Normal file
18
macros/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "binf_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
description = "A crate that adds utilities for dealing with binary flags"
|
||||||
|
authors = ["DavidOnTop <me@davidon.top>"]
|
||||||
|
readme = "../README.md"
|
||||||
|
repository = "https://github.com/D0A1V2I3D/binf"
|
||||||
|
documentation = "https://docs.rs/binf_macros/"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0.68"
|
||||||
|
quote = "1.0.33"
|
||||||
|
syn = { version = "2.0.38", features = ["full"] }
|
122
macros/src/lib.rs
Normal file
122
macros/src/lib.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn bitflag(_attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
let structdef: syn::ItemStruct = syn::parse_macro_input!(input as syn::ItemStruct);
|
||||||
|
// check if the struct has correct shape
|
||||||
|
if !check_struct(&structdef) {
|
||||||
|
panic!("struct has incorrect shape");
|
||||||
|
}
|
||||||
|
let structname = structdef.ident;
|
||||||
|
let structfields = match &structdef.fields {
|
||||||
|
syn::Fields::Named(f) => f.named.iter().map(|f| f.ident.clone().unwrap()).collect::<Vec<_>>(),
|
||||||
|
_ => panic!("struct has incorrect shape"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let vis = structdef.vis.clone();
|
||||||
|
let attrs = structdef.attrs.clone();
|
||||||
|
|
||||||
|
// a unsigned int type of a size larger then the number of fields in the struct
|
||||||
|
let u_type = {
|
||||||
|
let fields_len = structfields.len();
|
||||||
|
match fields_len {
|
||||||
|
0..=8 => quote::quote! { u8 },
|
||||||
|
9..=16 => quote::quote! { u16 },
|
||||||
|
17..=32 => quote::quote! { u32 },
|
||||||
|
33..=64 => quote::quote! { u64 },
|
||||||
|
65..=128 => quote::quote! { u128 },
|
||||||
|
_ => panic!("struct has too many fields"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let newstruct: syn::ItemStruct = syn::parse_quote! {
|
||||||
|
#(#attrs)*
|
||||||
|
#vis struct #structname {
|
||||||
|
value: #u_type
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// make functions for each field in the struct
|
||||||
|
let mut functions = Vec::new();
|
||||||
|
for (i, field) in structfields.iter().enumerate() {
|
||||||
|
let field = field.clone();
|
||||||
|
let i = i as u8;
|
||||||
|
let set_ident = syn::Ident::new(&format!("set_{}", field), field.span());
|
||||||
|
functions.push(quote::quote! {
|
||||||
|
pub fn #field(&self) -> bool {
|
||||||
|
self.value.get_flag(#i)
|
||||||
|
}
|
||||||
|
pub fn #set_ident(&mut self, value: bool) {
|
||||||
|
self.value.set_flag(#i, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let impls = quote::quote! {
|
||||||
|
impl #structname {
|
||||||
|
#(#functions)*
|
||||||
|
|
||||||
|
pub fn new(val: #u_type) -> Self {
|
||||||
|
Self {
|
||||||
|
value: val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> #u_type {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let deref_impl = quote::quote! {
|
||||||
|
impl std::ops::Deref for #structname {
|
||||||
|
type Target = #u_type;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let deref_mut_impl = quote::quote! {
|
||||||
|
impl std::ops::DerefMut for #structname {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
quote::quote! {
|
||||||
|
#newstruct
|
||||||
|
#impls
|
||||||
|
#deref_impl
|
||||||
|
#deref_mut_impl
|
||||||
|
}.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns true if the struct has a correct shape
|
||||||
|
fn check_struct(input: &syn::ItemStruct) -> bool {
|
||||||
|
if input.generics.lt_token.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if input.generics.gt_token.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if input.generics.where_clause.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !input.generics.params.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &input.fields {
|
||||||
|
syn::Fields::Unnamed(_) => false,
|
||||||
|
syn::Fields::Unit => false,
|
||||||
|
syn::Fields::Named(f) => {
|
||||||
|
if f.named.len() > 128 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,26 +0,0 @@
|
||||||
pub fn get_flags(flags: i32) -> Vec<i32> {
|
|
||||||
let mut fvec: Vec<bool> = Vec::new();
|
|
||||||
let mut rest = flags;
|
|
||||||
while rest != 0 {
|
|
||||||
fvec.push((rest % 2) != 0);
|
|
||||||
rest = rest / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut tvec: Vec<i32> = Vec::new();
|
|
||||||
for (i, value) in fvec.iter().enumerate() {
|
|
||||||
if *value {
|
|
||||||
tvec.push(i as i32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tvec
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn vec_to_flag(vec: Vec<bool>) -> u128 {
|
|
||||||
let mut flag = 0;
|
|
||||||
for (i, &val) in vec.iter().enumerate() {
|
|
||||||
if val {
|
|
||||||
flag |= 1 << i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
flag
|
|
||||||
}
|
|
209
src/lib.rs
Normal file → Executable file
209
src/lib.rs
Normal file → Executable file
|
@ -1,66 +1,187 @@
|
||||||
/*!
|
/*!
|
||||||
TODO documentation
|
# binf, Binary flags
|
||||||
|
This crate aims to make working with binary/bit flags easier. It also provides a macro to add similar functionality to zig's packed structs with boolean fields.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod flag_utils;
|
pub use binf_macros::*;
|
||||||
pub mod macros;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod tests;
|
||||||
|
|
||||||
///struct that holds data for a flag and useful functions
|
/// A trait for types that can be used as bit flags.
|
||||||
pub struct Flag {
|
pub trait BitFlag {
|
||||||
value: u128,
|
type T;
|
||||||
|
/// Sets the flag at the given position to the given value.
|
||||||
|
/// I don't know what happens if the position is larger than the number of bits in the type.
|
||||||
|
fn set_flag(&mut self, position: u8, value: bool);
|
||||||
|
/// Returns the value of the flag at the given position.
|
||||||
|
/// I don't know what happens if the position is larger than the number of bits in the type.
|
||||||
|
fn get_flag(&self, position: u8) -> bool;
|
||||||
|
/// Returns an array of bools representing the flags.
|
||||||
|
/// The first element in the array is the flag at position 0.
|
||||||
|
fn flags(&self) -> Self::T;
|
||||||
|
/// Sets the flags to the given values.
|
||||||
|
/// flags can be any size, but if it is larger than the number of bits in the type only the first bits will be used.
|
||||||
|
/// So if this is u8 flags should be 8 or less any more are ignored.
|
||||||
|
fn set_flags(&mut self, flags: &[bool]);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flag {
|
/// implementation for u8. u8 means it can store 8 flags 1 flag per bit.
|
||||||
/// set a flag value
|
impl BitFlag for u8 {
|
||||||
pub fn set_flag(&mut self, flag: u128, value: bool) {
|
type T = [bool; 8];
|
||||||
|
fn set_flag(&mut self, position: u8, value: bool) {
|
||||||
if value {
|
if value {
|
||||||
self.value |= 1 << flag;
|
*self |= 1 << position;
|
||||||
} else {
|
} else {
|
||||||
self.value &= !(1 << flag);
|
*self &= !(1 << position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// get a value of flag
|
fn get_flag(&self, position: u8) -> bool {
|
||||||
pub fn get_flag(&self, flag: u32) -> bool {
|
(*self & (1 << position)) != 0
|
||||||
(self.value & (1 << flag)) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a vector with all enabled flags
|
fn flags(&self) -> Self::T {
|
||||||
pub fn get_all_flags(&self) -> Vec<bool> {
|
let mut flags = [false; 8];
|
||||||
let mut ret = vec![];
|
for i in 0..8 {
|
||||||
for i in 0..128 {
|
flags[i] = self.get_flag(i as u8);
|
||||||
ret.push(&self.value & (1 << i) != 0);
|
|
||||||
}
|
}
|
||||||
for i in (0..ret.len()).rev() {
|
flags
|
||||||
if ret[i] {
|
}
|
||||||
ret.truncate(i + 1);
|
|
||||||
|
fn set_flags(&mut self, flags: &[bool]) {
|
||||||
|
for (i, v) in flags.into_iter().enumerate() {
|
||||||
|
if i >= 8 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
self.set_flag(i as u8, *v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BitFlag for u16 {
|
||||||
|
type T = [bool; 16];
|
||||||
/// creates Flag from int
|
fn set_flag(&mut self, position: u8, value: bool) {
|
||||||
pub fn new_from_value(value: u128) -> Self {
|
if value {
|
||||||
Self {value}
|
*self |= 1 << position;
|
||||||
}
|
} else {
|
||||||
|
*self &= !(1 << position);
|
||||||
/// returns flags value
|
}
|
||||||
pub fn get(&self) -> u128 {
|
}
|
||||||
self.value
|
|
||||||
}
|
fn get_flag(&self, position: u8) -> bool {
|
||||||
|
(*self & (1 << position)) != 0
|
||||||
/// sets internal value to this
|
}
|
||||||
pub fn set_value(&mut self, value: u128) {
|
|
||||||
self.value = value;
|
fn flags(&self) -> Self::T {
|
||||||
}
|
let mut flags = [false; 16];
|
||||||
|
for i in 0..16 {
|
||||||
/// initializes a Flag with 0, use flag_new! macro to create with flags instead
|
flags[i] = self.get_flag(i as u8);
|
||||||
pub fn new() -> Self {
|
}
|
||||||
Self { value: 0, }
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags(&mut self, flags: &[bool]) {
|
||||||
|
for (i, v) in flags.into_iter().enumerate() {
|
||||||
|
if i >= 16 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.set_flag(i as u8, *v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitFlag for u32 {
|
||||||
|
type T = [bool; 32];
|
||||||
|
fn set_flag(&mut self, position: u8, value: bool) {
|
||||||
|
if value {
|
||||||
|
*self |= 1 << position;
|
||||||
|
} else {
|
||||||
|
*self &= !(1 << position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_flag(&self, position: u8) -> bool {
|
||||||
|
(*self & (1 << position)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> Self::T {
|
||||||
|
let mut flags = [false; 32];
|
||||||
|
for i in 0..32 {
|
||||||
|
flags[i] = self.get_flag(i as u8);
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags(&mut self, flags: &[bool]) {
|
||||||
|
for (i, v) in flags.into_iter().enumerate() {
|
||||||
|
if i >= 32 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.set_flag(i as u8, *v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitFlag for u64 {
|
||||||
|
type T = [bool; 64];
|
||||||
|
fn set_flag(&mut self, position: u8, value: bool) {
|
||||||
|
if value {
|
||||||
|
*self |= 1 << position;
|
||||||
|
} else {
|
||||||
|
*self &= !(1 << position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_flag(&self, position: u8) -> bool {
|
||||||
|
(*self & (1 << position)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> Self::T {
|
||||||
|
let mut flags = [false; 64];
|
||||||
|
for i in 0..64 {
|
||||||
|
flags[i] = self.get_flag(i as u8);
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags(&mut self, flags: &[bool]) {
|
||||||
|
for (i, v) in flags.into_iter().enumerate() {
|
||||||
|
if i >= 64 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.set_flag(i as u8, *v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitFlag for u128 {
|
||||||
|
type T = [bool; 128];
|
||||||
|
fn set_flag(&mut self, position: u8, value: bool) {
|
||||||
|
if value {
|
||||||
|
*self |= 1 << position;
|
||||||
|
} else {
|
||||||
|
*self &= !(1 << position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_flag(&self, position: u8) -> bool {
|
||||||
|
(*self & (1 << position)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flags(&self) -> Self::T {
|
||||||
|
let mut flags = [false; 128];
|
||||||
|
for i in 0..128 {
|
||||||
|
flags[i] = self.get_flag(i as u8);
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags(&mut self, flags: &[bool]) {
|
||||||
|
for (i, v) in flags.into_iter().enumerate() {
|
||||||
|
if i >= 128 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
self.set_flag(i as u8, *v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
//#[macro_use]
|
|
||||||
/// macro that creates new Flag
|
|
||||||
/// for example: flag_new![3, 7] makes a flag that has those flags enabled (136)
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! flag_new {
|
|
||||||
( $($f:expr),* ) => {
|
|
||||||
{
|
|
||||||
let mut tflag = crate::Flag::new();
|
|
||||||
$(
|
|
||||||
tflag.set_flag($f, true);
|
|
||||||
)*
|
|
||||||
tflag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
11
src/test.rs
11
src/test.rs
|
@ -1,11 +0,0 @@
|
||||||
use crate::{flag_new, Flag, flag_utils:: vec_to_flag};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn all() {
|
|
||||||
let flag = flag_new![3, 7];
|
|
||||||
assert_eq!(flag.get(), 136);
|
|
||||||
assert_eq!(flag.get_flag(3), true);
|
|
||||||
assert_eq!(flag.get_flag(4), false);
|
|
||||||
assert_eq!(flag.get_all_flags(), vec![false, false, false, true, false, false, false, true]);
|
|
||||||
assert_eq!(vec_to_flag(vec![false, false, false, true, false, false, false, true]), flag.get());
|
|
||||||
}
|
|
71
src/tests.rs
Normal file
71
src/tests.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[bitflag]
|
||||||
|
pub struct Test {
|
||||||
|
a: bool,
|
||||||
|
b: bool,
|
||||||
|
c: bool,
|
||||||
|
d: bool,
|
||||||
|
e: bool,
|
||||||
|
f: bool,
|
||||||
|
g: bool,
|
||||||
|
h: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_test() {
|
||||||
|
let mut test = Test::new(0b10101010);
|
||||||
|
assert_eq!(test.a(), false);
|
||||||
|
assert_eq!(test.b(), true);
|
||||||
|
assert_eq!(test.c(), false);
|
||||||
|
assert_eq!(test.d(), true);
|
||||||
|
assert_eq!(test.e(), false);
|
||||||
|
assert_eq!(test.f(), true);
|
||||||
|
assert_eq!(test.g(), false);
|
||||||
|
assert_eq!(test.h(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_test_set() {
|
||||||
|
let mut test = Test::new(0b10101010);
|
||||||
|
test.set_a(true);
|
||||||
|
test.set_b(false);
|
||||||
|
test.set_c(true);
|
||||||
|
test.set_d(false);
|
||||||
|
test.set_e(true);
|
||||||
|
test.set_f(false);
|
||||||
|
test.set_g(true);
|
||||||
|
test.set_h(false);
|
||||||
|
assert_eq!(test.value(), 0b01010101);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_test_iter() {
|
||||||
|
let test = Test::new(0b10101010);
|
||||||
|
let flags = test.flags();
|
||||||
|
let mut iter = flags.iter();
|
||||||
|
assert_eq!(iter.next(), Some(false).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(true).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(false).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(true).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(false).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(true).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(false).as_ref());
|
||||||
|
assert_eq!(iter.next(), Some(true).as_ref());
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn u64_test() {
|
||||||
|
let mut flags = 0b10101010u64;
|
||||||
|
assert_eq!(flags.get_flag(0), false);
|
||||||
|
assert_eq!(flags.get_flag(1), true);
|
||||||
|
assert_eq!(flags.get_flag(2), false);
|
||||||
|
assert_eq!(flags.get_flag(3), true);
|
||||||
|
assert_eq!(flags.get_flag(4), false);
|
||||||
|
assert_eq!(flags.get_flag(5), true);
|
||||||
|
assert_eq!(flags.get_flag(6), false);
|
||||||
|
assert_eq!(flags.get_flag(7), true);
|
||||||
|
flags.set_flag(0, true);
|
||||||
|
assert_eq!(flags, 0b10101011);
|
||||||
|
}
|
Loading…
Reference in a new issue