Binary encoders

bincode

bincode bincode-crates.io bincode-github bincode-lib.rs cat-encoding cat-network-programming

bincode is a binary serialization / deserialization strategy for transforming structs into bytes and vice versa.

// use std::fs::File;
// use std::io::Cursor;

// use bincode::deserialize_from;
// use bincode::serialize_into;

// #[derive(Serialize, Deserialize, Debug)]
// struct MyData {
//     name: String,
//     age: u32,
//     is_active: bool,
// }

// fn main() -> anyhow::Result<()> {
//     // Create a sample data object
//     let data = MyData {
//         name: "Alice".to_string(),
//         age: 30,
//         is_active: true,
//     };

//     // Serialize to a file
//     let mut file = File::create("data.bin")?;
//     serialize_into(&mut file, &data)?;

//     // Deserialize from a file
//     let mut file = File::open("data.bin")?;
//     let deserialized_data: MyData = deserialize_from(&mut file)?;

//     // Serialize to a buffer
//     let mut buf = Vec::new();
//     serialize_into(&mut buf, &data)?;

//     // Deserialize from a buffer
//     let mut cursor = Cursor::new(&buf[..]);
//     let deserialized_data_from_buf: MyData = deserialize_from(&mut cursor)?;

//     // Print the deserialized data
//     println!("{:?}", deserialized_data);
//     println!("{:?}", deserialized_data_from_buf);

//     Ok(())
// }

ProtoBuf

prost

prost prost-crates.io prost-github prost-lib.rs cat-encoding

prost is a Protocol Buffers implementation for the Rust Language.

In you main.rs file:


// // This will serialize and then deserialize a Person protobuf message and
// // print out its contents.

// mod person {
//     include!(concat!(env!("OUT_DIR"), "/tests/proto/person.rs"));
// }

// use person::Person;
// use prost::Message;

// fn main() {
//     // Create a Person message
//     let person = Person {
//         name: "Alice".to_string(),
//         age: 30,
//     };

//     // Serialize the message
//     let mut buf = Vec::new();
//     person.encode(&mut buf).unwrap();

//     // Deserialize the message
//     let decoded_person = Person::decode(&*buf).unwrap();

//     println!("Name: {}, Age: {}", decoded_person.name, decoded_person.age);
// }

In person.proto:

syntax = "proto3";

package example;

message Person {
    string name = 1;
    int32 age = 2;
}

In build.rs:

fn prost() {
    prost_build::compile_protos(&["tests/proto/person.proto"], &["src/"])
        .unwrap();
}

protobuf

protobuf protobuf-crates.io protobuf-github protobuf-lib.rs

protobuf is a Rust implementation of Google protocol buffers.


// // In your `Cargo.toml`, add the `protobuf` crate as a dependency:
// // [dependencies]
// // protobuf = "3.7.1"

// // Compile the .proto file to generate the Rust code.
// // You can use the protoc compiler with the Rust plugin
// // ```sh
// // protoc --rust_out=. person.proto
// // ```
// // This will generate a Rust module with the compiled code for the Person
// // message.

// // Import the generated code for the Person message
// // The generated code is in the person.rs file
// mod person;

// use person::Person;
// use protobuf::Message;

// fn main() {
//     // Create an instance of the Person message
//     let mut person = Person::new();
//     person.set_name(String::from("Alice"));
//     person.set_age(30);

//     // Serialize the Person message to a byte array
//     let serialized_person = person.write_to_bytes().unwrap();

//     // Print the serialized data
//     println!("Serialized data: {:?}", serialized_person);

//     // Deserialize the byte array back into a Person message
//     let deserialized_person =
// Person::parse_from_bytes(&serialized_person).unwrap();

//     // Print the deserialized Person message
//     println!("Deserialized person: {:?}", deserialized_person);
// }

MessagePack with rmp-serde

rmp-serde rmp-serde-crates.io rmp-serde-github rmp-serde-lib.rs cat-encoding

