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

Feature request: Add write_uXX_from to WriteBytesExt trait #155

Open
kacejot opened this issue Nov 8, 2019 · 4 comments
Open

Feature request: Add write_uXX_from to WriteBytesExt trait #155

kacejot opened this issue Nov 8, 2019 · 4 comments

Comments

@kacejot
Copy link

kacejot commented Nov 8, 2019

I write some kind of protocol parser and I use next code to read protocol data:

reader.read_u32_into::<LittleEndian>(&mut buffer);

I would like to use the same logic for writer:

writer.write_u32_from::<LittleEndian>(&buffer);

Is that already possible? Or it needs implementation?

@kacejot kacejot changed the title Feature request: Imlement write_uXX_from for WriteBytesExt Feature request: Add write_uXX_from to WriteBytesExt trait Nov 8, 2019
@BurntSushi
Copy link
Owner

I think there would be fine to add, yes. I believe the necessary APIs for implementing this are already available on the ByteOrder trait.

@kacejot
Copy link
Author

kacejot commented Jan 20, 2021

@BurntSushi,
For now I see no other way except writing each chunk separately:

#[inline]
fn write_u16_from<T: ByteOrder>(&mut self, src: &[u16]) -> Result<()> {
    for &n in src {
        self.write_u16::<T>(n)?
    }

    Ok(())
}

The problem here is that we can get an error after some data is already written. In this case we can do nothing to roll back the writer to last stable state.
It is done mostly like write_slice macro. The only difference is that write_slice is able to check if src fits dst and panic if it doesn't.

@BurntSushi
Copy link
Owner

If that's the only implementation choice, then we probably shouldn't provide it.

@khoover
Copy link

khoover commented Feb 25, 2023

You can do a bit better if you check if T is the same as NativeEndian or not; if it is, you can just cast the slice into bytes and use write_all.

It would look something a bit like

pub trait ByteOrderIo: ByteOrder {
  fn write_u16_from<W: Write + ?Sized>(writer: &mut W, src: &[u16]) -> Result<()>;
  // ...
}

impl ByteOrderIo for LittleEndian {
    #[cfg(target_endian = "little")]
    fn write_u16_from<W: Write + ?Sized>(writer: &mut W, src: &[u16]) -> Result<()> {
        let base = src.as_ptr() as *const u8;
        // SAFETY: I don't think len can overflow here, since creating a slice at all
        // requires len * size_of <= isize::MAX, but maybe do some splitting into parts
        // if that's a worry.
        let casted_slice = unsafe { std::slice::from_raw_parts(base, src.len() * std::mem::size_of::<u16>()) };
        writer.write_all(casted_slice)
    }

    #[cfg(not(target_endian = "little"))]
    pub fn write_u16_from<W: Write + ?Sized>(writer: &mut W, src: &[u16]) -> Result<()> {
        // But we could also batch calls to write_all here via using a larger buffer.
        let mut buf = [0u8; 2];
        for &n in src {
            Self::write_u16(&mut buf, n);
            writer.write_all(&buf)?;
        }
        Ok(())
    }
}

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

No branches or pull requests

3 participants