#scale #parity #encoding

无 std scale-encode

根据所需的目标类型将类型编码为 SCALE 字节

28 个版本

0.7.1 2024 年 5 月 17 日
0.6.0 2024 年 2 月 16 日
0.5.0 2023 年 8 月 2 日
0.4.0 2023 年 7 月 12 日
0.0.1 2022 年 11 月 24 日

#20 in #parity

Download history 5998/week @ 2024-04-29 6778/week @ 2024-05-06 7284/week @ 2024-05-13 6789/week @ 2024-05-20 6497/week @ 2024-05-27 8691/week @ 2024-06-03 6604/week @ 2024-06-10 7061/week @ 2024-06-17 8295/week @ 2024-06-24 7146/week @ 2024-07-01 8303/week @ 2024-07-08 9168/week @ 2024-07-15 8715/week @ 2024-07-22 9156/week @ 2024-07-29 9413/week @ 2024-08-05 8459/week @ 2024-08-12

36,236 每月下载量
87 个 crate 中使用 (7 直接)

Apache-2.0

96KB
1.5K SLoC

scale-encode

parity-scale-codec 提供了一个 Encode 特性,它允许类型根据其形状进行 SCALE 编码。这个 crate 在此基础上构建,并允许类型根据 TypeResolver 实现的类型信息进行编码(一个实现是 scale_info::PortableRegistry)。它公开了两个特性

  • 一个 EncodeAsType 特性,当在某个类型上实现时,描述了如何使用类型 ID 和描述编码字节数据预期的形状的类型注册表来 SCALE 编码。
  • 一个 EncodeAsFields 特性,当在某个类型上实现时,描述了如何使用 Field 迭代器和描述编码字节数据预期的形状的类型注册表来 SCALE 编码。这通常仅用于元组和结构体,因为我们需要一组字段来映射到提供的迭代器。

还为每个特性提供了许多内置类型的实现,并且 EncodeAsType 宏使得为新结构体和枚举生成实现变得容易。

动机

通过解耦类型的形状与其编码方式,我们使得某些类型的编码更有可能成功,并且不再依赖于类型具有精确的布局才能正确编码。以下是一些例子。

use codec::Encode;
use scale_encode::EncodeAsType;
use scale_info::{PortableRegistry, TypeInfo};

// We are commonly provided type information, but for our examples we construct type info from
// any type that implements `TypeInfo`.
fn get_type_info<T: TypeInfo + 'static>() -> (u32, PortableRegistry) {
    let m = scale_info::MetaType::new::<T>();
    let mut types = scale_info::Registry::new();
    let ty = types.register_type(&m);
    let portable_registry: PortableRegistry = types.into();
    (ty.id, portable_registry)
}

// Encode the left value via EncodeAsType into the shape of the right value.
// Encode the right value statically.
// Assert that both outputs are identical.
fn assert_encodes_to<A, B>(a: A, b: B)
where
    A: EncodeAsType,
    B: TypeInfo + Encode + 'static,
{
    let (type_id, types) = get_type_info::<B>();
    let a_bytes = a.encode_as_type(&type_id, &types).unwrap();
    let b_bytes = b.encode();
    assert_eq!(a_bytes, b_bytes);
}

// Start simple; a u8 can EncodeAsType into a u64 and vice versa. Numbers will all
// try to convert into the desired output size, failing if this isn't possible:
assert_encodes_to(123u8, 123u64);
assert_encodes_to(123u64, 123u8);

// Compact encoding is also handled "under the hood" by EncodeAsType, so no "compact"
// annotations are needed on values.
assert_encodes_to(123u64, codec::Compact(123u64));

// Enum variants are lined up by variant name, so no explicit "index" annotation are
// needed either; EncodeAsType will take care of it.
#[derive(EncodeAsType)]
enum Foo {
    Something(u64),
}
#[derive(Encode, TypeInfo)]
enum FooTarget {
    #[codec(index = 10)]
    Something(u128),
}
assert_encodes_to(Foo::Something(123), FooTarget::Something(123));

// EncodeAstype will just ignore named fields that aren't needed:
#[derive(EncodeAsType)]
struct Bar {
    a: bool,
    b: String,
}
#[derive(Encode, TypeInfo)]
struct BarTarget {
    a: bool,
}
assert_encodes_to(
    Bar { a: true, b: "hello".to_string() },
    BarTarget { a: true },
);

// EncodeAsType will attempt to remove any newtype wrappers and such on either
// side, so that they can be omitted without any issue.
#[derive(EncodeAsType, Encode, TypeInfo)]
struct Wrapper {
    value: u64
}
assert_encodes_to(
    (Wrapper { value: 123 },),
    123u64
);
assert_encodes_to(
    123u64,
    (Wrapper { value: 123 },)
);

// Things like arrays and sequences are generally interchangeable despite the
// encoding format being slightly different:
assert_encodes_to([1u8,2,3,4,5], vec![1u64,2,3,4,5]);
assert_encodes_to(vec![1u64,2,3,4,5], [1u8,2,3,4,5]);

// BTreeMap, as a slightly special case, can encode to the same shape as either
// a sequence or a struct, depending on what's asked for:
use std::collections::BTreeMap;
#[derive(TypeInfo, Encode)]
struct MapOutput {
    a: u64,
    b: u64
}
assert_encodes_to(
    BTreeMap::from_iter([("a", 1u64), ("b", 2u64)]),
    vec![1u64,2]
);
assert_encodes_to(
    BTreeMap::from_iter([("a", 1u64), ("b", 2u64), ("c", 3u64)]),
    MapOutput { a: 1, b: 2 }
);

依赖项

~2–3MB
~59K SLoC