Skip to content

Commit 32793c1

Browse files
chitoyuumeithecatte
authored andcommitted
Add BitFlags::len, improve iterator
- Implemented `BitFlags::len` using `count_ones`. - Refactored the iterator into a real type, implemented `ExactSizeIterator` and `FusedIterator`. - Implemented `IntoIterator` for `BitFlags`. `DoubleEndedIterator` is deliberatedly not implemented as to not lock the implementation down.
1 parent e47c74e commit 32793c1

File tree

2 files changed

+84
-6
lines changed

2 files changed

+84
-6
lines changed

src/lib.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191
#![warn(missing_docs)]
9292
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
9393

94-
use core::iter::FromIterator;
94+
use core::iter::{FromIterator, FusedIterator};
9595
use core::marker::PhantomData;
9696
use core::{cmp, ops};
9797

@@ -232,13 +232,18 @@ pub mod _internal {
232232
{
233233
#[doc(hidden)]
234234
fn is_power_of_two(self) -> bool;
235+
#[doc(hidden)]
236+
fn count_ones(self) -> u32;
235237
}
236238

237239
for_each_uint! { $ty $hide_docs =>
238240
impl BitFlagNum for $ty {
239241
fn is_power_of_two(self) -> bool {
240242
<$ty>::is_power_of_two(self)
241243
}
244+
fn count_ones(self) -> u32 {
245+
<$ty>::count_ones(self)
246+
}
242247
}
243248
}
244249

@@ -563,6 +568,12 @@ where
563568
self.val == T::EMPTY
564569
}
565570

571+
/// Returns the number of flags set.
572+
#[inline(always)]
573+
pub fn len(self) -> usize {
574+
self.val.count_ones() as usize
575+
}
576+
566577
/// Returns the flag that is set if there is exactly one.
567578
#[inline(always)]
568579
pub fn to_flag(self) -> Option<T> {
@@ -626,14 +637,65 @@ where
626637

627638
/// Returns an iterator that yields each set flag
628639
#[inline]
629-
pub fn iter(self) -> impl Iterator<Item = T> + Clone {
630-
T::FLAG_LIST
631-
.iter()
632-
.cloned()
633-
.filter(move |&flag| self.contains(flag))
640+
pub fn iter(self) -> Iter<T> {
641+
Iter {
642+
rest: self,
643+
it: T::FLAG_LIST.iter(),
644+
}
645+
}
646+
}
647+
648+
impl<T: BitFlag> IntoIterator for BitFlags<T> {
649+
type IntoIter = Iter<T>;
650+
type Item = T;
651+
652+
fn into_iter(self) -> Self::IntoIter {
653+
self.iter()
654+
}
655+
}
656+
657+
/// Iterator that yields each set flag.
658+
#[derive(Clone, Debug)]
659+
pub struct Iter<T: BitFlag> {
660+
rest: BitFlags<T>,
661+
it: core::slice::Iter<'static, T>,
662+
}
663+
664+
impl<T> Iterator for Iter<T>
665+
where
666+
T: BitFlag,
667+
{
668+
type Item = T;
669+
670+
fn next(&mut self) -> Option<Self::Item> {
671+
for &flag in &mut self.it {
672+
let yes = self.rest.contains(flag);
673+
self.rest.remove(flag);
674+
if yes {
675+
return Some(flag);
676+
}
677+
}
678+
679+
None
680+
}
681+
682+
fn size_hint(&self) -> (usize, Option<usize>) {
683+
let l = self.rest.len();
684+
(l, Some(l))
634685
}
635686
}
636687

688+
impl<T> ExactSizeIterator for Iter<T>
689+
where
690+
T: BitFlag,
691+
{
692+
fn len(&self) -> usize {
693+
self.rest.len()
694+
}
695+
}
696+
697+
impl<T: BitFlag> FusedIterator for Iter<T> {}
698+
637699
for_each_uint! { $ty $hide_docs =>
638700
impl<T> BitFlags<T, $ty> {
639701
/// Create a new BitFlags unsafely, without checking if the bits form

test_suite/common.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@ fn test_to_flag() {
8989
assert_eq!((Test::A | Test::C).to_flag(), None);
9090
}
9191

92+
#[test]
93+
fn test_len() {
94+
use enumflags2::BitFlags;
95+
assert_eq!(BitFlags::<Test>::empty().len(), 0);
96+
assert_eq!(BitFlags::<Test>::all().len(), 4);
97+
assert_eq!((Test::A | Test::D).len(), 2);
98+
}
99+
92100
#[test]
93101
fn iterator() {
94102
use enumflags2::BitFlags;
@@ -105,6 +113,14 @@ fn iterator() {
105113
// If cloned, the iterator will yield the same elements.
106114
let it = bitflag.iter();
107115
assert!(it.clone().zip(it).all(|(a, b)| a == b));
116+
// The ExactLenIterator implementation should always yield the
117+
// correct remaining length.
118+
let mut it = bitflag.iter();
119+
for rest in (0..=expected.len()).rev() {
120+
assert_eq!(it.len(), rest);
121+
assert_eq!(it.size_hint(), (rest, Some(rest)));
122+
it.next();
123+
}
108124
}
109125
}
110126

0 commit comments

Comments
 (0)