13个稳定版本

3.3.0 2019年4月2日
3.2.0 2019年4月1日
3.1.0 2019年2月28日
3.0.0 2019年1月22日
1.0.0 2018年9月13日

#78#compact

Download history 309/week @ 2024-02-27 245/week @ 2024-03-05 208/week @ 2024-03-12 294/week @ 2024-03-19 257/week @ 2024-03-26 304/week @ 2024-04-02 202/week @ 2024-04-09 291/week @ 2024-04-16 288/week @ 2024-04-23 249/week @ 2024-04-30 232/week @ 2024-05-07 240/week @ 2024-05-14 304/week @ 2024-05-21 251/week @ 2024-05-28 233/week @ 2024-06-04 168/week @ 2024-06-11

998 每月下载量
7 个crate中使用(通过 parity-codec

Apache-2.0

26KB
646

Parity SCALE Codec

Parity Substrate框架中使用的类型对SCALE(Simple Concatenated Aggregate Little-Endian)数据格式的Rust实现。

SCALE是一种轻量级格式,允许编码(和解码),这使得它非常适合资源受限的执行环境,如区块链运行时和低功耗、低内存设备。

需要注意的是,编码上下文(了解类型和数据结构的外观)需要在编码和解析两端单独知道。编码的数据不包含此上下文信息。

要更好地了解不同类型的编码方式,请参阅Substrate文档中的“类型编码(SCALE)”页面

实现

该编解码器使用以下特性实现

编码

Encode trait用于将数据编码到SCALE格式。 Encode trait包含以下函数

  • size_hint(&self) -> usize:获取编码数据所需的容量(以字节为单位)。这是为了避免编码所需内存的重复分配。这可以是一个估计值,不一定需要是精确数字。如果大小未知,甚至没有合适的最大值,那么我们可以从特质实现中跳过这个函数。这需要是一个低成本的运算,因此不应涉及迭代等操作。
  • encode_to<T: Output>(&self, dest: &mut T):编码值并将其追加到目标缓冲区。
  • encode<u8>(&self) -> Vec<u8>:编码类型数据并返回一个切片。
  • using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R:编码类型数据并执行闭包于编码值。返回执行闭包的结果。

注意:实现应该覆盖 using_encoded 以处理值类型,以及 encode_to 以处理分配类型。尽可能实现 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 时,它将自动为您完成。基本上,该特性为您提供了将多个类型传递给一个函数的机会,这些类型都编码为相同的表示。

使用示例

以下是一些演示 codec 使用的示例。

简单类型

# // 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 个属性都接受一个 where 子句,用于分别对注解类型的 EncodeDecodeMaxEncodedLen 特性实现。
  • codec(encode_bound(skip_type_params))codec(decode_bound(skip_type_params))codec(mel_bound(skip_type_params)):这3个子属性都接受类型作为参数,以跳过相应特征的派生,例如在codec(encode_bound(skip_type_params(T)))中,将不会包含一个Encode特征约束,而Encode特征正在派生到注解类型。

已知问题

尽管这个crate支持使用此类类型反序列化任意大小的数组(例如[T; 1024 * 1024 * 1024]),但使用此类类型并不推荐,并且很可能会导致堆栈溢出。如果你想在你的结构体中有一个大数组进行解码,你应该用Box将其包装起来,例如Box<[T; 1024 * 1024 * 1024]>


许可证:Apache-2.0

依赖项

~2.5MB
~55K SLoC