Resend

Resend is a easy to use, performant, customizable and extendable Rust library for little-endian/big-endian serializing and deserializing.

Example

Two functions only:

snd() for any Write implementors (File, TcpStream etc)

rcv() for any Read implementors (File, TcpStream etc)

Cargo.toml: ```toml [dependencies]

with little-endian feature

resend = {version = "0.1", features = ["little"]} Code: rust use resend::{Snd, Rcv};

let mut vec = Vec::new(); vec.snd(-8i8)?; vec.snd(22u16)?; vec.snd(0xFFABCDEF as u32)?; vec.snd("Test")?;

let mut buf = &vec[..]; let v: i8 = buf.rcv()?; let v: u16 = buf.rcv()?; let v: u32 = buf.rcv()?; let v: String = buf.rcv()?; ```

Derive

toml [dependencies] resend = {version = "0.1", features = ["little"]} resend_derive = "0.1"

```rust use resend::{Snd, Rcv, endian::{Ascii, UTF16}}; use resend_derive::{Snd, Rcv};

[repr(u32)]

[derive(Snd, Rcv)]

pub enum DeviceType { PrinterType(IoPrinter) = 4, ScardType = 0x20, }

[derive(Snd, Rcv)]

struct Device{ deviceid: u32, #[len(8)] dosname: Ascii, }

[derive(Snd, Rcv)]

pub struct IoPrinter{ device: Device, length: u32, flags: u32, codepage: u32, pnpnamelen: u32, drivernamelen: u32, #[len(pnpnamelen)] pnpname: UTF16, #[len(drivernamelen)] driver_name: UTF16, }

... let dt: DeviceType = stream.rcv()?; stream.snd(&dt)?;

```

Performant

Write/Read trait based, no intermediate variables. You can enable the "unstable" feature on Rust nightly to get best performance:

toml resend = {version = "0.1", features = ["big", "unstable"]}

Format

```rust

[derive(Snd, Rcv)]

[repr(u16)]

enum Color { Red, Blue = 32, Green =4, }

``` Color::Red is serialized as 0u16. Color::Blue is serialized as 32u16.

```rust

[repr(u32)]

[derive(Snd, Rcv)]

pub enum DeviceType { PrinterType(IoPrinter) = 4, ScardType = 0x20, } ``` DeviceType::PrinterType(printer) is serialized as 4u32 + IoPrinter data. DeviceType::ScardType is serialized as 0x20u32.

Please be aware: discriminants on non-unit variants are experimental for now (Rust 1.65), you have to use nightly build.

Customizable (attributes)

  1. Send both little-endian and big-endian at the same time with the resend::endian::little:LE and resend::endian::big::BE:

rust stream.snd(BE(100_u32))?;

  1. No serialization with #[skip] attribute.

  2. The length of String, Vector etc. can be from another field or constant with #[len(fieldnameor_const)] attribute: ```rust

    [len(pnpnamelen)]

[len(8)]

``` 4. #[when(expr)] attribute is used on Option field. This field will be deserialized only if the expr is true. Warning: "expr" is not checked on serializing, no extra bool value in this case.

```rust

[when(code_page > 0)]

[when((flags & 2) != 0)]

5. Length can be u16 or VLQ with features (u32 by default) toml resend = {version = "0.1", features = ["little", "len16"]} resend = {version = "0.1", features = ["big", "lenvlq"]} ```

  1. Restricted length with features: MAXLEN100M, MAXLEN500M, MAXLEN2G toml resend = {version = "0.1", features = ["little", "len_16", "MAX_LEN_100M"]}

Extendable

For example, you want a string with variable-length quantity

```rust pub struct VarLenString (pub String);

impl Sendable for VarLenString { fn sndto(&self, writer: &mut S) -> io::Result<()> where S: resend::Sender { writer.snd(resend::endian::VLQ(self.0.len()))?; writer.sndall(self.0.as_bytes()) } }

impl Receivable for VarLenString { fn rcvfrom(reader: &mut R) -> io::Result where R: resend::Receiver { let len: VLQ = reader.rcv()?; let b = reader.rcvbytes(*len)?; let s = std::str::fromutf8(&b).maperr(|e| resend::error::Error::Utf8(e))?; Ok(Self(s.to_string())) } } ``` Resend includes the following types for your convenience:

rust use resend::endian::{Ascii, UTF16, UTF16Char, VLQ};

Implements resend::FromReader and resend::IntoWriter if you need the "len(fieldname)" attribute working on your type. For example: ```rust impl FromReader for Vec { #[inline] fn fromreader(reader: &mut R, len: usize) -> crate::Result { reader.rcv_bytes(len) } }

impl IntoWriter for Vec { #[inline] fn intowriter(&self, writer: &mut S, len: usize) -> crate::Result<()> { let lens = self.len(); let (l, left) = if len > lens { (lens, len - len_s) } else { (len, 0) };

    let b = &self[..l];

    writer.snd_all(b)?;

    if left > 0 {
        writer.snd_all(&vec![0; left])?;
    }

    Ok(())
}

} ```

Tips

  1. String, Ascii and UTF16 with #[len(fieldnameor_const)] attribute: if the specified length is bigger then the actual length: extra '\0' will be appended when it's serialized, and extra '\0' will be removed after it's deserialized; if the specified length is smaller, the string will be truncated to that length. This is useful if you need null terminated, fixed length string.

  2. Convert int to Enum rust //Conver little-endian u16 to Blue ("little" feature) //[u8] doesn't implement Read, convert it to &[u8] with as_ref() let c: Color = [32_u8, 0].as_ref().rcv()?;

License

MIT OR Apache-2.0

Credits

This library is developed for Remote Spark Corp's RDP (Remote Desktop Protocol) Project.