lib/machine/src/world/manifest/decode.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const fabric = @import("../../fabric/root.zig");
  2 const manifest_types = @import("types.zig");
  3 const os = @import("os");
  4 const schema = @import("schema.zig");
  5 const std = @import("std");
  6 const world = @import("../root.zig");
  7 
  8 const wire = os.abi.wire;
  9 
 10 pub fn manifest(
 11     input: *const [schema.stream_bytes]u8,
 12     expected_root: world.Root,
 13 ) manifest_types.Error!world.Cut {
 14     try validateEnvelope(input);
 15     const node_count = input[schema.RootLayout.node_count_offset];
 16     if (node_count == 0 or node_count > world.node_limit) {
 17         return error.MetadataShapeMismatch;
 18     }
 19     try validateReserved(input, node_count);
 20     const root_value = try decodeRoot(input);
 21     if (!std.meta.eql(root_value, expected_root)) return error.WorldRootMismatch;
 22     var result: world.Cut = .{
 23         .root = root_value,
 24         .nodes = @splat(emptyNode()),
 25     };
 26     for (&result.nodes, 0..) |*node, index| {
 27         if (index == node_count) break;
 28         node.* = decodeNode(input, index);
 29     }
 30     try world.verify(result);
 31     std.debug.assert(std.meta.eql(result.root, expected_root));
 32     return result;
 33 }
 34 
 35 fn validateEnvelope(
 36     input: *const [schema.stream_bytes]u8,
 37 ) manifest_types.Error!void {
 38     const layout = schema.Envelope;
 39     if (!std.mem.eql(
 40         u8,
 41         input[layout.magic_offset..layout.version_offset],
 42         &schema.magic,
 43     )) return error.BadMagic;
 44     if (get16(input, layout.version_offset) != schema.version) {
 45         return error.UnsupportedVersion;
 46     }
 47     if (get16(input, layout.stream_bytes_offset) != schema.stream_bytes) {
 48         return error.StreamBytesMismatch;
 49     }
 50     if (get16(input, layout.flags_offset) != schema.flags) {
 51         return error.UnsupportedFlags;
 52     }
 53     if (get16(input, layout.format_offset) != schema.format) {
 54         return error.ManifestFormatMismatch;
 55     }
 56     if (get16(input, layout.world_dialect_offset) !=
 57         @backingInt(world.Dialect.connected_world_v1) or
 58         get16(input, layout.fabric_dialect_offset) !=
 59             @backingInt(fabric.Dialect.ordered_effect_fabric_v3))
 60     {
 61         return error.UnknownManifestField;
 62     }
 63     try validateEnvelopeShape(input);
 64 }
 65 
 66 fn validateEnvelopeShape(
 67     input: *const [schema.stream_bytes]u8,
 68 ) manifest_types.Error!void {
 69     const layout = schema.Envelope;
 70     if (get16(input, layout.node_capacity_offset) != world.node_limit or
 71         get16(input, layout.node_bytes_offset) != schema.node_bytes or
 72         get16(input, layout.digest_bytes_offset) != schema.digest_bytes or
 73         get16(input, layout.root_bytes_offset) != schema.root_bytes)
 74     {
 75         return error.MetadataShapeMismatch;
 76     }
 77 }
 78 
 79 fn validateReserved(
 80     input: *const [schema.stream_bytes]u8,
 81     node_count: u8,
 82 ) manifest_types.Error!void {
 83     if (!zeroRange(input, schema.Envelope.reserved_offset, schema.Envelope.limit) or
 84         !zeroRange(
 85             input,
 86             schema.RootLayout.first_reserved_offset,
 87             schema.RootLayout.contract_offset,
 88         ) or
 89         !zeroRange(
 90             input,
 91             schema.RootLayout.second_reserved_offset,
 92             schema.RootLayout.fabric_contract_offset,
 93         ) or
 94         !zeroRange(
 95             input,
 96             schema.RootLayout.third_reserved_offset,
 97             schema.RootLayout.limit,
 98         ) or
 99         !wire.allZero(input[schema.material_limit..]))
100     {
101         return error.ReservedNonzero;
102     }
103     try validateNodeReserved(input, node_count);
104 }
105 
106 fn validateNodeReserved(
107     input: *const [schema.stream_bytes]u8,
108     node_count: u8,
109 ) manifest_types.Error!void {
110     for (0..world.node_limit) |index| {
111         const start = schema.nodes_offset + index * schema.node_bytes;
112         if (index >= node_count) {
113             if (!wire.allZero(input[start..][0..schema.node_bytes])) {
114                 return error.ReservedNonzero;
115             }
116         } else if (!zeroRange(
117             input,
118             start + schema.NodeLayout.reserved_offset,
119             start + schema.NodeLayout.limit,
120         )) {
121             return error.ReservedNonzero;
122         }
123     }
124 }
125 
126 fn decodeRoot(
127     input: *const [schema.stream_bytes]u8,
128 ) manifest_types.Error!world.Root {
129     const layout = schema.RootLayout;
130     return .{
131         .digest = digest(input, layout.digest_offset),
132         .dialect = try enumValue(world.Dialect, get16(input, layout.dialect_offset)),
133         .machine_contract = .{ .digest = digest(input, layout.contract_offset) },
134         .fabric = .{
135             .digest = digest(input, layout.fabric_digest_offset),
136             .dialect = try enumValue(
137                 fabric.Dialect,
138                 get16(input, layout.fabric_dialect_offset),
139             ),
140             .machine_contract = .{
141                 .digest = digest(input, layout.fabric_contract_offset),
142             },
143             .entry_frontier = get64(input, layout.entry_frontier_offset),
144             .admission_frontier = get64(input, layout.admission_frontier_offset),
145             .fault_frontier = get64(input, layout.fault_frontier_offset),
146         },
147         .node_count = input[layout.node_count_offset],
148     };
149 }
150 
151 fn decodeNode(
152     input: *const [schema.stream_bytes]u8,
153     index: usize,
154 ) world.Node {
155     std.debug.assert(index < world.node_limit);
156     const layout = schema.NodeLayout;
157     const start = schema.nodes_offset + index * schema.node_bytes;
158     return .{
159         .id = .{ .bytes = input[start + layout.id_offset ..][0..16].* },
160         .machine = .{
161             .digest = digest(input, start + layout.root_digest_offset),
162             .profile = .{
163                 .digest = digest(input, start + layout.profile_digest_offset),
164             },
165             .state = .{
166                 .digest = digest(input, start + layout.state_digest_offset),
167             },
168         },
169     };
170 }
171 
172 fn emptyNode() world.Node {
173     return .{
174         .id = .{ .bytes = @splat(0) },
175         .machine = .{
176             .digest = @splat(0),
177             .profile = .{ .digest = @splat(0) },
178             .state = .{ .digest = @splat(0) },
179         },
180     };
181 }
182 
183 fn enumValue(comptime T: type, value: anytype) manifest_types.Error!T {
184     inline for (std.meta.tags(T)) |candidate| {
185         if (@backingInt(candidate) == value) return candidate;
186     }
187     return error.UnknownManifestField;
188 }
189 
190 fn digest(input: *const [schema.stream_bytes]u8, offset: usize) os.abi.Digest {
191     std.debug.assert(offset + schema.digest_bytes <= input.len);
192     return input[offset..][0..schema.digest_bytes].*;
193 }
194 
195 fn zeroRange(
196     input: *const [schema.stream_bytes]u8,
197     first: usize,
198     last: usize,
199 ) bool {
200     std.debug.assert(first <= last);
201     std.debug.assert(last <= input.len);
202     return wire.allZero(input[first..last]);
203 }
204 
205 fn get16(input: *const [schema.stream_bytes]u8, offset: usize) u16 {
206     std.debug.assert(offset + 2 <= input.len);
207     return wire.read16(input[offset..][0..2]);
208 }
209 
210 fn get64(input: *const [schema.stream_bytes]u8, offset: usize) u64 {
211     std.debug.assert(offset + 8 <= input.len);
212     return wire.read64(input[offset..][0..8]);
213 }