mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-15 20:12:38 +00:00
125 lines
4.0 KiB
Zig
125 lines
4.0 KiB
Zig
const std = @import("std");
|
|
const assert = @import("../quirks.zig").inlineAssert;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
/// Creates a union that can be used to accommodate 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();
|
|
|
|
pub const Small = struct {
|
|
pub const Max = small_size;
|
|
pub const Array = [Max]Elem;
|
|
pub const Len = std.math.IntFittingRange(0, small_size);
|
|
data: Array = undefined,
|
|
len: Len = 0,
|
|
};
|
|
|
|
pub const Alloc = struct {
|
|
alloc: Allocator,
|
|
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: 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;
|
|
@memcpy(buf[0..data.len], data);
|
|
return Self{
|
|
.small = .{
|
|
.data = buf,
|
|
.len = @intCast(data.len),
|
|
},
|
|
};
|
|
}
|
|
|
|
// Otherwise, allocate
|
|
const buf = try alloc.dupe(Elem, data);
|
|
errdefer alloc.free(buf);
|
|
return Self{
|
|
.alloc = .{
|
|
.alloc = alloc,
|
|
.data = buf,
|
|
},
|
|
};
|
|
},
|
|
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn deinit(self: Self) void {
|
|
switch (self) {
|
|
.small, .stable => {},
|
|
.alloc => |v| v.alloc.free(v.data),
|
|
}
|
|
}
|
|
|
|
/// Returns a const slice of the data pointed to by this request.
|
|
pub fn slice(self: *const Self) []const Elem {
|
|
return switch (self.*) {
|
|
.small => |*v| v.data[0..v.len],
|
|
.stable => |v| v,
|
|
.alloc => |v| v.data,
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
test "MessageData init small" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const Data = MessageData(u8, 10);
|
|
const input = "hello!";
|
|
const io = try Data.init(alloc, @as([]const u8, input));
|
|
try testing.expect(io == .small);
|
|
}
|
|
|
|
test "MessageData init alloc" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const Data = MessageData(u8, 10);
|
|
const input = "hello! " ** 100;
|
|
const io = try Data.init(alloc, @as([]const u8, input));
|
|
try testing.expect(io == .alloc);
|
|
io.alloc.alloc.free(io.alloc.data);
|
|
}
|
|
|
|
test "MessageData small fits non-u8 sized data" {
|
|
const testing = std.testing;
|
|
const alloc = testing.allocator;
|
|
|
|
const len = 500;
|
|
const Data = MessageData(u8, len);
|
|
const input: []const u8 = "X" ** len;
|
|
const io = try Data.init(alloc, input);
|
|
try testing.expect(io == .small);
|
|
}
|