[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

ref(wcwidth): performance pass

Nate Moore (Jul 2, 2026, 12:16 AM EDT) 69755ea3 9e527c1e

+59 -12
+59 -12
tasks/gen-wcwidth.ts
··· 7 7 // 8 8 // BMP coarse filter: a 64-byte bitmap (1 bit per 128-codepoint block) gates 9 9 // the binary search. Clean blocks return width 1 without touching the table. 10 + // 11 + // Lookup order in wcwidth(): ASCII/Latin-1 fast paths, then a noncharacter 12 + // guard, then codepoint_in_special() which checks (1) a TUI box-drawing fast 13 + // lane, (2) the large contiguous blocks (CJK/Hangul/SIP/tags), (3) the BMP 14 + // coarse filter, (4) the packed small-range binary search. 10 15 11 16 let UNICODE_BASE = "https://www.unicode.org/Public/16.0.0/ucd"; 12 17 ··· 331 336 } 332 337 } 333 338 339 + // TUI box-drawing fast lane: the width-1-only prefix of the box-drawing region. 340 + // U+2500..BOX_DRAWING_END (box drawing, block elements, shading, geometric 341 + // shapes) are all width 1, so wcwidth() shortcuts that whole region with one 342 + // range check. The bound is the codepoint just before the first special 343 + // codepoint at/after U+2500 (emoji U+25FD in Unicode 16.0). The assertion makes 344 + // a future revision that widens a glyph in this region fail the build rather 345 + // than silently mis-measure TUI borders. 346 + let firstSpecialAt2500 = Infinity; 347 + for (const r of allRanges) { 348 + let hi = r.start + r.count; 349 + if (hi < 0x2500) continue; 350 + let c = Math.max(r.start, 0x2500); 351 + if (c <= hi && c < firstSpecialAt2500) firstSpecialAt2500 = c; 352 + } 353 + const boxDrawingEnd = firstSpecialAt2500 - 1; 354 + if (boxDrawingEnd < 0x259f) { 355 + throw new Error( 356 + `box fast lane too short: ends at U+${ 357 + boxDrawingEnd.toString(16) 358 + } (expected >= U+259F)`, 359 + ); 360 + } 361 + 334 362 let tableBytes = 16 * 4 + // bmp_filter 335 363 smallPacked.length * 4 + 336 364 largeStarts.length * 4 + ··· 340 368 console.error(`special_small_ranges: ${smallPacked.length} entries`); 341 369 console.error(`special_large_ranges: ${largeRanges.length} entries`); 342 370 console.error(`BMP dirty blocks: ${dirtyBlocks} / 512`); 371 + console.error( 372 + `Box fast lane: U+2500..U+${boxDrawingEnd.toString(16).toUpperCase()}`, 373 + ); 343 374 console.error(`Table data: ${tableBytes} bytes`); 344 375 345 376 // Fingerprint the generated tables (not the source text or comments) so the ··· 396 427 * 397 428 * Large-range encoding (special_large_* parallel arrays): 398 429 * Used for ranges whose span exceeds 1023 codepoints. 430 + * 431 + * TUI box-drawing fast lane (BOX_DRAWING_END): 432 + * U+2500..BOX_DRAWING_END (box drawing, block elements, shading, geometric 433 + * shapes) are all width 1, so wcwidth() returns 1 for them without searching. 434 + * 435 + * Lookup order in codepoint_in_special(): box fast lane, then the large table 436 + * (CJK/Hangul/SIP/tags exit in ~3 comparisons), then the BMP coarse filter, 437 + * then the packed small-range binary search. 399 438 */ 400 439 401 440 #include <stdint.h> ··· 421 460 ${formatUint8Array(largeWidths)} 422 461 }; 423 462 #define SPECIAL_LARGE_COUNT ${largeRanges.length} 463 + #define BOX_DRAWING_END 0x${boxDrawingEnd.toString(16)} 424 464 425 465 /* clang-format on */ 426 466 427 467 static int codepoint_in_special(uint32_t codepoint) { 468 + /* TUI fast lane: U+2500..BOX_DRAWING_END (box drawing, block elements, 469 + * shading, geometric shapes) are all width 1; skip the search. */ 470 + if (codepoint >= 0x2500 && codepoint <= BOX_DRAWING_END) 471 + return 1; 472 + /* Big contiguous blocks (CJK, Hangul, SIP, tag chars) live in the large 473 + * table - check it first so common ideographs exit in ~3 comparisons 474 + * instead of missing through all the small ranges. */ 475 + int left = 0, right = SPECIAL_LARGE_COUNT - 1; 476 + while (left <= right) { 477 + int mid = (left + right) / 2; 478 + if (codepoint < special_large_starts[mid]) 479 + right = mid - 1; 480 + else if (codepoint > special_large_starts[mid] + special_large_counts[mid]) 481 + left = mid + 1; 482 + else 483 + return special_large_widths[mid]; 484 + } 428 485 if (codepoint <= 0xffff) { 429 486 uint32_t block = codepoint >> 7; 430 487 if (!((bmp_filter[block >> 5] >> (block & 31u)) & 1u)) 431 488 return 1; 432 489 } 433 - int left = 0, right = SPECIAL_SMALL_COUNT - 1; 490 + left = 0; 491 + right = SPECIAL_SMALL_COUNT - 1; 434 492 while (left <= right) { 435 493 int mid = (left + right) / 2; 436 494 uint32_t entry = special_small_ranges[mid]; ··· 441 499 left = mid + 1; 442 500 else 443 501 return (entry & 1) ? 2 : 0; 444 - } 445 - left = 0; 446 - right = SPECIAL_LARGE_COUNT - 1; 447 - while (left <= right) { 448 - int mid = (left + right) / 2; 449 - if (codepoint < special_large_starts[mid]) 450 - right = mid - 1; 451 - else if (codepoint > special_large_starts[mid] + special_large_counts[mid]) 452 - left = mid + 1; 453 - else 454 - return special_large_widths[mid]; 455 502 } 456 503 return 1; 457 504 }