Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(from a contributor) Implement get_mut and iter_mut and MutBorrowedBit #85

Merged
merged 9 commits into from
May 31, 2024
177 changes: 177 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
#[macro_use]
extern crate std;
#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(feature = "std")]
use std::vec::Vec;

#[cfg(feature = "serde")]
Expand All @@ -108,8 +110,11 @@
#[macro_use]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::rc::Rc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use core::cell::RefCell;
use core::cmp;
use core::cmp::Ordering;
use core::fmt;
Expand Down Expand Up @@ -583,6 +588,56 @@
block & (B::one() << b) != B::zero()
}

/// Retrieves a smart pointer to the value at index `i`, or `None` if the index is out of bounds.
///
/// # Examples
///
/// ```
/// use bit_vec::BitVec;
///
/// let mut bv = BitVec::from_bytes(&[0b01100000]);
/// *bv.get_mut(0).unwrap() = true;
/// *bv.get_mut(1).unwrap() = false;
/// assert!(bv.get_mut(100).is_none());
/// assert_eq!(bv, BitVec::from_bytes(&[0b10100000]));
/// ```
#[inline]
pub fn get_mut(&mut self, index: usize) -> Option<MutBorrowedBit<B>> {
self.get(index).map(move |value| MutBorrowedBit {
vec: Rc::new(RefCell::new(self)),
index,
#[cfg(debug_assertions)]
old_value: value,
new_value: value,
})
}

/// Retrieves a smart pointer to the value at index `i`, without doing bounds checking.
///
/// # Examples
///
/// ```
/// use bit_vec::BitVec;
///
/// let mut bv = BitVec::from_bytes(&[0b01100000]);
/// unsafe {
/// *bv.get_unchecked_mut(0) = true;
/// *bv.get_unchecked_mut(1) = false;
/// }
/// assert_eq!(bv, BitVec::from_bytes(&[0b10100000]));
/// ```
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> MutBorrowedBit<B> {

Check warning on line 630 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

unsafe function's docs miss `# Safety` section

warning: unsafe function's docs miss `# Safety` section --> src/lib.rs:630:5 | 630 | pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> MutBorrowedBit<B> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc = note: `#[warn(clippy::missing_safety_doc)]` on by default
let value = self.get_unchecked(index);
MutBorrowedBit {
#[cfg(debug_assertions)]
old_value: value,
new_value: value,
vec: Rc::new(RefCell::new(self)),
index,
}
}

/// Sets the value of a bit at an index `i`.
///
/// # Panics
Expand Down Expand Up @@ -1055,6 +1110,31 @@
}
}

/// Returns an iterator over mutable smart pointers to the elements of the vector in order.
///
/// # Examples
///
/// ```
/// use bit_vec::BitVec;
///
/// let mut a = BitVec::from_elem(8, false);
/// a.iter_mut().enumerate().for_each(|(index, mut bit)| {
/// *bit = if index % 2 == 1 { true } else { false };
/// });
/// assert!(a.eq_vec(&[
/// false, true, false, true, false, true, false, true
/// ]));
/// ```
#[inline]
pub fn iter_mut(&mut self) -> IterMut<B> {
self.ensure_invariant();
let nbits = self.nbits;
IterMut {
vec: Rc::new(RefCell::new(self)),
range: 0..nbits,
}
}

/// Moves all bits from `other` into `Self`, leaving `other` empty.
///
/// # Examples
Expand Down Expand Up @@ -1653,6 +1733,62 @@
range: Range<usize>,
}

#[derive(Debug)]
pub struct MutBorrowedBit<'a, B: 'a + BitBlock> {
vec: Rc<RefCell<&'a mut BitVec<B>>>,
index: usize,
#[cfg(debug_assertions)]
old_value: bool,
new_value: bool,
}

/// An iterator for mutable references to the bits in a `BitVec`.
pub struct IterMut<'a, B: 'a + BitBlock = u32> {
vec: Rc<RefCell<&'a mut BitVec<B>>>,
range: Range<usize>,
}

