#binary-encoding #serialization #networking #data-model #encode-decode #binary-data #no-alloc

no-std musli-wire

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

116 releases

0.0.117 Apr 20, 2024
0.0.108 Mar 30, 2024
0.0.94 Dec 12, 2023
0.0.91 Nov 13, 2023
0.0.37 May 27, 2022

#2199 in Encoding

Download history 6/week @ 2024-07-05 6/week @ 2024-07-26 57/week @ 2024-09-20 13/week @ 2024-09-27

70 downloads per month
Used in musli-tests

MIT/Apache

1MB
24K 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_vec(&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::{Encode, Decode};
use musli_utils::options::{self, Options, Integer};
use musli_wire::Encoding;

const OPTIONS: Options = options::new().with_integer(Integer::Fixed).build();
const CONFIG: Encoding<OPTIONS> = Encoding::new().with_options();

#[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.4–0.9MB
~20K SLoC