Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix up incorrect sub assign behavior and other cleanups #366

Merged
merged 15 commits into from
Jun 27, 2023
20 changes: 16 additions & 4 deletions examples/custom_bits_type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::{BitAnd, BitOr, BitXor, Not};

use bitflags::{Flags, Flag, Bits};
use bitflags::{Bits, Flag, Flags};

// Define a custom container that can be used in flags types
// Note custom bits types can't be used in `bitflags!`
Expand All @@ -25,23 +25,35 @@ impl BitAnd for CustomBits {
type Output = Self;

fn bitand(self, other: Self) -> Self {
CustomBits([self.0[0] & other.0[0], self.0[1] & other.0[1], self.0[2] & other.0[2]])
CustomBits([
self.0[0] & other.0[0],
self.0[1] & other.0[1],
self.0[2] & other.0[2],
])
}
}

impl BitOr for CustomBits {
type Output = Self;

fn bitor(self, other: Self) -> Self {
CustomBits([self.0[0] | other.0[0], self.0[1] | other.0[1], self.0[2] | other.0[2]])
CustomBits([
self.0[0] | other.0[0],
self.0[1] | other.0[1],
self.0[2] | other.0[2],
])
}
}

impl BitXor for CustomBits {
type Output = Self;

fn bitxor(self, other: Self) -> Self {
CustomBits([self.0[0] & other.0[0], self.0[1] & other.0[1], self.0[2] & other.0[2]])
CustomBits([
self.0[0] & other.0[0],
self.0[1] & other.0[1],
self.0[2] & other.0[2],
])
}
}

Expand Down
7 changes: 5 additions & 2 deletions examples/macro_free.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::{fmt, str};

use bitflags::{Flags, Flag};
use bitflags::{Flag, Flags};

// First: Define your flags type. It just needs to be `Sized + 'static`.
pub struct ManualFlags(u32);
Expand Down Expand Up @@ -54,5 +54,8 @@ impl fmt::Display for ManualFlags {
}

fn main() {
println!("{}", ManualFlags::A.union(ManualFlags::B).union(ManualFlags::C));
println!(
"{}",
ManualFlags::A.union(ManualFlags::B).union(ManualFlags::C)
);
}
4 changes: 4 additions & 0 deletions src/example_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ __impl_public_bitflags_forward! {
Flags: u32, Field0
}

__impl_public_bitflags_ops! {
Flags
}

__impl_public_bitflags_iter! {
Flags: u32, Flags
}
Expand Down
16 changes: 5 additions & 11 deletions src/external.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ macro_rules! __impl_external_bitflags_serde {
fn deserialize<D: $crate::__private::serde::Deserializer<'de>>(
deserializer: D,
) -> $crate::__private::core::result::Result<Self, D::Error> {
let flags: $PublicBitFlags = $crate::serde::deserialize(
deserializer,
)?;
let flags: $PublicBitFlags = $crate::serde::deserialize(deserializer)?;

Ok(flags.0)
}
Expand Down Expand Up @@ -235,20 +233,16 @@ macro_rules! __impl_external_bitflags_bytemuck {
) => {
// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Pod
unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags
where
$T: $crate::__private::bytemuck::Pod,
unsafe impl $crate::__private::bytemuck::Pod for $InternalBitFlags where
$T: $crate::__private::bytemuck::Pod
{

}

// SAFETY: $InternalBitFlags is guaranteed to have the same ABI as $T,
// and $T implements Zeroable
unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags
where
$T: $crate::__private::bytemuck::Zeroable,
unsafe impl $crate::__private::bytemuck::Zeroable for $InternalBitFlags where
$T: $crate::__private::bytemuck::Zeroable
{

}
};
}
Expand Down
6 changes: 2 additions & 4 deletions src/external/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
use crate::Flags;

