Skip to content

Commit 850be73

Browse files
felipeaggerjannau
authored andcommitted
rust: add impl_flags! macro for defining common bitflag operations
We have seen a proliferation of `mod_whatever::foo::Flags` being defined with essentially the same implementation for `BitAnd`, `BitOr`, `.contains()` etc. This macro aims to bring a solution for this, allowing to generate these methods for user-defined structs. With some use cases in KMS and upcoming GPU drivers. Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/We.20really.20need.20a.20common.20.60Flags.60.20type Suggested-by: Daniel Almeida <daniel.almeida@collabora.com> Suggested-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Tested-by: Andreas Hindborg <a.hindborg@kernel.org> Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org> Signed-off-by: Filipe Xavier <felipeaggger@gmail.com> Link: https://patch.msgid.link/20260117-feat-add-bitmask-macro-v9-1-45ea1f00f846@gmail.com [ Implemented missing `BitXorAssign<$flag> for $flags`. Sorted `impl`s. Removed prelude addition for now -- I asked the team and they also felt it wasn't needed. We can always add it later on if needed. Fixed intra-doc link (by removing the sentence since it was superfluous anyway). Simplified `empty()` title. Reworded commit slightly. Added docs to enum variants in example to avoid 'missing_docs' lint when used in actual code. - Miguel ] Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
1 parent 71ab965 commit 850be73

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

