lib/machine/src/checkpoint/source.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const checkpoint = @import("root.zig");
2 const hot = @import("hot/root.zig");
3 const std = @import("std");
4
5 /// Verified identity and material prepared for building a machine from a
6 /// capture.
7 pub const Prepared = struct {
8 identity: checkpoint.Identity,
9 material: checkpoint.Material,
10 };
11
12 /// Borrowed capture used as the source of a restore. A durable source
13 /// references a complete normalized memory image. By contrast, a hot source
14 /// references ordered changed pages against a durable parent. The caller keeps
15 /// the selected checkpoint or snapshot alive for as long as the source can be
16 /// used.
17 pub const Source = union(enum) {
18 durable: *const checkpoint.Checkpoint,
19 hot: *const hot.Snapshot,
20
21 /// Checks the selected source and hands back the identity it would restore.
22 pub fn identity(self: @This()) checkpoint.Error!checkpoint.Identity {
23 return switch (self) {
24 .durable => |value| value.identity(),
25 .hot => |value| value.identity(),
26 };
27 }
28
29 /// Verifies the source and the expected checkpoint root before a machine is
30 /// constructed. A stored root other than the expected one returns
31 /// `CheckpointRootMismatch`. The destination must have `ram_alignment` and
32 /// `ram_bytes`. A hot source further requires the destination to hold
33 /// parent memory that has been authenticated.
34 pub fn prepareForRestore(
35 self: @This(),
36 expected: checkpoint.Root,
37 destination: []align(checkpoint.ram_alignment) const u8,
38 ) checkpoint.Error!Prepared {
39 return switch (self) {
40 .durable => |value| durable: {
41 const contents = try checkpoint.inspect(value);
42 if (!std.meta.eql(contents.root, expected)) {
43 return error.CheckpointRootMismatch;
44 }
45 if (destination.len != checkpoint.ram_bytes) {
46 return error.RamBytesMismatch;
47 }
48 break :durable .{
49 .identity = try checkpoint.identify(
50 contents.material,
51 contents.memory,
52 ),
53 .material = contents.material,
54 };
55 },
56 .hot => |value| .{
57 .identity = try value.prepareForRestore(expected, destination),
58 .material = value.material,
59 },
60 };
61 }
62
63 /// Fills a caller-owned memory region that lies outside the source and
64 /// returns the restore material. A durable source copies the whole memory
65 /// image. By contrast, a hot source overlays its ordered changed pages on
66 /// authenticated parent memory. A failure after the copying starts can
67 /// leave the destination bytes changed.
68 pub fn materializeForRestore(
69 self: @This(),
70 expected: checkpoint.Root,
71 destination: []align(checkpoint.ram_alignment) u8,
72 ) checkpoint.Error!checkpoint.Material {
73 return switch (self) {
74 .durable => |value| checkpoint.materializeForRestore(
75 value,
76 expected,
77 destination,
78 ),
79 .hot => |value| (try value.materializeForRestore(
80 expected,
81 destination,
82 )).material,
83 };
84 }
85
86 /// Returns the complete memory image used as the source's evidence. A hot
87 /// source returns its durable parent's normalized memory.
88 pub fn evidenceRam(
89 self: @This(),
90 ) []align(checkpoint.ram_alignment) const u8 {
91 return switch (self) {
92 .durable => |value| value.ram,
93 .hot => |value| value.evidenceRam(),
94 };
95 }
96
97 /// Reports whether bytes overlap any handle, metadata, memory, index, or
98 /// page storage the source borrows.
99 pub fn aliases(self: @This(), bytes: []const u8) bool {
100 return switch (self) {
101 .durable => |value| buffersOverlap(bytes, std.mem.asBytes(value)) or
102 buffersOverlap(bytes, &value.storage.bytes) or
103 buffersOverlap(bytes, value.ram),
104 .hot => |value| value.aliases(bytes),
105 };
106 }
107 };
108
109 fn buffersOverlap(left: []const u8, right: []const u8) bool {
110 if (left.len == 0 or right.len == 0) return false;
111 const left_start = @intFromPtr(left.ptr);
112 const right_start = @intFromPtr(right.ptr);
113 const left_end = std.math.add(usize, left_start, left.len) catch return true;
114 const right_end = std.math.add(usize, right_start, right.len) catch return true;
115 return left_start < right_end and right_start < left_end;
116 }