diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/synthetic/Utf8.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (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.zig | 108 |
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)); +} |
