borsh vec
export function vec<T>(
elementLayout: Layout<T>,
property?: string
): Layout<T[]> {
const length = u32("length");
const layout: Layout<{ values: T[] }> = struct([
length,
seq(elementLayout, offset(length, -length.span), "values"),
]);
return new WrappedLayout(
layout,
({ values }) => values,
(values) => ({ values }),
property
);
}
在这段代码中,{ values } 的使用是为了在 WrappedLayout 中对数据进行解构和重新封装。这涉及到 JavaScript 中对象的结构化(destructuring)和构造(constructing)操作。让我们逐步分析代码的逻辑和 values 的来源以及作用。
代码上下文
vec 函数的作用
vec 函数的目的是为一个动态长度的数组定义二进制数据的布局(Layout)。它使用 buffer-layout 提供的工具(如 seq 和 struct)来描述数组的二进制结构,并通过 WrappedLayout 包装,使得编码和解码时的数据更加直观。
构造的布局
const layout: Layout<{ values: T[] }> = struct([
length,
seq(elementLayout, offset(length, -length.span), "values"),
]);
-
length:- 定义了一个
u32类型的字段,表示数组的长度(以字节为单位)。 - 它的名称是
"length",用来存储数组的元素数量。
- 定义了一个
-
seq:- 定义了一个序列(数组)的布局,使用
elementLayout指定数组中每个元素的布局。 offset(length, -length.span):- 指定数组数据在缓冲区中的起始位置。
- 它的起始位置是
length字段的末尾(因为数组数据紧跟在length字段之后)。
- 定义了一个序列(数组)的布局,使用
-
struct:- 定义了一个结构体布局,包含两个字段:
length:表示数组的长度。values:表示数组的具体元素。
- 定义了一个结构体布局,包含两个字段:
最终,layout 的结构类似于:
{
length: number; // 数组的长度
values: T[]; // 数组的元素
}
WrappedLayout 包装
return new WrappedLayout(
layout,
({ values }) => values,
(values) => ({ values }),
property
);
-
WrappedLayout的作用:WrappedLayout是一个包装器,它将内部的布局(layout)与用户定义的转换函数结合,使得编码和解码时可以对数据进行自定义的转换。- 这里的
layout是我们定义的结构{ length, values }。
-
WrappedLayout的参数:- 第一个参数:
layout,表示内部的实际布局。 - 第二个参数:解码时的转换函数(
fromRaw)。 - 第三个参数:编码时的转换函数(
toRaw)。 - 第四个参数:可选的属性名称(
property)。
- 第一个参数:
为什么是 { values }?
关键在于 WrappedLayout 的第二个和第三个参数:
-
解码时的转换函数:
({ values }) => values- 解码时,
layout.decode()返回的数据是一个对象{ length, values },其中:length是数组的长度。values是数组的具体元素。
- 由于用户只关心数组的元素(
values),所以通过解构{ values }提取出values,然后直接返回它。
举例: 如果
layout.decode()返回:{ length: 3, values: [1, 2, 3] }那么解构后只会返回:
[1, 2, 3] - 解码时,
-
编码时的转换函数:
(values) => ({ values })- 编码时,用户提供的数据是一个数组(例如
[1, 2, 3])。 - 但是
layout.encode()需要的数据是一个对象{ length, values },所以需要将数组封装为对象:length会由layout自动计算(根据values的长度)。values是用户提供的数组。
- 因此,编码时通过
({ values })构造出符合layout结构的数据。
举例: 如果用户提供的数据是:
[1, 2, 3]那么封装后会变成:
{ values: [1, 2, 3] } - 编码时,用户提供的数据是一个数组(例如
总结
{ values }是解构和构造对象的关键,用于在WrappedLayout中对数据格式进行转换。- 解码时:从
{ length, values }中提取values,让用户只看到数组内容。 - 编码时:将用户提供的数组封装为
{ values }格式,以便与内部布局匹配。 - 这种设计的目的是简化用户的使用,让用户与
vec交互时只需要处理数组(而无需关心长度字段length)。