aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/lib/union.zig
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-10 21:51:57 +0300
committerGitHub <noreply@github.com>2026-09-10 21:51:57 +0300
commit425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch)
treee52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/lib/union.zig
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/lib/union.zig')
-rw-r--r--vendor/libghostty-vt/src/lib/union.zig126
1 files changed, 93 insertions, 33 deletions
diff --git a/vendor/libghostty-vt/src/lib/union.zig b/vendor/libghostty-vt/src/lib/union.zig
index 924d0e86..7ed3fc79 100644
--- a/vendor/libghostty-vt/src/lib/union.zig
+++ b/vendor/libghostty-vt/src/lib/union.zig
@@ -15,14 +15,18 @@ const Target = @import("target.zig").Target;
/// 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.
+/// `options.padding` is a type that is always added to the C union with the key
+/// `_padding`. It should have the size and alignment needed to pad the C union
+/// to the expected size and should never change to ensure ABI compatibility.
+///
+/// Each tag has an optional field in `options.field_renames` that may rename its
+/// C value field. Multiple tags may map to the same field when their C value
+/// types are identical. Tags with no payload are omitted. This is used
+/// for metadata and should match the C headers directly.
pub fn TaggedUnion(
comptime target: Target,
comptime Union: type,
- comptime Padding: type,
+ comptime options: TaggedUnionOptions(Union),
) type {
return struct {
comptime {
@@ -30,7 +34,7 @@ pub fn TaggedUnion(
.zig => {},
// For ABI compatibility, we expect that this is our union size.
- .c => if (@sizeOf(CValue) != @sizeOf(Padding)) {
+ .c => if (@sizeOf(CValue) != @sizeOf(options.padding)) {
@compileLog(@sizeOf(CValue));
@compileError("TaggedUnion CValue size does not match expected fixed size");
},
@@ -49,18 +53,35 @@ pub fn TaggedUnion(
.c => extern struct {
tag: Tag,
value: CValue,
+
+ /// Returns the public C value-union field name for `tag`, or
+ /// null when the tag has no value field. This is metadata only;
+ /// it does not rename fields in `CValue`.
+ pub fn cFieldRename(comptime tag: Tag) ?[]const u8 {
+ const tag_name = @tagName(tag);
+ const value = @field(@unionInit(Union, tag_name, undefined), tag_name);
+
+ if (@field(options.field_renames, tag_name)) |name| return name;
+ if (@sizeOf(@TypeOf(value)) == 0) return null;
+
+ return tag_name;
+ }
},
};
/// The C ABI compatible union value type.
pub const CValue = cvalue: {
+ @setEvalBranchQuota(10_000);
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;
+ var names: [tag_fields.len + 1][]const u8 = undefined;
+ var types: [tag_fields.len + 1]type = undefined;
+ var attrs: [tag_fields.len + 1]std.builtin.Type.UnionField.Attributes = undefined;
+
for (tag_fields, 0..) |field, i| {
const action = @unionInit(Union, field.name, undefined);
const Type = t: {
@@ -74,25 +95,16 @@ pub fn TaggedUnion(
break :t Type;
};
- union_fields[i] = .{
- .name = field.name,
- .type = Type,
- .alignment = @alignOf(Type),
- };
+ names[i] = field.name;
+ types[i] = Type;
+ attrs[i] = .{ .@"align" = @alignOf(Type) };
}
- union_fields[tag_fields.len] = .{
- .name = "_padding",
- .type = Padding,
- .alignment = @alignOf(Padding),
- };
+ names[tag_fields.len] = "_padding";
+ types[tag_fields.len] = options.padding;
+ attrs[tag_fields.len] = .{ .@"align" = @alignOf(options.padding) };
- break :cvalue @Type(.{ .@"union" = .{
- .layout = .@"extern",
- .tag_type = null,
- .fields = &union_fields,
- .decls = &.{},
- } });
+ break :cvalue @Union(.@"extern", null, &names, &types, &attrs);
};
/// Convert to C union.
@@ -120,17 +132,35 @@ pub fn TaggedUnion(
/// 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;
+ return @FieldType(Union, @tagName(tag));
}
};
}
+/// Options for generating the C representation of a tagged union.
+pub fn TaggedUnionOptions(comptime Union: type) type {
+ const Tag = @typeInfo(Union).@"union".tag_type.?;
+ const tag_fields = @typeInfo(Tag).@"enum".fields;
+ const FieldRenames: type = field_renames: {
+ const default_rename: ?[]const u8 = null;
+
+ var names: [tag_fields.len][]const u8 = undefined;
+ var types: [tag_fields.len]type = undefined;
+ var attrs: [tag_fields.len]std.builtin.Type.StructField.Attributes = undefined;
+
+ for (tag_fields, 0..) |field, i| {
+ names[i] = field.name;
+ types[i] = ?[]const u8;
+ attrs[i] = .{ .default_value_ptr = &default_rename };
+ }
+
+ break :field_renames @Struct(.auto, null, &names, &types, &attrs);
+ };
+ return struct {
+ padding: type,
+ field_renames: FieldRenames = .{},
+ };
+}
test "TaggedUnion: matching size" {
const Tag = enum(c_int) { a, b };
const U = TaggedUnion(
@@ -139,7 +169,7 @@ test "TaggedUnion: matching size" {
a: u32,
b: u64,
},
- u64,
+ .{ .padding = u64 },
);
try testing.expectEqual(8, @sizeOf(U.CValue));
@@ -152,7 +182,7 @@ test "TaggedUnion: padded size" {
union(Tag) {
a: u32,
},
- u64,
+ .{ .padding = u64 },
);
try testing.expectEqual(8, @sizeOf(U.CValue));
@@ -163,9 +193,39 @@ test "TaggedUnion: c conversion" {
const U = TaggedUnion(.c, union(Tag) {
a: u32,
b: u64,
- }, u64);
+ }, .{ .padding = u64 });
const c = U.cval(.{ .a = 42 });
try testing.expectEqual(Tag.a, c.tag);
try testing.expectEqual(42, c.value.a);
}
+
+test "TaggedUnion: custom C value fields" {
+ const Tag = enum(c_int) { a, b, none };
+ const Union = union(Tag) {
+ a: u32,
+ b: u32,
+ none,
+ };
+ const U = TaggedUnion(.c, Union, .{
+ .padding = u64,
+ .field_renames = .{
+ .a = "value",
+ .b = "value",
+ },
+ });
+
+ try testing.expect(!@hasField(U.CValue, "value"));
+ try testing.expect(@hasField(U.CValue, "a"));
+ try testing.expect(@hasField(U.CValue, "b"));
+ try testing.expect(@hasField(U.CValue, "none"));
+ try testing.expectEqualStrings("value", U.C.cFieldRename(.a).?);
+ try testing.expect(U.C.cFieldRename(.none) == null);
+
+ const a = U.cval(.{ .a = 42 });
+ try testing.expectEqual(Tag.a, a.tag);
+ try testing.expectEqual(42, a.value.a);
+
+ const none = U.cval(.none);
+ try testing.expectEqual(Tag.none, none.tag);
+}