lib/machine/src/profile/capacity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const profile = @import("root.zig");
3
4 /// The ten bounded resources one profile pins, so a caller names one to ask for
5 /// its bound: instances, checkpoint candidates, admissions, request records, event
6 /// records, and the byte bounds for terminal, entropy, semantic, effect-request,
7 /// and effect-result payloads.
8 pub const Capacity = enum(u8) {
9 instances,
10 checkpoint_candidates,
11 admissions,
12 request_records,
13 event_records,
14 terminal_bytes,
15 entropy_bytes,
16 semantic_bytes,
17 effect_request_bytes,
18 effect_result_bytes,
19 };
20
21 /// A requested value per bounded resource, held as one fixed array with a getter
22 /// and a setter so a caller fills one in to have a whole set of requests checked
23 /// at once. Every value starts at zero.
24 pub const Usage = struct {
25 values: [std.meta.tags(Capacity).len]u32 = @splat(0),
26
27 pub fn get(self: Usage, kind: Capacity) u32 {
28 return self.values[@backingInt(kind)];
29 }
30
31 pub fn set(self: *Usage, kind: Capacity, value: u32) void {
32 self.values[@backingInt(kind)] = value;
33 }
34 };
35
36 /// Returns the contract's bound for one resource, so a caller sizes a buffer before
37 /// asking for anything. Each bound is read straight from the contract, either from
38 /// a limit field or from the transport sizes.
39 pub fn capacity(value: profile.Profile, kind: Capacity) u32 {
40 return switch (kind) {
41 .instances => value.contract.instance_limit,
42 .checkpoint_candidates => value.contract.checkpoint_candidate_limit,
43 .admissions => value.contract.admission_limit,
44 .request_records => value.contract.transport.request_records,
45 .event_records => value.contract.transport.event_records,
46 .terminal_bytes => value.contract.transport.terminal_bytes,
47 .entropy_bytes => value.contract.transport.entropy_bytes,
48 .semantic_bytes => value.contract.transport.semantic_bytes,
49 .effect_request_bytes => value.contract.transport.effect_request_bytes,
50 .effect_result_bytes => value.contract.transport.effect_result_bytes,
51 };
52 }
53
54 /// Checks the profile first, then refuses a request larger than the bound that profile
55 /// sets, so a caller checks a whole request set before committing to it. Every one
56 /// of the ten resources is checked, and the first one over its bound rejects.
57 pub fn usage(value: profile.Profile, requested: Usage) profile.Error!void {
58 try profile.validate(value);
59 for (std.meta.tags(Capacity)) |kind| {
60 if (requested.get(kind) > capacity(value, kind)) {
61 return error.CapacityExceeded;
62 }
63 }
64 }
65
66 /// Checks the profile first, then refuses a geometry unequal to the one the contract
67 /// pins, so a caller proves its own memory layout matches the profile before constructing
68 /// an instance. The comparison covers all four fields, so a changed vCPU count,
69 /// page size, base address, or byte count rejects.
70 pub fn geometry(value: profile.Profile, requested: profile.Geometry) profile.Error!void {
71 try profile.validate(value);
72 if (!std.meta.eql(value.contract.geometry, requested)) {
73 return error.IncompatibleGeometry;
74 }
75 }