aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/synthetic/Utf8.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/synthetic/Utf8.zig
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/synthetic/Utf8.zig')
-rw-r--r--vendor/libghostty-vt/src/synthetic/Utf8.zig108
1 files changed, 108 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/synthetic/Utf8.zig b/vendor/libghostty-vt/src/synthetic/Utf8.zig
new file mode 100644
index 00000000..0d72a8bb
--- /dev/null
+++ b/vendor/libghostty-vt/src/synthetic/Utf8.zig
@@ -0,0 +1,108 @@
+/// Generates UTF-8.
+///
+/// This doesn't yet generate multi-codepoint graphemes, but it
+/// has the ability to generate a custom distribution of UTF-8
+/// encoding lengths (1, 2, 3, or 4 bytes).
+const Utf8 = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const Generator = @import("Generator.zig");
+
+/// Possible UTF-8 encoding lengths.
+pub const Utf8Len = enum(u3) {
+ one = 1,
+ two = 2,
+ three = 3,
+ four = 4,
+};
+
+/// Random number generator.
+rand: std.Random,
+
+/// The minimum and maximum length of the generated bytes. The maximum
+/// length will be capped to the length of the buffer passed in if the
+/// buffer length is smaller.
+min_len: usize = 1,
+max_len: usize = std.math.maxInt(usize),
+
+/// Probability of a specific UTF-8 encoding length being generated.
+/// The probabilities are weighted relative to each other, so they
+/// can sum greater than 1.0. A length of weight 1.0 and a length
+/// of weight 2.0 will have a 2:1 chance of the latter being
+/// selected.
+///
+/// If a UTF-8 encoding of a chosen length can't fit into the remaining
+/// buffer, a smaller length will be chosen. For small buffers this may
+/// skew the distribution of lengths.
+p_length: std.enums.EnumArray(Utf8Len, f64) = .initFill(1.0),
+
+pub fn generator(self: *Utf8) Generator {
+ return .init(self, next);
+}
+
+pub fn next(self: *Utf8, writer: *std.Io.Writer, max_len: usize) Generator.Error!void {
+ const len = @min(
+ self.rand.intRangeAtMostBiased(usize, self.min_len, self.max_len),
+ max_len,
+ );
+
+ var rem: usize = len;
+ while (rem > 0) {
+ // Pick a utf8 byte count to generate.
+ const utf8_len: Utf8Len = len: {
+ const Indexer = @TypeOf(self.p_length).Indexer;
+ const idx = self.rand.weightedIndex(f64, &self.p_length.values);
+ var utf8_len = Indexer.keyForIndex(idx);
+ assert(rem > 0);
+ while (@intFromEnum(utf8_len) > rem) {
+ // If the chosen length can't fit into the remaining buffer,
+ // choose a smaller length.
+ utf8_len = @enumFromInt(@intFromEnum(utf8_len) - 1);
+ }
+ break :len utf8_len;
+ };
+
+ // Generate a UTF-8 sequence that encodes to this length.
+ const cp: u21 = switch (utf8_len) {
+ .one => self.rand.intRangeAtMostBiased(u21, 0x00, 0x7F),
+ .two => self.rand.intRangeAtMostBiased(u21, 0x80, 0x7FF),
+ .three => self.rand.intRangeAtMostBiased(u21, 0x800, 0xFFFF),
+ .four => self.rand.intRangeAtMostBiased(u21, 0x10000, 0x10FFFF),
+ };
+
+ assert(std.unicode.utf8CodepointSequenceLength(
+ cp,
+ ) catch unreachable == @intFromEnum(utf8_len));
+
+ var buf: [4]u8 = undefined;
+ const l = std.unicode.utf8Encode(
+ cp,
+ &buf,
+ ) catch |err| switch (err) {
+ // Impossible because our generation above is hardcoded to
+ // produce a valid range. If not, a bug.
+ error.CodepointTooLarge => unreachable,
+
+ // Possible, in which case we redo the loop and encode nothing.
+ error.Utf8CannotEncodeSurrogateHalf => continue,
+ };
+ try writer.writeAll(buf[0..l]);
+ rem -= l;
+ }
+}
+
+test "utf8" {
+ const testing = std.testing;
+ var prng = std.Random.DefaultPrng.init(0);
+ var buf: [256]u8 = undefined;
+ var writer: std.Io.Writer = .fixed(&buf);
+ var v: Utf8 = .{ .rand = prng.random() };
+ v.min_len = buf.len;
+ v.max_len = buf.len;
+ const gen = v.generator();
+ try gen.next(&writer, buf.len);
+ const result = writer.buffered();
+ try testing.expectEqual(256, result.len);
+ try testing.expect(std.unicode.utf8ValidateSlice(result));
+}