#![no_std]
#[doc(hidden)]
pub extern crate core as __core;
#[macro_export]
macro_rules! bitmask {
(
$(#[$st_attr: meta])* mask $st_name: ident : $T: tt where
$(#[$en_attr: meta])* flags $en_name: ident { $($token: tt)+ }
) => {
bitmask! {
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
[]
],
$($token)+
}
};
(
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$prev_attr: meta])* ]
],
#[$next_attr: meta] $($token: tt)*
) => {
bitmask! {
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
[ $(#[$prev_attr])* #[$next_attr] ]
],
$($token)*
}
};
(
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$next_attr: meta])* ]
],
$next_name: ident = $next_value: expr, $($token: tt)*
) => {
bitmask! {
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
meta: [ $(#[$next_attr])* ]
flag: $next_name = $next_value;
[]
],
$($token)*
}
};
(
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$next_attr: meta])* ]
],
$next_name: ident = $next_value: expr
) => {
bitmask! {
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
meta: [ $(#[$next_attr])* ]
flag: $next_name = $next_value;
[]
],
}
};
(
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)+
[]
],
) => {
#[repr($T)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[allow(dead_code)]
$(#[$en_attr])*
enum $en_name {
$(
$(#[$flag_attr])*
$flag_name = $flag_value
),+
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[allow(dead_code)]
$(#[$st_attr])*
struct $st_name {
mask: $T
}
bitmask!(@IMPL $st_name $T $en_name, {
$($flag_name = $flag_value),+
});
};
(
$(#[$st_attr: meta])* pub mask $st_name: ident : $T: tt where
$(#[$en_attr: meta])* flags $en_name: ident { $($token: tt)+ }
) => {
bitmask! {
pub
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
[]
],
$($token)+
}
};
(
pub
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$prev_attr: meta])* ]
],
#[$next_attr: meta] $($token: tt)*
) => {
bitmask! {
pub
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
[ $(#[$prev_attr])* #[$next_attr] ]
],
$($token)*
}
};
(
pub
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$next_attr: meta])* ]
],
$next_name: ident = $next_value: expr, $($token: tt)*
) => {
bitmask! {
pub
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
meta: [ $(#[$next_attr])* ]
flag: $next_name = $next_value;
[]
],
$($token)*
}
};
(
pub
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)*
[ $(#[$next_attr: meta])* ]
],
$next_name: ident = $next_value: expr
) => {
bitmask! {
pub
st_meta: [ $(#[$st_attr])* ],
st_name: $st_name,
mask_type: $T,
en_meta: [ $(#[$en_attr])* ],
en_name: $en_name,
flags: [
$(
meta: [ $(#[$flag_attr])* ]
flag: $flag_name = $flag_value;
)*
meta: [ $(#[$next_attr])* ]
flag: $next_name = $next_value;
[]
],
}
};
(
pub
st_meta: [ $(#[$st_attr: meta])* ],
st_name: $st_name: ident,
mask_type: $T: tt,
en_meta: [ $(#[$en_attr: meta])* ],
en_name: $en_name: ident,
flags: [
$(
meta: [ $(#[$flag_attr: meta])* ]
flag: $flag_name: ident = $flag_value: expr;
)+
[]
],
) => {
#[repr($T)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[allow(dead_code)]
$(#[$en_attr])*
pub enum $en_name {
$(
$(#[$flag_attr])*
$flag_name = $flag_value
),+
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[allow(dead_code)]
$(#[$st_attr])*
pub struct $st_name {
mask: $T
}
bitmask!(@IMPL $st_name $T $en_name, {
$($flag_name = $flag_value),+
});
};
(@IMPL $st_name: ident $T: tt $en_name: ident, {
$($flag_name: ident = $flag_val: expr),+
}) => {
#[allow(dead_code)]
impl $st_name {
#[inline]
pub fn none() -> Self {
$st_name {
mask: 0
}
}
#[inline]
pub fn all() -> Self {
$st_name {
mask: $($flag_val)|+
}
}
#[inline]
pub fn set<T>(&mut self, other: T)
where T: Into<$st_name> + $crate::__core::ops::Deref<Target = $T> {
self.mask |= *other;
}
#[inline]
pub fn unset<T>(&mut self, other: T)
where T: Into<$st_name> + $crate::__core::ops::Deref<Target = $T> {
self.mask &= Self::all().mask ^ *other;
}
#[inline]
pub fn toggle<T>(&mut self, other: T)
where T: Into<$st_name> + $crate::__core::ops::Deref<Target = $T> {
self.mask ^= *other;
}
#[inline]
pub fn contains<T>(&self, other: T) -> bool
where T: Into<$st_name> + $crate::__core::ops::Deref<Target = $T> {
self.mask & *other == *other
}
#[inline]
pub fn intersects<T>(&self, other: T) -> bool
where T: Into<$st_name> + $crate::__core::ops::Deref<Target = $T> {
self.mask & *other != 0
}
pub fn is_all(&self) -> bool {
self.mask == Self::all().mask
}
pub fn is_none(&self) -> bool {
self.mask == 0
}
}
impl $crate::__core::convert::From<$en_name> for $st_name {
#[inline]
fn from(flag: $en_name) -> Self {
$st_name {
mask: flag as $T
}
}
}
impl $crate::__core::ops::Deref for $st_name {
type Target = $T;
#[inline]
fn deref(&self) -> &$T {
&self.mask as &$T
}
}
impl $crate::__core::ops::Deref for $en_name {
type Target = $T;
#[inline]
fn deref(&self) -> &$T {
unsafe { $crate::__core::mem::transmute(self) }
}
}
bitmask! { @IMPL_BITOR
$st_name, $st_name, $st_name;
$st_name, $en_name, $st_name;
$en_name, $st_name, $st_name;
$en_name, $en_name, $st_name;
}
bitmask! { @IMPL_BITAND
$st_name, $st_name, $st_name;
$st_name, $en_name, $st_name;
$en_name, $st_name, $st_name;
$en_name, $en_name, $st_name;
}
bitmask! { @IMPL_BITXOR
$st_name, $st_name, $st_name;
$st_name, $en_name, $st_name;
$en_name, $st_name, $st_name;
$en_name, $en_name, $st_name;
}
bitmask! { @IMPL_BITOR_ASSIGN
$st_name, $st_name;
$st_name, $en_name;
}
bitmask! { @IMPL_BITAND_ASSIGN
$st_name, $st_name;
$st_name, $en_name;
}
bitmask! { @IMPL_BITXOR_ASSIGN
$st_name, $st_name;
$st_name, $en_name;
}
bitmask! { @IMPL_NOT
$st_name, $st_name;
$en_name, $st_name;
}
};
(@IMPL_BITOR $($target: ty, $other: ty, $st_name: ident);*;) => {
$(impl $crate::__core::ops::BitOr<$other> for $target {
type Output = $st_name;
#[inline]
fn bitor(self, other: $other) -> Self::Output {
$st_name {
mask: *self | *other
}
}
})*
};
(@IMPL_BITAND $($target: ty, $other: ty, $st_name: ident);*;) => {
$(impl $crate::__core::ops::BitAnd<$other> for $target {
type Output = $st_name;
#[inline]
fn bitand(self, other: $other) -> Self::Output {
$st_name {
mask: *self & *other
}
}
})*
};
(@IMPL_BITXOR $($target: ty, $other: ty, $st_name: ident);*;) => {
$(impl $crate::__core::ops::BitXor<$other> for $target {
type Output = $st_name;
#[inline]
fn bitxor(self, other: $other) -> Self::Output {
$st_name {
mask: *self ^ *other
}
}
})*
};
(@IMPL_BITOR_ASSIGN $($target: ty, $other: ty);*;) => {
$(impl $crate::__core::ops::BitOrAssign<$other> for $target {
#[inline]
fn bitor_assign(&mut self, other: $other) {
self.mask |= *other
}
})*
};
(@IMPL_BITAND_ASSIGN $($target: ty, $other: ty);*;) => {
$(impl $crate::__core::ops::BitAndAssign<$other> for $target {
#[inline]
fn bitand_assign(&mut self, other: $other) {
self.mask &= *other
}
})*
};
(@IMPL_BITXOR_ASSIGN $($target: ty, $other: ty);*;) => {
$(impl $crate::__core::ops::BitXorAssign<$other> for $target {
#[inline]
fn bitxor_assign(&mut self, other: $other) {
self.mask ^= *other
}
})*
};
(@IMPL_NOT $($target: ty, $st_name: ident);*;) => {
$(impl $crate::__core::ops::Not for $target {
type Output = $st_name;
#[inline]
fn not(self) -> Self::Output {
let all_flags = $st_name::all();
$st_name {
mask: *all_flags ^ *self
}
}
})*
}
}
#[cfg(test)]
mod tests {
extern crate std;
bitmask! {
mask BitMask: isize where
flags Flags {
Flag1 = 0b00000001,
Flag2 = 0b00000010,
Flag3 = 0b00000100,
FlagMin = std::isize::MIN,
Flag123 = 0b00000111
}
}
bitmask! {
mask A: u8 where flags B {
Trailing = 1,
}
}
use self::Flags::*;
#[test]
fn test_set_unset() {
let mut bm = BitMask::none();
assert_eq!(*bm, 0b00000000);
bm.set(Flag2);
assert_eq!(*bm, Flag2 as isize);
bm.set(Flag3);
assert_eq!(*bm, 0b00000110);
bm.unset(Flag123);
assert_eq!(*bm, 0b00000000);
bm.set(FlagMin);
assert_eq!(*bm, isize::min_value());
}
#[test]
fn test_toggle() {
let mut bm = BitMask::none();
bm.toggle(Flag2);
assert_eq!(*bm, 0b00000010);
bm.toggle(BitMask::from(Flag3));
assert_eq!(*bm, 0b00000110);
bm.toggle(Flag123);
assert_eq!(*bm, 0b00000001);
}
#[test]
fn test_contains() {
let mut bm = BitMask::from(Flag2 | Flag3);
assert_eq!(bm.contains(Flag1), false);
assert_eq!(bm.contains(Flag2), true);
assert_eq!(bm.contains(Flag3), true);
assert_eq!(bm.contains(Flag123), false);
bm.set(Flag123);
assert_eq!(bm.contains(Flag123), true);
bm.set(FlagMin);
assert_eq!(bm.contains(Flag123), true);
}
#[test]
fn test_intersects() {
let bm = BitMask::from(Flag2 | Flag3);
assert_eq!(bm.intersects(Flag1), false);
assert_eq!(bm.intersects(Flag2), true);
assert_eq!(bm.intersects(Flag3), true);
assert_eq!(bm.intersects(Flag1 | Flag3), true);
assert_eq!(bm.intersects(Flag123), true);
}
#[test]
fn test_is_all() {
assert_eq!(BitMask::all().is_all(), true);
assert_eq!(BitMask::none().is_all(), false);
assert_eq!(BitMask::from(Flag1).is_all(), false);
assert_eq!(BitMask::from(Flag123 | FlagMin).is_all(), true);
}
#[test]
fn test_is_none() {
assert_eq!(BitMask::all().is_none(), false);
assert_eq!(BitMask::none().is_none(), true);
assert_eq!(BitMask::from(Flag1).is_none(), false);
assert_eq!(BitMask::from(Flag123 | FlagMin).is_none(), false);
}
#[test]
fn test_bitor() {
let bm = Flag1 | Flag3;
assert_eq!(*bm, 0b00000101);
let bm = BitMask::from(Flag1) | BitMask::from(Flag3);
assert_eq!(*bm, 0b00000101);
let bm = Flag1 | BitMask::from(Flag3);
assert_eq!(*bm, 0b00000101);
let bm = BitMask::from(Flag1) | Flag3;
assert_eq!(*bm, 0b00000101);
}
#[test]
fn test_bitand() {
let bm = Flag1 & Flag3;
assert_eq!(*bm, 0);
let bm = BitMask::from(Flag1) & BitMask::from(Flag3);
assert_eq!(*bm, 0);
let bm = Flag1 & BitMask::from(Flag3);
assert_eq!(*bm, 0);
let bm = BitMask::from(Flag1) & Flag3;
assert_eq!(*bm, 0);
}
#[test]
fn test_bitxor() {
let bm = Flag123 ^ Flag3;
assert_eq!(*bm, 0b00000011);
let bm = BitMask::from(Flag123) ^ BitMask::from(Flag3);
assert_eq!(*bm, 0b00000011);
let bm = Flag123 ^ BitMask::from(Flag3);
assert_eq!(*bm, 0b00000011);
let bm = BitMask::from(Flag123) ^ Flag3;
assert_eq!(*bm, 0b00000011);
}
#[test]
fn test_bitor_assign() {
let mut bm = BitMask::from(Flag1);
bm |= Flag3;
assert_eq!(*bm, 0b00000101);
let mut bm = BitMask::from(Flag1);
bm |= BitMask::from(Flag3);
assert_eq!(*bm, 0b00000101);
}
#[test]
fn test_bitand_assign() {
let mut bm = BitMask::from(Flag1);
bm &= Flag3;
assert_eq!(*bm, 0);
let mut bm = BitMask::from(Flag1);
bm &= BitMask::from(Flag3);
assert_eq!(*bm, 0);
}
#[test]
fn test_bitxor_assign() {
let mut bm = BitMask::from(Flag123);
bm ^= Flag3;
assert_eq!(*bm, 0b00000011);
let mut bm = BitMask::from(Flag123);
bm ^= BitMask::from(Flag3);
assert_eq!(*bm, 0b00000011);
}
#[test]
fn test_bitnot() {
let res = !Flag2;
assert_eq!(*res, isize::min_value() + 0b00000101);
let res = !BitMask::from(Flag1);
assert_eq!(*res, isize::min_value() + 0b00000110);
}
#[test]
fn test_pub_mask() {
mod inner {
bitmask! {
pub mask InnerMask: u8 where flags InnerFlags {
InnerFlag1 = 0
}
}
}
let _ = inner::InnerMask::none();
let _ = inner::InnerFlags::InnerFlag1;
}
}