rmp-serde connects the MessagePack library with serde, providing the ability to easily serialize and deserialize Rust built-in types, types from the standard library, and custom data structures.

// use std::io::Cursor;
// // use std::io::Read;
// // use std::io::Write;

// use rmp_serde::Deserializer;
// use rmp_serde::Serializer;
// use serde::Deserialize;
// use serde::Serialize;

// // Provides serialization and deserialization to/from MessagePack.
// // Key features of rmp_serde:
// // Efficient: MessagePack is a compact binary format, leading to smaller
// // serialized data sizes and faster serialization/deserialization. Versatile:
// // Supports a wide range of data types and can be used with various
// input/output // sources. Interoperable: MessagePack is language-agnostic,
// enabling data // exchange between different programming languages.

// #[derive(Serialize, Deserialize, Debug)]
// struct MyData {
//     name: String,
//     age: u32,
//     is_active: bool,
// }

// fn main() -> Result<(), Box<dyn std::error::Error>> {
//     // Create a sample data object
//     let data = MyData {
//         name: "Alice".to_string(),
//         age: 30,
//         is_active: true,
//     };

//     // Serialize to MessagePack
//     let mut buf = Vec::new();
//     // Creates a serializer that writes to the buffer,
//     // Serializes the data object into MessagePack format and writes it to
// the     // buffer.
//     data.serialize(&mut Serializer::new(&mut buf))?;

//     // Deserialize from MessagePack
//     let mut cursor = Cursor::new(&buf[..]);
//     let deserialized_data: MyData =
//         Deserializer::from_reader(&mut cursor)?.deserialize()?;

//     // Print the deserialized data
//     println!("{:?}", deserialized_data);

//     Ok(())
// }

CBOR with ciborium

ciborium ciborium-crates.io ciborium-github ciborium-lib.rs cat-data-structures cat-no-std cat-embedded cat-encoding cat-parsing

Concise Binary Object Representation is a binary data serialization format loosely based on JSON. ciborium is a serde implementation of CBOR using ciborium-basic.

use std::io::Cursor;

// use std::io::Read;
// use std::io::Write;
use ciborium::de::from_reader;
use ciborium::ser::into_writer;
use serde::Deserialize;
use serde::Serialize;

#[derive(Serialize, Deserialize, Debug)]
struct MyData {
    name: String,
    age: u32,
    is_active: bool,
}

fn main() -> anyhow::Result<()> {
    // Create a sample data object
    let data = MyData {
        name: "Alice".to_string(),
        age: 30,
        is_active: true,
    };

    // Serialize to CBOR
    let mut buf = Vec::new();
    into_writer(&data, &mut buf)?;

    // Deserialize from CBOR
    let mut cursor = Cursor::new(&buf[..]);
    let deserialized_data: MyData = from_reader(&mut cursor)?;

    // Print the deserialized data
    println!("{:?}", deserialized_data);

    Ok(())
}

Flatbuffers

flatbuffers-website flatbuffers flatbuffers-crates.io flatbuffers-github flatbuffers-lib.rs cat-data-structures cat-encoding cat-memory-management

flatbuffers is the official FlatBuffers Rust runtime library. FlatBuffers is a free software library implementing a serialization format similar to Protocol Buffers, Thrift, Apache Avro, SBE, and Cap'n Proto, open-sourced by Google. It supports "zero-copy" deserialization, so that accessing the serialized data does not require first copying it into a separate part of memory.

// use flatbuffers::FlatBufferBuilder;

// // The `flatbuffers` crate provides runtime support
// // for the FlatBuffers format.

// // 'FlatBuffers' is a cross platform serialization library
// // architected for maximum memory efficiency.
// // It allows you to directly access serialized data
// // without parsing/unpacking it first, while
// // still having great forwards/backwards compatibility.
// // It was originally created at Google for game development
// // and other performance-critical applications.

// // Make sure to run flatc to generate the Rust code from the `.fbs` file:
// // flatc --rust -o . my_game.fbs

// // https://flatbuffers.dev/

