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

Regarding the safety of chunks_mut(...).remove_alias() #247

Open
koenichiwa opened this issue Oct 24, 2023 · 1 comment
Open

Regarding the safety of chunks_mut(...).remove_alias() #247

koenichiwa opened this issue Oct 24, 2023 · 1 comment

Comments

@koenichiwa
Copy link

Hi I had a question regarding the safety of remove_alias. The safety explanation is a bit hard for me to understand. Especially this part:

You must consume the adapted iterator in a loop that does not allow multiple yielded items to exist in the same scope. Each yielded item must have a completely non-overlapping lifetime from all the others.

I kinda think you mean that it's unsafe to have multiple chuncks pointing to overlapping parts of the same data, but I'm not sure. Could you help me understand it a bit better?

In my project I need a fixed size array of nibbles, which is one of the reasons I'm using bit_vec in my project. I tried to make a simple example of how I use it:

use bitvec::prelude::{BitArray, BitSlice, Lsb0};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Nibble(BitArray<u8, Lsb0>);

impl Nibble {
    pub const BITS: usize = 4;
    pub const MAX: u8 = 2u8.pow(Self::BITS as u32);

    pub fn new(value: u8) -> Self {
        assert!(value < Self::MAX);
        Nibble(BitArray::new(value))
    }

    pub fn as_bitslice(&self) -> &BitSlice<u8, Lsb0> {
        &self.0[0..Self::BITS]
    }
}

fn example(nibbles: &[Nibble; 40]) -> BitArray<[u8; 20], Lsb0> {
    let mut result = BitArray::ZERO;
    nibbles.iter()
        .map(Nibble::as_bitslice)
        .zip( unsafe { result.chunks_mut(Nibble::BITS).remove_alias() } )
        .for_each(|(incoming, outgoing)|{
            outgoing.copy_from_bitslice(incoming);
    });
    result
}

Is this safe? Is this an XY problem and should I use a different method?

@koenichiwa
Copy link
Author

koenichiwa commented Oct 24, 2023

I think it might've been an XY problem. I'm still hoping for the explanation regarding safety though. Just out of curiosity. Maybe you have an even better solution.

I solved it with something like:

use bitvec::prelude::{BitArray, BitSlice, Lsb0, BitVec};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Nibble(BitArray<u8, Lsb0>);

impl Nibble {
    pub const ZERO: Self = Self(BitArray::ZERO);
    pub const BITS: usize = 4;
    pub const MAX: u8 = 2u8.pow(Self::BITS as u32);

    pub fn new(value: u8) -> Self {
        assert!(value < Self::MAX, "{value} is too high");
        Nibble(BitArray::new(value))
    }

    pub fn as_bitslice(&self) -> &BitSlice<u8, Lsb0> {
        &self.0[0..Self::BITS]
    }
}

impl FromIterator<Nibble> for BitVec<u8, Lsb0> {
    fn from_iter<T: IntoIterator<Item = Nibble>>(iter: T) -> Self {
        iter.into_iter().fold(BitVec::<u8, Lsb0>::EMPTY, |mut acc, nibble| {
            acc.extend_from_bitslice(nibble.as_bitslice());
            acc
        })
    }
}

#[cfg(test)]
mod test {
    use bitvec::prelude::{BitArray,Lsb0, BitVec};
    use super::Nibble;

    #[test]
    fn example() {
        let mut nibbles: [Nibble; Nibble::MAX as usize] = [Nibble::ZERO; Nibble::MAX as usize];
        for index in 0..nibbles.len() {
            nibbles[index] = Nibble::new(index as u8);
        }

        let mut result: BitArray<[u8; 8], Lsb0> = BitArray::ZERO;
        result.copy_from_bitslice(nibbles.iter().copied().collect::<BitVec<u8, Lsb0>>().as_bitslice());
        eprintln!("BitVec contents: {result}");

        result.chunks(4).enumerate().for_each(|(index, chunk)| {
            let vec: BitVec<u8, Lsb0> = BitVec::from_element(index as u8);
            let expected = &vec.as_bitslice()[0..Nibble::BITS];
            assert_eq!(chunk, expected);
        });
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant