1 个不稳定版本
0.1.0 | 2019 年 7 月 8 日 |
---|
481 在 #deserialize 中排名
每月 22 次下载
5KB
56 行
Parity SCALE Codec
Parity Substrate 框架中使用的类型对 SCALE (Simple Concatenated Aggregate Little-Endian) 数据格式的 Rust 实现。
SCALE 是一种轻量级格式,它允许编码(和解码),这使得它非常适合资源受限的执行环境,如区块链运行时和低功耗、低内存设备。
需要注意的是,编码上下文(对类型和数据结构外观的了解)需要在编码和解码两端单独知道。编码的数据不包含此上下文信息。
要更好地了解不同类型的编码方式,请参阅 Substrate 文档中的“类型编码(SCALE)”页面。
实现
该编解码器使用以下 traits 实现
Encode
Encode
trait 用于将数据编码到 SCALE 格式。该 Encode
trait 包含以下函数
size_hint(&self) -> usize
:获取编码数据的容量(以字节为单位)。这是为了避免在编码过程中对内存进行重复分配。这可以是一个估计值,不需要是精确的数字。如果大小未知,即使没有合适的最大值,我们也可以从 trait 实现中跳过这个函数。这需要是一个低成本的运算,因此不应该涉及迭代等操作。encode_to<T: Output>(&self, dest: &mut T)
: 将值编码并附加到目标缓冲区。encode(&self) -> Vec<u8>
: 将类型数据编码并返回一个切片。using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R
: 将类型数据编码并执行闭包。返回执行闭包的结果。
注意: 实现应覆盖 using_encoded
以支持值类型,以及 encode_to
以支持分配类型。 wherever possible 应实现 size_hint
。包装类型应覆盖所有方法。
解码
Decode
特性用于将编码数据反序列化/解码到相应的类型。
fn decode<I: Input>(value: &mut I) -> Result<Self, Error>
: 尝试将值从 SCALE 格式解码到调用该函数的类型。如果解码失败,则返回Err
。
CompactAs
CompactAs
特性用于将自定义类型/结构体包装为紧凑类型,这使它们在空间/内存效率方面更加高效。紧凑编码的描述 在这里。
encode_as(&self) -> &Self::As
: 将类型(self)编码为紧凑类型。类型As
在相同的特性中定义,其实现应该是紧凑编码的。decode_from(_: Self::As) -> Result<Self, Error>
: 从紧凑编码的类型解码类型(self)。
HasCompact
如果实现了HasCompact
特质,则表示相应的类型是一种紧凑编码类型。
EncodeLike
对于每种类型,需要手动实现EncodeLike
特质。当使用 derive 时,它会自动为您完成。基本上,这个特质给您提供了将多个类型传递给一个函数的机会,这些类型都具有相同的编码表示。
使用示例
以下是一些示例,用于展示编码器的使用。
简单类型
# // Import macros if derive feature is not used.
# #[cfg(not(feature="derive"))]
# use parity_scale_codec_derive::{Encode, Decode};
use parity_scale_codec::{Encode, Decode};
#[derive(Debug, PartialEq, Encode, Decode)]
enum EnumType {
#[codec(index = 15)]
A,
B(u32, u64),
C {
a: u32,
b: u64,
},
}
let a = EnumType::A;
let b = EnumType::B(1, 2);
let c = EnumType::C { a: 1, b: 2 };
a.using_encoded(|ref slice| {
assert_eq!(slice, &b"\x0f");
});
b.using_encoded(|ref slice| {
assert_eq!(slice, &b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0");
});
c.using_encoded(|ref slice| {
assert_eq!(slice, &b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0");
});
let mut da: &[u8] = b"\x0f";
assert_eq!(EnumType::decode(&mut da).ok(), Some(a));
let mut db: &[u8] = b"\x01\x01\0\0\0\x02\0\0\0\0\0\0\0";
assert_eq!(EnumType::decode(&mut db).ok(), Some(b));
let mut dc: &[u8] = b"\x02\x01\0\0\0\x02\0\0\0\0\0\0\0";
assert_eq!(EnumType::decode(&mut dc).ok(), Some(c));
let mut dz: &[u8] = &[0];
assert_eq!(EnumType::decode(&mut dz).ok(), None);
# fn main() { }
带有 HasCompact 的紧凑类型
# // Import macros if derive feature is not used.
# #[cfg(not(feature="derive"))]
# use parity_scale_codec_derive::{Encode, Decode};
use parity_scale_codec::{Encode, Decode, Compact, HasCompact};
#[derive(Debug, PartialEq, Encode, Decode)]
struct Test1CompactHasCompact<T: HasCompact> {
#[codec(compact)]
bar: T,
}
#[derive(Debug, PartialEq, Encode, Decode)]
struct Test1HasCompact<T: HasCompact> {
#[codec(encoded_as = "<T as HasCompact>::Type")]
bar: T,
}
let test_val: (u64, usize) = (0u64, 1usize);
let encoded = Test1HasCompact { bar: test_val.0 }.encode();
assert_eq!(encoded.len(), test_val.1);
assert_eq!(<Test1CompactHasCompact<u64>>::decode(&mut &encoded[..]).unwrap().bar, test_val.0);
# fn main() { }
带有 CompactAs 的类型
# // Import macros if derive feature is not used.
# #[cfg(not(feature="derive"))]
# use parity_scale_codec_derive::{Encode, Decode};
use serde_derive::{Serialize, Deserialize};
use parity_scale_codec::{Encode, Decode, Compact, HasCompact, CompactAs, Error};
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
#[derive(PartialEq, Eq, Clone)]
struct StructHasCompact(u32);
impl CompactAs for StructHasCompact {
type As = u32;
fn encode_as(&self) -> &Self::As {
&12
}
fn decode_from(_: Self::As) -> Result<Self, Error> {
Ok(StructHasCompact(12))
}
}
impl From<Compact<StructHasCompact>> for StructHasCompact {
fn from(_: Compact<StructHasCompact>) -> Self {
StructHasCompact(12)
}
}
#[derive(Debug, PartialEq, Encode, Decode)]
enum TestGenericHasCompact<T> {
A {
#[codec(compact)] a: T
},
}
let a = TestGenericHasCompact::A::<StructHasCompact> {
a: StructHasCompact(12325678),
};
let encoded = a.encode();
assert_eq!(encoded.len(), 2);
# fn main() { }
derive 属性
derive 实现支持以下属性
codec(dumb_trait_bound)
:这个属性需要放置在应该实现特质的类型之上。这将使确定要添加的特质边界的算法仅使用类型的类型参数。这在算法包括私有类型在公共接口中的情况下可能很有用。通过使用此属性,您应该不再收到此错误/警告。codec(skip)
:需要放置在字段或变体之上,使其在编码/解码时被跳过。codec(compact)
:需要放置在字段之上,使字段使用紧凑编码。(类型需要支持紧凑编码。)codec(encoded_as = "OtherType")
:需要放置在字段之上,使字段使用OtherType
进行编码。codec(index = 0)
:需要放置在枚举变体之上,以便在编码时使用给定的索引。默认情况下,索引是从0
开始的第一个变体进行计数。codec(encode_bound)
、codec(decode_bound)
和codec(mel_bound)
:所有 3 个属性都接受一个用于Encode
、Decode
和MaxEncodedLen
特质实现的where
子句。codec(encode_bound(skip_type_params))
、codec(decode_bound(skip_type_params))
和codec(mel_bound(skip_type_params))
:这三个子属性都接受类型作为参数以跳过相应特征的派生,例如在codec(encode_bound(skip_type_params(T)))
中,将不会包含Encode
特征约束,当正在派生注解类型的Encode
时。
已知问题
尽管此软件包支持使用此类类型反序列化任意大小的数组(例如 [T; 1024 * 1024 * 1024]
),但使用此类类型不建议,并且很可能会导致栈溢出。如果您想在结构体内部解码大数组,应将其包装在 Box
中,例如 Box<[T; 1024 * 1024 * 1024]>
。
许可:Apache-2.0