// // Define the schema for our data
// flatbuffers_gen::r#gen::include_file!("my_game.fbs"); // Assuming your schema
// is in my_game.fbs

// fn main() {
//     // Create a FlatBufferBuilder
//     let mut builder = FlatBufferBuilder::new();

//     // Create some data to store
//     let name = builder.create_string("MyMonster");
//     let hp = 100;

//     // Create an object of our Monster struct
//     let monster = Monster::create(
//         &mut builder,
//         &MonsterArgs {
//             name: Some(name),
//             hp,
//             ..Default::default()
//         }
//     );

//     // Finish building the buffer
//     builder.finish(monster, None);

//     // Get the finished buffer as a byte slice
//     let finished_bytes = builder.finished_data();

//     // Now you can:
//     // 1. Serialize the finished_bytes to a file or send it over the network
//     // 2. Deserialize the finished_bytes to access the data in another
// program

//     println!("FlatBuffer size: {}", finished_bytes.len());
// }

Cap'n Proto

capnp capnp-crates.io capnp-github capnp-lib.rs capnpc capnpc-crates.io capnpc-github capnpc-lib.rs

capnp is the runtime library for Cap'n Proto data encoding. capnpc is used for Cap'n Proto code generation.

// use capnp::serialize;
// use foo_capnp::my_struct::Builder;

// // This crate contains basic facilities for reading and writing Cap'n Proto
// messages in Rust. It is intended to be used in conjunction with code
// generated by the capnpc-rust crate.

// // Add the following dependencies to your `Cargo.toml`:
// // [dependencies]
// // capnp = "0.20.3" # or latest version
// // [build-dependencies]
// // capnpc = "0.20.1

// // Make sure the following lines are in your `build.rs` file:
// // ```rust
// // fn main() {
// //     capnpc::CompilerCommand::new()
// //         //.src_prefix("schema")
// //         .file("tests/proto/foo.capnp")
// //         .run()
// //         .expect("schema compiler command");
// // }
// // ```
// // Or compile the `.capnp` file manually using the `capnp` compiler:
// // ```sh
// // capnp compile -o . foo.capnp
// // ```

// // Define the schema for our data
// mod foo_capnp {
//     include!(concat!(env!("OUT_DIR"), "/tests/proto/foo_capnp.rs"));  //
// Assuming your schema is in foo_capnp.capnp }

// fn main() {
//     // Create a message builder
//     let mut message = Builder::new_default();

//     // Create a root object of our MyStruct type
//     let mut root = message.init_root::<foo_capnp::my_struct::MyStruct>();

//     // Sets the value of the name field.
//     root.set_name("Hello from Rust");
//     // Sets the value of the value field.
//     root.set_value(42);

//     // Serialize the message
//     let mut write_buffer = Vec::new();
//     serialize::write_message(&mut write_buffer, &message).unwrap();

//     // Now you can:
//     // 1. Serialize the write_buffer to a file or send it over the network
//     // 2. Deserialize the write_buffer to access the data in another program

//     println!("Cap'n Proto message size: {}", write_buffer.len());
// }

In build.rs:

fn capnp() {
    capnpc::CompilerCommand::new()
        //.src_prefix("schema") //  For all files specified for compilation that start with prefix, removes the prefix when computing output filenames.
        .file("tests/proto/foo.capnp") // Add a file to the list of files to be compiled.
        .run()
        .expect("schema compiler command");
}

In foo.capnp:

@0xb729500ee8baacd8;
struct MyStruct {
  name @0 : Text;
  value @1 : Int32;
}

Key Features of Cap'n Proto

  • Compact and efficient: Cap'n Proto is designed to be very space-efficient for both on-the-wire and in-memory representations.
  • Fast: Cap'n Proto offers excellent performance, especially for serialization and deserialization.
  • Language-agnostic: You can generate code in various programming languages from a single .capnp definition file.
  • Schema evolution: Cap'n Proto supports schema evolution, allowing you to modify the structure of your data over time without breaking compatibility.