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 }