Skip to content

Commit

Permalink
Merge pull request #281 from KodrAus/chore/all-cleanup
Browse files Browse the repository at this point in the history
rework the way cfgs are handled
  • Loading branch information
KodrAus committed May 10, 2022
2 parents f38ce72 + 8c67a43 commit 810dc35
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 148 deletions.
63 changes: 61 additions & 2 deletions src/bitflags_trait.rs
@@ -1,11 +1,14 @@
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

#[doc(hidden)]
pub trait ImplementedByBitFlagsMacro {}

/// A trait that is automatically implemented for all bitflags.
///
/// It should not be implemented manually.
pub trait BitFlags: ImplementedByBitFlagsMacro {
type Bits;
type Bits: Bits;

/// Returns an empty set of flags.
fn empty() -> Self;
/// Returns the set containing all flags.
Expand All @@ -15,7 +18,8 @@ pub trait BitFlags: ImplementedByBitFlagsMacro {
/// Convert from underlying bit representation, unless that
/// representation contains bits that do not correspond to a flag.
fn from_bits(bits: Self::Bits) -> Option<Self>
where Self: Sized;
where
Self: Sized;
/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
fn from_bits_truncate(bits: Self::Bits) -> Self;
Expand Down Expand Up @@ -48,3 +52,58 @@ pub trait BitFlags: ImplementedByBitFlagsMacro {
/// Inserts or removes the specified flags depending on the passed value.
fn set(&mut self, other: Self, value: bool);
}

// Not re-exported
pub trait Sealed {}

/// A private trait that encodes the requirements of underlying bits types that can hold flags.
///
/// This trait may be made public at some future point, but it presents a compatibility hazard
/// so is left internal for now.
#[doc(hidden)]
pub trait Bits:
Clone
+ Copy
+ BitAnd
+ BitAndAssign
+ BitOr
+ BitOrAssign
+ BitXor
+ BitXorAssign
+ Not
+ Sized
+ Sealed
{
/// The value of `Self` where no bits are set.
const EMPTY: Self;

/// The value of `Self` where all bits are set.
const ALL: Self;
}

macro_rules! impl_bits {
($($u:ty, $i:ty,)*) => {
$(
impl Bits for $u {
const EMPTY: $u = 0;
const ALL: $u = <$u>::MAX;
}

impl Bits for $i {
const EMPTY: $i = 0;
const ALL: $i = <$u>::MAX as $i;
}

impl Sealed for $u {}
impl Sealed for $i {}
)*
}
}

impl_bits! {
u8, i8,
u16, i16,
u32, i32,
u64, i64,
u128, i128,
}
124 changes: 54 additions & 70 deletions src/lib.rs
Expand Up @@ -285,8 +285,8 @@ mod bitflags_trait;

#[doc(hidden)]
pub mod __private {
pub use crate::bitflags_trait::{Bits, ImplementedByBitFlagsMacro};
pub use core;
pub use crate::bitflags_trait::ImplementedByBitFlagsMacro;
}

/// The macro used to generate the flag structure.
Expand Down Expand Up @@ -389,45 +389,6 @@ macro_rules! bitflags {
() => {};
}

// A helper macro to implement the `all` function.
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __impl_all_bitflags {
(
$BitFlags:ident: $T:ty {
$(
$(#[$attr:ident $($args:tt)*])*
$Flag:ident = $value:expr;
)+
}
) => {
// See `Debug::fmt` for why this approach is taken.
#[allow(non_snake_case)]
trait __BitFlags {
$(
#[allow(deprecated)]
const $Flag: $T = 0;
)+
}
#[allow(non_snake_case)]
impl __BitFlags for $BitFlags {
$(
__impl_bitflags! {
#[allow(deprecated)]
$(? #[$attr $($args)*])*
const $Flag: $T = Self::$Flag.bits;
}
)+
}
Self { bits: $(<Self as __BitFlags>::$Flag)|+ }
};
(
$BitFlags:ident: $T:ty { }
) => {
Self { bits: 0 }
};
}

#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __impl_bitflags {
Expand Down Expand Up @@ -455,7 +416,7 @@ macro_rules! __impl_bitflags {
// Append any extra bits that correspond to flags to the end of the format
let extra_bits = self.bits & !Self::all().bits();

if extra_bits != 0 {
if extra_bits != <$T as $crate::__private::Bits>::EMPTY {
if !first {
f.write_str(" | ")?;
}
Expand Down Expand Up @@ -495,7 +456,14 @@ macro_rules! __impl_bitflags {
}
}

#[allow(dead_code)]
#[allow(
dead_code,
deprecated,
unused_doc_comments,
unused_attributes,
unused_mut,
non_upper_case_globals
)]
impl $BitFlags {
$(
$(#[$attr $($args)*])*
Expand All @@ -505,20 +473,13 @@ macro_rules! __impl_bitflags {
/// Returns an empty set of flags.
#[inline]
pub const fn empty() -> Self {
Self { bits: 0 }
Self { bits: <$T as $crate::__private::Bits>::EMPTY }
}

/// Returns the set containing all flags.
#[inline]
pub const fn all() -> Self {
__impl_all_bitflags! {
$BitFlags: $T {
$(
$(#[$attr $($args)*])*
$Flag = $value;
)*
}
}
Self::from_bits_truncate(<$T as $crate::__private::Bits>::ALL)
}

/// Returns the raw value of the flags currently stored.
Expand All @@ -532,8 +493,9 @@ macro_rules! __impl_bitflags {
#[inline]
pub const fn from_bits(bits: $T) -> $crate::__private::core::option::Option<Self> {
let truncated = Self::from_bits_truncate(bits).bits;

if truncated == bits {
$crate::__private::core::option::Option::Some(Self{ bits })
$crate::__private::core::option::Option::Some(Self { bits })
} else {
$crate::__private::core::option::Option::None
}
Expand All @@ -543,15 +505,13 @@ macro_rules! __impl_bitflags {
/// that do not correspond to flags.
#[inline]
pub const fn from_bits_truncate(bits: $T) -> Self {
if bits == 0 {
if bits == <$T as $crate::__private::Bits>::EMPTY {
return Self { bits }
}

#[allow(unused_mut)]
let mut truncated = 0;
let mut truncated = <$T as $crate::__private::Bits>::EMPTY;

$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
if bits & Self::$Flag.bits == Self::$Flag.bits {
truncated |= Self::$Flag.bits
Expand Down Expand Up @@ -719,15 +679,13 @@ macro_rules! __impl_bitflags {
}

/// Returns an iterator over set flags and their names.
pub fn iter(mut self) -> impl $crate::__private::core::iter::Iterator<Item = (&'static str, Self)> {
pub fn iter(self) -> impl $crate::__private::core::iter::Iterator<Item = (&'static str, Self)> {
use $crate::__private::core::iter::Iterator as _;

const NUM_FLAGS: usize = {
#[allow(unused_mut)]
let mut num_flags = 0;

$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
{
num_flags += 1;
Expand All @@ -739,13 +697,11 @@ macro_rules! __impl_bitflags {

const OPTIONS: [$BitFlags; NUM_FLAGS] = [
$(
#[allow(unused_doc_comments, unused_attributes)]
$(#[$attr $($args)*])*
$BitFlags::$Flag,
)*
];

#[allow(unused_doc_comments, unused_attributes)]
const OPTIONS_NAMES: [&'static str; NUM_FLAGS] = [
$(
$(#[$attr $($args)*])*
Expand All @@ -754,17 +710,29 @@ macro_rules! __impl_bitflags {
];

let mut start = 0;
let mut state = self;

$crate::__private::core::iter::from_fn(move || {
if self.is_empty() || NUM_FLAGS == 0 {
if state.is_empty() || NUM_FLAGS == 0 {
$crate::__private::core::option::Option::None
} else {
for (flag, flag_name) in OPTIONS[start..NUM_FLAGS].iter().copied()
.zip(OPTIONS_NAMES[start..NUM_FLAGS].iter().copied())
{
start += 1;

// NOTE: We check whether the flag exists in self, but remove it from
// a different value. This ensure that overlapping flags are handled
// properly. Take the following example:
//
// const A: 0b00000001;
// const B: 0b00000101;
//
// Given the bits 0b00000101, both A and B are set. But if we removed A
// as we encountered it we'd be left with 0b00000100, which doesn't
// correspond to a valid flag on its own.
if self.contains(flag) {
self.remove(flag);
state.remove(flag);

return $crate::__private::core::option::Option::Some((flag_name, flag))
}
Expand Down Expand Up @@ -1353,7 +1321,10 @@ mod tests {
assert_eq!(UNION, Flags::A | Flags::C);
assert_eq!(DIFFERENCE, Flags::all() - Flags::A);
assert_eq!(COMPLEMENT, !Flags::C);
assert_eq!(SYM_DIFFERENCE, (Flags::A | Flags::C) ^ (Flags::all() - Flags::A));
assert_eq!(
SYM_DIFFERENCE,
(Flags::A | Flags::C) ^ (Flags::all() - Flags::A)
);
}

#[test]
Expand Down Expand Up @@ -1609,13 +1580,15 @@ mod tests {
assert_eq!(format!("{:?}", Flags::A | Flags::B), "A | B");
assert_eq!(format!("{:?}", Flags::empty()), "(empty)");
assert_eq!(format!("{:?}", Flags::ABC), "A | B | C");

let extra = unsafe { Flags::from_bits_unchecked(0xb8) };

assert_eq!(format!("{:?}", extra), "0xb8");
assert_eq!(format!("{:?}", Flags::A | extra), "A | 0xb8");

assert_eq!(
format!("{:?}", Flags::ABC | extra),
"A | B | C | 0xb8"
"A | B | C | ABC | 0xb8"
);

assert_eq!(format!("{:?}", EmptyFlags::empty()), "(empty)");
Expand Down Expand Up @@ -1830,7 +1803,8 @@ mod tests {
fn test_serde_bitflags_roundtrip() {
let flags = SerdeFlags::A | SerdeFlags::B;

let deserialized: SerdeFlags = serde_json::from_str(&serde_json::to_string(&flags).unwrap()).unwrap();
let deserialized: SerdeFlags =
serde_json::from_str(&serde_json::to_string(&flags).unwrap()).unwrap();

assert_eq!(deserialized.bits, flags.bits);
}
Expand All @@ -1854,7 +1828,6 @@ mod tests {
}
}


let flags = Flags::from_bits(0b00000100);
assert_eq!(flags, None);
let flags = Flags::from_bits(0b00000101);
Expand All @@ -1875,7 +1848,7 @@ mod tests {
let flags = Flags::from_bits_truncate(0b00000101);
assert_eq!(flags, Flags::A);
}

#[test]
fn test_iter() {
bitflags! {
Expand All @@ -1887,24 +1860,31 @@ mod tests {
const FOUR_WIN = 0b1000;
#[cfg(unix)]
const FOUR_UNIX = 0b10000;
const FIVE = 0b01000100;
}
}

let count = {
#[cfg(any(unix, windows))]
{
4
5
}

#[cfg(not(any(unix, windows)))]
{
3
4
}
};

let flags = Flags::all();
assert_eq!(flags.iter().count(), count);

for (_, flag) in flags.iter() {
assert!(flags.contains(flag));
}

let mut iter = flags.iter();

assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
assert_eq!(iter.next().unwrap(), ("TWO", Flags::TWO));
assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
Expand All @@ -1918,14 +1898,18 @@ mod tests {
assert_eq!(iter.next().unwrap(), ("FOUR_WIN", Flags::FOUR_WIN));
}

assert_eq!(iter.next().unwrap(), ("FIVE", Flags::FIVE));

assert_eq!(iter.next(), None);

let flags = Flags::empty();
assert_eq!(flags.iter().count(), 0);

let flags = Flags::ONE | Flags::THREE;
assert_eq!(flags.iter().count(), 2);

let mut iter = flags.iter();

assert_eq!(iter.next().unwrap(), ("ONE", Flags::ONE));
assert_eq!(iter.next().unwrap(), ("THREE", Flags::THREE));
assert_eq!(iter.next(), None);
Expand Down

0 comments on commit 810dc35

Please sign in to comment.