impl<'a, B: 'a + BitBlock> IterMut<'a, B> {
fn get(&mut self, index: Option<usize>) -> Option<MutBorrowedBit<'a, B>> {
let index = index?;
let value = (*self.vec).borrow().get(index)?;
Some(MutBorrowedBit {
vec: self.vec.clone(),
index,
#[cfg(debug_assertions)]
old_value: value,
new_value: value,
})
}
}

impl<'a, B: BitBlock> Deref for MutBorrowedBit<'a, B> {
type Target = bool;

fn deref(&self) -> &Self::Target {
&self.new_value
}
}

impl<'a, B: BitBlock> DerefMut for MutBorrowedBit<'a, B> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.new_value
}
}

impl<'a, B: BitBlock> Drop for MutBorrowedBit<'a, B> {
fn drop(&mut self) {
let mut vec = (*self.vec).borrow_mut();
#[cfg(debug_assertions)]
debug_assert_eq!(
Some(self.old_value),
vec.get(self.index),
"Mutably-borrowed bit was modified externally!"
);
vec.set(self.index, self.new_value);
}
}

impl<'a, B: BitBlock> Iterator for Iter<'a, B> {
type Item = bool;

Expand All @@ -1668,15 +1804,39 @@
}
}

impl<'a, B: BitBlock> Iterator for IterMut<'a, B> {
type Item = MutBorrowedBit<'a, B>;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
let index = self.range.next();
self.get(index)
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}

impl<'a, B: BitBlock> DoubleEndedIterator for Iter<'a, B> {
#[inline]
fn next_back(&mut self) -> Option<bool> {
self.range.next_back().map(|i| self.bit_vec.get(i).unwrap())
}
}

impl<'a, B: BitBlock> DoubleEndedIterator for IterMut<'a, B> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
let index = self.range.next_back();
self.get(index)
}
}

impl<'a, B: BitBlock> ExactSizeIterator for Iter<'a, B> {}

impl<'a, B: BitBlock> ExactSizeIterator for IterMut<'a, B> {}

