Skip to content

Commit 9c19b3c

Browse files
committed
bits: fix BitSet behavior
Allows values above `V::MAX`, as long as they fit in the underlying word array. Fixes the return value of `BitSet::insert`. Closes #35
1 parent 307b1a5 commit 9c19b3c

1 file changed

Lines changed: 51 additions & 35 deletions

File tree

src/bits.rs

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ pub type Word = c_ulong;
4141
/// This is a sealed trait with no interface. It is implemented for types in this library that
4242
/// `evdev` reports to userspace using bitfields.
4343
pub trait BitValue: Copy + sealed::BitValueImpl {
44-
/// The largest value that can be stored in a [`BitSet`].
45-
///
46-
/// Attempting to insert a value above this into a [`BitSet`] will panic.
44+
/// The largest value that can be stored in a [`BitSet`], after rounding to the next
45+
/// `unsigned long`.
4746
///
4847
/// Note that the exact value used for this associated constant should not be relied on as it
4948
/// is not stable.
@@ -85,7 +84,9 @@ impl<V: BitValue> BitSet<V> {
8584
///
8685
/// The number of [`Word`]s that make up any given [`BitSet`] can also vary between platforms,
8786
/// and is generally only guaranteed to be large enough to store
88-
/// [`<V as BitValue>::MAX`][BitValue::MAX], but may be arbitrarily larger.
87+
/// [`<V as BitValue>::MAX`][BitValue::MAX], but will be rounded up to the next multiple of
88+
/// `sizeof(unsigned long)`.
89+
///
8990
/// Additionally, the number of [`Word`]s may increase in minor and patch releases to make room
9091
/// for newly added enumeration constants.
9192
pub fn words(&self) -> &[Word] {
@@ -94,10 +95,6 @@ impl<V: BitValue> BitSet<V> {
9495

9596
/// Returns a mutable reference to the underlying [`Word`]s making up this [`BitSet`].
9697
///
97-
/// You should not set any bits to 1 whose indices are larger than
98-
/// [`<V as BitValue>::MAX`][BitValue::MAX]. Doing so might cause the [`BitSet`] to behave
99-
/// incorrectly.
100-
///
10198
/// Further, all the same considerations from [`BitSet::words`] apply here as well.
10299
pub fn words_mut(&mut self) -> &mut [Word] {
103100
self.words.as_mut()
@@ -119,16 +116,17 @@ impl<V: BitValue> BitSet<V> {
119116

120117
/// Returns whether `self` contains `value`.
121118
pub fn contains(&self, value: V) -> bool {
122-
if value.into_index() > V::MAX.into_index() {
123-
return false;
124-
}
125119
let index = value.into_index();
126120
let wordpos = index / Word::BITS as usize;
127121
let bitpos = index % Word::BITS as usize;
128122

129-
let word = self.words.as_ref()[wordpos];
130-
let bit = word & (1 << bitpos) != 0;
131-
bit
123+
match self.words.as_ref().get(wordpos) {
124+
Some(&word) => {
125+
let bit = word & (1 << bitpos) != 0;
126+
bit
127+
}
128+
None => false,
129+
}
132130
}
133131

134132
/// Inserts `value` into `self`, setting the appropriate bit.
@@ -137,38 +135,42 @@ impl<V: BitValue> BitSet<V> {
137135
///
138136
/// # Panics
139137
///
140-
/// Panics if `value` is larger than [`<V as BitValue>::MAX`][BitValue::MAX].
138+
/// Panics if `value` is out of range and there is no bit allocated to store it.
139+
/// The total number of bits available is [`<V as BitValue>::MAX`][BitValue::MAX], rounded up to
140+
/// the nearest multiple of `sizeof(unsigned long)`.
141141
pub fn insert(&mut self, value: V) -> bool {
142-
assert!(
143-
value.into_index() <= V::MAX.into_index(),
144-
"value out of range for `BitSet` storage (value's index is {}, max is {})",
145-
value.into_index(),
146-
V::MAX.into_index(),
147-
);
148-
149-
let present = self.contains(value);
150-
151142
let index = value.into_index();
152143
let wordpos = index / Word::BITS as usize;
153144
let bitpos = index % Word::BITS as usize;
154-
self.words.as_mut()[wordpos] |= 1 << bitpos;
155-
present
145+
match self.words.as_mut().get_mut(wordpos) {
146+
Some(word) => {
147+
let old = *word;
148+
*word |= 1 << bitpos;
149+
*word != old
150+
}
151+
None => panic!(
152+
"value out of range for `BitSet`: value's index is {index}, capacity is {}",
153+
size_of::<Word>() * self.words().len(),
154+
),
155+
}
156156
}
157157

158158
/// Removes `value` from the set.
159159
///
160160
/// Returns `true` if it was present and has been removed, or `false` if it was not present.
161161
pub fn remove(&mut self, value: V) -> bool {
162-
if value.into_index() > V::MAX.into_index() {
163-
return false;
164-
}
165162
let present = self.contains(value);
166163

167164
let index = value.into_index();
168165
let wordpos = index / Word::BITS as usize;
169166
let bitpos = index % Word::BITS as usize;
170-
self.words.as_mut()[wordpos] &= !(1 << bitpos);
171-
present
167+
match self.words.as_mut().get_mut(wordpos) {
168+
Some(word) => {
169+
*word &= !(1 << bitpos);
170+
present
171+
}
172+
None => false,
173+
}
172174
}
173175

174176
/// Returns an iterator over all values in `self`.
@@ -338,7 +340,7 @@ mod tests {
338340
#[test]
339341
fn bit0() {
340342
let mut set = BitSet::new();
341-
set.insert(InputProp(0));
343+
assert!(set.insert(InputProp(0)));
342344

343345
assert!(set.contains(InputProp::POINTER));
344346
assert!(!set.contains(InputProp::DIRECT));
@@ -352,20 +354,34 @@ mod tests {
352354
#[test]
353355
fn max() {
354356
let mut set = BitSet::new();
355-
set.insert(InputProp::MAX);
357+
assert!(set.insert(InputProp::MAX));
356358

357359
assert!(!set.contains(InputProp::POINTER));
358360
assert!(!set.contains(InputProp::DIRECT));
359361
assert!(set.contains(InputProp::MAX));
360362
assert!(!set.contains(InputProp(InputProp::MAX.0 + 1)));
361363
assert!(!set.remove(InputProp(InputProp::MAX.0 + 1)));
364+
365+
assert!(set.insert(InputProp(InputProp::MAX.0 + 1)));
366+
367+
assert_eq!(
368+
set.iter().collect::<Vec<_>>(),
369+
&[InputProp::MAX, InputProp(InputProp::MAX.0 + 1)],
370+
);
371+
372+
assert!(!set.contains(InputProp::DIRECT));
373+
assert!(set.contains(InputProp::MAX));
374+
assert!(set.contains(InputProp(InputProp::MAX.0 + 1)));
375+
assert!(set.remove(InputProp(InputProp::MAX.0 + 1)));
376+
assert!(set.contains(InputProp::MAX));
377+
assert!(!set.contains(InputProp(InputProp::MAX.0 + 1)));
362378
}
363379

364380
#[test]
365381
#[should_panic = "value out of range for `BitSet`"]
366-
fn above_max() {
382+
fn out_of_range() {
367383
let mut set = BitSet::new();
368-
set.insert(Abs::from_raw(Abs::MAX.raw() + 1));
384+
set.insert(InputProp(200));
369385
}
370386

371387
#[test]

0 commit comments

Comments
 (0)