Skip to content

Commit

Permalink
Merge pull request #85 from contain-rs/iter-mut-get-mut
Browse files Browse the repository at this point in the history
(from a contributor) Implement `get_mut` and `iter_mut` and `MutBorrowedBit`
  • Loading branch information
pczarn authored May 31, 2024
2 parents 56ab55d + 8bd0242 commit a5d445d
Showing 1 changed file with 177 additions and 0 deletions.
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 @@ use nanoserde::{DeBin, DeJson, DeRon, SerBin, SerJson, SerRon};
#[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 @@ impl<B: BitBlock> BitVec<B> {
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 @@ impl<B: BitBlock> BitVec<B> {
}
}

/// 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 @@ pub struct Iter<'a, B: 'a = u32> {
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 Iter<'a, B> {
}
}

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 @@ -2825,4 +2985,21 @@ mod tests {
}
}
}

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]));
}
}

0 comments on commit a5d445d

Please sign in to comment.