impl<'a, B: BitBlock> IntoIterator for &'a BitVec<B> {
type Item = bool;
type IntoIter = Iter<'a, B>;
Expand Down Expand Up @@ -2159,7 +2319,7 @@

#[test]
fn test_from_bools() {
let bools = vec![true, false, true, true];

Check warning on line 2322 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

useless use of `vec!`

warning: useless use of `vec!` --> src/lib.rs:2322:21 | 2322 | let bools = vec![true, false, true, true]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[true, false, true, true]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec = note: `#[warn(clippy::useless_vec)]` on by default
let bit_vec: BitVec = bools.iter().copied().collect();
assert_eq!(format!("{:?}", bit_vec), "1011");
}
Expand Down Expand Up @@ -2393,16 +2553,16 @@
fn test_bit_vec_push_pop() {
let mut s = BitVec::from_elem(5 * U32_BITS - 2, false);
assert_eq!(s.len(), 5 * U32_BITS - 2);
assert_eq!(s[5 * U32_BITS - 3], false);

Check warning on line 2556 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2556:9 | 2556 | assert_eq!(s[5 * U32_BITS - 3], false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison = note: `#[warn(clippy::bool_assert_comparison)]` on by default help: replace it with `assert!(..)` | 2556 - assert_eq!(s[5 * U32_BITS - 3], false); 2556 + assert!(!s[5 * U32_BITS - 3]); |
s.push(true);
s.push(true);
assert_eq!(s[5 * U32_BITS - 2], true);

Check warning on line 2559 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2559:9 | 2559 | assert_eq!(s[5 * U32_BITS - 2], true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2559 - assert_eq!(s[5 * U32_BITS - 2], true); 2559 + assert!(s[5 * U32_BITS - 2]); |
assert_eq!(s[5 * U32_BITS - 1], true);

Check warning on line 2560 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2560:9 | 2560 | assert_eq!(s[5 * U32_BITS - 1], true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2560 - assert_eq!(s[5 * U32_BITS - 1], true); 2560 + assert!(s[5 * U32_BITS - 1]); |
// Here the internal vector will need to be extended
s.push(false);
assert_eq!(s[5 * U32_BITS], false);

Check warning on line 2563 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2563:9 | 2563 | assert_eq!(s[5 * U32_BITS], false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2563 - assert_eq!(s[5 * U32_BITS], false); 2563 + assert!(!s[5 * U32_BITS]); |
s.push(false);
assert_eq!(s[5 * U32_BITS + 1], false);

Check warning on line 2565 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2565:9 | 2565 | assert_eq!(s[5 * U32_BITS + 1], false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2565 - assert_eq!(s[5 * U32_BITS + 1], false); 2565 + assert!(!s[5 * U32_BITS + 1]); |
assert_eq!(s.len(), 5 * U32_BITS + 2);
// Pop it all off
assert_eq!(s.pop(), Some(false));
Expand Down Expand Up @@ -2451,10 +2611,10 @@
s.push(true);
s.push(false);
s.push(true);
assert_eq!(s[5 * U32_BITS - 1], true);

Check warning on line 2614 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2614:9 | 2614 | assert_eq!(s[5 * U32_BITS - 1], true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2614 - assert_eq!(s[5 * U32_BITS - 1], true); 2614 + assert!(s[5 * U32_BITS - 1]); |
assert_eq!(s[5 * U32_BITS - 0], true);

Check warning on line 2615 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

this operation has no effect

warning: this operation has no effect --> src/lib.rs:2615:22 | 2615 | assert_eq!(s[5 * U32_BITS - 0], true); | ^^^^^^^^^^^^^^^^ help: consider reducing it to: `5 * U32_BITS` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#identity_op = note: `#[warn(clippy::identity_op)]` on by default

Check warning on line 2615 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2615:9 | 2615 | assert_eq!(s[5 * U32_BITS - 0], true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2615 - assert_eq!(s[5 * U32_BITS - 0], true); 2615 + assert!(s[5 * U32_BITS - 0]); |
assert_eq!(s[5 * U32_BITS + 1], false);

Check warning on line 2616 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2616:9 | 2616 | assert_eq!(s[5 * U32_BITS + 1], false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2616 - assert_eq!(s[5 * U32_BITS + 1], false); 2616 + assert!(!s[5 * U32_BITS + 1]); |
assert_eq!(s[5 * U32_BITS + 2], true);

Check warning on line 2617 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2617:9 | 2617 | assert_eq!(s[5 * U32_BITS + 2], true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2617 - assert_eq!(s[5 * U32_BITS + 2], true); 2617 + assert!(s[5 * U32_BITS + 2]); |
}

#[test]
Expand Down Expand Up @@ -2641,7 +2801,7 @@

#[test]
fn test_into_iter() {
let bools = vec![true, false, true, true];

Check warning on line 2804 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

useless use of `vec!`

warning: useless use of `vec!` --> src/lib.rs:2804:21 | 2804 | let bools = vec![true, false, true, true]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can use an array directly: `[true, false, true, true]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
let bit_vec: BitVec = bools.iter().copied().collect();
let mut iter = bit_vec.into_iter();
assert_eq!(Some(true), iter.next());
Expand Down Expand Up @@ -2753,7 +2913,7 @@
a.append(&mut b);
a.append(&mut c);

assert_eq!(&[01, 00, 02, 03][..], &*a.to_bytes());

Check warning on line 2916 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

this is a decimal constant

warning: this is a decimal constant --> src/lib.rs:2916:34 | 2916 | assert_eq!(&[01, 00, 02, 03][..], &*a.to_bytes()); | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal help: if you mean to use a decimal constant, remove the `0` to avoid confusion | 2916 | assert_eq!(&[01, 00, 02, 3][..], &*a.to_bytes()); | ~ help: if you mean to use an octal constant, use `0o` | 2916 | assert_eq!(&[01, 00, 02, 0o3][..], &*a.to_bytes()); | ~~~

Check warning on line 2916 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

this is a decimal constant

warning: this is a decimal constant --> src/lib.rs:2916:30 | 2916 | assert_eq!(&[01, 00, 02, 03][..], &*a.to_bytes()); | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal help: if you mean to use a decimal constant, remove the `0` to avoid confusion | 2916 | assert_eq!(&[01, 00, 2, 03][..], &*a.to_bytes()); | ~ help: if you mean to use an octal constant, use `0o` | 2916 | assert_eq!(&[01, 00, 0o2, 03][..], &*a.to_bytes()); | ~~~

Check warning on line 2916 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

this is a decimal constant

warning: this is a decimal constant --> src/lib.rs:2916:22 | 2916 | assert_eq!(&[01, 00, 02, 03][..], &*a.to_bytes()); | ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal = note: `#[warn(clippy::zero_prefixed_literal)]` on by default help: if you mean to use a decimal constant, remove the `0` to avoid confusion | 2916 | assert_eq!(&[1, 00, 02, 03][..], &*a.to_bytes()); | ~ help: if you mean to use an octal constant, use `0o` | 2916 | assert_eq!(&[0o1, 00, 02, 03][..], &*a.to_bytes()); | ~~~
}

#[test]
Expand Down Expand Up @@ -2796,7 +2956,7 @@
let mut t = BitVec::from_elem(i, true);
let mut f = BitVec::from_elem(i, false);
assert_eq!(i as u64, t.count_ones());
assert_eq!(0 as u64, f.count_ones());

Check warning on line 2959 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

casting integer literal to `u64` is unnecessary

warning: casting integer literal to `u64` is unnecessary --> src/lib.rs:2959:24 | 2959 | assert_eq!(0 as u64, f.count_ones()); | ^^^^^^^^ help: try: `0_u64` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: `#[warn(clippy::unnecessary_cast)]` on by default
if i > 20 {
t.set(10, false);
t.set(i - 10, false);
Expand All @@ -2814,7 +2974,7 @@
let mut tbits = BitVec::from_elem(i, true);
let mut fbits = BitVec::from_elem(i, false);
assert_eq!(i as u64, fbits.count_zeros());
assert_eq!(0 as u64, tbits.count_zeros());

Check warning on line 2977 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

casting integer literal to `u64` is unnecessary

warning: casting integer literal to `u64` is unnecessary --> src/lib.rs:2977:24 | 2977 | assert_eq!(0 as u64, tbits.count_zeros()); | ^^^^^^^^ help: try: `0_u64` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
if i > 20 {
fbits.set(10, true);
fbits.set(i - 10, true);
Expand All @@ -2825,4 +2985,21 @@
}
}
}

fn test_get_mut() {

Check warning on line 2989 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

function `test_get_mut` is never used

warning: function `test_get_mut` is never used --> src/lib.rs:2989:8 | 2989 | fn test_get_mut() { | ^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default
let mut a = BitVec::from_elem(3, false);
let mut a_bit_1 = a.get_mut(1).unwrap();
assert_eq!(false, *a_bit_1);

Check warning on line 2992 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

used `assert_eq!` with a literal bool

warning: used `assert_eq!` with a literal bool --> src/lib.rs:2992:9 | 2992 | assert_eq!(false, *a_bit_1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison help: replace it with `assert!(..)` | 2992 - assert_eq!(false, *a_bit_1); 2992 + assert!(!(*a_bit_1)); |
*a_bit_1 = true;
drop(a_bit_1);
assert!(a.eq_vec(&[false, true, false]));
}
#[test]
fn test_iter_mut() {
let mut a = BitVec::from_elem(8, false);
a.iter_mut().enumerate().for_each(|(index, mut bit)| {
*bit = if index % 2 == 1 { true } else { false };

Check warning on line 3001 in src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

this if-then-else expression returns a bool literal

warning: this if-then-else expression returns a bool literal --> src/lib.rs:3001:20 | 3001 | *bit = if index % 2 == 1 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `index % 2 == 1` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool = note: `#[warn(clippy::needless_bool)]` on by default
});
assert!(a.eq_vec(&[false, true, false, true, false, true, false, true]));
}
}
Loading