rust/kernel/impl_flags.rs

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Bitflag type generator.
4+
5+
/// Common helper for declaring bitflag and bitmask types.
6+
///
7+
/// This macro takes as input:
8+
/// - A struct declaration representing a bitmask type
9+
/// (e.g., `pub struct Permissions(u32)`).
10+
/// - An enumeration declaration representing individual bit flags
11+
/// (e.g., `pub enum Permission { ... }`).
12+
///
13+
/// And generates:
14+
/// - The struct and enum types with appropriate `#[repr]` attributes.
15+
/// - Implementations of common bitflag operators
16+
/// ([`::core::ops::BitOr`], [`::core::ops::BitAnd`], etc.).
17+
/// - Utility methods such as `.contains()` to check flags.
18+
///
19+
/// # Examples
20+
///
21+
/// ```
22+
/// use kernel::impl_flags;
23+
///
24+
/// impl_flags!(
25+
/// /// Represents multiple permissions.
26+
/// #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
27+
/// pub struct Permissions(u32);
28+
///
29+
/// /// Represents a single permission.
30+
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
31+
/// pub enum Permission {
32+
/// /// Read permission.
33+
/// Read = 1 << 0,
34+
///
35+
/// /// Write permission.
36+
/// Write = 1 << 1,
37+
///
38+
/// /// Execute permission.
39+
/// Execute = 1 << 2,
40+
/// }
41+
/// );
42+
///
43+
/// // Combine multiple permissions using the bitwise OR (`|`) operator.
44+
/// let mut read_write: Permissions = Permission::Read | Permission::Write;
45+
/// assert!(read_write.contains(Permission::Read));
46+
/// assert!(read_write.contains(Permission::Write));
47+
/// assert!(!read_write.contains(Permission::Execute));
48+
/// assert!(read_write.contains_any(Permission::Read | Permission::Execute));
49+
/// assert!(read_write.contains_all(Permission::Read | Permission::Write));
50+
///
51+
/// // Using the bitwise OR assignment (`|=`) operator.
52+
/// read_write |= Permission::Execute;
53+
/// assert!(read_write.contains(Permission::Execute));
54+
///
55+
/// // Masking a permission with the bitwise AND (`&`) operator.
56+
/// let read_only: Permissions = read_write & Permission::Read;
57+
/// assert!(read_only.contains(Permission::Read));
58+
/// assert!(!read_only.contains(Permission::Write));
59+
///
60+
/// // Toggling permissions with the bitwise XOR (`^`) operator.
61+
/// let toggled: Permissions = read_only ^ Permission::Read;
62+
/// assert!(!toggled.contains(Permission::Read));
63+
///
64+
/// // Inverting permissions with the bitwise NOT (`!`) operator.
65+
/// let negated = !read_only;
66+
/// assert!(negated.contains(Permission::Write));
67+
/// assert!(!negated.contains(Permission::Read));
68+
/// ```
69+
#[macro_export]
70+
macro_rules! impl_flags {
71+
(
72+
$(#[$outer_flags:meta])*
73+
$vis_flags:vis struct $flags:ident($ty:ty);
74+
75+
$(#[$outer_flag:meta])*
76+
$vis_flag:vis enum $flag:ident {
77+
$(
78+
$(#[$inner_flag:meta])*
79+
$name:ident = $value:expr
80+
),+ $( , )?
81+
}
82+
) => {
83+
$(#[$outer_flags])*
84+
#[repr(transparent)]
85+
$vis_flags struct $flags($ty);
86+
87+
$(#[$outer_flag])*
88+
#[repr($ty)]
89+
$vis_flag enum $flag {
90+
$(
91+
$(#[$inner_flag])*
92+
$name = $value
93+
),+
94+
}
95+
96+
impl ::core::convert::From<$flag> for $flags {
97+
#[inline]
98+
fn from(value: $flag) -> Self {
99+
Self(value as $ty)
100+
}
101+
}
102+
103+
impl ::core::convert::From<$flags> for $ty {
104+
#[inline]
105+
fn from(value: $flags) -> Self {
106+
value.0
107+
}
108+
}
109+
110+
impl ::core::ops::BitOr for $flags {
111+
type Output = Self;
112+
#[inline]
113+
fn bitor(self, rhs: Self) -> Self::Output {
114+
Self(self.0 | rhs.0)
115+
}
116+
}
117+
118+
impl ::core::ops::BitOrAssign for $flags {
119+
#[inline]
120+
fn bitor_assign(&mut self, rhs: Self) {
121+
*self = *self | rhs;
122+
}
123+
}
124+
125+
impl ::core::ops::BitOr<$flag> for $flags {
126+
type Output = Self;
127+
#[inline]
128+
fn bitor(self, rhs: $flag) -> Self::Output {
129+
self | Self::from(rhs)
130+
}
131+
}
132+
133+
impl ::core::ops::BitOrAssign<$flag> for $flags {
134+
#[inline]
135+
fn bitor_assign(&mut self, rhs: $flag) {
136+
*self = *self | rhs;
137+
}
138+
}
139+
140+
impl ::core::ops::BitAnd for $flags {
141+
type Output = Self;
142+
#[inline]
143+
fn bitand(self, rhs: Self) -> Self::Output {
144+
Self(self.0 & rhs.0)
145+
}
146+
}
147+
148+
impl ::core::ops::BitAndAssign for $flags {
149+
#[inline]
150+
fn bitand_assign(&mut self, rhs: Self) {
151+
*self = *self & rhs;
152+
}
153+
}
154+
155+
impl ::core::ops::BitAnd<$flag> for $flags {
156+
type Output = Self;
157+
#[inline]
158+
fn bitand(self, rhs: $flag) -> Self::Output {
159+
self & Self::from(rhs)
160+
}
161+
}
162+
163+
impl ::core::ops::BitAndAssign<$flag> for $flags {
164+
#[inline]
165+
fn bitand_assign(&mut self, rhs: $flag) {
166+
*self = *self & rhs;
167+
}
168+
}
169+
170+
impl ::core::ops::BitXor for $flags {
171+
type Output = Self;
172+
#[inline]
173+
fn bitxor(self, rhs: Self) -> Self::Output {
174+
Self((self.0 ^ rhs.0) & Self::all_bits())
175+
}
176+
}
177+
178+
impl ::core::ops::BitXorAssign for $flags {
179+
#[inline]
180+
fn bitxor_assign(&mut self, rhs: Self) {
181+
*self = *self ^ rhs;
182+
}
183+
}
184+
185+
impl ::core::ops::BitXor<$flag> for $flags {
186+
type Output = Self;
187+
#[inline]
188+
fn bitxor(self, rhs: $flag) -> Self::Output {
189+
self ^ Self::from(rhs)
190+
}
191+
}
192+
193+
impl ::core::ops::BitXorAssign<$flag> for $flags {
194+
#[inline]
195+
fn bitxor_assign(&mut self, rhs: $flag) {
196+
*self = *self ^ rhs;
197+
}
198+
}
199+
200+
impl ::core::ops::Not for $flags {
201+
type Output = Self;
202+
#[inline]
203+
fn not(self) -> Self::Output {
204+
Self((!self.0) & Self::all_bits())
205+
}
206+
}
207+
208+
impl ::core::ops::BitOr for $flag {
209+
type Output = $flags;
210+
#[inline]
211+
fn bitor(self, rhs: Self) -> Self::Output {
212+
$flags(self as $ty | rhs as $ty)
213+
}
214+
}
215+
216+
impl ::core::ops::BitAnd for $flag {
217+
type Output = $flags;
218+
#[inline]
219+
fn bitand(self, rhs: Self) -> Self::Output {
220+
$flags(self as $ty & rhs as $ty)
221+
}
222+
}
223+
224+
impl ::core::ops::BitXor for $flag {
225+
type Output = $flags;
226+
#[inline]
227+
fn bitxor(self, rhs: Self) -> Self::Output {
228+
$flags((self as $ty ^ rhs as $ty) & $flags::all_bits())
229+
}
230+
}
231+
232+
impl ::core::ops::Not for $flag {
233+
type Output = $flags;
234+
#[inline]
235+
fn not(self) -> Self::Output {
236+
$flags((!(self as $ty)) & $flags::all_bits())
237+
}
238+
}
239+
240+
impl $flags {
241+
/// Returns an empty instance where no flags are set.
242+
#[inline]
243+
pub const fn empty() -> Self {
244+
Self(0)
245+
}
246+
247+
/// Returns a mask containing all valid flag bits.
248+
#[inline]
249+
pub const fn all_bits() -> $ty {
250+
0 $( | $value )+
251+
}
252+
253+
/// Checks if a specific flag is set.
254+
#[inline]
255+
pub fn contains(self, flag: $flag) -> bool {
256+
(self.0 & flag as $ty) == flag as $ty
257+
}
258+
259+
/// Checks if at least one of the provided flags is set.
260+
#[inline]
261+
pub fn contains_any(self, flags: $flags) -> bool {
262+
(self.0 & flags.0) != 0
263+
}
264+
265+
/// Checks if all of the provided flags are set.
266+
#[inline]
267+
pub fn contains_all(self, flags: $flags) -> bool {
268+
(self.0 & flags.0) == flags.0
269+
}
270+
}
271+
};
272+
}

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub mod fs;
100100
#[cfg(CONFIG_I2C = "y")]
101101
pub mod i2c;
102102
pub mod id_pool;
103+
#[doc(hidden)]
104+
pub mod impl_flags;
103105
pub mod init;
104106
pub mod io;
105107
pub mod ioctl;

0 commit comments

Comments
 (0)