Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 63 additions & 12 deletions src/ssz/type/container.zig
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,21 @@ pub fn FixedContainerType(comptime ST: type) type {
}

/// Creates a new `FixedContainerType` and clones all underlying fields in the container.
///
/// `out` is a pointer to any types that contains all fields of `Type`.
/// Caller owns the memory.
pub fn clone(value: *const Type, out: *Type) !void {
out.* = value.*;
pub fn clone(value: *const Type, out: anytype) !void {
const OutType = @TypeOf(out.*);
comptime {
const OutInfo = @typeInfo(@TypeOf(out));
std.debug.assert(OutInfo == .pointer);
}
if (OutType == Type) {
out.* = value.*;
} else {
inline for (fields) |field| {
@field(out, field.name) = @field(value, field.name);
}
}
}

pub fn hashTreeRoot(value: *const Type, out: *[32]u8) !void {
Expand Down Expand Up @@ -358,13 +369,18 @@ pub fn VariableContainerType(comptime ST: type) type {
}

/// Creates a new `VariableContainerType` and clones all underlying fields in the container.
///
/// `out` is a pointer to any types that contains all fields of `Type`.
/// Caller owns the memory.
pub fn clone(
allocator: std.mem.Allocator,
value: *const Type,
out: *Type,
out: anytype,
) !void {
comptime {
const OutInfo = @typeInfo(@TypeOf(out));
std.debug.assert(OutInfo == .pointer);
}

inline for (fields) |field| {
if (comptime isFixedType(field.type)) {
try field.type.clone(&@field(value, field.name), &@field(out, field.name));
Expand Down Expand Up @@ -720,24 +736,44 @@ test "ContainerType - sanity" {
try Foo.deserializeFromBytes(allocator, f_buf, &f);
}

test "clone" {
const allocator = std.testing.allocator;
test "clone FixedContainerType" {
const Checkpoint = FixedContainerType(struct {
slot: UintType(8),
epoch: UintType(8),
root: ByteVectorType(32),
});
const CheckpointHex = FixedContainerType(struct {
epoch: UintType(8),
root: ByteVectorType(32),
root_hex: ByteVectorType(64),
});

var c: Checkpoint.Type = Checkpoint.default_value;

var cloned: Checkpoint.Type = undefined;
try Checkpoint.clone(&c, &cloned);
try std.testing.expect(&cloned != &c);
try std.testing.expect(Checkpoint.equals(&cloned, &c));

// clone into a larger container
var cloned2: CheckpointHex.Type = undefined;
cloned2.root_hex = ByteVectorType(64).default_value;
try Checkpoint.clone(&c, &cloned2);
try std.testing.expect(cloned2.epoch == c.epoch);
try std.testing.expectEqualSlices(u8, &cloned2.root, &c.root);
try std.testing.expectEqualSlices(u8, &cloned2.root_hex, &ByteVectorType(64).default_value);
}

test "clone VariableContainerType" {
const allocator = std.testing.allocator;
const FieldA = FixedListType(UintType(8), 32);
const FieldB = FixedListType(UintType(8), 32);
const Foo = VariableContainerType(struct {
a: FixedListType(UintType(8), 32),
b: FixedListType(UintType(8), 32),
c: FixedListType(UintType(8), 32),
a: FieldA,
b: FieldB,
});
var f = Foo.default_value;
try f.a.append(allocator, 42);
try f.b.append(allocator, 42);
defer Foo.deinit(allocator, &f);
var cloned_f: Foo.Type = undefined;
try Foo.clone(allocator, &f, &cloned_f);
Expand All @@ -746,7 +782,22 @@ test "clone" {

try expectEqualRootsAlloc(Foo, allocator, f, cloned_f);
try expectEqualSerializedAlloc(Foo, allocator, f, cloned_f);
// TODO(bing): test equals when ready
try std.testing.expect(Foo.equals(&cloned_f, &f));

// clone into a larger container
const FieldC = FixedListType(UintType(8), 32);
const Foo2 = VariableContainerType(struct {
a: FieldA,
b: FieldB,
// 1 additional field
c: FieldC,
});
var cloned_f2: Foo2.Type = undefined;
cloned_f2.c = FieldC.default_value;
try Foo.clone(allocator, &f, &cloned_f2);
defer Foo2.deinit(allocator, &cloned_f2);
try std.testing.expectEqualSlices(u8, f.a.items, cloned_f2.a.items);
try std.testing.expectEqualSlices(u8, f.b.items, cloned_f2.b.items);
}

// Refer to https://github.com/ChainSafe/ssz/blob/f5ed0b457333749b5c3f49fa5eafa096a725f033/packages/ssz/test/unit/byType/container/valid.test.ts#L9-L64
Expand Down
105 changes: 77 additions & 28 deletions src/ssz/type/list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ pub fn FixedListType(comptime ST: type, comptime _limit: comptime_int) type {
/// Clones the underlying `ArrayList`.
///
/// Caller owns the memory.
pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: *Type) !void {
pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: anytype) !void {
comptime {
const OutInfo = @typeInfo(@TypeOf(out));
std.debug.assert(OutInfo == .pointer);
}

try out.resize(allocator, value.items.len);

for (value.items, 0..) |v, i| {
Expand Down Expand Up @@ -425,9 +430,13 @@ pub fn VariableListType(comptime ST: type, comptime _limit: comptime_int) type {
}

/// Clones the underlying `ArrayList`.
///
/// Caller owns the memory.
pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: *Type) !void {
pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: anytype) !void {
comptime {
const OutInfo = @typeInfo(@TypeOf(out));
std.debug.assert(OutInfo == .pointer);
}

try out.resize(allocator, value.items.len);
for (0..value.items.len) |i|
try Element.clone(allocator, &value.items[i], &out.items[i]);
Expand Down Expand Up @@ -722,6 +731,7 @@ const UintType = @import("uint.zig").UintType;
const BoolType = @import("bool.zig").BoolType;
const ByteVectorType = @import("byte_vector.zig").ByteVectorType;
const FixedContainerType = @import("container.zig").FixedContainerType;
const VariableContainerType = @import("container.zig").VariableContainerType;

test "ListType - sanity" {
const allocator = std.testing.allocator;
Expand Down Expand Up @@ -753,33 +763,72 @@ test "ListType - sanity" {
try BytesBytes.deserializeFromBytes(allocator, bb_buf, &bb);
}

test "clone" {
test "clone FixedListType" {
const allocator = std.testing.allocator;
const BytesFixed = FixedListType(UintType(8), 32);
const BytesVariable = VariableListType(BytesFixed, 32);

var b: BytesFixed.Type = BytesFixed.default_value;
defer b.deinit(allocator);
try b.append(allocator, 5);
var cloned: BytesFixed.Type = BytesFixed.default_value;
try BytesFixed.clone(allocator, &b, &cloned);
const Checkpoint = FixedContainerType(struct {
epoch: UintType(8),
root: ByteVectorType(32),
});
const CheckpointList = FixedListType(Checkpoint, 8);
var list: CheckpointList.Type = CheckpointList.default_value;
defer CheckpointList.deinit(allocator, &list);
const cp: Checkpoint.Type = .{
.epoch = 41,
.root = [_]u8{1} ** 32,
};
try list.append(allocator, cp);
var cloned: CheckpointList.Type = CheckpointList.default_value;
try CheckpointList.clone(allocator, &list, &cloned);
defer cloned.deinit(allocator);
try std.testing.expect(&b != &cloned);
try std.testing.expect(std.mem.eql(u8, b.items[0..], cloned.items[0..]));
try expectEqualRootsAlloc(BytesFixed, allocator, b, cloned);
try expectEqualSerializedAlloc(BytesFixed, allocator, b, cloned);

var bv: BytesVariable.Type = BytesVariable.default_value;
defer bv.deinit(allocator);
const bb: BytesFixed.Type = BytesFixed.default_value;
try bv.append(allocator, bb);
var cloned_v: BytesVariable.Type = BytesVariable.default_value;
try BytesVariable.clone(allocator, &bv, &cloned_v);
defer cloned_v.deinit(allocator);
try std.testing.expect(&bv != &cloned_v);
try expectEqualRootsAlloc(BytesVariable, allocator, bv, cloned_v);
try expectEqualSerializedAlloc(BytesVariable, allocator, bv, cloned_v);
// TODO(bing): Equals test
try std.testing.expect(&list != &cloned);
try std.testing.expect(CheckpointList.equals(&list, &cloned));

// clone to a list of a different type
const CheckpointHex = FixedContainerType(struct {
epoch: UintType(8),
root: ByteVectorType(32),
root_hex: ByteVectorType(64),
});
const CheckpointHexList = FixedListType(CheckpointHex, 8);
var list_hex: CheckpointHexList.Type = CheckpointHexList.default_value;
defer list_hex.deinit(allocator);
try CheckpointList.clone(allocator, &list, &list_hex);
try std.testing.expect(list_hex.items.len == 1);
try std.testing.expect(list_hex.items[0].epoch == cp.epoch);
try std.testing.expectEqualSlices(u8, &list_hex.items[0].root, &cp.root);
}

test "clone VariableListType" {
const allocator = std.testing.allocator;
const FieldA = FixedListType(UintType(8), 32);
const Foo = VariableContainerType(struct {
a: FieldA,
});
const ListFoo = VariableListType(Foo, 8);
var list = ListFoo.default_value;
defer ListFoo.deinit(allocator, &list);
var fielda = FieldA.default_value;
try fielda.append(allocator, 100);
try list.append(allocator, .{ .a = fielda });

var cloned: ListFoo.Type = ListFoo.default_value;
defer ListFoo.deinit(allocator, &cloned);
try ListFoo.clone(allocator, &list, &cloned);
try std.testing.expect(&list != &cloned);
try std.testing.expect(cloned.items.len == 1);
try std.testing.expect(ListFoo.equals(&list, &cloned));

// clone to a list of a different type
const Bar = VariableContainerType(struct {
a: FieldA,
b: UintType(8),
});
const ListBar = VariableListType(Bar, 8);
var list_bar: ListBar.Type = ListBar.default_value;
defer ListBar.deinit(allocator, &list_bar);
try ListFoo.clone(allocator, &list, &list_bar);
try std.testing.expect(list_bar.items.len == 1);
try std.testing.expect(FieldA.equals(&list_bar.items[0].a, &fielda));
}

// Tests ported from TypeScript ssz packages/ssz/test/unit/byType/listBasic/valid.test.ts
Expand Down
109 changes: 81 additions & 28 deletions src/ssz/type/vector.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,21 @@ pub fn FixedVectorType(comptime ST: type, comptime _length: comptime_int) type {
try merkleize(@ptrCast(&chunks), chunk_depth, out);
}

pub fn clone(value: *const Type, out: *Type) !void {
out.* = value.*;
pub fn clone(value: *const Type, out: anytype) !void {
comptime {
const OutInfo = @typeInfo(@TypeOf(out.*));
std.debug.assert(OutInfo == .array);
std.debug.assert(OutInfo.array.len == length);
}

const OutType = @TypeOf(out.*);
if (OutType == Type) {
out.* = value.*;
} else {
inline for (value, 0..) |*element, i| {
try Element.clone(element, &out[i]);
}
}
}

pub fn serializeIntoBytes(value: *const Type, out: []u8) usize {
Expand Down Expand Up @@ -286,8 +299,16 @@ pub fn VariableVectorType(comptime ST: type, comptime _length: comptime_int) typ
try merkleize(@ptrCast(&chunks), chunk_depth, out);
}

pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: *Type) !void {
for (0..length) |i| try Element.clone(allocator, &value[i], &out[i]);
pub fn clone(allocator: std.mem.Allocator, value: *const Type, out: anytype) !void {
comptime {
const OutInfo = @typeInfo(@TypeOf(out.*));
std.debug.assert(OutInfo == .array);
std.debug.assert(OutInfo.array.len == length);
}

for (value, 0..) |*element, i| {
try Element.clone(allocator, element, &out[i]);
}
}

pub fn serializedSize(value: *const Type) usize {
Expand Down Expand Up @@ -457,6 +478,7 @@ const BitListType = @import("bit_list.zig").BitListType;
const ByteVectorType = @import("byte_vector.zig").ByteVectorType;
const FixedContainerType = @import("container.zig").FixedContainerType;
const FixedListType = @import("list.zig").FixedListType;
const VariableContainerType = @import("container.zig").VariableContainerType;

test "vector - sanity" {
// create a fixed vector type and instance and round-trip serialize
Expand All @@ -468,31 +490,62 @@ test "vector - sanity" {
try Bytes32.deserializeFromBytes(&b0_buf, &b0);
}

test "clone" {
test "clone FixedVectorType" {
const Checkpoint = FixedContainerType(struct {
epoch: UintType(8),
root: ByteVectorType(32),
});
const CheckpointVector = FixedVectorType(Checkpoint, 4);
var vector: CheckpointVector.Type = CheckpointVector.default_value;
vector[0].epoch = 42;

var cloned: CheckpointVector.Type = undefined;
try CheckpointVector.clone(&vector, &cloned);
try std.testing.expect(&vector != &cloned);
try std.testing.expect(CheckpointVector.equals(&vector, &cloned));

// clone into another type
const CheckpointHex = FixedContainerType(struct {
epoch: UintType(8),
root: ByteVectorType(32),
root_hex: ByteVectorType(64),
});
const CheckpointHexVector = FixedVectorType(CheckpointHex, 4);
var cloned2: CheckpointHexVector.Type = undefined;
try CheckpointVector.clone(&vector, &cloned2);
try std.testing.expect(cloned2[0].epoch == 42);
}

test "clone VariableVectorType" {
const allocator = std.testing.allocator;
const BoolVectorFixed = FixedVectorType(BoolType(), 8);
var bvf: BoolVectorFixed.Type = BoolVectorFixed.default_value;

var cloned: BoolVectorFixed.Type = undefined;
try BoolVectorFixed.clone(&bvf, &cloned);
try expectEqualRoots(BoolVectorFixed, bvf, cloned);
try expectEqualSerialized(BoolVectorFixed, bvf, cloned);

try std.testing.expect(&bvf != &cloned);
try std.testing.expect(std.mem.eql(bool, bvf[0..], cloned[0..]));

const limit = 16;
const BitList = BitListType(limit);
const bl = BitList.default_value;
const BoolVectorVariable = VariableVectorType(BitList, 8);
var bvv: BoolVectorVariable.Type = BoolVectorVariable.default_value;
bvv[0] = bl;

var cloned_v: BoolVectorVariable.Type = undefined;
try BoolVectorVariable.clone(allocator, &bvv, &cloned_v);
try std.testing.expect(&bvv != &cloned_v);
try expectEqualRootsAlloc(BoolVectorVariable, allocator, bvv, cloned_v);
try expectEqualSerializedAlloc(BoolVectorVariable, allocator, bvv, cloned_v);
const FieldA = FixedListType(UintType(8), 32);
const Foo = VariableContainerType(struct {
a: FieldA,
});
const FooVector = VariableVectorType(Foo, 4);
var foo_vector: FooVector.Type = FooVector.default_value;
defer FooVector.deinit(allocator, &foo_vector);
try foo_vector[0].a.append(allocator, 100);

var cloned: FooVector.Type = undefined;
defer FooVector.deinit(allocator, &cloned);
try FooVector.clone(allocator, &foo_vector, &cloned);
try std.testing.expect(&foo_vector != &cloned);
try std.testing.expect(FooVector.equals(&foo_vector, &cloned));
try std.testing.expect(cloned[0].a.items.len == 1);
try std.testing.expect(cloned[0].a.items[0] == 100);

// clone into another type
const Bar = VariableContainerType(struct {
a: FieldA,
b: UintType(8),
});
const BarVector = VariableVectorType(Bar, 4);
var cloned2: BarVector.Type = undefined;
defer BarVector.deinit(allocator, &cloned2);
try FooVector.clone(allocator, &foo_vector, &cloned2);
try std.testing.expect(cloned2[0].a.items.len == 1);
try std.testing.expect(cloned2[0].a.items[0] == 100);
}

// Refer to https://github.com/ChainSafe/ssz/blob/f5ed0b457333749b5c3f49fa5eafa096a725f033/packages/ssz/test/unit/byType/vector/valid.test.ts#L15-L85
Expand Down
Loading