#Flattening an Iterator of Cow<[u8]>.

15 messages · Page 1 of 1 (latest)

undone roost
#

I've an iterator of cow<[u8]>, which i want to flatten to an iterator of u8, ideally without allocating.

fallen python
#

?eval ```rust
use std::borrow::Cow;

fn main() {
let cows: &[Cow<'static, [u8]>] = &[
Cow::Borrowed([0_u8].as_slice()),
Cow::Borrowed([1_u8, 2, 3].as_slice()),
Cow::Owned(vec![4_u8, 5, 6]),
];

for n in cows.iter().flat_map(AsRef::as_ref).copied() {
    let n: u8 = n;
    println!("{n}");
}

}

mental notchBOT
#
0
1
2
3
4
5
6```
fallen python
#

@undone roost You basically do .flat_map(AsRef::as_ref).copied(). That turns the Cows into &[u8]s and flattens them into a bunch of &u8s followed by copying the value out of each &u8 to give you u8s.

#

The let n: u8 = n; is just there to prove that the loop's n has the type u8.

undone roost
#

Thanks a lot!

misty seal
#

BTW, this only works for an iterator of &Cow, not an iterator of Cow

undone roost
#

Yeah I've got an iterator of Cow. Currently this is my solution and it's fast enough: rust self.tlvs.iter().map(TLV::to_bytes).collect::<Vec<Cow<[u8]>>>().concat()

misty seal
#

You can make a custom iterator to do this, but usually you'd actually want to make a custom Read implementer.

#

What are you doing with the bytes?

undone roost
#

They get chained to a header and then returned as a Cow<[u8]>.

misty seal
#

Like Read::chain?

#

Well, I made a thing you can use as Read or Iterator.

#[derive(Debug, Clone)]
struct FlattenedCow<'a, I, T>
where
    T: Clone,
{
    current: Option<Cow<'a, [T]>>,
    index: usize,
    inner: I,
}

impl<I, T> FlattenedCow<'_, I, T>
where
    T: Clone,
{
    fn new<J: IntoIterator<IntoIter = I>>(iter: J) -> Self {
        Self {
            current: None,
            index: 0,
            inner: iter.into_iter(),
        }
    }
}
#
impl<'a, I> Read for FlattenedCow<'a, I, u8>
where
    I: Iterator<Item = Cow<'a, [u8]>>,
{
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        loop {
            if let Some(slice) = self.current.as_deref() {
                let slice = &slice[self.index..];

                if slice.is_empty() {
                    self.current = None;
                    continue;
                }

                use std::cmp::Ordering;
                match buf.len().cmp(&slice.len()) {
                    Ordering::Less => {
                        buf.copy_from_slice(&slice[..buf.len()]);
                        self.index += buf.len();
                        break Ok(buf.len());
                    }

                    Ordering::Equal => {
                        buf.copy_from_slice(slice);
                        self.current = None;
                        break Ok(buf.len());
                    }

                    Ordering::Greater => {
                        let len = slice.len();
                        buf[..len].copy_from_slice(slice);
                        self.current = None;
                        break Ok(len);
                    }
                }
            } else {
                self.current = self.inner.next();

                if self.current.is_none() {
                    break Ok(0);
                }
            }
        }
    }
}
#

impl<'a, I, T> Iterator for FlattenedCow<'a, I, T>
where
    I: Iterator<Item = Cow<'a, [T]>>,
    T: Clone,
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let byte = self
                .current
                .as_ref()
                .and_then(|cow| cow.get(self.index).cloned());

            if let Some(byte) = byte {
                self.index += 1;
                break Some(byte);
            } else {
                self.current = self.inner.next();

                if self.current.is_none() {
                    break None;
                }

                self.index = 0;
            }
        }
    }
}