diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..99ab12a --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/Cargo.lock +macros/target +macros/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml old mode 100644 new mode 100755 index 7abf24f..6b9280a --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "binf" -version = "0.2.3" +version = "1.0.0-rc1" edition = "2021" license = "MIT" 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/" [dependencies] +binf_macros = { path = "macros" } diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b3f95e2..adc79e0 --- a/README.md +++ b/README.md @@ -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 \ No newline at end of file +i suggest looking at tests.rs for examples diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..b81251c --- /dev/null +++ b/macros/Cargo.toml @@ -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 "] +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"] } diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..2677365 --- /dev/null +++ b/macros/src/lib.rs @@ -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::>(), + _ => 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 + }, + } +} diff --git a/src/flag_utils.rs b/src/flag_utils.rs deleted file mode 100644 index a5d0bad..0000000 --- a/src/flag_utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub fn get_flags(flags: i32) -> Vec { - let mut fvec: Vec = Vec::new(); - let mut rest = flags; - while rest != 0 { - fvec.push((rest % 2) != 0); - rest = rest / 2; - } - - let mut tvec: Vec = Vec::new(); - for (i, value) in fvec.iter().enumerate() { - if *value { - tvec.push(i as i32); - } - } - tvec -} - -pub fn vec_to_flag(vec: Vec) -> u128 { - let mut flag = 0; - for (i, &val) in vec.iter().enumerate() { - if val { - flag |= 1 << i; - } - } - flag -} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs old mode 100644 new mode 100755 index 2c32610..23e288c --- a/src/lib.rs +++ b/src/lib.rs @@ -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 mod macros; +pub use binf_macros::*; #[cfg(test)] -mod test; +mod tests; -///struct that holds data for a flag and useful functions -pub struct Flag { - value: u128, +/// A trait for types that can be used as bit flags. +pub trait BitFlag { + 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 { - /// set a flag value - pub fn set_flag(&mut self, flag: u128, value: bool) { +/// implementation for u8. u8 means it can store 8 flags 1 flag per bit. +impl BitFlag for u8 { + type T = [bool; 8]; + fn set_flag(&mut self, position: u8, value: bool) { if value { - self.value |= 1 << flag; + *self |= 1 << position; } else { - self.value &= !(1 << flag); + *self &= !(1 << position); } } - /// get a value of flag - pub fn get_flag(&self, flag: u32) -> bool { - (self.value & (1 << flag)) != 0 + fn get_flag(&self, position: u8) -> bool { + (*self & (1 << position)) != 0 } - /// returns a vector with all enabled flags - pub fn get_all_flags(&self) -> Vec { - let mut ret = vec![]; - for i in 0..128 { - ret.push(&self.value & (1 << i) != 0); + fn flags(&self) -> Self::T { + let mut flags = [false; 8]; + for i in 0..8 { + flags[i] = self.get_flag(i as u8); } - for i in (0..ret.len()).rev() { - if ret[i] { - ret.truncate(i + 1); + flags + } + + fn set_flags(&mut self, flags: &[bool]) { + for (i, v) in flags.into_iter().enumerate() { + if i >= 8 { break; } + self.set_flag(i as u8, *v); + } + } +} + +impl BitFlag for u16 { + type T = [bool; 16]; + 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; 16]; + for i in 0..16 { + 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 >= 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); } - ret - } - - - - /// creates Flag from int - pub fn new_from_value(value: u128) -> Self { - Self {value} - } - - /// returns flags value - pub fn get(&self) -> u128 { - self.value - } - - /// sets internal value to this - pub fn set_value(&mut self, value: u128) { - self.value = value; - } - - /// initializes a Flag with 0, use flag_new! macro to create with flags instead - pub fn new() -> Self { - Self { value: 0, } } } diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index c69912b..0000000 --- a/src/macros.rs +++ /dev/null @@ -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 - } - } -} diff --git a/src/test.rs b/src/test.rs deleted file mode 100644 index af98a1c..0000000 --- a/src/test.rs +++ /dev/null @@ -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()); -} \ No newline at end of file diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..031fe57 --- /dev/null +++ b/src/tests.rs @@ -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); +}