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 }