lib/machine/src/checkpoint/stream/codec.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const decode = @import("decode.zig");
2 const encode = @import("encode.zig");
3 const owner = @import("../owner/root.zig");
4 const schema = @import("schema.zig");
5 const stream_types = @import("types.zig");
6 const std = @import("std");
7
8 pub const Error = stream_types.Error;
9 pub const header_bytes = schema.header_bytes;
10 pub const stream_bytes = schema.stream_bytes;
11
12 /// Checks the durable checkpoint first, then emits a header and the whole
13 /// normalized memory behind it. The writer and its buffer sit outside the
14 /// checkpoint handle, the storage behind it, and its memory. When input or
15 /// output fails partway, the bytes already handed to the writer stay there.
16 pub fn encodeDisjoint(
17 checkpoint: *const owner.Checkpoint,
18 writer: *std.Io.Writer,
19 ) Error!void {
20 try validateSink(checkpoint, writer);
21 const contents = try owner.inspect(checkpoint);
22 var header_bytes_buffer: [header_bytes]u8 = undefined;
23 encode.header(contents, &header_bytes_buffer);
24 try writer.writeAll(&header_bytes_buffer);
25 try writer.writeAll(checkpoint.ram);
26 }
27
28 /// Draws exactly one stream from the reader into empty storage and page-aligned
29 /// memory that the caller owns. The reader and its buffer sit outside both
30 /// destinations. Framing, trailing data, a root mismatch, or failed
31 /// authentication aborts the publication. An abort returns the claimed storage
32 /// to empty, so the caller can try again with the same storage. The destination
33 /// memory can hold received bytes after a failure. Success returns a handle
34 /// borrowing the caller's storage and memory.
35 pub fn decodeDisjoint(
36 reader: *std.Io.Reader,
37 expected_root: owner.Root,
38 storage: *owner.Storage,
39 ram: []align(owner.ram_alignment) u8,
40 ) Error!owner.Checkpoint {
41 try validateSource(reader, storage, ram);
42 var header_bytes_buffer: [header_bytes]u8 = undefined;
43 try readExact(reader, &header_bytes_buffer);
44 const decoded = try decode.header(&header_bytes_buffer, expected_root);
45 var materialization = try owner.beginMaterialization(storage, ram);
46 errdefer owner.abortMaterialization(&materialization);
47 try readExact(reader, ram);
48 var trailing: [1]u8 = undefined;
49 if (try reader.readSliceShort(&trailing) != 0) return error.TrailingData;
50 return owner.publishMaterialized(
51 &materialization,
52 decoded.material,
53 expected_root,
54 decoded.memory,
55 );
56 }
57
58 fn readExact(reader: *std.Io.Reader, output: []u8) Error!void {
59 reader.readSliceAll(output) catch |failure| switch (failure) {
60 error.EndOfStream => return error.TruncatedStream,
61 error.ReadFailed => return error.ReadFailed,
62 };
63 }
64
65 fn validateSink(
66 checkpoint: *const owner.Checkpoint,
67 writer: *std.Io.Writer,
68 ) Error!void {
69 const handle = std.mem.asBytes(checkpoint);
70 const storage = &checkpoint.storage.bytes;
71 const ram = checkpoint.ram;
72 if (buffersOverlap(std.mem.asBytes(writer), handle) or
73 buffersOverlap(std.mem.asBytes(writer), storage) or
74 buffersOverlap(std.mem.asBytes(writer), ram) or
75 buffersOverlap(writer.buffer, handle) or
76 buffersOverlap(writer.buffer, storage) or
77 buffersOverlap(writer.buffer, ram))
78 {
79 return error.SinkAliasesCheckpoint;
80 }
81 }
82
83 fn validateSource(
84 reader: *std.Io.Reader,
85 storage: *owner.Storage,
86 ram: []align(owner.ram_alignment) u8,
87 ) Error!void {
88 if (ram.len != owner.ram_bytes) return error.RamBytesMismatch;
89 if (buffersOverlap(&storage.bytes, ram)) {
90 return error.MemoryAliasesStorage;
91 }
92 if (buffersOverlap(std.mem.asBytes(reader), &storage.bytes) or
93 buffersOverlap(std.mem.asBytes(reader), ram) or
94 buffersOverlap(reader.buffer, &storage.bytes) or
95 buffersOverlap(reader.buffer, ram))
96 {
97 return error.SourceAliasesDestination;
98 }
99 }
100
101 fn buffersOverlap(left: []const u8, right: []const u8) bool {
102 if (left.len == 0 or right.len == 0) return false;
103 const left_start = @intFromPtr(left.ptr);
104 const right_start = @intFromPtr(right.ptr);
105 const left_end = std.math.add(usize, left_start, left.len) catch return true;
106 const right_end = std.math.add(usize, right_start, right.len) catch return true;
107 return left_start < right_end and right_start < left_end;
108 }