tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

io_v2_native.zig (4213B)


      1 //! Native v2 entry boundary. Never probes by executing or retries another driver.
      2 const std = @import("std");
      3 const Arena = @import("arena.zig").Arena;
      4 const P = @import("io_v2_protocol.zig");
      5 const Core = @import("io_v2_core.zig");
      6 const Host = @import("io_v2_file_host.zig").Host;
      7 const C = @import("codecs.zig");
      8 const Identities = struct {
      9     next: std.atomic.Value(u64) = .init(1),
     10     fn issue(self: *@This()) !u64 {
     11         var value = self.next.load(.monotonic);
     12         while (true) {
     13             if (value == std.math.maxInt(u64)) return error.RunIdentityExhausted;
     14             if (self.next.cmpxchgWeak(value, value + 1, .monotonic, .monotonic)) |changed| value = changed else return value;
     15         }
     16     }
     17 };
     18 var identities: Identities = .{};
     19 pub const Envelope = struct { version: u64, action: u32 };
     20 pub fn envelope(arena: *Arena, program: u32, fuel: u64) !Envelope {
     21     const base = arena.roots.items.len;
     22     defer arena.roots.shrinkRetainingCapacity(base);
     23     try arena.roots.append(arena.allocator, program);
     24     const r = P.Reader{ .arena = arena, .fuel = fuel };
     25     const outer = try r.pair(program) orelse return error.InvalidIOEnvelope;
     26     var rest = outer.first;
     27     for ("tricuIO") |ch| {
     28         const cell = try r.pair(rest) orelse return error.InvalidIOEnvelope;
     29         if (try r.nat(cell.first, 7) != @as(?u64, ch)) return error.InvalidIOEnvelope;
     30         rest = cell.second;
     31     }
     32     if (try r.node(rest) != .leaf) return error.InvalidIOEnvelope;
     33     const inner = try r.pair(outer.second) orelse return error.InvalidIOEnvelope;
     34     return .{ .version = try r.nat(inner.first, 16) orelse return error.InvalidIOEnvelope, .action = inner.second };
     35 }
     36 pub fn run(arena: *Arena, program: u32, fuel: u64) !u32 {
     37     const decoded = try envelope(arena, program, fuel);
     38     if (decoded.version != 2) return error.UnsupportedIOVersion;
     39     var host: Host = .{};
     40     const outcome = try Core.runWithHost(Host, &host, arena, decoded.action, try identities.issue(), fuel);
     41     return switch (outcome.kind) {
     42         .completed => outcome.value,
     43         .failed => error.IOV2Failed,
     44         .cancelled => error.IOV2Cancelled,
     45     };
     46 }
     47 test "native run identities never repeat or wrap" {
     48     var ids: Identities = .{};
     49     try std.testing.expectEqual(@as(u64, 1), try ids.issue());
     50     try std.testing.expectEqual(@as(u64, 2), try ids.issue());
     51     ids.next.store(std.math.maxInt(u64) - 1, .monotonic);
     52     _ = try ids.issue();
     53     try std.testing.expectError(error.RunIdentityExhausted, ids.issue());
     54     try std.testing.expectError(error.RunIdentityExhausted, ids.issue());
     55 }
     56 test "retired versions rejected without demanding action" {
     57     var a = Arena.init(std.testing.allocator);
     58     defer a.deinit();
     59     const bad = try a.allocApp(try a.alloc(.leaf), try a.alloc(.leaf));
     60     for ([_]u64{ 0, 1, 3, 65535 }) |version| {
     61         const program = try a.alloc(.{ .fork = .{ .left = try C.ofString(&a, "tricuIO"), .right = try a.alloc(.{ .fork = .{ .left = try C.ofNumber(&a, version), .right = bad } }) } });
     62         try std.testing.expectError(error.UnsupportedIOVersion, run(&a, program, 0));
     63     }
     64 }
     65 
     66 test "successive native runs produce distinct scope identities" {
     67     var arena = Arena.init(std.testing.allocator);
     68     defer arena.deinit();
     69     const leaf = try arena.alloc(.leaf);
     70     const action = try arena.alloc(.{ .fork = .{ .left = try C.ofNumber(&arena, 4), .right = leaf } });
     71     const program = try arena.alloc(.{ .fork = .{ .left = try C.ofString(&arena, "tricuIO"), .right = try arena.alloc(.{ .fork = .{ .left = try C.ofNumber(&arena, 2), .right = action } }) } });
     72     const first = try run(&arena, program, 10000);
     73     try arena.roots.append(arena.allocator, first);
     74     const second = try run(&arena, program, 10000);
     75     const reader = P.Reader{ .arena = &arena, .fuel = 10000 };
     76     const first_fields = (try reader.pair(first)).?;
     77     const second_fields = (try reader.pair(second)).?;
     78     const first_id = try reader.nat((try reader.pair(first_fields.second)).?.first, 64);
     79     const second_id = try reader.nat((try reader.pair(second_fields.second)).?.first, 64);
     80     try std.testing.expect(first_id != null and second_id != null and first_id != second_id);
     81 }