aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/src/simd/vt.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/simd/vt.zig
parent90e947a6e26e02f10ec7f1694c4eb5b78a86406b (diff)
chore: upgrade libghostty and preserve terminal behavior (#3906)
Diffstat (limited to 'vendor/libghostty-vt/src/simd/vt.zig')
-rw-r--r--vendor/libghostty-vt/src/simd/vt.zig99
1 files changed, 48 insertions, 51 deletions
diff --git a/vendor/libghostty-vt/src/simd/vt.zig b/vendor/libghostty-vt/src/simd/vt.zig
index 807285f7..ded4ab3f 100644
--- a/vendor/libghostty-vt/src/simd/vt.zig
+++ b/vendor/libghostty-vt/src/simd/vt.zig
@@ -53,11 +53,29 @@ fn utf8DecodeUntilControlSeqScalar(
while (decode_offset < decode.len) {
const b0 = decode[decode_offset];
- // ASCII fast path
+ // ASCII fast path. Use vectorization if it is available. This
+ // path is only run when simd=false, but that only controls our C++
+ // simd builds. We can still rely on Zig intrinsics for platforms
+ // like wasm32+simd128.
if (b0 < 0x80) {
- output[decode_count] = b0;
- decode_count += 1;
- decode_offset += 1;
+ if (comptime std.simd.suggestVectorLength(u8)) |vl| {
+ const V = @Vector(vl, u8);
+ while (decode_offset + vl <= decode.len) {
+ const v: V = decode[decode_offset..][0..vl].*;
+ if (@reduce(.Or, v >= @as(V, @splat(0x80)))) break;
+ const w: @Vector(vl, u32) = @intCast(v);
+ output[decode_count..][0..vl].* = w;
+ decode_count += vl;
+ decode_offset += vl;
+ }
+ }
+ while (decode_offset < decode.len) {
+ const b = decode[decode_offset];
+ if (b >= 0x80) break;
+ output[decode_count] = b;
+ decode_count += 1;
+ decode_offset += 1;
+ }
continue;
}
@@ -70,15 +88,27 @@ fn utf8DecodeUntilControlSeqScalar(
continue;
}
- // Multi-byte sequence. Determine expected length and the valid
- // range for each continuation byte per Unicode Table 3-7.
- const seq = utf8SeqInfo(b0);
+ // Multi-byte sequence. Only the first continuation byte has a
+ // lead-dependent valid range per Unicode Table 3-7; later
+ // continuation bytes are always 80-BF. Range validity per
+ // Table 3-7 excludes overlong, surrogate, and out-of-range
+ // encodings, so a fully valid sequence can be decoded by
+ // direct bit assembly with no further checks.
+ const seq_len: usize = if (b0 < 0xE0) 2 else if (b0 < 0xF0) 3 else 4;
+ const cb1_lo: u8, const cb1_hi: u8 = switch (b0) {
+ 0xE0 => .{ 0xA0, 0xBF },
+ 0xED => .{ 0x80, 0x9F },
+ 0xF0 => .{ 0x90, 0xBF },
+ 0xF4 => .{ 0x80, 0x8F },
+ else => .{ 0x80, 0xBF },
+ };
// Check how many continuation bytes form a valid prefix (the
- // maximal subpart). We check each byte against its specific
- // valid range.
+ // maximal subpart), accumulating codepoint bits as we go. The
+ // lead byte contributes its low 7-len bits.
+ var cp: u32 = b0 & (@as(u32, 0x7F) >> @intCast(seq_len));
var valid: usize = 1; // lead byte is valid
- for (0..seq.len - 1) |ci| {
+ while (valid < seq_len) {
if (decode_offset + valid >= decode.len) {
// The sequence is cut off by the end of the decode
// region. If the region ends at the true end of the
@@ -94,29 +124,17 @@ fn utf8DecodeUntilControlSeqScalar(
break;
}
const cb = decode[decode_offset + valid];
- if (cb < seq.ranges[ci][0] or cb > seq.ranges[ci][1]) {
- // Byte doesn't match expected range. The maximal
- // subpart ends here.
- break;
- }
+ const lo: u8 = if (valid == 1) cb1_lo else 0x80;
+ const hi: u8 = if (valid == 1) cb1_hi else 0xBF;
+ if (cb < lo or cb > hi) break;
+ cp = (cp << 6) | (cb & 0x3F);
valid += 1;
}
- if (valid == seq.len) {
- // Full sequence present and structurally valid. Decode it.
- // (Structural validity per Table 3-7 guarantees decode success.)
- const cp_bytes = decode[decode_offset..][0..seq.len];
- if (std.unicode.utf8Decode(cp_bytes)) |cp| {
- output[decode_count] = @intCast(cp);
- decode_count += 1;
- decode_offset += seq.len;
- } else |_| {
- // Should not happen given Table 3-7 validation, but
- // be safe: emit FFFD for the lead byte.
- output[decode_count] = 0xFFFD;
- decode_count += 1;
- decode_offset += 1;
- }
+ if (valid == seq_len) {
+ output[decode_count] = cp;
+ decode_count += 1;
+ decode_offset += seq_len;
} else {
// Incomplete/ill-formed: the maximal subpart (valid bytes)
// maps to a single FFFD.
@@ -132,27 +150,6 @@ fn utf8DecodeUntilControlSeqScalar(
};
}
-const Utf8SeqInfo = struct {
- len: u3,
- ranges: [3][2]u8,
-};
-
-/// Returns the expected byte count and valid continuation byte ranges
-/// for a UTF-8 sequence based on its lead byte, per Unicode Table 3-7.
-fn utf8SeqInfo(lead: u8) Utf8SeqInfo {
- return switch (lead) {
- 0xC2...0xDF => .{ .len = 2, .ranges = .{ .{ 0x80, 0xBF }, .{ 0, 0 }, .{ 0, 0 } } },
- 0xE0 => .{ .len = 3, .ranges = .{ .{ 0xA0, 0xBF }, .{ 0x80, 0xBF }, .{ 0, 0 } } },
- 0xE1...0xEC => .{ .len = 3, .ranges = .{ .{ 0x80, 0xBF }, .{ 0x80, 0xBF }, .{ 0, 0 } } },
- 0xED => .{ .len = 3, .ranges = .{ .{ 0x80, 0x9F }, .{ 0x80, 0xBF }, .{ 0, 0 } } },
- 0xEE...0xEF => .{ .len = 3, .ranges = .{ .{ 0x80, 0xBF }, .{ 0x80, 0xBF }, .{ 0, 0 } } },
- 0xF0 => .{ .len = 4, .ranges = .{ .{ 0x90, 0xBF }, .{ 0x80, 0xBF }, .{ 0x80, 0xBF } } },
- 0xF1...0xF3 => .{ .len = 4, .ranges = .{ .{ 0x80, 0xBF }, .{ 0x80, 0xBF }, .{ 0x80, 0xBF } } },
- 0xF4 => .{ .len = 4, .ranges = .{ .{ 0x80, 0x8F }, .{ 0x80, 0xBF }, .{ 0x80, 0xBF } } },
- else => unreachable,
- };
-}
-
// Differential test: the SIMD implementation must agree with the
// scalar implementation on any input. Exercises random mixtures of
// ASCII, escapes, controls, valid and invalid UTF-8, at various