zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

main.zig (7209B) - Raw


      1 const std = @import("std");
      2 const assert = std.debug.assert;
      3 const abi = std.Build.abi;
      4 const gpa = std.heap.wasm_allocator;
      5 const log = std.log;
      6 const Allocator = std.mem.Allocator;
      7 
      8 const fuzz = @import("fuzz.zig");
      9 const time_report = @import("time_report.zig");
     10 
     11 /// Nanoseconds.
     12 var server_base_timestamp: i64 = 0;
     13 /// Milliseconds.
     14 var client_base_timestamp: i64 = 0;
     15 
     16 pub var step_list: []Step = &.{};
     17 /// Not accessed after initialization, but must be freed alongside `step_list`.
     18 pub var step_list_data: []u8 = &.{};
     19 
     20 const Step = struct {
     21     name: []const u8,
     22     status: abi.StepUpdate.Status,
     23 };
     24 
     25 const js = struct {
     26     extern "core" fn log(ptr: [*]const u8, len: usize) void;
     27     extern "core" fn panic(ptr: [*]const u8, len: usize) noreturn;
     28     extern "core" fn timestamp() i64;
     29     extern "core" fn hello(
     30         steps_len: u32,
     31         status: abi.BuildStatus,
     32         time_report: bool,
     33     ) void;
     34     extern "core" fn updateBuildStatus(status: abi.BuildStatus) void;
     35     extern "core" fn updateStepStatus(step_idx: u32) void;
     36     extern "core" fn sendWsMessage(ptr: [*]const u8, len: usize) void;
     37 };
     38 
     39 pub const std_options: std.Options = .{
     40     .logFn = logFn,
     41 };
     42 
     43 pub fn panic(msg: []const u8, st: ?*std.builtin.StackTrace, addr: ?usize) noreturn {
     44     _ = st;
     45     _ = addr;
     46     log.err("panic: {s}", .{msg});
     47     @trap();
     48 }
     49 
     50 fn logFn(
     51     comptime message_level: log.Level,
     52     comptime scope: @TypeOf(.enum_literal),
     53     comptime format: []const u8,
     54     args: anytype,
     55 ) void {
     56     const level_txt = comptime message_level.asText();
     57     const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
     58     var buf: [500]u8 = undefined;
     59     const line = std.fmt.bufPrint(&buf, level_txt ++ prefix2 ++ format, args) catch l: {
     60         buf[buf.len - 3 ..][0..3].* = "...".*;
     61         break :l &buf;
     62     };
     63     js.log(line.ptr, line.len);
     64 }
     65 
     66 export fn alloc(n: usize) [*]u8 {
     67     const slice = gpa.alloc(u8, n) catch @panic("OOM");
     68     return slice.ptr;
     69 }
     70 
     71 var message_buffer: std.ArrayListAlignedUnmanaged(u8, .of(u64)) = .empty;
     72 
     73 /// Resizes the message buffer to be the correct length; returns the pointer to
     74 /// the query string.
     75 export fn message_begin(len: usize) [*]u8 {
     76     message_buffer.resize(gpa, len) catch @panic("OOM");
     77     return message_buffer.items.ptr;
     78 }
     79 
     80 export fn message_end() void {
     81     const msg_bytes = message_buffer.items;
     82 
     83     const tag: abi.ToClientTag = @enumFromInt(msg_bytes[0]);
     84     switch (tag) {
     85         _ => @panic("malformed message"),
     86 
     87         .hello => return helloMessage(msg_bytes) catch @panic("OOM"),
     88         .status_update => return statusUpdateMessage(msg_bytes) catch @panic("OOM"),
     89         .step_update => return stepUpdateMessage(msg_bytes) catch @panic("OOM"),
     90 
     91         .fuzz_source_index => return fuzz.sourceIndexMessage(msg_bytes) catch @panic("OOM"),
     92         .fuzz_coverage_update => return fuzz.coverageUpdateMessage(msg_bytes) catch @panic("OOM"),
     93         .fuzz_entry_points => return fuzz.entryPointsMessage(msg_bytes) catch @panic("OOM"),
     94 
     95         .time_report_generic_result => return time_report.genericResultMessage(msg_bytes) catch @panic("OOM"),
     96         .time_report_compile_result => return time_report.compileResultMessage(msg_bytes) catch @panic("OOM"),
     97     }
     98 }
     99 
    100 const String = Slice(u8);
    101 
    102 pub fn Slice(T: type) type {
    103     return packed struct(u64) {
    104         ptr: u32,
    105         len: u32,
    106 
    107         pub fn init(s: []const T) @This() {
    108             return .{
    109                 .ptr = @intFromPtr(s.ptr),
    110                 .len = s.len,
    111             };
    112         }
    113     };
    114 }
    115 
    116 pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
    117     var buf: [500]u8 = undefined;
    118     const line = std.fmt.bufPrint(&buf, format, args) catch l: {
    119         buf[buf.len - 3 ..][0..3].* = "...".*;
    120         break :l &buf;
    121     };
    122     js.panic(line.ptr, line.len);
    123 }
    124 
    125 fn helloMessage(msg_bytes: []align(4) u8) Allocator.Error!void {
    126     if (msg_bytes.len < @sizeOf(abi.Hello)) @panic("malformed Hello message");
    127     const hdr: *const abi.Hello = @ptrCast(msg_bytes[0..@sizeOf(abi.Hello)]);
    128     const trailing = msg_bytes[@sizeOf(abi.Hello)..];
    129 
    130     client_base_timestamp = js.timestamp();
    131     server_base_timestamp = hdr.timestamp;
    132 
    133     const steps = try gpa.alloc(Step, hdr.steps_len);
    134     errdefer gpa.free(steps);
    135 
    136     const step_name_lens: []align(1) const u32 = @ptrCast(trailing[0 .. steps.len * 4]);
    137 
    138     const step_name_data_len: usize = len: {
    139         var sum: usize = 0;
    140         for (step_name_lens) |n| sum += n;
    141         break :len sum;
    142     };
    143     const step_name_data: []const u8 = trailing[steps.len * 4 ..][0..step_name_data_len];
    144     const step_status_bits: []const u8 = trailing[steps.len * 4 + step_name_data_len ..];
    145 
    146     const duped_step_name_data = try gpa.dupe(u8, step_name_data);
    147     errdefer gpa.free(duped_step_name_data);
    148 
    149     var name_off: usize = 0;
    150     for (steps, step_name_lens, 0..) |*step_out, name_len, step_idx| {
    151         step_out.* = .{
    152             .name = duped_step_name_data[name_off..][0..name_len],
    153             .status = @enumFromInt(@as(u2, @truncate(step_status_bits[step_idx / 4] >> @intCast((step_idx % 4) * 2)))),
    154         };
    155         name_off += name_len;
    156     }
    157 
    158     gpa.free(step_list);
    159     gpa.free(step_list_data);
    160     step_list = steps;
    161     step_list_data = duped_step_name_data;
    162 
    163     js.hello(step_list.len, hdr.status, hdr.flags.time_report);
    164 }
    165 fn statusUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
    166     if (msg_bytes.len < @sizeOf(abi.StatusUpdate)) @panic("malformed StatusUpdate message");
    167     const msg: *const abi.StatusUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StatusUpdate)]);
    168     js.updateBuildStatus(msg.new);
    169 }
    170 fn stepUpdateMessage(msg_bytes: []u8) Allocator.Error!void {
    171     if (msg_bytes.len < @sizeOf(abi.StepUpdate)) @panic("malformed StepUpdate message");
    172     const msg: *const abi.StepUpdate = @ptrCast(msg_bytes[0..@sizeOf(abi.StepUpdate)]);
    173     if (msg.step_idx >= step_list.len) @panic("malformed StepUpdate message");
    174     step_list[msg.step_idx].status = msg.bits.status;
    175     js.updateStepStatus(msg.step_idx);
    176 }
    177 
    178 export fn stepName(idx: usize) String {
    179     return .init(step_list[idx].name);
    180 }
    181 export fn stepStatus(idx: usize) u8 {
    182     return @intFromEnum(step_list[idx].status);
    183 }
    184 
    185 export fn rebuild() void {
    186     const msg: abi.Rebuild = .{};
    187     const raw: []const u8 = @ptrCast(&msg);
    188     js.sendWsMessage(raw.ptr, raw.len);
    189 }
    190 
    191 /// Nanoseconds passed since a server timestamp.
    192 pub fn nsSince(server_timestamp: i64) i64 {
    193     const ms_passed = js.timestamp() - client_base_timestamp;
    194     const ns_passed = server_base_timestamp - server_timestamp;
    195     return ns_passed + ms_passed * std.time.ns_per_ms;
    196 }
    197 
    198 pub fn fmtEscapeHtml(unescaped: []const u8) HtmlEscaper {
    199     return .{ .unescaped = unescaped };
    200 }
    201 const HtmlEscaper = struct {
    202     unescaped: []const u8,
    203     pub fn format(he: HtmlEscaper, w: *std.Io.Writer) !void {
    204         for (he.unescaped) |c| switch (c) {
    205             '&' => try w.writeAll("&amp;"),
    206             '<' => try w.writeAll("&lt;"),
    207             '>' => try w.writeAll("&gt;"),
    208             '"' => try w.writeAll("&quot;"),
    209             '\'' => try w.writeAll("&#39;"),
    210             else => try w.writeByte(c),
    211         };
    212     }
    213 };