169 lines
5.3 KiB
Zig
169 lines
5.3 KiB
Zig
//! modified version of https://github.com/ziglang/zig/blob/master/lib/std/zig/Server.zig
|
|
//! I don't know why but this code seems to work
|
|
//! zig binary serialization library in stdlib, when?
|
|
|
|
in: std.fs.File,
|
|
out: std.fs.File,
|
|
pooler: std.io.Poller(StreamEnum),
|
|
|
|
const StreamEnum = enum { in };
|
|
|
|
pub const Options = struct {
|
|
gpa: Allocator,
|
|
in: std.fs.File,
|
|
out: std.fs.File,
|
|
};
|
|
|
|
pub fn init(options: Options) Client {
|
|
var s: Client = .{
|
|
.in = options.in,
|
|
.out = options.out,
|
|
.pooler = std.io.poll(options.gpa, StreamEnum, .{ .in = options.in }),
|
|
};
|
|
return s;
|
|
}
|
|
|
|
pub fn deinit(s: *Client) void {
|
|
s.pooler.deinit();
|
|
s.* = undefined;
|
|
}
|
|
|
|
pub fn receiveMessage(client: *Client) !InMessage.Header {
|
|
const Header = InMessage.Header;
|
|
const fifo = client.pooler.fifo(.in);
|
|
|
|
while (try client.pooler.poll()) {
|
|
const buf = fifo.readableSlice(0);
|
|
assert(fifo.readableLength() == buf.len);
|
|
if (buf.len >= @sizeOf(Header)) {
|
|
// workaround for https://github.com/ziglang/zig/issues/14904
|
|
const bytes_len = bswap_and_workaround_u32(buf[4..][0..4]);
|
|
const tag = bswap_and_workaround_tag(buf[0..][0..4]);
|
|
|
|
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 client.in.readAll(write_buffer);
|
|
fifo.update(amt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const write_buffer = try fifo.writableWithSize(256);
|
|
const amt = try client.in.read(write_buffer);
|
|
fifo.update(amt);
|
|
}
|
|
return error.Timeout;
|
|
}
|
|
|
|
pub fn receiveEmitBinPath(client: *Client) !InMessage.EmitBinPath {
|
|
const reader = client.pooler.fifo(.in).reader();
|
|
return reader.readStruct(InMessage.EmitBinPath);
|
|
}
|
|
|
|
pub fn receiveErrorBundle(client: *Client) !InMessage.ErrorBundle {
|
|
const reader = client.pooler.fifo(.in).reader();
|
|
return .{
|
|
.extra_len = try reader.readIntLittle(u32),
|
|
.string_bytes_len = try reader.readIntLittle(u32),
|
|
};
|
|
}
|
|
|
|
pub fn receiveBytes(client: *Client, allocator: std.mem.Allocator, len: usize) ![]u8 {
|
|
const reader = client.pooler.fifo(.in).reader();
|
|
const result = try reader.readAllAlloc(allocator, len);
|
|
if (result.len != len) return error.UnexpectedEOF;
|
|
return result;
|
|
}
|
|
|
|
pub fn receiveIntArray(client: *Client, allocator: std.mem.Allocator, len: usize) ![]u32 {
|
|
const reader = client.pooler.fifo(.in).reader();
|
|
var array_list = std.ArrayListAligned(u8, 4).init(allocator);
|
|
errdefer array_list.deinit();
|
|
try reader.readAllArrayListAligned(4, &array_list, len);
|
|
const bytes = try array_list.toOwnedSlice();
|
|
const result = std.mem.bytesAsSlice(u32, bytes);
|
|
if (need_bswap) {
|
|
bswap_u32_array(result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pub fn serveMessage(
|
|
client: *const Client,
|
|
header: OutMessage.Header,
|
|
bufs: []const []const u8,
|
|
) !void {
|
|
var iovecs: [10]std.os.iovec_const = undefined;
|
|
const header_le = bswap(header);
|
|
iovecs[0] = .{
|
|
.iov_base = @ptrCast([*]const u8, &header_le),
|
|
.iov_len = @sizeOf(OutMessage.Header),
|
|
};
|
|
for (bufs, iovecs[1 .. bufs.len + 1]) |buf, *iovec| {
|
|
iovec.* = .{
|
|
.iov_base = buf.ptr,
|
|
.iov_len = buf.len,
|
|
};
|
|
}
|
|
try client.out.writevAll(iovecs[0 .. bufs.len + 1]);
|
|
}
|
|
|
|
fn bswap(x: anytype) @TypeOf(x) {
|
|
if (!need_bswap) return x;
|
|
|
|
const T = @TypeOf(x);
|
|
switch (@typeInfo(T)) {
|
|
.Enum => return @intToEnum(T, @byteSwap(@enumToInt(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 @bitCast(T, @byteSwap(@bitCast(I, 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.*);
|
|
}
|
|
|
|
/// workaround for https://github.com/ziglang/zig/issues/14904
|
|
fn bswap_and_workaround_u32(bytes_ptr: *const [4]u8) u32 {
|
|
return std.mem.readIntLittle(u32, bytes_ptr);
|
|
}
|
|
|
|
/// workaround for https://github.com/ziglang/zig/issues/14904
|
|
fn bswap_and_workaround_tag(bytes_ptr: *const [4]u8) InMessage.Tag {
|
|
const int = std.mem.readIntLittle(u32, bytes_ptr);
|
|
return @intToEnum(InMessage.Tag, int);
|
|
}
|
|
|
|
const OutMessage = std.zig.Client.Message;
|
|
const InMessage = std.zig.Server.Message;
|
|
|
|
const Client = @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;
|