Expand description
This crate provides the ability to encode and decode all primitive types into different endianness
§How it works?
Crate automatically implements Primitive trait for each primitive type.
This allows to write abstractions and call the appropriate method depending on
the byte order that you passed to the function template. Endian it’s something
like a proxy to do it.
Macros create implementations for I/O endianness:
NativeEndian, LittleEndian and BigEndian
All these types are enums, which means that you cannot create them, only pass to the template.
Now it’s possible to have traits that expand Read and Write with new methods.
§Simple example
use endiannezz::ext::{EndianReader, EndianWriter};
use endiannezz::{BigEndian, LittleEndian, NativeEndian};
use std::io::Result;
fn main() -> Result<()> {
let mut vec = Vec::new();
vec.try_write::<LittleEndian, i32>(1)?;
vec.try_write::<BigEndian, _>(2)?;
vec.try_write::<NativeEndian, _>(3_u16)?;
let mut slice = vec.as_slice();
slice.try_read::<LittleEndian, i32>()?;
let _num32: i32 = slice.try_read::<BigEndian, _>()?;
let _num16: u16 = slice.try_read::<NativeEndian, _>()?;
Ok(())
}You can also use this syntax:
use endiannezz::{BigEndian, Endian, LittleEndian};
use std::io::Result;
fn main() -> Result<()> {
let mut vec = Vec::new();
BigEndian::write(1, &mut vec)?;
LittleEndian::write::<u16, _>(2, &mut vec)?;
assert_eq!(vec.as_slice(), &[0, 0, 0, 1, 2, 0]);
Ok(())
}§Using #[derive(Io)] to describe complex binary formats
use endiannezz::ext::{EndianReader, EndianWriter};
use endiannezz::{Io, LittleEndian};
use std::io::{Read, Result, Write};
struct Bytes(Vec<u8>);
//Custom implementation of read and write
//Use it for complex types, which can be built from primitives
impl Io for Bytes {
fn write<W: Write>(&self, mut w: W) -> Result<()> {
w.try_write::<LittleEndian, u32>(self.0.len() as u32)?;
w.write_all(self.0.as_slice())?;
Ok(())
}
fn read<R: Read>(mut r: R) -> Result<Self> {
let capacity = r.try_read::<LittleEndian, u32>()? as usize;
let mut vec = vec![0; capacity];
r.read_exact(&mut vec)?;
Ok(Self(vec))
}
}
#[derive(Io)]
//default endian for fields of struct (except custom impl, such as Bytes)
#[endian(little)]
//There are 3 types of endianness and they can be written in the `#[endian]` attribute as follows:
// - NativeEndian: `_`, `ne`, `native`
// - LittleEndian: `le`, `little`
// - BigEndian: `be`, `big`
struct Message {
//will read/write data as is (according to implementation)
bytes: Bytes,
//u16 in little-endian
distance: u16,
//f32 in big-endian, you can override default endian!
#[endian(big)]
delta: f32,
//machine byte order
#[endian(native)]
machine_data: u32,
}
fn main() -> Result<()> {
let message = Message {
bytes: Bytes(vec![0xde, 0xad, 0xbe, 0xef]),
distance: 5,
delta: 2.41,
machine_data: 41,
};
//writing message into Vec
let mut vec = Vec::new();
message.write(&mut vec)?;
//explain
let mut excepted = vec![
4, 0, 0, 0, //bytes len in little-endian
0xde, 0xad, 0xbe, 0xef, //buffer
5, 0, //distance in little-endian
0x40, 0x1a, 0x3d, 0x71, //delta in big-endian
];
if cfg!(target_endian = "little") {
excepted.extend(&[41, 0, 0, 0]); //machine_data on little-endian CPUs
} else {
excepted.extend(&[0, 0, 0, 41]); //machine_data on big-endian CPUs
}
assert_eq!(vec, excepted);
//reading message from slice
let mut slice = vec.as_slice();
let _message1 = Message::read(&mut slice)?;
Ok(())
}Modules§
- ext
- Provides extensions for
ReadandWritetraits - internal
- Internal module to simplify
proc_macroimplementation
Enums§
Traits§
- Endian
- Proxy for reading and writing primitive types
- Hardcoded
Payload - Io
- Allows the type to be encoded/decoded using binary format
- Primitive
- This trait is implemented for all primitive types that exist in rust, and allows to read types from bytes or write them into bytes