extract out message data from termio

This will be used for other mailboxes, for example this is required for
sending the window title to the app thread. Right now we hardcode an
array size but we can do better than that.
This commit is contained in:
Mitchell Hashimoto
2022-11-15 13:41:11 -08:00
parent 18bc5b2e9a
commit e24dabd67a

View File

@ -10,6 +10,11 @@ const terminal = @import("../terminal/main.zig");
/// but the messages are IO thread sends are also very few. At the current size
/// we can queue 26,000 messages before consuming a MB of RAM.
pub const Message = union(enum) {
/// Represents a write request. Magic number comes from the largest
/// other union value. It can be upped if we add a larger union member
/// in the future.
pub const WriteReq = MessageData(u8, 38);
/// Resize the window.
resize: struct {
/// The grid size for the given screen size with padding applied.
@ -28,7 +33,7 @@ pub const Message = union(enum) {
write_small: WriteReq.Small,
/// Write where the data pointer is stable.
write_stable: []const u8,
write_stable: WriteReq.Stable,
/// Write where the data is allocated and must be freed.
write_alloc: WriteReq.Alloc,
@ -37,64 +42,85 @@ pub const Message = union(enum) {
/// write_small if it fits or write_alloc otherwise. This should NOT
/// be used for stable pointers which can be manually set to write_stable.
pub fn writeReq(alloc: Allocator, data: anytype) !Message {
switch (@typeInfo(@TypeOf(data))) {
.Pointer => |info| {
assert(info.size == .Slice);
assert(info.child == u8);
// If it fits in our small request, do that.
if (data.len <= WriteReq.Small.Max) {
var buf: WriteReq.Small.Array = undefined;
std.mem.copy(u8, &buf, data);
return Message{
.write_small = .{
.data = buf,
.len = @intCast(u8, data.len),
},
};
}
// Otherwise, allocate
var buf = try alloc.dupe(u8, data);
errdefer alloc.free(buf);
return Message{
.write_alloc = .{
.alloc = alloc,
.data = buf,
},
};
},
else => unreachable,
}
return switch (try WriteReq.init(alloc, data)) {
.stable => unreachable,
.small => |v| Message{ .write_small = v },
.alloc => |v| Message{ .write_alloc = v },
};
}
};
/// Creates a union that can be used to accomodate data that fit within an array,
/// are a stable pointer, or require deallocation. This is helpful for thread
/// messaging utilities.
pub fn MessageData(comptime Elem: type, comptime small_size: comptime_int) type {
return union(enum) {
pub const Self = @This();
/// Represents a write request.
pub const WriteReq = union(enum) {
pub const Small = struct {
pub const Max = 38;
pub const Array = [Max]u8;
pub const Max = small_size;
pub const Array = [Max]Elem;
data: Array,
len: u8,
};
pub const Alloc = struct {
alloc: Allocator,
data: []u8,
data: []Elem,
};
pub const Stable = []const Elem;
/// A small write where the data fits into this union size.
small: Small,
/// A stable pointer so we can just pass the slice directly through.
/// This is useful i.e. for const data.
stable: []const u8,
stable: Stable,
/// Allocated and must be freed with the provided allocator. This
/// should be rarely used.
alloc: Alloc,
/// Initializes the union for a given data type. This will
/// attempt to fit into a small value if possible, otherwise
/// will allocate and put into alloc.
///
/// This can't and will never detect stable pointers.
pub fn init(alloc: Allocator, data: anytype) !Self {
switch (@typeInfo(@TypeOf(data))) {
.Pointer => |info| {
assert(info.size == .Slice);
assert(info.child == Elem);
// If it fits in our small request, do that.
if (data.len <= Small.Max) {
var buf: Small.Array = undefined;
std.mem.copy(Elem, &buf, data);
return Self{
.small = .{
.data = buf,
.len = @intCast(u8, data.len),
},
};
}
// Otherwise, allocate
var buf = try alloc.dupe(Elem, data);
errdefer alloc.free(buf);
return Self{
.alloc = .{
.alloc = alloc,
.data = buf,
},
};
},
else => unreachable,
}
}
};
};
}
test {
std.testing.refAllDecls(@This());
@ -106,21 +132,23 @@ test {
try testing.expectEqual(@as(usize, 40), @sizeOf(Message));
}
test "Message.writeReq small" {
test "MessageData init small" {
const testing = std.testing;
const alloc = testing.allocator;
const Data = MessageData(u8, 10);
const input = "hello!";
const io = try Message.writeReq(alloc, @as([]const u8, input));
try testing.expect(io == .write_small);
const io = try Data.init(alloc, @as([]const u8, input));
try testing.expect(io == .small);
}
test "Message.writeReq alloc" {
test "MessageData init alloc" {
const testing = std.testing;
const alloc = testing.allocator;
const Data = MessageData(u8, 10);
const input = "hello! " ** 100;
const io = try Message.writeReq(alloc, @as([]const u8, input));
try testing.expect(io == .write_alloc);
io.write_alloc.alloc.free(io.write_alloc.data);
const io = try Data.init(alloc, @as([]const u8, input));
try testing.expect(io == .alloc);
io.alloc.alloc.free(io.alloc.data);
}