#no-std #serialization

no-std musli-wire

Fully upgrade stable format for Müsli suitable for network communication

49 releases

0.0.49 May 18, 2023
0.0.48 May 18, 2023
0.0.39 Apr 26, 2023
0.0.38 Mar 22, 2023
0.0.29 Apr 30, 2022

#273 in Encoding

Download history 6/week @ 2023-02-01 42/week @ 2023-02-08 136/week @ 2023-02-15 1/week @ 2023-03-15 96/week @ 2023-03-22 2/week @ 2023-03-29 1/week @ 2023-04-05 47/week @ 2023-04-12 1/week @ 2023-04-19 21/week @ 2023-04-26 58/week @ 2023-05-03 163/week @ 2023-05-10 161/week @ 2023-05-17

403 downloads per month
Used in musli-tests

MIT/Apache

385KB
7.5K SLoC

musli-wire

github crates.io docs.rs build status

Fully upgrade stable format for Müsli suitable for network communication.

Wire encoding is fully upgrade stable:

  • ✔ Can tolerate missing fields if they are annotated with #[musli(default)].
  • ✔ Can skip over unknown fields.

This means that it's suitable as a wire format, since the data model can evolve independently among clients. Once some clients are upgraded they will start sending unknown fields which non-upgraded clients will be forced to skip over for the duration of the upgrade.

use musli::{Encode, Decode};

#[derive(Debug, PartialEq, Encode, Decode)]
struct Version1 {
    name: String,
}

#[derive(Debug, PartialEq, Encode, Decode)]
struct Version2 {
    name: String,
    #[musli(default)]
    age: Option<u32>,
}

let version2 = musli_wire::to_buffer(&Version2 {
    name: String::from("Aristotle"),
    age: Some(62),
})?;

let version1: Version1 = musli_wire::decode(version2.as_slice())?;

assert_eq!(version1, Version1 {
    name: String::from("Aristotle"),
});

Configuring

To configure the behavior of the wire format you can use the Encoding type:

use musli_wire::Encoding;
use musli_wire::int::{Fixed, Variable};
use musli::{Encode, Decode};
use musli::mode::DefaultMode;

const CONFIG: Encoding<DefaultMode, Fixed, Variable, 128> = Encoding::new()
    .with_fixed_integers()
    .with_max_pack::<128>();

#[derive(Debug, PartialEq, Encode, Decode)]
struct Struct<'a> {
    name: &'a str,
    age: u32,
}

let mut out = Vec::new();

let expected = Struct {
    name: "Aristotle",
    age: 61,
};

CONFIG.encode(&mut out, &expected)?;
let actual = CONFIG.decode(&out[..])?;

assert_eq!(expected, actual);

Implementation details

Each field is prefix typed with a single byte tag that allows a receiver to figure out exactly how much should be skipped over.

Packed items are prefix-length encoded, and have a limited size. Its exact length is defined by MAX_INLINE_LEN and can be modified with Encoding::with_max_pack.

Dependencies

~0.5–1MB
~22K SLoC