Skip to main content

bitflags_lite/
flag_ext.rs

1use core::ops::{BitAnd, BitOr, BitXor, Not};
2
3use crate::{BitFlag, BitFlags, BitFlagsExt, BitFlagsSpec, util};
4
5/// Extension trait providing generic, non-const equivalents of the
6/// type-specific inherent methods on [`BitFlag`].
7///
8/// The inherent methods are implemented for each concrete integer type so they
9/// can be `const`.
10/// This trait exposes the same operations for generic `T`.
11pub trait BitFlagExt<T, U>: Sized
12where
13    U: ?Sized,
14{
15    /// Creates a bit flag from a [`BitFlags`] set.
16    ///
17    /// Returns `Some` if `flags` contains exactly one set bit and that bit is
18    /// valid according to [`BitFlagsSpec::VALID_BITS_MASK`].
19    ///
20    /// Returns `None` if `flags` is empty, has more than one bit set, or the
21    /// single set bit is not valid.
22    ///
23    /// This is equivalent to [`BitFlag::try_from_bits`] on `flags.bits()`.
24    #[must_use]
25    fn try_from_flags(flags: BitFlags<T, U>) -> Option<Self>
26    where
27        T: 'static + Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
28        U: BitFlagsSpec<T>;
29
30    /// Creates a validated bit flag for bit index `i`.
31    ///
32    /// Returns `None` if `i` is not set in
33    /// [`BitFlagsSpec::VALID_BITS_MASK`].
34    ///
35    /// The index `i` must be less than the number of bits in the
36    /// storage type.
37    #[must_use]
38    fn try_from_bit(i: u8) -> Option<Self>
39    where
40        T: 'static + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
41        U: BitFlagsSpec<T>;
42
43    /// Creates a validated bit flag from `bits` if exactly one bit is
44    /// set and that bit is valid according to
45    /// [`BitFlagsSpec::VALID_BITS_MASK`].
46    ///
47    /// Returns `None` if `bits` is zero, has more than one bit set, or
48    /// the single set bit is not valid.
49    #[must_use]
50    fn try_from_bits(bits: T) -> Option<Self>
51    where
52        T: 'static + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
53        U: BitFlagsSpec<T>;
54
55    /// Creates a single-bit flag from `bits` if exactly one bit is set.
56    ///
57    /// The single set bit is not checked against
58    /// [`BitFlagsSpec::VALID_BITS_MASK`].
59    ///
60    /// Returns `None` if `bits` is `0` or has more than one bit set.
61    #[must_use]
62    fn try_from_bits_retain(bits: T) -> Option<Self>
63    where
64        T: util::IntOpsExt;
65
66    /// Returns `true` if this flag refers to a valid bit.
67    ///
68    /// A flag is valid when its bit index is within the width of `T` and the
69    /// corresponding bit is present in [`BitFlagsSpec::VALID_BITS_MASK`].
70    ///
71    /// [`BitFlag::from_bit_retain`] does not validate the bit index, so a
72    /// `BitFlag` constructed through it may be invalid.
73    #[must_use]
74    fn is_valid(&self) -> bool
75    where
76        T: 'static + Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
77        U: BitFlagsSpec<T>;
78
79    /// Returns the single-bit mask for this flag, or `None` if the bit index is
80    /// outside the width of `T`.
81    ///
82    /// The mask is `1 << bit`. It is not checked against
83    /// [`BitFlagsSpec::VALID_BITS_MASK`]; use [`BitFlag::is_valid`] when tag
84    /// validity is required.
85    #[must_use]
86    fn mask(&self) -> Option<T>
87    where
88        T: util::IntOpsExt;
89
90    /// Returns the single-bit mask for this flag, or `0` if the bit index is
91    /// outside the width of `$ty`.
92    ///
93    /// This is the const-friendly equivalent of `self.mask().unwrap_or(0)`.
94    #[must_use]
95    fn mask_or_default(&self) -> T
96    where
97        T: Default + util::IntOpsExt;
98
99    /// Returns the intersection of `self` and `other`.
100    ///
101    /// This is a raw-bit operation.
102    /// Invalid bits in either operand are preserved when they are set
103    /// in both operands.
104    #[must_use]
105    fn intersection(self, other: Self) -> BitFlags<T, U>
106    where
107        T: Copy + BitAnd<T, Output = T> + util::IntOpsExt;
108
109    /// Returns the union of `self` and `other`.
110    ///
111    /// This is a raw-bit operation.
112    /// Invalid bits in either operand are preserved.
113    #[must_use]
114    fn union(self, other: Self) -> BitFlags<T, U>
115    where
116        T: Copy + BitOr<T, Output = T> + util::IntOpsExt;
117
118    /// Returns the difference of `self` and `other`.
119    ///
120    /// The result contains bits that are set in `self` but not in
121    /// `other`.
122    /// This is a raw-bit operation.
123    /// Invalid bits already present in `self` are preserved unless they
124    /// are also set in `other`.
125    #[must_use]
126    fn difference(self, other: Self) -> BitFlags<T, U>
127    where
128        T: Copy + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt;
129
130    /// Returns the symmetric difference of `self` and `other`.
131    ///
132    /// The result contains bits that are set in exactly one of `self`
133    /// or `other`.
134    /// This is a raw-bit operation.
135    /// Invalid bits participate and may be preserved.
136    #[must_use]
137    fn symmetric_difference(self, other: Self) -> BitFlags<T, U>
138    where
139        T: Copy + BitXor<T, Output = T> + util::IntOpsExt;
140
141    /// Returns the complement of `self`.
142    ///
143    /// All valid bits not set in `self` become set, and all invalid
144    /// bits become unset.
145    /// The result always contains only valid bits.
146    #[must_use]
147    fn complement(self) -> BitFlags<T, U>
148    where
149        T: 'static + Copy + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
150        U: BitFlagsSpec<T>;
151}
152
153impl<T, U> BitFlagExt<T, U> for BitFlag<T, U>
154where
155    U: ?Sized,
156{
157    #[inline]
158    fn try_from_flags(flags: BitFlags<T, U>) -> Option<Self>
159    where
160        T: 'static + Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
161        U: BitFlagsSpec<T>,
162    {
163        Self::try_from_bits(flags.bits())
164    }
165
166    #[inline]
167    fn try_from_bit(i: u8) -> Option<Self>
168    where
169        T: 'static + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
170        U: BitFlagsSpec<T>,
171    {
172        let bits = T::ONE.checked_shl(u32::from(i))?;
173        ((i < T::BITS) && (bits & !U::VALID_BITS_MASK) == T::ZERO).then(|| Self::from_bit_retain(i))
174    }
175
176    #[inline]
177    fn try_from_bits(bits: T) -> Option<Self>
178    where
179        T: 'static + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
180        U: BitFlagsSpec<T>,
181    {
182        if bits.count_ones() == 1
183            && let Some(bit) = bits.lowest_one()
184            && let Ok(bit) = u8::try_from(bit)
185        {
186            Self::try_from_bit(bit)
187        } else {
188            None
189        }
190    }
191
192    #[inline]
193    fn try_from_bits_retain(bits: T) -> Option<Self>
194    where
195        T: util::IntOpsExt,
196    {
197        if bits.count_ones() == 1
198            && let Some(bit) = bits.lowest_one()
199            && let Ok(bit) = u8::try_from(bit)
200        {
201            Some(Self::from_bit_retain(bit))
202        } else {
203            None
204        }
205    }
206
207    #[inline]
208    fn is_valid(&self) -> bool
209    where
210        T: 'static + Copy + PartialEq + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
211        U: BitFlagsSpec<T>,
212    {
213        (self.bit() < T::BITS) && BitFlags::<T, U>::from_flag(*self).is_valid()
214    }
215
216    #[inline]
217    fn mask(&self) -> Option<T>
218    where
219        T: util::IntOpsExt,
220    {
221        T::ONE.checked_shl(u32::from(self.bit()))
222    }
223
224    #[inline]
225    fn mask_or_default(&self) -> T
226    where
227        T: Default + util::IntOpsExt,
228    {
229        self.mask().unwrap_or_default()
230    }
231
232    #[inline]
233    fn intersection(self, other: Self) -> BitFlags<T, U>
234    where
235        T: Copy + BitAnd<T, Output = T> + util::IntOpsExt,
236    {
237        <BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(self)
238            .intersection(<BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(other))
239    }
240
241    #[inline]
242    fn union(self, other: Self) -> BitFlags<T, U>
243    where
244        T: Copy + BitOr<T, Output = T> + util::IntOpsExt,
245    {
246        <BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(self)
247            .union(<BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(other))
248    }
249
250    #[inline]
251    fn difference(self, other: Self) -> BitFlags<T, U>
252    where
253        T: Copy + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
254    {
255        <BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(self)
256            .difference(<BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(other))
257    }
258
259    #[inline]
260    fn symmetric_difference(self, other: Self) -> BitFlags<T, U>
261    where
262        T: Copy + BitXor<T, Output = T> + util::IntOpsExt,
263    {
264        <BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(self)
265            .symmetric_difference(<BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(other))
266    }
267
268    #[inline]
269    fn complement(self) -> BitFlags<T, U>
270    where
271        T: 'static + Copy + Not<Output = T> + BitAnd<T, Output = T> + util::IntOpsExt,
272        U: BitFlagsSpec<T>,
273    {
274        <BitFlags<T, U> as BitFlagsExt<T, U>>::from_flag(self).complement()
275    }
276}