aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/lib/union.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/lib/union.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/lib/union.zig')
-rw-r--r--vendor/libghostty-vt/src/lib/union.zig171
1 files changed, 171 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/lib/union.zig b/vendor/libghostty-vt/src/lib/union.zig
new file mode 100644
index 00000000..924d0e86
--- /dev/null
+++ b/vendor/libghostty-vt/src/lib/union.zig
@@ -0,0 +1,171 @@
+const std = @import("std");
+const testing = std.testing;
+const Target = @import("target.zig").Target;
+
+/// Create a tagged union type that supports a C ABI and maintains
+/// C ABI compatibility when adding new tags. This returns a set of types
+/// and functions to augment the given Union type, not create a wholly new
+/// union type.
+///
+/// The C ABI compatible types and functions are only available when the
+/// target produces C values.
+///
+/// The `Union` type should be a standard Zig tagged union. The tag type
+/// should be explicit (i.e. not `union(enum)`) and the tag type should
+/// be an enum created with the `Enum` function in this library, so that
+/// automatic C ABI compatibility is ensured.
+///
+/// The `Padding` type is a type that is always added to the C union
+/// with the key `_padding`. This should be set to a type that has the size
+/// and alignment needed to pad the C union to the expected size. This
+/// should never change to ensure ABI compatibility.
+pub fn TaggedUnion(
+ comptime target: Target,
+ comptime Union: type,
+ comptime Padding: type,
+) type {
+ return struct {
+ comptime {
+ switch (target) {
+ .zig => {},
+
+ // For ABI compatibility, we expect that this is our union size.
+ .c => if (@sizeOf(CValue) != @sizeOf(Padding)) {
+ @compileLog(@sizeOf(CValue));
+ @compileError("TaggedUnion CValue size does not match expected fixed size");
+ },
+ }
+ }
+
+ /// The tag type.
+ pub const Tag = @typeInfo(Union).@"union".tag_type.?;
+
+ /// The Zig union.
+ pub const Zig = Union;
+
+ /// The C ABI compatible tagged union type.
+ pub const C = switch (target) {
+ .zig => struct {},
+ .c => extern struct {
+ tag: Tag,
+ value: CValue,
+ },
+ };
+
+ /// The C ABI compatible union value type.
+ pub const CValue = cvalue: {
+ switch (target) {
+ .zig => break :cvalue extern struct {},
+ .c => {},
+ }
+
+ const tag_fields = @typeInfo(Tag).@"enum".fields;
+ var union_fields: [tag_fields.len + 1]std.builtin.Type.UnionField = undefined;
+ for (tag_fields, 0..) |field, i| {
+ const action = @unionInit(Union, field.name, undefined);
+ const Type = t: {
+ const Type = @TypeOf(@field(action, field.name));
+ // Types can provide custom types for their CValue.
+ switch (@typeInfo(Type)) {
+ .@"enum", .@"struct", .@"union" => if (@hasDecl(Type, "C")) break :t Type.C,
+ else => {},
+ }
+
+ break :t Type;
+ };
+
+ union_fields[i] = .{
+ .name = field.name,
+ .type = Type,
+ .alignment = @alignOf(Type),
+ };
+ }
+
+ union_fields[tag_fields.len] = .{
+ .name = "_padding",
+ .type = Padding,
+ .alignment = @alignOf(Padding),
+ };
+
+ break :cvalue @Type(.{ .@"union" = .{
+ .layout = .@"extern",
+ .tag_type = null,
+ .fields = &union_fields,
+ .decls = &.{},
+ } });
+ };
+
+ /// Convert to C union.
+ pub fn cval(self: Union) C {
+ const value: CValue = switch (self) {
+ inline else => |v, tag| @unionInit(
+ CValue,
+ @tagName(tag),
+ value: {
+ switch (@typeInfo(@TypeOf(v))) {
+ .@"enum", .@"struct", .@"union" => if (@hasDecl(@TypeOf(v), "cval")) break :value v.cval(),
+ else => {},
+ }
+
+ break :value v;
+ },
+ ),
+ };
+
+ return .{
+ .tag = @as(Tag, self),
+ .value = value,
+ };
+ }
+
+ /// Returns the value type for the given tag.
+ pub fn Value(comptime tag: Tag) type {
+ @setEvalBranchQuota(10000);
+ inline for (@typeInfo(Union).@"union".fields) |field| {
+ const field_tag = @field(Tag, field.name);
+ if (field_tag == tag) return field.type;
+ }
+
+ unreachable;
+ }
+ };
+}
+
+test "TaggedUnion: matching size" {
+ const Tag = enum(c_int) { a, b };
+ const U = TaggedUnion(
+ .c,
+ union(Tag) {
+ a: u32,
+ b: u64,
+ },
+ u64,
+ );
+
+ try testing.expectEqual(8, @sizeOf(U.CValue));
+}
+
+test "TaggedUnion: padded size" {
+ const Tag = enum(c_int) { a };
+ const U = TaggedUnion(
+ .c,
+ union(Tag) {
+ a: u32,
+ },
+ u64,
+ );
+
+ try testing.expectEqual(8, @sizeOf(U.CValue));
+}
+
+test "TaggedUnion: c conversion" {
+ const Tag = enum(c_int) { a, b };
+ const U = TaggedUnion(.c, union(Tag) {
+ a: u32,
+ b: u64,
+ }, u64);
+
+ const c = U.cval(.{ .a = 42 });
+ try testing.expectEqual(Tag.a, c.tag);
+ try testing.expectEqual(42, c.value.a);
+}