diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-09-10 21:51:57 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-10 21:51:57 +0300 |
| commit | 425c86179791cd32e2d4ce0ae7f269940b4b0663 (patch) | |
| tree | e52c4383a6d69216cbe2a32638bf90841d7e2189 /vendor/libghostty-vt/src/simd/vt.zig | |
| parent | 90e947a6e26e02f10ec7f1694c4eb5b78a86406b (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.zig | 99 |
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 |
