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 }