//! Helpers for `Write`. use std::io; use endian_trait::Endian; /// Adds some additional related functionality for types implementing [`Write`](std::io::Write). /// /// Particularly for writing values of a specific endianness (types implementing [`Endian`]). /// /// Examples: /// ```no_run /// # use endian_trait::Endian; /// # use proxmox_io::WriteExt; /// /// #[derive(Endian)] /// #[repr(C)] /// struct Header { /// version: u16, /// data_size: u16, /// } /// /// # fn code(mut file: std::fs::File) -> std::io::Result<()> { /// let header = Header { /// version: 1, /// data_size: 16, /// }; /// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe: /// unsafe { /// file.write_le_value(header)?; /// } /// # Ok(()) /// # } /// ``` /// /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html pub trait WriteExt { /// Write a value with host endianness. /// /// This is limited to types implementing the [`Endian`] trait under the assumption that this /// is only done for types which are supposed to be read/writable directly. /// /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore /// this is considered unsafe. /// /// The underlying write call is `.write_all()`, so there are no partial writes. /// /// ```no_run /// # use endian_trait::Endian; /// use proxmox_io::WriteExt; /// /// #[derive(Endian)] /// #[repr(C, packed)] /// struct Data { /// value: u16, /// count: u32, /// } /// /// # fn code() -> std::io::Result<()> { /// let mut file = std::fs::File::create("my-raw.dat")?; /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can /// // safely use our helper: /// unsafe { /// file.write_host_value(Data { /// value: 1, /// count: 2, /// })?; /// } /// # Ok(()) /// # } /// ``` /// /// # Safety /// /// This should only used for types with a defined storage representation, usually /// `#[repr(C)]`, otherwise the results may be inconsistent. /// /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html unsafe fn write_host_value(&mut self, value: T) -> io::Result<()>; /// Write a little endian value. /// /// The input type is required to implement the [`Endian`] trait, and we make the assumption /// that this is only done for types which are supposed to be read/writable directly. /// /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore /// this is considered unsafe. /// /// The underlying write call is `.write_all()`, so there are no partial writes. /// /// ```no_run /// # use endian_trait::Endian; /// use proxmox_io::WriteExt; /// /// #[derive(Endian)] /// #[repr(C, packed)] /// struct Data { /// value: u16, /// count: u32, /// } /// /// # fn code() -> std::io::Result<()> { /// let mut file = std::fs::File::create("my-raw.dat")?; /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can /// // safely use our helper: /// unsafe { /// file.write_le_value(Data { /// value: 1, /// count: 2, /// })?; /// } /// # Ok(()) /// # } /// ``` /// /// # Safety /// /// This should only used for types with a defined storage representation, usually /// `#[repr(C)]`, otherwise the results may be inconsistent. /// /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html unsafe fn write_le_value(&mut self, value: T) -> io::Result<()>; /// Read a big endian value. /// /// The input type is required to implement the [`Endian`] trait, and we make the assumption /// that this is only done for types which are supposed to be read/writable directly. /// /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore /// this is considered unsafe. /// /// The underlying write call is `.write_all()`, so there are no partial writes. /// /// ```no_run /// # use endian_trait::Endian; /// use proxmox_io::WriteExt; /// /// #[derive(Endian)] /// #[repr(C, packed)] /// struct Data { /// value: u16, /// count: u32, /// } /// /// # fn code() -> std::io::Result<()> { /// let mut file = std::fs::File::create("my-raw.dat")?; /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can /// // safely use our helper: /// unsafe { /// file.write_be_value(Data { /// value: 1, /// count: 2, /// })?; /// } /// # Ok(()) /// # } /// ``` /// /// # Safety /// /// This should only used for types with a defined storage representation, usually /// `#[repr(C)]`, otherwise the results may be inconsistent. /// /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html unsafe fn write_be_value(&mut self, value: T) -> io::Result<()>; } impl WriteExt for W { unsafe fn write_host_value(&mut self, value: T) -> io::Result<()> { unsafe { self.write_all(std::slice::from_raw_parts( &value as *const T as *const u8, std::mem::size_of::(), )) } } unsafe fn write_le_value(&mut self, value: T) -> io::Result<()> { unsafe { self.write_host_value::(value.to_le()) } } unsafe fn write_be_value(&mut self, value: T) -> io::Result<()> { unsafe { self.write_host_value::(value.to_be()) } } }