lib/machine/src/fabric/transition/codec.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const decode = @import("decode.zig");
 2 const encode = @import("encode.zig");
 3 const fabric = @import("../root.zig");
 4 const schema = @import("schema.zig");
 5 const std = @import("std");
 6 const transition_types = @import("types.zig");
 7 
 8 pub const Error = transition_types.Error;
 9 pub const stream_bytes = schema.stream_bytes;
10 
11 pub fn encodeDisjoint(
12     before: *const fabric.Fabric,
13     value: *const fabric.Transition,
14     writer: *std.Io.Writer,
15 ) Error!void {
16     try validateSink(before, value, writer);
17     var bytes: [stream_bytes]u8 = undefined;
18     try encode.transition(before, value, &bytes);
19     try writer.writeAll(&bytes);
20 }
21 
22 pub fn replayDisjoint(
23     owner: *fabric.Fabric,
24     reader: *std.Io.Reader,
25     expected_root: fabric.Root,
26 ) Error!fabric.Transition {
27     try validateSource(owner, reader);
28     var bytes: [stream_bytes]u8 = undefined;
29     try readExact(reader, &bytes);
30     var trailing: [1]u8 = undefined;
31     if (try reader.readSliceShort(&trailing) != 0) return error.TrailingData;
32     const decoded = try decode.transition(&bytes, owner, expected_root);
33     owner.* = decoded.owner;
34     std.debug.assert(std.meta.eql(owner.root(), expected_root));
35     return decoded.value;
36 }
37 
38 fn readExact(reader: *std.Io.Reader, output: []u8) Error!void {
39     std.debug.assert(output.len == stream_bytes);
40     reader.readSliceAll(output) catch |failure| switch (failure) {
41         error.EndOfStream => return error.TruncatedStream,
42         error.ReadFailed => return error.ReadFailed,
43     };
44 }
45 
46 fn validateSink(
47     before: *const fabric.Fabric,
48     value: *const fabric.Transition,
49     writer: *std.Io.Writer,
50 ) Error!void {
51     const writer_bytes = std.mem.asBytes(writer);
52     const before_bytes = std.mem.asBytes(before);
53     const value_bytes = std.mem.asBytes(value);
54     if (buffersOverlap(writer_bytes, before_bytes) or
55         buffersOverlap(writer.buffer, before_bytes))
56     {
57         return error.SinkAliasesFabric;
58     }
59     if (buffersOverlap(writer_bytes, value_bytes) or
60         buffersOverlap(writer.buffer, value_bytes))
61     {
62         return error.SinkAliasesTransition;
63     }
64 }
65 
66 fn validateSource(
67     owner: *const fabric.Fabric,
68     reader: *std.Io.Reader,
69 ) Error!void {
70     const owner_bytes = std.mem.asBytes(owner);
71     if (buffersOverlap(std.mem.asBytes(reader), owner_bytes) or
72         buffersOverlap(reader.buffer, owner_bytes))
73     {
74         return error.SourceAliasesFabric;
75     }
76 }
77 
78 fn buffersOverlap(left: []const u8, right: []const u8) bool {
79     if (left.len == 0 or right.len == 0) return false;
80     const left_start = @intFromPtr(left.ptr);
81     const right_start = @intFromPtr(right.ptr);
82     const left_end = std.math.add(usize, left_start, left.len) catch return true;
83     const right_end = std.math.add(usize, right_start, right.len) catch return true;
84     return left_start < right_end and right_start < left_end;
85 }