Skip to content
Snippets Groups Projects
read_write_extension.rs 5.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Müller's avatar
    Daniel Müller committed
    use paste::paste;
    use std::io::{Read, Result, Write};
    
    /// Read extension to directly read numeric datatypes from a reader.
    /// All types are read in little endian byte order.
    pub trait ReadExtTypes {
        fn read_exact_u8(&mut self) -> Result<u8>;
        fn read_exact_i8(&mut self) -> Result<i8>;
    
        fn read_exact_u16(&mut self) -> Result<u16>;
        fn read_exact_i16(&mut self) -> Result<i16>;
    
        fn read_exact_u32(&mut self) -> Result<u32>;
        fn read_exact_i32(&mut self) -> Result<i32>;
    
        fn read_exact_u64(&mut self) -> Result<u64>;
        fn read_exact_i64(&mut self) -> Result<i64>;
    }
    
    /// Write extension to directly write numeric datatypes to a writer.
    /// All types are written in little endian byte order.
    pub trait WriteExtTypes {
        fn write_all_u8(&mut self, val: u8) -> Result<()>;
        fn write_all_i8(&mut self, val: i8) -> Result<()>;
    
        fn write_all_u16(&mut self, val: u16) -> Result<()>;
        fn write_all_i16(&mut self, val: i16) -> Result<()>;
    
        fn write_all_u32(&mut self, val: u32) -> Result<()>;
        fn write_all_i32(&mut self, val: i32) -> Result<()>;
    
        fn write_all_u64(&mut self, val: u64) -> Result<()>;
        fn write_all_i64(&mut self, val: i64) -> Result<()>;
    }
    
    macro_rules! impl_read_ext_types {
        ($bits:tt) => {
            paste! {
                fn [< read_exact_ u $bits >](&mut self) -> Result< [< u $bits >] > {
                    let mut buf = [0; (<[< u $bits >]>::BITS / 8) as usize];
                    self.read_exact(&mut buf)?;
                    Ok(<[< u $bits >]>::from_le_bytes(buf))
                }
    
                fn [< read_exact_ i $bits >](&mut self) -> Result< [< i $bits >] > {
                    Ok(self. [< read_exact_ u $bits >] ()? as [< i $bits >])
                }
            }
        };
    }
    
    macro_rules! impl_write_ext_types {
        ($bits:tt) => {
            paste! {
                fn [< write_all_ u $bits >](&mut self, val: [< u $bits >]) -> Result<()> {
                    self.write_all(&val.to_le_bytes())?;
                    Ok(())
                }
    
                fn [< write_all_ i $bits >](&mut self, val: [< i $bits >]) -> Result<()> {
                    self.[< write_all_u $bits >] (val as [< u $bits >])
                }
            }
        };
    }
    
    impl<T: Read> ReadExtTypes for T {
        impl_read_ext_types!(8);
        impl_read_ext_types!(16);
        impl_read_ext_types!(32);
        impl_read_ext_types!(64);
    }
    
    impl<T: Write> WriteExtTypes for T {
        impl_write_ext_types!(8);
        impl_write_ext_types!(16);
        impl_write_ext_types!(32);
        impl_write_ext_types!(64);
    }
    
    #[cfg(test)]
    pub mod test {
        use super::{ReadExtTypes, WriteExtTypes};
        use paste::paste;
        use std::io::Cursor;
    
        macro_rules! impl_test_case_read_ext_types {
            ($bits:tt) => {
                paste! {
                    let test_val: $bits = <$bits>::MAX;
                    let mut test_val_read = Cursor::new(test_val.clone().to_le_bytes());
                    let res_val = test_val_read.[< read_exact_ $bits >]().unwrap();
                    assert_eq!(test_val, res_val);
    
                    let test_val: $bits = <$bits>::MIN;
                    let mut test_val_read = Cursor::new(test_val.clone().to_le_bytes());
                    let res_val = test_val_read.[< read_exact_ $bits >]().unwrap();
                    assert_eq!(test_val, res_val);
    
                    let test_val: $bits = 0;
                    let mut test_val_read = Cursor::new(test_val.clone().to_le_bytes());
                    let res_val = test_val_read.[< read_exact_ $bits >]().unwrap();
                    assert_eq!(test_val, res_val);
                }
            };
        }
    
        /// Test if values are written and read correctly
        #[test]
        pub fn test_read_ext_types() {
            impl_test_case_read_ext_types!(u8);
            impl_test_case_read_ext_types!(i8);
            impl_test_case_read_ext_types!(u16);
            impl_test_case_read_ext_types!(i16);
            impl_test_case_read_ext_types!(u32);
            impl_test_case_read_ext_types!(i32);
            impl_test_case_read_ext_types!(u64);
            impl_test_case_read_ext_types!(i64);
        }
    
        macro_rules! impl_test_case_write_ext_types {
            ($bits:tt) => {
                paste! {
                    let test_val: $bits = <$bits>::MAX;
                    let mut write_test = Vec::new();
                    write_test.[< write_all_ $bits >] (test_val).unwrap();
                    let res_val = <$bits>::from_le_bytes((&write_test[..].try_into()).unwrap());
                    assert_eq!(test_val, res_val);
    
                    let test_val: $bits = <$bits>::MIN;
                    let mut write_test = Vec::new();
                    write_test.[< write_all_ $bits >] (test_val).unwrap();
                    let res_val = <$bits>::from_le_bytes((&write_test[..].try_into()).unwrap());
                    assert_eq!(test_val, res_val);
    
                    let test_val: $bits = 0;
                    let mut write_test = Vec::new();
                    write_test.[< write_all_ $bits >] (test_val).unwrap();
                    let res_val = <$bits>::from_le_bytes((&write_test[..].try_into()).unwrap());
                    assert_eq!(test_val, res_val);
                }
            };
        }
    
        #[test]
        pub fn test_write_ext_types() {
            impl_test_case_write_ext_types!(u8);
            impl_test_case_write_ext_types!(i8);
            impl_test_case_write_ext_types!(u16);
            impl_test_case_write_ext_types!(i16);
            impl_test_case_write_ext_types!(u32);
            impl_test_case_write_ext_types!(i32);
            impl_test_case_write_ext_types!(u64);
            impl_test_case_write_ext_types!(i64);
        }
    }