diff --git a/src/base/Ident.zig b/src/base/Ident.zig index b0095821f8..4783b565cf 100644 --- a/src/base/Ident.zig +++ b/src/base/Ident.zig @@ -94,6 +94,58 @@ pub const Store = struct { attributes: collections.SafeList(Attributes) = .{}, next_unique_name: u32 = 0, + /// Serialized representation of a Store + pub const Serialized = struct { + interner: SmallStringInterner, + attributes: collections.SafeList(Attributes).Serialized, + next_unique_name: u32, + + /// Serialize a Store into this Serialized struct, appending data to the writer + pub fn serialize( + self: *Serialized, + store: *const Store, + allocator: std.mem.Allocator, + writer: *collections.CompactWriter, + ) std.mem.Allocator.Error!void { + // Serialize the interner + self.interner = (try store.interner.serialize(allocator, writer)).*; + + // Serialize the attributes SafeList + const attributes_serialized = try writer.appendAlloc(allocator, collections.SafeList(Attributes).Serialized); + try attributes_serialized.serialize(&store.attributes, allocator, writer); + self.attributes = attributes_serialized.*; + + // Copy next_unique_name directly + self.next_unique_name = store.next_unique_name; + } + + /// Deserialize this Serialized struct into a Store + pub fn deserialize(self: *Serialized, offset: i64) *Store { + // Debug assert that Serialized is at least as big as Store + std.debug.assert(@sizeOf(Serialized) >= @sizeOf(Store)); + + // Apply relocations + self.interner.relocate(@intCast(offset)); + + // Deserialize the attributes SafeList + const attributes = self.attributes.deserialize(offset); + + // Build the Store + const store = Store{ + .interner = self.interner, + .attributes = attributes.*, + .next_unique_name = self.next_unique_name, + }; + + // Write the Store to our memory location + const self_ptr = @intFromPtr(self); + const store_ptr = @as(*Store, @ptrFromInt(self_ptr)); + store_ptr.* = store; + + return store_ptr; + } + }; + /// Initialize the memory for an `Ident.Store` with a specific capaicty. pub fn initCapacity(gpa: std.mem.Allocator, capacity: usize) std.mem.Allocator.Error!Store { return .{ diff --git a/src/collections/safe_list.zig b/src/collections/safe_list.zig index 7fc09d1e97..c4dcf68018 100644 --- a/src/collections/safe_list.zig +++ b/src/collections/safe_list.zig @@ -112,6 +112,60 @@ pub fn SafeList(comptime T: type) type { nonempty: Range, }; + /// Serialized representation of a SafeList + pub const Serialized = struct { + offset: u64, + len: u64, + capacity: u64, + + /// Serialize a SafeList into this Serialized struct, appending data to the writer + pub fn serialize( + self: *Serialized, + safe_list: *const SafeList(T), + allocator: Allocator, + writer: *CompactWriter, + ) Allocator.Error!void { + const items = safe_list.items.items; + + // Append the slice data first + const slice_ptr = try writer.appendSlice(allocator, items); + + // Store the offset, len, and capacity + self.offset = @intFromPtr(slice_ptr.ptr); + self.len = items.len; + self.capacity = items.len; + } + + /// Deserialize this Serialized struct into a SafeList + pub fn deserialize(self: *Serialized, offset: i64) *SafeList(T) { + // Debug assert that Serialized is at least as big as SafeList + std.debug.assert(@sizeOf(Serialized) >= @sizeOf(SafeList(T))); + + // Apply the offset to convert from serialized offset to actual pointer + const adjusted_offset: u64 = if (offset >= 0) + self.offset + @as(u64, @intCast(offset)) + else + self.offset - @as(u64, @intCast(-offset)); + + // Build the SafeList + const items_ptr: [*]T = @ptrFromInt(adjusted_offset); + const items_slice = items_ptr[0..@intCast(self.len)]; + const safe_list = SafeList(T){ + .items = .{ + .items = items_slice, + .capacity = @intCast(self.capacity), + }, + }; + + // Write the SafeList to our memory location + const self_ptr = @intFromPtr(self); + const safe_list_ptr = @as(*SafeList(T), @ptrFromInt(self_ptr)); + safe_list_ptr.* = safe_list; + + return safe_list_ptr; + } + }; + /// Initialize the `SafeList` with the specified capacity. pub fn initCapacity(gpa: Allocator, capacity: usize) std.mem.Allocator.Error!SafeList(T) { return .{ @@ -1371,7 +1425,9 @@ test "SafeList(u32) CompactWriter roundtrip with file" { }; defer writer.deinit(gpa); - _ = try original.serialize(gpa, &writer); + // Allocate and serialize using SafeList.Serialized + const serialized = try writer.appendAlloc(gpa, SafeList(u32).Serialized); + try serialized.serialize(&original, gpa, &writer); // Write to file try writer.writeGather(gpa, file); @@ -1384,13 +1440,12 @@ test "SafeList(u32) CompactWriter roundtrip with file" { _ = try file.read(buffer); - // The key insight: we can just cast the buffer to a SafeList pointer - // The layout in memory is exactly what we need - const deserialized = @as(*SafeList(u32), @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(u32))))); + // The key insight: we can just cast the buffer to a SafeList.Serialized pointer + const serialized_ptr = @as(*SafeList(u32).Serialized, @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(u32).Serialized)))); - // Relocate the pointers - this adjusts the internal pointer from offset to actual memory address + // Deserialize - this converts the Serialized struct to a SafeList const base_addr = @intFromPtr(buffer.ptr); - deserialized.relocate(@as(isize, @intCast(base_addr))); + const deserialized = serialized_ptr.deserialize(@as(i64, @intCast(base_addr))); // Verify the data matches try testing.expectEqual(original.len(), deserialized.len()); @@ -1430,7 +1485,9 @@ test "SafeList(struct) CompactWriter roundtrip with file" { }; defer writer.deinit(gpa); - _ = try original.serialize(gpa, &writer); + // Allocate and serialize using SafeList.Serialized + const serialized = try writer.appendAlloc(gpa, SafeList(Point).Serialized); + try serialized.serialize(&original, gpa, &writer); // Write to file try writer.writeGather(gpa, file); @@ -1443,9 +1500,9 @@ test "SafeList(struct) CompactWriter roundtrip with file" { _ = try file.read(buffer); - // Cast and relocate - const deserialized = @as(*SafeList(Point), @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(Point))))); - deserialized.relocate(@as(isize, @intCast(@intFromPtr(buffer.ptr)))); + // Cast to SafeList.Serialized and deserialize + const serialized_ptr = @as(*SafeList(Point).Serialized, @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(Point).Serialized)))); + const deserialized = serialized_ptr.deserialize(@as(i64, @intCast(@intFromPtr(buffer.ptr)))); // Verify the data try testing.expectEqual(@as(usize, 3), deserialized.len()); @@ -1478,7 +1535,9 @@ test "SafeList empty list CompactWriter roundtrip" { }; defer writer.deinit(gpa); - _ = try original.serialize(gpa, &writer); + // Allocate and serialize using SafeList.Serialized + const serialized = try writer.appendAlloc(gpa, SafeList(u64).Serialized); + try serialized.serialize(&original, gpa, &writer); // Write to file try writer.writeGather(gpa, file); @@ -1491,9 +1550,9 @@ test "SafeList empty list CompactWriter roundtrip" { _ = try file.read(buffer); - // Cast and relocate - empty list should still work - const deserialized = @as(*SafeList(u64), @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(u64))))); - deserialized.relocate(@as(isize, @intCast(@intFromPtr(buffer.ptr)))); + // Cast to SafeList.Serialized and deserialize - empty list should still work + const serialized_ptr = @as(*SafeList(u64).Serialized, @ptrCast(@alignCast(buffer.ptr + writer.total_bytes - @sizeOf(SafeList(u64).Serialized)))); + const deserialized = serialized_ptr.deserialize(@as(i64, @intCast(@intFromPtr(buffer.ptr)))); // Verify empty try testing.expectEqual(@as(usize, 0), deserialized.len()); @@ -1541,14 +1600,17 @@ test "SafeList empty lists CompactWriter roundtrip multiple types" { }; defer writer.deinit(gpa); - _ = try list1.serialize(gpa, &writer); - const offset1 = writer.total_bytes - @sizeOf(SafeList(T)); + const serialized1 = try writer.appendAlloc(gpa, SafeList(T).Serialized); + try serialized1.serialize(&list1, gpa, &writer); + const offset1 = writer.total_bytes - @sizeOf(SafeList(T).Serialized); - _ = try list_u8.serialize(gpa, &writer); - const offset_u8 = writer.total_bytes - @sizeOf(SafeList(u8)); + const serialized_u8 = try writer.appendAlloc(gpa, SafeList(u8).Serialized); + try serialized_u8.serialize(&list_u8, gpa, &writer); + const offset_u8 = writer.total_bytes - @sizeOf(SafeList(u8).Serialized); - _ = try list2.serialize(gpa, &writer); - const offset2 = writer.total_bytes - @sizeOf(SafeList(T)); + const serialized2 = try writer.appendAlloc(gpa, SafeList(T).Serialized); + try serialized2.serialize(&list2, gpa, &writer); + const offset2 = writer.total_bytes - @sizeOf(SafeList(T).Serialized); // Write to file try writer.writeGather(gpa, file); @@ -1564,19 +1626,19 @@ test "SafeList empty lists CompactWriter roundtrip multiple types" { const base = @intFromPtr(buffer.ptr); // Verify first empty list - const d1 = @as(*SafeList(T), @ptrCast(@alignCast(buffer.ptr + offset1))); - d1.relocate(@as(isize, @intCast(base))); + const s1 = @as(*SafeList(T).Serialized, @ptrCast(@alignCast(buffer.ptr + offset1))); + const d1 = s1.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 0), d1.len()); // Verify non-empty u8 list - const d_u8 = @as(*SafeList(u8), @ptrCast(@alignCast(buffer.ptr + offset_u8))); - d_u8.relocate(@as(isize, @intCast(base))); + const s_u8 = @as(*SafeList(u8).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_u8))); + const d_u8 = s_u8.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 1), d_u8.len()); try testing.expectEqual(@as(u8, 123), d_u8.get(@enumFromInt(0)).*); // Verify second empty list - const d2 = @as(*SafeList(T), @ptrCast(@alignCast(buffer.ptr + offset2))); - d2.relocate(@as(isize, @intCast(base))); + const s2 = @as(*SafeList(T).Serialized, @ptrCast(@alignCast(buffer.ptr + offset2))); + const d2 = s2.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 0), d2.len()); } } @@ -1600,12 +1662,13 @@ test "SafeList CompactWriter verify offset calculation" { }; defer writer.deinit(gpa); - const serialized_ptr = try list.serialize(gpa, &writer); + const serialized = try writer.appendAlloc(gpa, SafeList(u16).Serialized); + try serialized.serialize(&list, gpa, &writer); // The offset should be the aligned size of the data // 4 items * 2 bytes = 8 bytes, which is already aligned to 8 const expected_offset = 8; - try testing.expectEqual(@as(usize, expected_offset), @intFromPtr(serialized_ptr.items.items.ptr)); + try testing.expectEqual(@as(u64, expected_offset), serialized.offset); } test "SafeList CompactWriter complete roundtrip example" { @@ -1633,11 +1696,12 @@ test "SafeList CompactWriter complete roundtrip example" { }; defer writer.deinit(gpa); - // Step 3: Serialize - this writes data first, then the SafeList struct - const serialized_ptr = try original.serialize(gpa, &writer); + // Step 3: Serialize - this writes data first, then the SafeList.Serialized struct + const serialized = try writer.appendAlloc(gpa, SafeList(u32).Serialized); + try serialized.serialize(&original, gpa, &writer); // Verify the offset is correct (4 * 4 = 16 bytes, already aligned to 8) - try testing.expectEqual(@as(usize, 16), @intFromPtr(serialized_ptr.items.items.ptr)); + try testing.expectEqual(@as(u64, 16), serialized.offset); // Step 4: Write to file using vectored I/O try writer.writeGather(gpa, file); @@ -1650,12 +1714,12 @@ test "SafeList CompactWriter complete roundtrip example" { _ = try file.read(buffer); - // Step 6: Cast buffer to SafeList - the struct is at the end - const list_offset = writer.total_bytes - @sizeOf(SafeList(u32)); - const deserialized = @as(*SafeList(u32), @ptrCast(@alignCast(buffer.ptr + list_offset))); + // Step 6: Cast buffer to SafeList.Serialized - the struct is at the end + const list_offset = writer.total_bytes - @sizeOf(SafeList(u32).Serialized); + const serialized_ptr = @as(*SafeList(u32).Serialized, @ptrCast(@alignCast(buffer.ptr + list_offset))); - // Step 7: Relocate - convert offset to pointer - deserialized.relocate(@as(isize, @intCast(@intFromPtr(buffer.ptr)))); + // Step 7: Deserialize - convert offset to pointer + const deserialized = serialized_ptr.deserialize(@as(i64, @intCast(@intFromPtr(buffer.ptr)))); // Step 8: Verify data is accessible and correct try testing.expectEqual(@as(usize, 4), deserialized.len()); @@ -1722,20 +1786,25 @@ test "SafeList CompactWriter multiple lists with different alignments" { defer writer.deinit(gpa); // Serialize all lists and track their positions - const ptr_u8 = try list_u8.serialize(gpa, &writer); - const offset_u8 = writer.total_bytes - @sizeOf(SafeList(u8)); + const serialized_u8 = try writer.appendAlloc(gpa, SafeList(u8).Serialized); + try serialized_u8.serialize(&list_u8, gpa, &writer); + const offset_u8 = writer.total_bytes - @sizeOf(SafeList(u8).Serialized); - const ptr_u16 = try list_u16.serialize(gpa, &writer); - const offset_u16 = writer.total_bytes - @sizeOf(SafeList(u16)); + const serialized_u16 = try writer.appendAlloc(gpa, SafeList(u16).Serialized); + try serialized_u16.serialize(&list_u16, gpa, &writer); + const offset_u16 = writer.total_bytes - @sizeOf(SafeList(u16).Serialized); - const ptr_u32 = try list_u32.serialize(gpa, &writer); - const offset_u32 = writer.total_bytes - @sizeOf(SafeList(u32)); + const serialized_u32 = try writer.appendAlloc(gpa, SafeList(u32).Serialized); + try serialized_u32.serialize(&list_u32, gpa, &writer); + const offset_u32 = writer.total_bytes - @sizeOf(SafeList(u32).Serialized); - const ptr_u64 = try list_u64.serialize(gpa, &writer); - const offset_u64 = writer.total_bytes - @sizeOf(SafeList(u64)); + const serialized_u64 = try writer.appendAlloc(gpa, SafeList(u64).Serialized); + try serialized_u64.serialize(&list_u64, gpa, &writer); + const offset_u64 = writer.total_bytes - @sizeOf(SafeList(u64).Serialized); - const ptr_struct = try list_struct.serialize(gpa, &writer); - const offset_struct = writer.total_bytes - @sizeOf(SafeList(AlignedStruct)); + const serialized_struct = try writer.appendAlloc(gpa, SafeList(AlignedStruct).Serialized); + try serialized_struct.serialize(&list_struct, gpa, &writer); + const offset_struct = writer.total_bytes - @sizeOf(SafeList(AlignedStruct).Serialized); // Write to file try writer.writeGather(gpa, file); @@ -1752,23 +1821,23 @@ test "SafeList CompactWriter multiple lists with different alignments" { const base_addr = @intFromPtr(buffer.ptr); // 1. Deserialize u8 list - const deser_u8 = @as(*SafeList(u8), @ptrCast(@alignCast(buffer.ptr + offset_u8))); - deser_u8.relocate(@as(isize, @intCast(base_addr))); + const s_u8 = @as(*SafeList(u8).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_u8))); + const deser_u8 = s_u8.deserialize(@as(i64, @intCast(base_addr))); try testing.expectEqual(@as(usize, 3), deser_u8.len()); try testing.expectEqual(@as(u8, 10), deser_u8.get(@enumFromInt(0)).*); try testing.expectEqual(@as(u8, 20), deser_u8.get(@enumFromInt(1)).*); try testing.expectEqual(@as(u8, 30), deser_u8.get(@enumFromInt(2)).*); // 2. Deserialize u16 list - const deser_u16 = @as(*SafeList(u16), @ptrCast(@alignCast(buffer.ptr + offset_u16))); - deser_u16.relocate(@as(isize, @intCast(base_addr))); + const s_u16 = @as(*SafeList(u16).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_u16))); + const deser_u16 = s_u16.deserialize(@as(i64, @intCast(base_addr))); try testing.expectEqual(@as(usize, 2), deser_u16.len()); try testing.expectEqual(@as(u16, 1000), deser_u16.get(@enumFromInt(0)).*); try testing.expectEqual(@as(u16, 2000), deser_u16.get(@enumFromInt(1)).*); // 3. Deserialize u32 list - const deser_u32 = @as(*SafeList(u32), @ptrCast(@alignCast(buffer.ptr + offset_u32))); - deser_u32.relocate(@as(isize, @intCast(base_addr))); + const s_u32 = @as(*SafeList(u32).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_u32))); + const deser_u32 = s_u32.deserialize(@as(i64, @intCast(base_addr))); try testing.expectEqual(@as(usize, 4), deser_u32.len()); try testing.expectEqual(@as(u32, 100_000), deser_u32.get(@enumFromInt(0)).*); try testing.expectEqual(@as(u32, 200_000), deser_u32.get(@enumFromInt(1)).*); @@ -1776,15 +1845,15 @@ test "SafeList CompactWriter multiple lists with different alignments" { try testing.expectEqual(@as(u32, 400_000), deser_u32.get(@enumFromInt(3)).*); // 4. Deserialize u64 list - const deser_u64 = @as(*SafeList(u64), @ptrCast(@alignCast(buffer.ptr + offset_u64))); - deser_u64.relocate(@as(isize, @intCast(base_addr))); + const s_u64 = @as(*SafeList(u64).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_u64))); + const deser_u64 = s_u64.deserialize(@as(i64, @intCast(base_addr))); try testing.expectEqual(@as(usize, 2), deser_u64.len()); try testing.expectEqual(@as(u64, 10_000_000_000), deser_u64.get(@enumFromInt(0)).*); try testing.expectEqual(@as(u64, 20_000_000_000), deser_u64.get(@enumFromInt(1)).*); // 5. Deserialize struct list - const deser_struct = @as(*SafeList(AlignedStruct), @ptrCast(@alignCast(buffer.ptr + offset_struct))); - deser_struct.relocate(@as(isize, @intCast(base_addr))); + const s_struct = @as(*SafeList(AlignedStruct).Serialized, @ptrCast(@alignCast(buffer.ptr + offset_struct))); + const deser_struct = s_struct.deserialize(@as(i64, @intCast(base_addr))); try testing.expectEqual(@as(usize, 2), deser_struct.len()); const item0 = deser_struct.get(@enumFromInt(0)); @@ -1796,13 +1865,6 @@ test "SafeList CompactWriter multiple lists with different alignments" { try testing.expectEqual(@as(u32, 99), item1.x); try testing.expectEqual(@as(u64, 9999), item1.y); try testing.expectEqual(@as(u8, 128), item1.z); - - // Verify the serialized pointers match what we deserialized - _ = ptr_u8; - _ = ptr_u16; - _ = ptr_u32; - _ = ptr_u64; - _ = ptr_struct; } test "SafeList CompactWriter interleaved pattern with alignment tracking" { @@ -1838,8 +1900,9 @@ test "SafeList CompactWriter interleaved pattern with alignment tracking" { _ = try list1.append(gpa, 3); const start1 = writer.total_bytes; - _ = try list1.serialize(gpa, &writer); - try offsets.append(writer.total_bytes - @sizeOf(SafeList(u8))); + const serialized1 = try writer.appendAlloc(gpa, SafeList(u8).Serialized); + try serialized1.serialize(&list1, gpa, &writer); + try offsets.append(writer.total_bytes - @sizeOf(SafeList(u8).Serialized)); // 2. u64 list (8-byte aligned, forces significant padding) var list2 = try SafeList(u64).initCapacity(gpa, 2); @@ -1848,11 +1911,12 @@ test "SafeList CompactWriter interleaved pattern with alignment tracking" { _ = try list2.append(gpa, 2_000_000); const start2 = writer.total_bytes; - _ = try list2.serialize(gpa, &writer); - try offsets.append(writer.total_bytes - @sizeOf(SafeList(u64))); + const serialized2 = try writer.appendAlloc(gpa, SafeList(u64).Serialized); + try serialized2.serialize(&list2, gpa, &writer); + try offsets.append(writer.total_bytes - @sizeOf(SafeList(u64).Serialized)); // Verify padding was added before u64 data - const padding_before_u64 = start2 - start1 - (3 + @sizeOf(SafeList(u8))); + const padding_before_u64 = start2 - start1 - (3 + @sizeOf(SafeList(u8).Serialized)); try testing.expect(padding_before_u64 > 0); // 3. u16 list (2-byte aligned) @@ -1863,16 +1927,18 @@ test "SafeList CompactWriter interleaved pattern with alignment tracking" { _ = try list3.append(gpa, 300); _ = try list3.append(gpa, 400); - _ = try list3.serialize(gpa, &writer); - try offsets.append(writer.total_bytes - @sizeOf(SafeList(u16))); + const serialized3 = try writer.appendAlloc(gpa, SafeList(u16).Serialized); + try serialized3.serialize(&list3, gpa, &writer); + try offsets.append(writer.total_bytes - @sizeOf(SafeList(u16).Serialized)); // 4. u32 list (4-byte aligned) var list4 = try SafeList(u32).initCapacity(gpa, 1); defer list4.deinit(gpa); _ = try list4.append(gpa, 42); - _ = try list4.serialize(gpa, &writer); - try offsets.append(writer.total_bytes - @sizeOf(SafeList(u32))); + const serialized4 = try writer.appendAlloc(gpa, SafeList(u32).Serialized); + try serialized4.serialize(&list4, gpa, &writer); + try offsets.append(writer.total_bytes - @sizeOf(SafeList(u32).Serialized)); // Write to file try writer.writeGather(gpa, file); @@ -1887,23 +1953,23 @@ test "SafeList CompactWriter interleaved pattern with alignment tracking" { const base = @intFromPtr(buffer.ptr); // Deserialize and verify all lists - const d1 = @as(*SafeList(u8), @ptrCast(@alignCast(buffer.ptr + offsets.items[0]))); - d1.relocate(@as(isize, @intCast(base))); + const s1 = @as(*SafeList(u8).Serialized, @ptrCast(@alignCast(buffer.ptr + offsets.items[0]))); + const d1 = s1.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 3), d1.len()); try testing.expectEqual(@as(u8, 1), d1.get(@enumFromInt(0)).*); - const d2 = @as(*SafeList(u64), @ptrCast(@alignCast(buffer.ptr + offsets.items[1]))); - d2.relocate(@as(isize, @intCast(base))); + const s2 = @as(*SafeList(u64).Serialized, @ptrCast(@alignCast(buffer.ptr + offsets.items[1]))); + const d2 = s2.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 2), d2.len()); try testing.expectEqual(@as(u64, 1_000_000), d2.get(@enumFromInt(0)).*); - const d3 = @as(*SafeList(u16), @ptrCast(@alignCast(buffer.ptr + offsets.items[2]))); - d3.relocate(@as(isize, @intCast(base))); + const s3 = @as(*SafeList(u16).Serialized, @ptrCast(@alignCast(buffer.ptr + offsets.items[2]))); + const d3 = s3.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 4), d3.len()); try testing.expectEqual(@as(u16, 100), d3.get(@enumFromInt(0)).*); - const d4 = @as(*SafeList(u32), @ptrCast(@alignCast(buffer.ptr + offsets.items[3]))); - d4.relocate(@as(isize, @intCast(base))); + const s4 = @as(*SafeList(u32).Serialized, @ptrCast(@alignCast(buffer.ptr + offsets.items[3]))); + const d4 = s4.deserialize(@as(i64, @intCast(base))); try testing.expectEqual(@as(usize, 1), d4.len()); try testing.expectEqual(@as(u32, 42), d4.get(@enumFromInt(0)).*); }