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 }