Crates.io | serde_molecule |
lib.rs | serde_molecule |
version | 1.1.1 |
source | src |
created_at | 2024-08-29 01:59:06.387036 |
updated_at | 2024-10-24 01:57:05.498312 |
description | Implementation of Molecule using Serde. |
homepage | |
repository | https://github.com/XuJiandong/serde_molecule |
max_upload_size | |
id | 1355525 |
size | 66,829 |
The molecule is a serialization format used on CKB. It has several implementations in different languages (C, Rust, JavaScript). In Rust, the implementation introduces numerous types and functions, making it difficult to remember. However, with the power of the serde framework, this process can be greatly simplified. This project offers an implementation of Molecule using Serde.
Here is a simple case about how to use:
use serde::{Serialize, Deserialize};
use serde_molecule::{to_vec, from_slice};
#[derive(Serialize, Deserialize)]
pub struct Table1 {
pub f1: u8,
pub f2: u16,
}
fn main() {
let t1 = Table1{f1: 0, f2: 0};
// serialize
let bytes = to_vec(&t1, false).unwrap();
// deserialize
let t2: Table1 = from_slice(&bytes, false).unwrap();
assert_eq!(t1.f1, t2.f1);
}
First step is to add dependency in Cargo.toml:
serde = { version = "???", features = ["derive"] }
serde_molecule = { version = "???" }
Then to annotate the types with #[derive(Serialize, Deserialize)]
. After that,
use to_vec
or from_slice
to serialize/deserialize.
Rust types are mapping to molecule types, according to the RFC:
Rust Type | Molecule Type | Fixed Size |
---|---|---|
i8,u8 | byte | yes |
i16, u16, i32, u32, i64, u64, i128, u128 | array | yes |
f32 | array([byte; 4]) | yes |
f64 | array([byte; 8]) | yes |
[u8; N] | array([byte; N]) | yes |
[T; N] | array([T; N]) | yes |
Vec |
fixvec | no |
struct | table | no |
#[serde(with = "struct_serde")] | struct | yes |
#[serde(with = "dynvec_serde")] | dynvec | no |
Option |
option | no |
enum | union | no |
String | fixvec | no |
BTreeMap | dynvec | no |
HashMap | dynvec | no |
BinaryHeap | fixvec | no |
LinkedList | fixvec | no |
VecDeque | fixvec | no |
HashSet | fixvec | no |
Tuple Struct | table | no |
Tuple Variant | table | no |
Struct Variant | table | no |
Tuple | not supported | N/A |
By default, Vec
-like containers (such as Vec
, BinaryHeap
, etc.) are
serialized into fixvec
. Every element in the Vec
must have a fixed size
(like a molecule struct, array, or primitive type). If the element is not of a
fixed size, it should be annotated with #[serde(with = "dynvec_serde")]
:
use serde_molecule::dynvec_serde;
#[derive(Serialize, Deserialize)]
struct RawTransaction {
// ...
#[serde(with = "dynvec_serde")]
pub outputs: Vec<CellOutput>,
}
By default, every field is considered as molecule table
. If it is molecule
struct
, we should annotate it explicitly.
use serde_molecule::struct_serde;
#[derive(Serialize, Deserialize)]
pub struct CellInput {
pub since: u64,
#[serde(with = "struct_serde")]
pub previous_output: OutPoint,
}
If the top-level type is a molecule struct
, the second argument to to_vec
or
from_slice
should be true
. If the value is false
, the top-level type is
considered a molecule table
.
For all Molecule struct
s, their inner and descendant fields should be "fixed
size" (see the table above).
The Rust map types (like BTreeMap
and HashMap
) can be mapped to the following Molecule schemas:
table MapEntry {
key: KEY_TYPE,
value: VALUE_TYPE,
}
vector Map <MapEntry>;
It is not recommended to use HashMap because its key-value pairs are stored in arbitrary order.
For molecule union with customized id, see example.
To use this library in a no_std
environment:
serde_molecule
and serde
alloc
feature for serde_molecule
derive
feature for serde
Add the following to your Cargo.toml
:
serde_molecule = { version = "x.x.x", default-features = false, features = ["alloc"] }
serde = { version = "x.x.x", default-features = false, features = ["derive"] }
See the no_std example for more details.
The Serde framework doesn't support arrays with element sizes greater than 32.
See this solution. This limitation
can be addressed using a new serde with
annotation (big_array_serde
):
use serde::{Deserialize, Serialize};
use serde_molecule::big_array_serde;
#[derive(Serialize, Deserialize)]
struct BigArray {
f1: u8,
#[serde(with = "big_array_serde")]
f2: [u8; 33],
#[serde(with = "big_array_serde")]
f3: [u8; 64],
}
Compared to the Rust version of the Molecule
implementation, deserialization with
serde_molecule
consumes at least double the memory. In memory-limited
scenarios, such as on-chain scripts, it's not recommended to use.
Serde treats tuples (e.g., (u8, String)
) similarly to arrays (e.g., [u8; 2]
), which leads to limited support in the context of Molecule serialization. This is due to Serde's implementation.
However, there are some exceptions:
()
is supported and maps to an empty array.For general tuples, it's recommended to use a rust struct instead. For example:
use serde::{Deserialize, Serialize};
// Instead of this:
// type MyTuple = (u8, String);
// Use this:
#[derive(Serialize, Deserialize)]
struct MyStruct {
field1: u8,
field2: String,
}
Here is an example definition of CKB types.