|
in: std.fs.File, out: std.fs.File, receive_fifo: std.fifo.LinearFifo(u8, .Dynamic), |
MessageBody is a UTF-8 string. |
pub const Message = struct { |
HeaderBody is an ErrorBundle. |
pub const Header = extern struct { tag: Tag, /// Size of the body only; does not include this Header. bytes_len: u32, }; |
TagBody is a EmitDigest. |
pub const Tag = enum(u32) { /// Body is a UTF-8 string. zig_version, /// Body is an ErrorBundle. error_bundle, /// Body is a EmitDigest. emit_digest, /// Body is a TestMetadata test_metadata, /// Body is a TestResults test_results, /// Body is a series of strings, delimited by null bytes. /// Each string is a prefixed file path. /// The first byte indicates the file prefix path (see prefixes fields /// of Cache). This byte is sent over the wire incremented so that null /// bytes are not confused with string terminators. /// The remaining bytes is the file path relative to that prefix. /// The prefixes are hard-coded in Compilation.create (cwd, zig lib dir, local cache dir) file_system_inputs, /// Body is a u64le that indicates the file path within the cache used /// to store coverage information. The integer is a hash of the PCs /// stored within that file. coverage_id, /// Body is a u64le that indicates the function pointer virtual memory /// address of the fuzz unit test. This is used to provide a starting /// point to view coverage. fuzz_start_addr, |
PathPrefixBody is a TestMetadata |
_, }; |
ErrorBundleBody is a TestResults |
pub const PathPrefix = enum(u8) { cwd, zig_lib, local_cache, global_cache, }; |
TestMetadataBody is a series of strings, delimited by null bytes. Each string is a prefixed file path. The first byte indicates the file prefix path (see prefixes fields of Cache). This byte is sent over the wire incremented so that null bytes are not confused with string terminators. The remaining bytes is the file path relative to that prefix. The prefixes are hard-coded in Compilation.create (cwd, zig lib dir, local cache dir) |
/// Trailing: /// * extra: [extra_len]u32, /// * string_bytes: [string_bytes_len]u8, /// See `std.zig.ErrorBundle`. pub const ErrorBundle = extern struct { extra_len: u32, string_bytes_len: u32, }; |
TestResultsBody is a u64le that indicates the file path within the cache used to store coverage information. The integer is a hash of the PCs stored within that file. |
/// Trailing: /// * name: [tests_len]u32 /// - null-terminated string_bytes index /// * expected_panic_msg: [tests_len]u32, /// - null-terminated string_bytes index /// - 0 means does not expect panic /// * string_bytes: [string_bytes_len]u8, pub const TestMetadata = extern struct { string_bytes_len: u32, tests_len: u32, }; |
FlagsBody is a u64le that indicates the function pointer virtual memory address of the fuzz unit test. This is used to provide a starting point to view coverage. |
pub const TestResults = extern struct { index: u32, flags: Flags, |
EmitDigestTrailing:
* extra: [extra_len]u32,
* string_bytes: [string_bytes_len]u8,
See |
pub const Flags = packed struct(u32) { fail: bool, skip: bool, leak: bool, fuzz: bool, log_err_count: u28 = 0, }; }; |
FlagsTrailing: * name: [tests_len]u32 - null-terminated string_bytes index * expected_panic_msg: [tests_len]u32, - null-terminated string_bytes index - 0 means does not expect panic * string_bytes: [string_bytes_len]u8, |
/// Trailing: /// * the hex digest of the cache directory within the /o/ subdirectory. pub const EmitDigest = extern struct { flags: Flags, |
OptionsTrailing: * the hex digest of the cache directory within the /o/ subdirectory. |
pub const Flags = packed struct(u8) { cache_hit: bool, reserved: u7 = 0, }; }; }; |
init() |
pub const Options = struct { gpa: Allocator, in: std.fs.File, out: std.fs.File, zig_version: []const u8, }; |
deinit() |
pub fn init(options: Options) !Server { var s: Server = .{ .in = options.in, .out = options.out, .receive_fifo = std.fifo.LinearFifo(u8, .Dynamic).init(options.gpa), }; try s.serveStringMessage(.zig_version, options.zig_version); return s; } |
receiveMessage() |
pub fn deinit(s: *Server) void { s.receive_fifo.deinit(); s.* = undefined; } |
receiveBody_u32() |
pub fn receiveMessage(s: *Server) !InMessage.Header { const Header = InMessage.Header; const fifo = &s.receive_fifo; var last_amt_zero = false; |
serveStringMessage() |
while (true) { const buf = fifo.readableSlice(0); assert(fifo.readableLength() == buf.len); if (buf.len >= @sizeOf(Header)) { const header: *align(1) const Header = @ptrCast(buf[0..@sizeOf(Header)]); const bytes_len = bswap(header.bytes_len); const tag = bswap(header.tag); |
serveMessage() |
if (buf.len - @sizeOf(Header) >= bytes_len) { fifo.discard(@sizeOf(Header)); return .{ .tag = tag, .bytes_len = bytes_len, }; } else { const needed = bytes_len - (buf.len - @sizeOf(Header)); const write_buffer = try fifo.writableWithSize(needed); const amt = try s.in.read(write_buffer); fifo.update(amt); continue; } } |
serveU64Message() |
const write_buffer = try fifo.writableWithSize(256); const amt = try s.in.read(write_buffer); fifo.update(amt); if (amt == 0) { if (last_amt_zero) return error.BrokenPipe; last_amt_zero = true; } } } |
serveEmitDigest() |
pub fn receiveBody_u32(s: *Server) !u32 { const fifo = &s.receive_fifo; const buf = fifo.readableSlice(0); const result = @as(*align(1) const u32, @ptrCast(buf[0..4])).*; fifo.discard(4); return bswap(result); } |
serveTestResults() |
pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void { return s.serveMessage(.{ .tag = tag, .bytes_len = @as(u32, @intCast(msg.len)), }, &.{msg}); } |
serveErrorBundle() |
pub fn serveMessage( s: *const Server, header: OutMessage.Header, bufs: []const []const u8, |
serveTestMetadata() |
) !void { var iovecs: [10]std.posix.iovec_const = undefined; const header_le = bswap(header); iovecs[0] = .{ .base = @as([*]const u8, @ptrCast(&header_le)), .len = @sizeOf(OutMessage.Header), }; for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| { iovec.* = .{ .base = buf.ptr, .len = buf.len, }; } try s.out.writevAll(iovecs[0 .. bufs.len + 1]); } |
serveTestMetadata() |
pub fn serveU64Message(s: *Server, tag: OutMessage.Tag, int: u64) !void { const msg_le = bswap(int); return s.serveMessage(.{ .tag = tag, .bytes_len = @sizeOf(u64), }, &.{std.mem.asBytes(&msg_le)}); } pub fn serveEmitDigest( s: *Server, digest: *const [Cache.bin_digest_len]u8, header: OutMessage.EmitDigest, ) !void { try s.serveMessage(.{ .tag = .emit_digest, .bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)), }, &.{ std.mem.asBytes(&header), digest, }); } pub fn serveTestResults( s: *Server, msg: OutMessage.TestResults, ) !void { const msg_le = bswap(msg); try s.serveMessage(.{ .tag = .test_results, .bytes_len = @intCast(@sizeOf(OutMessage.TestResults)), }, &.{ std.mem.asBytes(&msg_le), }); } pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { const eb_hdr: OutMessage.ErrorBundle = .{ .extra_len = @intCast(error_bundle.extra.len), .string_bytes_len = @intCast(error_bundle.string_bytes.len), }; const bytes_len = @sizeOf(OutMessage.ErrorBundle) + 4 * error_bundle.extra.len + error_bundle.string_bytes.len; try s.serveMessage(.{ .tag = .error_bundle, .bytes_len = @intCast(bytes_len), }, &.{ std.mem.asBytes(&eb_hdr), // TODO: implement @ptrCast between slices changing the length std.mem.sliceAsBytes(error_bundle.extra), error_bundle.string_bytes, }); } pub const TestMetadata = struct { names: []u32, expected_panic_msgs: []u32, string_bytes: []const u8, }; pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void { const header: OutMessage.TestMetadata = .{ .tests_len = bswap(@as(u32, @intCast(test_metadata.names.len))), .string_bytes_len = bswap(@as(u32, @intCast(test_metadata.string_bytes.len))), }; const trailing = 2; const bytes_len = @sizeOf(OutMessage.TestMetadata) + trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len; if (need_bswap) { bswap_u32_array(test_metadata.names); bswap_u32_array(test_metadata.expected_panic_msgs); } defer if (need_bswap) { bswap_u32_array(test_metadata.names); bswap_u32_array(test_metadata.expected_panic_msgs); }; return s.serveMessage(.{ .tag = .test_metadata, .bytes_len = @intCast(bytes_len), }, &.{ std.mem.asBytes(&header), // TODO: implement @ptrCast between slices changing the length std.mem.sliceAsBytes(test_metadata.names), std.mem.sliceAsBytes(test_metadata.expected_panic_msgs), test_metadata.string_bytes, }); } fn bswap(x: anytype) @TypeOf(x) { if (!need_bswap) return x; const T = @TypeOf(x); switch (@typeInfo(T)) { .@"enum" => return @as(T, @enumFromInt(@byteSwap(@intFromEnum(x)))), .int => return @byteSwap(x), .@"struct" => |info| switch (info.layout) { .@"extern" => { var result: T = undefined; inline for (info.fields) |field| { @field(result, field.name) = bswap(@field(x, field.name)); } return result; }, .@"packed" => { const I = info.backing_integer.?; return @as(T, @bitCast(@byteSwap(@as(I, @bitCast(x))))); }, .auto => @compileError("auto layout struct"), }, else => @compileError("bswap on type " ++ @typeName(T)), } } fn bswap_u32_array(slice: []u32) void { comptime assert(need_bswap); for (slice) |*elem| elem.* = @byteSwap(elem.*); } const OutMessage = std.zig.Server.Message; const InMessage = std.zig.Client.Message; const Server = @This(); const builtin = @import("builtin"); const std = @import("std"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; const native_endian = builtin.target.cpu.arch.endian(); const need_bswap = native_endian != .little; const Cache = std.Build.Cache; |
Generated by zstd-live on 2025-08-13 02:35:12 UTC. |