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

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const decode = @import("decode.zig");
  2 const fabric = @import("../root.zig");
  3 const fault = @import("../../fault/root.zig");
  4 const os = @import("os");
  5 const schema = @import("schema.zig");
  6 const transition_types = @import("types.zig");
  7 
  8 pub const Terminal = struct {
  9     offset: u64,
 10     length: u16,
 11 };
 12 
 13 pub const VirtualTime = struct {
 14     from_tick: u64,
 15     to_tick: u64,
 16 };
 17 
 18 pub const Entropy = struct {
 19     generation: u64,
 20     length: u16,
 21 };
 22 
 23 pub const EffectResult = struct {
 24     correlation: u64,
 25     status: os.abi.EffectStatus,
 26     output_root: os.abi.Digest,
 27     length: u16,
 28 };
 29 
 30 pub const AdmissionRecord = union(enum) {
 31     terminal: Terminal,
 32     virtual_time: VirtualTime,
 33     entropy: Entropy,
 34     effect_result: EffectResult,
 35 };
 36 
 37 pub const PacketSend = struct {
 38     id: u64,
 39     source: fabric.NodeId,
 40     destination: fabric.NodeId,
 41     channel: u16,
 42     correlation: u64,
 43     length: u16,
 44     digest: os.abi.Digest,
 45 };
 46 
 47 pub const AdmissionEffect = union(enum) {
 48     direct,
 49     packet_send: PacketSend,
 50     packet_delivery: u64,
 51 };
 52 
 53 pub const Fault = struct {
 54     kind: fault.Kind,
 55     choice: fault.Choice,
 56     primary: u64 = 0,
 57     secondary: u64 = 0,
 58 };
 59 
 60 pub const Admission = struct {
 61     node: fabric.NodeId,
 62     record: AdmissionRecord,
 63     effect: AdmissionEffect,
 64     fault_value: ?Fault,
 65 };
 66 
 67 pub const Settlement = struct {
 68     node: fabric.NodeId,
 69 };
 70 
 71 pub const Value = union(enum) {
 72     admission: Admission,
 73     settlement: Settlement,
 74     fault: Fault,
 75 };
 76 
 77 pub const View = struct {
 78     previous: fabric.Root,
 79     result: fabric.Root,
 80     sequence: u64,
 81     value: Value,
 82 };
 83 
 84 pub fn inspect(
 85     input: *const [schema.stream_bytes]u8,
 86 ) transition_types.Error!View {
 87     try decode.validateEnvelope(input);
 88     const kind = input[schema.Header.kind_offset];
 89     try decode.validateReserved(input, kind);
 90     return .{
 91         .previous = try decode.decodeRoot(input, schema.Header.previous_offset),
 92         .result = try decode.decodeRoot(input, schema.Header.result_offset),
 93         .sequence = decode.get64(input, schema.Header.sequence_offset),
 94         .value = switch (kind) {
 95             1 => .{ .admission = try inspectAdmission(input) },
 96             2 => .{ .settlement = inspectSettlement(input) },
 97             3 => .{ .fault = try inspectFault(
 98                 input,
 99                 schema.Header.limit + schema.Fault.effect_offset,
100                 schema.Header.limit + schema.Fault.decision_offset,
101             ) },
102             else => return error.UnknownTransitionField,
103         },
104     };
105 }
106 
107 fn inspectAdmission(
108     input: *const [schema.stream_bytes]u8,
109 ) transition_types.Error!Admission {
110     const start = schema.Header.limit;
111     const layout = schema.Admission;
112     const fault_value: ?Fault = if (try decode.boolean(
113         input[start + layout.fault_present_offset],
114     )) try inspectFault(
115         input,
116         start + layout.fault_effect_offset,
117         start + layout.decision_offset,
118     ) else null;
119     return .{
120         .node = decode.nodeId(input, start + layout.node_offset),
121         .record = try inspectRecord(
122             input,
123             start + layout.record_offset,
124             input[start + layout.record_kind_offset],
125         ),
126         .effect = try inspectEffect(
127             input,
128             start + layout.effect_offset,
129             input[start + layout.effect_kind_offset],
130         ),
131         .fault_value = fault_value,
132     };
133 }
134 
135 fn inspectRecord(
136     input: *const [schema.stream_bytes]u8,
137     start: usize,
138     kind: u8,
139 ) transition_types.Error!AdmissionRecord {
140     const layout = schema.Record;
141     return switch (kind) {
142         1 => .{ .terminal = .{
143             .offset = decode.get64(input, start + layout.first_offset),
144             .length = decode.get16(input, start + layout.payload_length_offset),
145         } },
146         2 => .{ .virtual_time = .{
147             .from_tick = decode.get64(input, start + layout.first_offset),
148             .to_tick = decode.get64(input, start + layout.second_offset),
149         } },
150         3 => .{ .entropy = .{
151             .generation = decode.get64(input, start + layout.first_offset),
152             .length = decode.get16(input, start + layout.payload_length_offset),
153         } },
154         4 => .{ .effect_result = .{
155             .correlation = decode.get64(input, start + layout.correlation_offset),
156             .status = try decode.enumValue(
157                 os.abi.EffectStatus,
158                 decode.get16(input, start + layout.status_offset),
159             ),
160             .output_root = decode.digest(input, start + layout.output_root_offset),
161             .length = decode.get16(input, start + layout.effect_length_offset),
162         } },
163         else => error.UnknownTransitionField,
164     };
165 }
166 
167 fn inspectEffect(
168     input: *const [schema.stream_bytes]u8,
169     start: usize,
170     kind: u8,
171 ) transition_types.Error!AdmissionEffect {
172     const layout = schema.Effect;
173     return switch (kind) {
174         1 => .direct,
175         2 => .{ .packet_send = .{
176             .id = decode.get64(input, start + layout.id_offset),
177             .source = decode.nodeId(input, start + layout.source_offset),
178             .destination = decode.nodeId(input, start + layout.destination_offset),
179             .channel = decode.get16(input, start + layout.channel_offset),
180             .correlation = decode.get64(
181                 input,
182                 start + layout.request_correlation_offset,
183             ),
184             .length = decode.get16(input, start + layout.length_offset),
185             .digest = decode.digest(input, start + layout.digest_offset),
186         } },
187         3 => .{ .packet_delivery = decode.get64(input, start + layout.id_offset) },
188         else => error.UnknownTransitionField,
189     };
190 }
191 
192 fn inspectSettlement(input: *const [schema.stream_bytes]u8) Settlement {
193     const start = schema.Header.limit + schema.Settlement.node_offset;
194     return .{ .node = decode.nodeId(input, start) };
195 }
196 
197 fn inspectFault(
198     input: *const [schema.stream_bytes]u8,
199     effect_start: usize,
200     decision_start: usize,
201 ) transition_types.Error!Fault {
202     const layout = schema.FaultEffect;
203     const kind = try decode.enumValue(
204         fault.Kind,
205         input[effect_start + layout.kind_offset],
206     );
207     const values = faultSubjects(input, effect_start, kind);
208     return .{
209         .kind = kind,
210         .choice = try decode.enumValue(
211             fault.Choice,
212             input[decision_start + schema.Decision.choice_offset],
213         ),
214         .primary = values[0],
215         .secondary = values[1],
216     };
217 }
218 
219 fn faultSubjects(
220     input: *const [schema.stream_bytes]u8,
221     start: usize,
222     kind: fault.Kind,
223 ) [2]u64 {
224     const value = start + schema.FaultEffect.value_offset;
225     return switch (kind) {
226         .packet_loss => .{ decode.get64(input, value), 0 },
227         .packet_delay, .packet_reorder => .{
228             decode.get64(input, value),
229             decode.get64(input, value + 8),
230         },
231         .clock_jump => .{ decode.get64(input, value), 0 },
232         else => .{ 0, 0 },
233     };
234 }