From 8b58981871660bd83454878af50de8157ed3ea5e Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sat, 22 Mar 2014 19:38:15 +1100 Subject: [PATCH 1/5] Add a bitflags! macro The `bitflags!` macro generates a `struct` that holds a set of C-style bitmask flags. It is useful for creating typesafe wrappers for C APIs. For example: ~~~rust #[feature(phase)]; #[phase(syntax)] extern crate collections; bitflags!(Flags: u32 { FlagA = 0x00000001, FlagB = 0x00000010, FlagC = 0x00000100, FlagABC = FlagA.bits | FlagB.bits | FlagC.bits }) fn main() { let e1 = FlagA | FlagC; let e2 = FlagB | FlagC; assert!((e1 | e2) == FlagABC); // union assert!((e1 & e2) == FlagC); // intersection assert!((e1 - e2) == FlagA); // set difference } ~~~ --- src/libcollections/bitflags.rs | 222 +++++++++++++++++++++++++++++++++ src/libcollections/lib.rs | 1 + 2 files changed, 223 insertions(+) create mode 100644 src/libcollections/bitflags.rs diff --git a/src/libcollections/bitflags.rs b/src/libcollections/bitflags.rs new file mode 100644 index 0000000000000..294d5fe6d8e46 --- /dev/null +++ b/src/libcollections/bitflags.rs @@ -0,0 +1,222 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The `bitflags!` macro generates a `struct` that holds a set of C-style +//! bitmask flags. It is useful for creating typesafe wrappers for C APIs. +//! +//! The flags should only be defined for integer types, otherwise unexpected +//! type errors may occur at compile time. +//! +//! # Example +//! +//! ~~~rust +//! #[feature(phase)]; +//! #[phase(syntax)] extern crate collections; +//! +//! bitflags!(Flags: u32 { +//! FlagA = 0x00000001, +//! FlagB = 0x00000010, +//! FlagC = 0x00000100, +//! FlagABC = FlagA.bits +//! | FlagB.bits +//! | FlagC.bits +//! }) +//! +//! fn main() { +//! let e1 = FlagA | FlagC; +//! let e2 = FlagB | FlagC; +//! assert!((e1 | e2) == FlagABC); // union +//! assert!((e1 & e2) == FlagC); // intersection +//! assert!((e1 - e2) == FlagA); // set difference +//! } +//! ~~~ +//! +//! # Operators +//! +//! The following operator traits are implemented for the generated `struct`: +//! +//! - `BitOr`: union +//! - `BitAnd`: intersection +//! - `Sub`: set difference +//! +//! # Methods +//! +//! The following methods are defined for the generated `struct`: +//! +//! - `empty`: an empty set of flags +//! - `bits`: the raw value of the flags currently stored +//! - `is_empty`: `true` if no flags are currently stored +//! - `intersects`: `true` if there are flags common to both `self` and `other` +//! - `contains`: `true` all of the flags in `other` are contained within `self` +//! - `insert`: inserts the specified flags in-place +//! - `remove`: removes the specified flags in-place + +#[macro_export] +macro_rules! bitflags( + ($BitFlags:ident: $T:ty { + $($Flag:ident = $value:expr),+ + }) => ( + #[deriving(Eq, TotalEq, Clone)] + pub struct $BitFlags { + priv bits: $T, + } + + $(pub static $Flag: $BitFlags = $BitFlags { bits: $value };)+ + + impl $BitFlags { + /// Returns an empty set of flags. + pub fn empty() -> $BitFlags { + $BitFlags { bits: 0 } + } + + /// Returns the raw value of the flags currently stored. + pub fn bits(&self) -> $T { + self.bits + } + + /// Returns `true` if no flags are currently stored. + pub fn is_empty(&self) -> bool { + *self == $BitFlags::empty() + } + + /// Returns `true` if there are flags common to both `self` and `other`. + pub fn intersects(&self, other: $BitFlags) -> bool { + !(self & other).is_empty() + } + + /// Returns `true` all of the flags in `other` are contained within `self`. + pub fn contains(&self, other: $BitFlags) -> bool { + (self & other) == other + } + + /// Inserts the specified flags in-place. + pub fn insert(&mut self, other: $BitFlags) { + self.bits |= other.bits; + } + + /// Removes the specified flags in-place. + pub fn remove(&mut self, other: $BitFlags) { + self.bits &= !other.bits; + } + } + + impl BitOr<$BitFlags, $BitFlags> for $BitFlags { + /// Returns the union of the two sets of flags. + #[inline] + fn bitor(&self, other: &$BitFlags) -> $BitFlags { + $BitFlags { bits: self.bits | other.bits } + } + } + + impl BitAnd<$BitFlags, $BitFlags> for $BitFlags { + /// Returns the intersection between the two sets of flags. + #[inline] + fn bitand(&self, other: &$BitFlags) -> $BitFlags { + $BitFlags { bits: self.bits & other.bits } + } + } + + impl Sub<$BitFlags, $BitFlags> for $BitFlags { + /// Returns the set difference of the two sets of flags. + #[inline] + fn sub(&self, other: &$BitFlags) -> $BitFlags { + $BitFlags { bits: self.bits & !other.bits } + } + } + ) +) + +#[cfg(test)] +mod tests { + bitflags!(Flags: u32 { + FlagA = 0x00000001, + FlagB = 0x00000010, + FlagC = 0x00000100, + FlagABC = FlagA.bits + | FlagB.bits + | FlagC.bits + }) + + #[test] + fn test_bits(){ + assert_eq!(Flags::empty().bits(), 0x00000000); + assert_eq!(FlagA.bits(), 0x00000001); + assert_eq!(FlagABC.bits(), 0x00000111); + } + + #[test] + fn test_is_empty(){ + assert!(Flags::empty().is_empty()); + assert!(!FlagA.is_empty()); + assert!(!FlagABC.is_empty()); + } + + #[test] + fn test_two_empties_do_not_intersect() { + let e1 = Flags::empty(); + let e2 = Flags::empty(); + assert!(!e1.intersects(e2)); + } + + #[test] + fn test_empty_does_not_intersect_with_full() { + let e1 = Flags::empty(); + let e2 = FlagABC; + assert!(!e1.intersects(e2)); + } + + #[test] + fn test_disjoint_intersects() { + let e1 = FlagA; + let e2 = FlagB; + assert!(!e1.intersects(e2)); + } + + #[test] + fn test_overlapping_intersects() { + let e1 = FlagA; + let e2 = FlagA | FlagB; + assert!(e1.intersects(e2)); + } + + #[test] + fn test_contains() { + let e1 = FlagA; + let e2 = FlagA | FlagB; + assert!(!e1.contains(e2)); + assert!(e2.contains(e1)); + assert!(FlagABC.contains(e2)); + } + + #[test] + fn test_insert(){ + let mut e1 = FlagA; + let e2 = FlagA | FlagB; + e1.insert(e2); + assert!(e1 == e2); + } + + #[test] + fn test_remove(){ + let mut e1 = FlagA | FlagB; + let e2 = FlagA | FlagC; + e1.remove(e2); + assert!(e1 == FlagB); + } + + #[test] + fn test_operators() { + let e1 = FlagA | FlagC; + let e2 = FlagB | FlagC; + assert!((e1 | e2) == FlagABC); // union + assert!((e1 & e2) == FlagC); // intersection + assert!((e1 - e2) == FlagA); // set difference + } +} diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 2121e129c3523..891ac3be0e92c 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -42,6 +42,7 @@ pub use smallintmap::SmallIntMap; pub use treemap::{TreeMap, TreeSet}; pub use trie::{TrieMap, TrieSet}; +pub mod bitflags; pub mod bitv; pub mod btree; pub mod deque; From a3d9980b25a239da612df9f8a38dec95f57adf4a Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 23 Mar 2014 11:34:07 +1100 Subject: [PATCH 2/5] Document how generated bitflags can be extended with type and trait implementations --- src/libcollections/bitflags.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/libcollections/bitflags.rs b/src/libcollections/bitflags.rs index 294d5fe6d8e46..22b98faf6bf0b 100644 --- a/src/libcollections/bitflags.rs +++ b/src/libcollections/bitflags.rs @@ -38,6 +38,40 @@ //! } //! ~~~ //! +//! The generated `struct`s can also be extended with type and trait implementations: +//! +//! ~~~rust +//! #[feature(phase)]; +//! #[phase(syntax)] extern crate collections; +//! +//! use std::fmt; +//! +//! bitflags!(Flags: u32 { +//! FlagA = 0x00000001, +//! FlagB = 0x00000010 +//! }) +//! +//! impl Flags { +//! pub fn clear(&mut self) { +//! self.bits = 0; // The `bits` field can be accessed from within the +//! // same module where the `bitflags!` macro was invoked. +//! } +//! } +//! +//! impl fmt::Show for Flags { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! write!(f.buf, "hi!") +//! } +//! } +//! +//! fn main() { +//! let mut flags = FlagA | FlagB; +//! flags.clear(); +//! assert!(flags.is_empty()); +//! assert_eq!(format!("{}", flags), ~"hi!"); +//! } +//! ~~~ +//! //! # Operators //! //! The following operator traits are implemented for the generated `struct`: From 43320e584738113f1e5fb379fc34a1b38de10db5 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Sun, 23 Mar 2014 12:31:20 +1100 Subject: [PATCH 3/5] Document derived traits for bitset! macro --- src/libcollections/bitflags.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcollections/bitflags.rs b/src/libcollections/bitflags.rs index 22b98faf6bf0b..3e50f4153927e 100644 --- a/src/libcollections/bitflags.rs +++ b/src/libcollections/bitflags.rs @@ -72,6 +72,11 @@ //! } //! ~~~ //! +//! # Derived traits +//! +//! The `Eq`, `TotalEq`, and `Clone` traits are automatically derived for the +//! `struct` using the `deriving` attribute. +//! //! # Operators //! //! The following operator traits are implemented for the generated `struct`: From 464e375d2f14de91aeb3263fa9331270667f8a79 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Tue, 29 Apr 2014 20:05:51 -0700 Subject: [PATCH 4/5] Move bitflags module to libstd This will allow us to provide type-safe APIs in libstd that are C-compatible. --- src/libcollections/lib.rs | 1 - src/{libcollections => libstd}/bitflags.rs | 8 ++------ src/libstd/lib.rs | 3 +++ 3 files changed, 5 insertions(+), 7 deletions(-) rename src/{libcollections => libstd}/bitflags.rs (98%) diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 891ac3be0e92c..2121e129c3523 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -42,7 +42,6 @@ pub use smallintmap::SmallIntMap; pub use treemap::{TreeMap, TreeSet}; pub use trie::{TrieMap, TrieSet}; -pub mod bitflags; pub mod bitv; pub mod btree; pub mod deque; diff --git a/src/libcollections/bitflags.rs b/src/libstd/bitflags.rs similarity index 98% rename from src/libcollections/bitflags.rs rename to src/libstd/bitflags.rs index 3e50f4153927e..3caa5b5ab3cf8 100644 --- a/src/libcollections/bitflags.rs +++ b/src/libstd/bitflags.rs @@ -17,9 +17,6 @@ //! # Example //! //! ~~~rust -//! #[feature(phase)]; -//! #[phase(syntax)] extern crate collections; -//! //! bitflags!(Flags: u32 { //! FlagA = 0x00000001, //! FlagB = 0x00000010, @@ -41,9 +38,6 @@ //! The generated `struct`s can also be extended with type and trait implementations: //! //! ~~~rust -//! #[feature(phase)]; -//! #[phase(syntax)] extern crate collections; -//! //! use std::fmt; //! //! bitflags!(Flags: u32 { @@ -174,6 +168,8 @@ macro_rules! bitflags( #[cfg(test)] mod tests { + use ops::{BitOr, BitAnd, Sub}; + bitflags!(Flags: u32 { FlagA = 0x00000001, FlagB = 0x00000010, diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 841a73fa01fcb..c34ebfdf7c20b 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -142,7 +142,10 @@ fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, rustuv::event_loop, __test::main) } +/* Exported macros */ + pub mod macros; +pub mod bitflags; mod rtdeps; From 63ee7bb0db642e43e19b8ec597521858805ad21e Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 30 Apr 2014 10:02:11 -0700 Subject: [PATCH 5/5] Update for language changes --- src/libstd/bitflags.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs index 3caa5b5ab3cf8..8764163ddc574 100644 --- a/src/libstd/bitflags.rs +++ b/src/libstd/bitflags.rs @@ -98,7 +98,7 @@ macro_rules! bitflags( }) => ( #[deriving(Eq, TotalEq, Clone)] pub struct $BitFlags { - priv bits: $T, + bits: $T, } $(pub static $Flag: $BitFlags = $BitFlags { bits: $value };)+