/// Get a random known flags value.
pub fn arbitrary<'a, B: Flags>(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<B>
pub fn arbitrary<'a, B: Flags>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<B>
where
B::Bits: arbitrary::Arbitrary<'a>
B::Bits: arbitrary::Arbitrary<'a>,
{
B::from_bits(u.arbitrary()?).ok_or_else(|| arbitrary::Error::IncorrectFormat)
}
Expand Down
2 changes: 1 addition & 1 deletion src/external/bytemuck.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(test)]
mod tests {
use bytemuck::{Pod, Zeroable};

bitflags! {
#[derive(Pod, Zeroable, Clone, Copy)]
#[repr(transparent)]
Expand Down
18 changes: 6 additions & 12 deletions src/external/serde.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! Specialized serialization for flags types using `serde`.

use crate::{
parser::{self, ParseHex, WriteHex},
Flags,
};
use core::{fmt, str};
use crate::{Flags, parser::{self, ParseHex, WriteHex}};
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};

/// Serialize a set of flags as a human-readable string or their underlying bits.
pub fn serialize<B: Flags, S: Serializer>(
flags: &B,
serializer: S,
) -> Result<S::Ok, S::Error>
pub fn serialize<B: Flags, S: Serializer>(flags: &B, serializer: S) -> Result<S::Ok, S::Error>
where
B::Bits: WriteHex + Serialize,
{
Expand All @@ -26,13 +26,7 @@ where
}

/// Deserialize a set of flags from a human-readable string or their underlying bits.
pub fn deserialize<
'de,
B: Flags,
D: Deserializer<'de>,
>(
deserializer: D,
) -> Result<B, D::Error>
pub fn deserialize<'de, B: Flags, D: Deserializer<'de>>(deserializer: D) -> Result<B, D::Error>
where
B::Bits: ParseHex + Deserialize<'de>,
{
Expand Down
6 changes: 5 additions & 1 deletion src/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ macro_rules! __impl_internal_bitflags {
if self.is_empty() {
// If no flags are set then write an empty hex flag to avoid
// writing an empty string. In some contexts, like serialization,
// an empty string is preferrable, but it may be unexpected in
// an empty string is preferable, but it may be unexpected in
// others for a format not to produce any output.
//
// We can remove this `0x0` and remain compatible with `FromStr`,
Expand Down Expand Up @@ -106,6 +106,10 @@ macro_rules! __impl_internal_bitflags {
}
}

__impl_public_bitflags_ops! {
$InternalBitFlags
}

__impl_public_bitflags_iter! {
$InternalBitFlags: $T, $PublicBitFlags
}
Expand Down
50 changes: 25 additions & 25 deletions src/iter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Iterating over set flag values.

use crate::{Flags, Flag};
use crate::{Flag, Flags};

/// An iterator over a set of flags.
///
Expand All @@ -23,28 +23,28 @@ impl<B: Flags> Iter<B> {

impl<B: 'static> Iter<B> {
#[doc(hidden)]
pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
Iter {
inner: IterNames::__private_const_new(flags, source, state),
inner: IterNames::__private_const_new(flags, source, remaining),
done: false,
}
}
}

impl<B: Flags> Iterator for Iter<B> {
type Item = B;

fn next(&mut self) -> Option<Self::Item> {
match self.inner.next() {
Some((_, flag)) => Some(flag),
None if !self.done => {
self.done = true;

// After iterating through valid names, if there are any bits left over
// then return one final value that includes them. This makes `into_iter`
// and `from_iter` roundtrip
if !self.inner.remaining().is_empty() {
Some(B::from_bits_retain(self.inner.state.bits()))
Some(B::from_bits_retain(self.inner.remaining.bits()))
} else {
None
}
Expand All @@ -61,7 +61,7 @@ pub struct IterNames<B: 'static> {
flags: &'static [Flag<B>],
idx: usize,
source: B,
state: B,
remaining: B,
}

impl<B: Flags> IterNames<B> {
Expand All @@ -70,19 +70,19 @@ impl<B: Flags> IterNames<B> {
IterNames {
flags: B::FLAGS,
idx: 0,
state: B::from_bits_retain(flags.bits()),
remaining: B::from_bits_retain(flags.bits()),
source: B::from_bits_retain(flags.bits()),
}
}
}

impl<B: 'static> IterNames<B> {
#[doc(hidden)]
pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, state: B) -> Self {
pub const fn __private_const_new(flags: &'static [Flag<B>], source: B, remaining: B) -> Self {
IterNames {
flags,
idx: 0,
state,
remaining,
source,
}
}
Expand All @@ -93,41 +93,41 @@ impl<B: 'static> IterNames<B> {
/// check whether or not there are any bits that didn't correspond
/// to a valid flag remaining.
pub fn remaining(&self) -> &B {
&self.state
&self.remaining
}
}

impl<B: Flags> Iterator for IterNames<B> {
type Item = (&'static str, B);

fn next(&mut self) -> Option<Self::Item> {
while let Some(flag) = self.flags.get(self.idx) {
// Short-circuit if our state is empty
if self.state.is_empty() {
if self.remaining.is_empty() {
return None;
}

self.idx += 1;

let bits = flag.value().bits();

// 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:
// If the flag is set in the original source _and_ it has bits that haven't
// been covered by a previous flag yet then yield it. These conditions cover
// two cases for multi-bit flags:
//
// 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.source.contains(B::from_bits_retain(bits)) {
self.state.remove(B::from_bits_retain(bits));
// 1. When flags partially overlap, such as `0b00000001` and `0b00000101`, we'll
// yield both flags.
// 2. When flags fully overlap, such as in convenience flags that are a shorthand for others,
// we won't yield both flags.
if self.source.contains(B::from_bits_retain(bits))
&& self.remaining.intersects(B::from_bits_retain(bits))
{
self.remaining.remove(B::from_bits_retain(bits));

return Some((flag.name(), B::from_bits_retain(bits)));
}
}

None
}
}