terminal: swap codepointwidth implementations

This commit is contained in:
Mitchell Hashimoto
2024-02-07 09:38:17 -08:00
parent 3c31217f3c
commit 17dc64053e
3 changed files with 13 additions and 40 deletions

View File

@ -8,7 +8,7 @@
# - "ascii", uniform random ASCII bytes
# - "utf8", uniform random unicode characters, encoded as utf8
# - "rand", pure random data, will contain many invalid code sequences.
DATA="ascii"
DATA="utf8"
SIZE="25000000"
# Add additional arguments

View File

@ -187,12 +187,15 @@ static_assert(std::size(eaw_gte) == std::size(eaw_lte));
static_assert(std::size(zero_gte) == std::size(zero_lte));
static_assert(std::size(nsm_gte) == std::size(nsm_lte));
/// Vectorized implementation of Unicode display width. Determining width
/// unfortunately requires many small range checks, so we test some fast paths
/// and otherwise try to do N (vector lane width) range checks at a time.
template <class D>
int8_t CodepointWidthImpl(D d, T input) {
// If the input is ASCII, then we return 1. We do NOT check for
// control characters because we assume that the input has already
// been checked for that case.
if (input < 0xFF) {
if (input <= 0xFF) {
return 1;
}
@ -201,14 +204,6 @@ int8_t CodepointWidthImpl(D d, T input) {
const hn::Vec<D> input_vec = Set(d, input);
{
// Thes are the ranges (inclusive) of the codepoints that are DEFINITELY
// width 2. We will check as many in parallel as possible.
//
// The zero padding is so that we can always load aligned directly into
// a vector register of any size up to 16 bytes (AVX512).
//
// Ranges: two-em dash, gbp.isRegionalIndicator, CJK...
//
// NOTE: 0x2E3B is technically width 3 but for our terminal we only
// handle up to width 2 as wide so we will treat it as width 2.
HWY_ALIGN constexpr T gte_keys[] = {
@ -233,7 +228,6 @@ int8_t CodepointWidthImpl(D d, T input) {
}
{
// Definitely width 0
HWY_ALIGN constexpr T gte_keys[] = {
0x1160, 0x2060, 0xFFF0, 0xE0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

View File

@ -10,6 +10,7 @@ const ziglyph = @import("ziglyph");
const testing = std.testing;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const simd = @import("../simd/main.zig");
const ansi = @import("ansi.zig");
const modes = @import("modes.zig");
@ -869,10 +870,13 @@ pub fn print(self: *Terminal, c: u21) !void {
// Determine the width of this character so we can handle
// non-single-width characters properly.
const width: usize = @intCast(@min(
@max(0, ziglyph.display_width.codePointWidth(c, .half)),
2,
));
const width: usize = @intCast(simd.codepointWidth(c));
// Old implementation, 3x slower on ASCII, 2x slower on CJK, etc.
// const width: usize = @intCast(@min(
// @max(0, ziglyph.display_width.codePointWidth(c, .half)),
// 2,
// ));
// Note: it is possible to have a width of "3" and a width of "-1"
// from ziglyph. We should look into those cases and handle them
@ -2304,31 +2308,6 @@ test "Terminal: print over wide spacer tail" {
}
}
test "Terminal: zero width chars with grapheme clustering can be put in their own cell" {
var t = try init(testing.allocator, 5, 5);
defer t.deinit(testing.allocator);
// Enable grapheme clustering
t.modes.set(.grapheme_cluster, true);
try t.print('x');
try t.print(0x7F); // zero-width control character
{
const str = try t.plainString(testing.allocator);
defer testing.allocator.free(str);
try testing.expectEqualStrings("x", str);
}
const row = t.screen.getRow(.{ .screen = 0 });
{
const cell = row.getCell(0);
try testing.expectEqual(@as(u32, 'x'), cell.char);
try testing.expect(!cell.attrs.wide);
try testing.expect(!cell.attrs.grapheme);
}
}
test "Terminal: VS15 to make narrow character" {
var t = try init(testing.allocator, 5, 5);
defer t.deinit(testing.allocator);