#serialization #netcode #towards #packing #traits #geared #unpack

netpack

Packing traits and implementations geared towards netcode

1 unstable release

0.0.1 Jan 25, 2021

#8 in #towards

MIT license

46KB
991 lines

Documentation coming soon. Geared towards netcode, so all types are serialized in network-byte-order.

Why not just use serde? Serde has basic types, such as strings, which are variable-length and require some sort of format to serialize. This crate chooses to not force a particular protocol on the user, so if you have a variable-length type, you will need to implement a serialization protocol on it yourself.

Sample usage:

use std::io::{self, Cursor, Read, Write};

use netpack::{PackError, Packable, Unpackable, unpack, unpack_from};

// Sample struct with a sub-struct that also implements Packable/Unpackable
#[derive(Debug, PartialEq)]
struct Sample {
    num: i128,
    sample: SubSample,
    b: bool,
}

// Implement Packable for our struct
impl Packable for Sample {
    type Error = io::Error; // Error packables use by default

    fn pack_into(&self, stream: &mut impl Write) -> Result<(), Self::Error> {
        self.num.pack_into(stream)?;
        self.sample.pack_into(stream)?;
        self.b.pack_into(stream)?;
        Ok(())
    }
}

// Implement Unpackable for our struct
impl Unpackable for Sample {
    type Error = PackError; // Error unpackables use by default

    fn unpack_from(rdr: &mut impl Read) -> Result<Self, Self::Error> {
        Ok(Self {
            num: unpack_from(rdr)?,
            sample: unpack_from(rdr)?,
            b: unpack_from(rdr)?,
        })
    }
}

// Embedded structure that's also packable/unpackable
#[derive(Debug, PartialEq)]
struct SubSample {
    num: i32,
    arr: [u16; 5],
    float: f32,
}

impl Packable for SubSample {
    type Error = io::Error; // Error packables use by default

    fn pack_into(&self, stream: &mut impl Write) -> Result<(), Self::Error> {
        self.num.pack_into(stream)?;
        self.arr.pack_into(stream)?;
        self.float.pack_into(stream)?;
        Ok(())
    }
}

impl Unpackable for SubSample {
    type Error = PackError; // Error unpackables use by default

    fn unpack_from(rdr: &mut impl Read) -> Result<Self, Self::Error> {
        Ok(Self {
            num: unpack_from(rdr)?,
            arr: unpack_from(rdr)?,
            float: unpack_from(rdr)?,
        })
    }
}

fn main() {
    let sub = SubSample {
        num: 20,
        arr: [10, 100, 1000, 5, 0],
        float: 0.3
    };
    let sample = Sample {
        num: -5,
        sample: sub,
        b: true
    };

    let mut v = Vec::new();
    // Create a Write stream for the vec
    let mut c = Cursor::new(&mut v);

    // Serialize our struct into the stream
    sample.pack_into(&mut c).unwrap();
    // Serialized data is now in the vec
    assert_eq!(&v, &vec![
        // Sample::num
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251,
            // SubSample::num
            0, 0, 0, 20,
            // SubSample::arr
            0, 10, 0, 100, 3, 232, 0, 5, 0, 0,
            // SubSample::float
            62, 153, 153, 154,
        // Sample::b
        1
    ]);

    // Can also return a vec
    let mut v = sample.pack().unwrap();
    // Serialized data is now in the vec
    assert_eq!(&v, &vec![255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 251, 0, 0, 0, 20, 0, 10, 0, 100, 3, 232, 0, 5, 0, 0, 62, 153, 153, 154, 1]);

    // Extra data simulating a buffer with more than just our struct
    v.push(0xff);

    // Unpacking is just as simple:
    let mut c = Cursor::new(&mut v);
    let new_sample: Sample = unpack_from(&mut c).unwrap();
    assert_eq!(&new_sample, &sample);

    // Or from a slice:
    let (new_sample, rest): (Sample, &[u8]) = unpack(&v).unwrap();
    assert_eq!(&new_sample, &sample);
    // rest contains remaining bytes that weren't part of our struct
    assert_eq!(&rest, &[0xff]);
}

Dependencies

~120KB