//! Additional methods for Read and Write //! //! The additional methods implemented allow reading and writing integers and floats //! in the specified endianness. //! //! # Usage //! //! Basically, you need to `use` the trait WritePodExt or ReadPodExt. //! //! # Examples //! //! ## Reading //! //! To read some value from a reader, import ReadPodExt and the needed endianness. //! //! ``` //! use podio::{ReadPodExt, BigEndian}; //! //! let slice: &[u8] = &[0x10, 0x20, 0x30, 0x40]; //! let mut reader = std::io::Cursor::new(slice); //! //! let value = reader.read_u32::().unwrap(); //! //! assert_eq!(value, 0x10203040); //! ``` //! //! ## Writing //! //! For writing, you need to import the trait WritePodExt. //! //! ``` //! use podio::{WritePodExt, LittleEndian}; //! //! let slice: &mut [u8] = &mut [0; 2]; //! let mut writer = std::io::Cursor::new(slice); //! //! writer.write_u16::(0x8802).unwrap(); //! //! assert_eq!(writer.into_inner(), &[0x02, 0x88]); //! ``` //! //! ## Read exact //! //! One additional method, not really dealing with POD, is `read_exact`. //! //! ``` //! use podio::ReadPodExt; //! //! let slice: &[u8] = &[0, 1, 2, 3]; //! let mut reader = std::io::Cursor::new(slice); //! //! assert_eq!(reader.read_exact(1).unwrap(), [0]); //! assert_eq!(reader.read_exact(2).unwrap(), [1,2]); //! assert_eq!(reader.read_exact(0).unwrap(), []); //! assert_eq!(reader.read_exact(1).unwrap(), [3]); //! assert!(reader.read_exact(1).is_err()); #![warn(missing_docs)] use std::io; use std::io::prelude::*; /// Little endian. The number `0xABCD` is stored `[0xCD, 0xAB]` pub enum LittleEndian {} /// Big endian. The number `0xABCD` is stored `[0xAB, 0xCD]` pub enum BigEndian {} /// Trait implementing conversion methods for a specific endianness pub trait Endianness { /// Converts a value from the platform type to the specified endianness fn int_to_target(val: T) -> T; /// Converts a value from the sepcified endianness to the platform type fn int_from_target(val: T) -> T; } /// Generic trait for endian conversions on integers pub trait EndianConvert { /// Convert self to a big-endian value fn to_be(self) -> Self; /// Convert self to a little-endian value fn to_le(self) -> Self; /// Convert a big-endian value to the target endianness fn from_be(x: Self) -> Self; /// Convert a little-endian value to the target endiannes fn from_le(x: Self) -> Self; } /// Additional write methods for a io::Write pub trait WritePodExt { /// Write a u64 fn write_u64(&mut self, u64) -> io::Result<()>; /// Write a u32 fn write_u32(&mut self, u32) -> io::Result<()>; /// Write a u16 fn write_u16(&mut self, u16) -> io::Result<()>; /// Write a u8 fn write_u8(&mut self, u8) -> io::Result<()>; /// Write a i64 fn write_i64(&mut self, i64) -> io::Result<()>; /// Write a i32 fn write_i32(&mut self, i32) -> io::Result<()>; /// Write a i16 fn write_i16(&mut self, i16) -> io::Result<()>; /// Write a i8 fn write_i8(&mut self, i8) -> io::Result<()>; /// Write a f32 fn write_f32(&mut self, f32) -> io::Result<()>; /// Write a f64 fn write_f64(&mut self, f64) -> io::Result<()>; } /// Additional read methods for a io::Read pub trait ReadPodExt { /// Read a u64 fn read_u64(&mut self) -> io::Result; /// Read a u32 fn read_u32(&mut self) -> io::Result; /// Read a u16 fn read_u16(&mut self) -> io::Result; /// Read a u8 fn read_u8(&mut self) -> io::Result; /// Read a i64 fn read_i64(&mut self) -> io::Result; /// Read a i32 fn read_i32(&mut self) -> io::Result; /// Read a i16 fn read_i16(&mut self) -> io::Result; /// Read a i8 fn read_i8(&mut self) -> io::Result; /// Read a f32 fn read_f32(&mut self) -> io::Result; /// Read a f64 fn read_f64(&mut self) -> io::Result; /// Read a specific number of bytes fn read_exact(&mut self, usize) -> io::Result>; } impl Endianness for LittleEndian { #[inline] fn int_to_target(val: T) -> T { val.to_le() } #[inline] fn int_from_target(val: T) -> T { ::from_le(val) } } impl Endianness for BigEndian { #[inline] fn int_to_target(val: T) -> T { val.to_be() } #[inline] fn int_from_target(val: T) -> T { ::from_be(val) } } macro_rules! impl_platform_convert { ($T:ty) => { impl EndianConvert for $T { #[inline] fn to_be(self) -> $T { self.to_be() } #[inline] fn to_le(self) -> $T { self.to_le() } #[inline] fn from_be(x: $T) -> $T { if cfg!(target_endian = "big") { x } else { x.swap_bytes() } } #[inline] fn from_le(x: $T) -> $T { if cfg!(target_endian = "little") { x } else { x.swap_bytes() } } } }; } impl_platform_convert!(u8); impl_platform_convert!(u16); impl_platform_convert!(u32); impl_platform_convert!(u64); macro_rules! val_to_buf { ($val:ident, $T:expr) => { { let mut buf = [0; $T]; for i in 0..buf.len() { buf[i] = ($val >> (i * 8)) as u8; } buf } }; } impl WritePodExt for W { fn write_u64(&mut self, val: u64) -> io::Result<()> { let tval = ::int_to_target(val); let buf = val_to_buf!(tval, 8); self.write_all(&buf) } fn write_u32(&mut self, val: u32) -> io::Result<()> { let tval = ::int_to_target(val); let buf = val_to_buf!(tval, 4); self.write_all(&buf) } fn write_u16(&mut self, val: u16) -> io::Result<()> { let tval = ::int_to_target(val); let buf = val_to_buf!(tval, 2); self.write_all(&buf) } fn write_u8(&mut self, val: u8) -> io::Result<()> { self.write_all(&[val]) } fn write_i64(&mut self, val: i64) -> io::Result<()> { self.write_u64::(val as u64) } fn write_i32(&mut self, val: i32) -> io::Result<()> { self.write_u32::(val as u32) } fn write_i16(&mut self, val: i16) -> io::Result<()> { self.write_u16::(val as u16) } fn write_i8(&mut self, val: i8) -> io::Result<()> { self.write_u8(val as u8) } fn write_f32(&mut self, val: f32) -> io::Result<()> { let tval: u32 = unsafe { std::mem::transmute::(val) }; self.write_u32::(tval) } fn write_f64(&mut self, val: f64) -> io::Result<()> { let tval: u64 = unsafe { std::mem::transmute::(val) }; self.write_u64::(tval) } } #[inline] fn fill_buf(reader: &mut R, buf: &mut [u8]) -> io::Result<()> { let mut idx = 0; while idx != buf.len() { match reader.read(&mut buf[idx..]) { Ok(0) => return Err(io::Error::new(io::ErrorKind::Other, "Could not read enough bytes")), Ok(v) => { idx += v; } Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} Err(e) => return Err(e), } } Ok(()) } macro_rules! buf_to_val { ($buf:ident, $T:ty) => { { let mut val: $T = 0; for i in 0..$buf.len() { val |= ($buf[i] as $T) << (i * 8); } val } }; } impl ReadPodExt for R { fn read_u64(&mut self) -> io::Result { let buf = &mut [0u8; 8]; try!(fill_buf(self, buf)); let tval = buf_to_val!(buf, u64); Ok(::int_from_target(tval)) } fn read_u32(&mut self) -> io::Result { let buf = &mut [0u8; 4]; try!(fill_buf(self, buf)); let tval = buf_to_val!(buf, u32); Ok(::int_from_target(tval)) } fn read_u16(&mut self) -> io::Result { let buf = &mut [0u8; 2]; try!(fill_buf(self, buf)); let tval = buf_to_val!(buf, u16); Ok(::int_from_target(tval)) } fn read_u8(&mut self) -> io::Result { let buf = &mut [0u8; 1]; try!(fill_buf(self, buf)); Ok(buf[0]) } fn read_i64(&mut self) -> io::Result { self.read_u64::().map(|v| v as i64) } fn read_i32(&mut self) -> io::Result { self.read_u32::().map(|v| v as i32) } fn read_i16(&mut self) -> io::Result { self.read_u16::().map(|v| v as i16) } fn read_i8(&mut self) -> io::Result { self.read_u8().map(|v| v as i8) } fn read_f64(&mut self) -> io::Result { self.read_u64::().map(|v| unsafe { std::mem::transmute::(v) }) } fn read_f32(&mut self) -> io::Result { self.read_u32::().map(|v| unsafe { std::mem::transmute::(v) }) } fn read_exact(&mut self, len: usize) -> io::Result> { let mut res = vec![0; len]; try!(fill_buf(self, &mut res)); Ok(res) } }