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("&"), 206 '<' => try w.writeAll("<"), 207 '>' => try w.writeAll(">"), 208 '"' => try w.writeAll("""), 209 '\'' => try w.writeAll("'"), 210 else => try w.writeByte(c), 211 }; 212 } 213 };