[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay bomb.sh/docs/tty
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200/* wcwidth.c - Unicode character width lookup * Unicode 17.0 - generated by tasks/gen-wcwidth.ts * Data hash: d73f21ced2426cb4 * * Only zero-width (combining marks + default ignorable) and double-width * (wide/fullwidth) codepoints are stored. Everything else defaults to * width 1. Control characters (U+0000-U+001F, U+007F-U+009F) are handled * by the fast-path checks in wcwidth() and are not in the tables. * * Sources: * DerivedGeneralCategory.txt: Mn/Me categories -> width 0 * DerivedCoreProperties.txt: Default_Ignorable_Code_Point -> width 0 * EastAsianWidth.txt: W/F properties -> width 2 * * NOTE: U+00AD SOFT HYPHEN is Default_Ignorable but is emitted as width 1, * not 0: the 0xA0-0xFF fast path in wcwidth() returns 1 for the whole block. * This is intentional and matches musl and U+00AD's EastAsianWidth=Narrow. * * Noncharacters (U+FDD0-U+FDEF and U+nFFFE/U+nFFFF) return -1: they are * permanently unassigned and never printable. They cannot be stored in the * packed table (the width bit only encodes 0 vs 2), so wcwidth() handles them * with a dedicated guard. Surrogates (U+D800-U+DFFF) return -1 for the same * reason: they are not scalar values and only reach us via malformed UTF-8. * * Combining (width 0) and wide (width 2) ranges are merged into a single * sorted table so wcwidth() needs only one binary search for any codepoint. * * BMP coarse filter (bmp_filter): * 64-byte bitmap, 1 bit per 128-codepoint BMP block. A 0-bit means no * special codepoints in that block - return width 1 without searching. * * Packed encoding (special_small_ranges): * Each uint32_t entry packs one Unicode range as * bits 31-11 start codepoint (fits in 21 bits; Unicode max is U+10FFFF) * bits 10-1 count of additional codepoints beyond start (max 1023) * bit 0 0 = width 0 (combining), 1 = width 2 (wide) * Array is sorted by start so binary search operates on raw uint32_t values. * * Large-range encoding (special_large_* parallel arrays): * Used for ranges whose span exceeds 1023 codepoints. * * TUI box-drawing fast lane (BOX_DRAWING_END): * U+2500..BOX_DRAWING_END (box drawing, block elements, shading, geometric * shapes) are all width 1, so wcwidth() returns 1 for them without searching. * * Lookup order in codepoint_in_special(): box fast lane, then the large table * (CJK/Hangul/SIP/tags exit in ~3 comparisons), then the BMP coarse filter, * then the packed small-range binary search. */
#include <stdint.h>
/* clang-format off */
static const uint32_t bmp_filter[16] = { 0xfffffa40, 0x0bf7c047, 0xee40f8c3, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffbf33ff, 0xffffffff, 0xffffffff, 0x0000ffff, 0x00000000, 0xf07c0000,};
static const uint32_t special_small_ranges[] = { 0x001800de, 0x0024180c, 0x002c8858, 0x002df800, 0x002e0802, 0x002e2002, 0x002e3800, 0x00308014, 0x0030e000, 0x00325828, 0x00338000, 0x0036b00c, 0x0036f80a, 0x00373802, 0x00375006, 0x00388800, 0x00398034, 0x003d3014, 0x003f5810, 0x003fe800, 0x0040b006, 0x0040d810, 0x00412804, 0x00414808, 0x0042c804, 0x0044b810, 0x0046502e, 0x0047183e, 0x0049d000, 0x0049e000, 0x004a080e, 0x004a6800, 0x004a880c, 0x004b1002, 0x004c0800, 0x004de000, 0x004e0806, 0x004e6800, 0x004f1002, 0x004ff000, 0x00500802, 0x0051e000, 0x00520802, 0x00523802, 0x00525804, 0x00528800, 0x00538002, 0x0053a800, 0x00540802, 0x0055e000, 0x00560808, 0x00563802, 0x00566800, 0x00571002, 0x0057d00a, 0x00580800, 0x0059e000, 0x0059f800, 0x005a0806, 0x005a6800, 0x005aa802, 0x005b1002, 0x005c1000, 0x005e0000, 0x005e6800, 0x00600000, 0x00602000, 0x0061e000, 0x0061f004, 0x00623004, 0x00625006, 0x0062a802, 0x00631002, 0x00640800, 0x0065e000, 0x0065f800, 0x00663000, 0x00666002, 0x00671002, 0x00680002, 0x0069d802, 0x006a0806, 0x006a6800, 0x006b1002, 0x006c0800, 0x006e5000, 0x006e9004, 0x006eb000, 0x00718800, 0x0071a00c, 0x0072380e, 0x00758800, 0x0075a010, 0x0076400c, 0x0078c002, 0x0079a800, 0x0079b800, 0x0079c800, 0x007b881a, 0x007c0008, 0x007c3002, 0x007c6814, 0x007cc846, 0x007e3000, 0x00816806, 0x0081900a, 0x0081c802, 0x0081e802, 0x0082c002, 0x0082f004, 0x00838806, 0x00841000, 0x00842802, 0x00846800, 0x0084e800, 0x008800bd, 0x008af802, 0x009ae804, 0x00b89004, 0x00b99002, 0x00ba9002, 0x00bb9002, 0x00bda002, 0x00bdb80c, 0x00be3000, 0x00be4814, 0x00bee800, 0x00c05808, 0x00c42802, 0x00c54800, 0x00c90004, 0x00c93802, 0x00c99000, 0x00c9c804, 0x00d0b802, 0x00d0d800, 0x00d2b000, 0x00d2c00c, 0x00d30000, 0x00d31000, 0x00d3280e, 0x00d39812, 0x00d3f800, 0x00d5805a, 0x00d70016, 0x00d80006, 0x00d9a000, 0x00d9b008, 0x00d9e000, 0x00da1000, 0x00db5810, 0x00dc0002, 0x00dd1006, 0x00dd4002, 0x00dd5804, 0x00df3000, 0x00df4002, 0x00df6800, 0x00df7804, 0x00e1600e, 0x00e1b002, 0x00e68004, 0x00e6a018, 0x00e7100c, 0x00e76800, 0x00e7a000, 0x00e7c002, 0x00ee007e, 0x01005808, 0x01015008, 0x0103001e, 0x01068040, 0x0118d003, 0x01194803, 0x011f4807, 0x011f8001, 0x011f9801, 0x012fe803, 0x0130a003, 0x0131800f, 0x01324017, 0x0133f801, 0x0134500b, 0x01349801, 0x01350801, 0x01355003, 0x0135e803, 0x01362003, 0x01367001, 0x0136a001, 0x01375001, 0x01379003, 0x0137a801, 0x0137d001, 0x0137e801, 0x01382801, 0x01385003, 0x01394001, 0x013a6001, 0x013a7001, 0x013a9805, 0x013ab801, 0x013ca805, 0x013d8001, 0x013df801, 0x0158d803, 0x015a8001, 0x015aa801, 0x01677804, 0x016bf800, 0x016f003e, 0x01740033, 0x0174d8b1, 0x017801ab, 0x017f8073, 0x01815006, 0x01817021, 0x018208ab, 0x0184c802, 0x0184d8c9, 0x01882855, 0x01898865, 0x018b2000, 0x018b2853, 0x018c80ab, 0x018f785f, 0x0191004f, 0x0524806d, 0x05337806, 0x0533a012, 0x0534f002, 0x05378002, 0x05401000, 0x05403000, 0x05405800, 0x05412802, 0x05416000, 0x05462002, 0x05470022, 0x0547f800, 0x0549300e, 0x054a3814, 0x054b0039, 0x054c0004, 0x054d9800, 0x054db006, 0x054de002, 0x054f2800, 0x0551480a, 0x05518802, 0x0551a802, 0x05521800, 0x05526000, 0x0553e000, 0x05558000, 0x05559004, 0x0555b802, 0x0555f002, 0x05560800, 0x05576002, 0x0557b000, 0x055f2800, 0x055f4000, 0x055f6800, 0x07c803ff, 0x07d8f000, 0x07f0001e, 0x07f08013, 0x07f1001e, 0x07f18045, 0x07f2a025, 0x07f34007, 0x07f7f800, 0x07f808bf, 0x07fd0000, 0x07ff000d, 0x07ff8010, 0x080fe800, 0x08170000, 0x081bb008, 0x08500804, 0x08502802, 0x08506006, 0x0851c004, 0x0851f800, 0x08572802, 0x08692006, 0x086b4808, 0x08755802, 0x0877d00a, 0x087a3014, 0x087c1006, 0x08800800, 0x0881c01c, 0x08838000, 0x08839802, 0x0883f804, 0x08859806, 0x0885c802, 0x08861000, 0x08880004, 0x08893808, 0x0889680e, 0x088b9800, 0x088c0002, 0x088db010, 0x088e4806, 0x088e7800, 0x08917804, 0x0891a000, 0x0891b002, 0x0891f000, 0x08920800, 0x0896f800, 0x0897180e, 0x08980002, 0x0899d802, 0x089a0000, 0x089b300c, 0x089b8008, 0x089dd80a, 0x089e7000, 0x089e8000, 0x089e9000, 0x089f0802, 0x08a1c00e, 0x08a21004, 0x08a23000, 0x08a2f000, 0x08a5980a, 0x08a5d000, 0x08a5f802, 0x08a61002, 0x08ad9006, 0x08ade002, 0x08adf802, 0x08aee002, 0x08b1980e, 0x08b1e800, 0x08b1f802, 0x08b55800, 0x08b56800, 0x08b5800a, 0x08b5b800, 0x08b8e800, 0x08b8f800, 0x08b91006, 0x08b93808, 0x08c17810, 0x08c1c802, 0x08c9d802, 0x08c9f000, 0x08ca1800, 0x08cea006, 0x08ced002, 0x08cf0000, 0x08d00812, 0x08d1980a, 0x08d1d806, 0x08d23800, 0x08d2880a, 0x08d2c804, 0x08d45018, 0x08d4c002, 0x08db0000, 0x08db1004, 0x08db3000, 0x08e1800c, 0x08e1c00a, 0x08e1f800, 0x08e4902a, 0x08e5500c, 0x08e59002, 0x08e5a802, 0x08e9880a, 0x08e9d000, 0x08e9e002, 0x08e9f80c, 0x08ea3800, 0x08ec8002, 0x08eca800, 0x08ecb800, 0x08f79802, 0x08f80002, 0x08f9b008, 0x08fa0000, 0x08fa1000, 0x08fad000, 0x09a20000, 0x09a2381c, 0x0b08f016, 0x0b096804, 0x0b578008, 0x0b59800c, 0x0b7a7800, 0x0b7c7806, 0x0b7f0007, 0x0b7f2000, 0x0b7f800d, 0x0c67f83f, 0x0c6c00e5, 0x0d7f8007, 0x0d7fa80d, 0x0d7fe803, 0x0d800245, 0x0d899001, 0x0d8a8005, 0x0d8aa801, 0x0d8b2007, 0x0d8b8317, 0x0de4e802, 0x0de50006, 0x0e78005a, 0x0e79802c, 0x0e8b3804, 0x0e8b981e, 0x0e8c280c, 0x0e8d5006, 0x0e921004, 0x0e9800ad, 0x0e9b002d, 0x0ed0006c, 0x0ed1d862, 0x0ed3a800, 0x0ed42000, 0x0ed4d808, 0x0ed5081c, 0x0f00000c, 0x0f004020, 0x0f00d80c, 0x0f011802, 0x0f013008, 0x0f047800, 0x0f09800c, 0x0f157000, 0x0f176006, 0x0f276006, 0x0f2f7002, 0x0f371800, 0x0f373000, 0x0f377002, 0x0f37a800, 0x0f46800c, 0x0f4a200c, 0x0f802001, 0x0f867801, 0x0f8c7001, 0x0f8c8813, 0x0f900005, 0x0f908057, 0x0f920011, 0x0f928003, 0x0f93000b, 0x0f980041, 0x0f996811, 0x0f99b88b, 0x0f9bf02b, 0x0f9d0055, 0x0f9e7809, 0x0f9f0021, 0x0f9fa001, 0x0f9fc08d, 0x0fa20001, 0x0fa21175, 0x0fa7f87d, 0x0faa5807, 0x0faa802f, 0x0fabd001, 0x0faca803, 0x0fad2001, 0x0fafd8a9, 0x0fb4008b, 0x0fb66001, 0x0fb68005, 0x0fb6a807, 0x0fb6e007, 0x0fb75803, 0x0fb7a011, 0x0fbf0017, 0x0fbf8001, 0x0fc8605d, 0x0fc9e013, 0x0fca3971, 0x0fd38019, 0x0fd40015, 0x0fd47071, 0x0fd64001, 0x0fd6681f, 0x0fd6f817, 0x0fd77813,};#define SPECIAL_SMALL_COUNT 490
static const uint32_t special_large_starts[] = { 0x00003250, 0x0000ac00, 0x00017000, 0x00020000, 0x00030000, 0x000e0000,};static const uint16_t special_large_counts[] = { 0x723c, 0x2ba3, 0x1cd5, 0xfffd, 0xfffd, 0x0fff,};static const uint8_t special_large_widths[] = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x00,};#define SPECIAL_LARGE_COUNT 6#define BOX_DRAWING_END 0x25fc
/* clang-format on */
static int codepoint_in_special(uint32_t codepoint) { /* TUI fast lane: U+2500..BOX_DRAWING_END (box drawing, block elements, * shading, geometric shapes) are all width 1; skip the search. */ if (codepoint >= 0x2500 && codepoint <= BOX_DRAWING_END) return 1; /* Big contiguous blocks (CJK, Hangul, SIP, tag chars) live in the large * table - check it first so common ideographs exit in ~3 comparisons * instead of missing through all the small ranges. */ int left = 0, right = SPECIAL_LARGE_COUNT - 1; while (left <= right) { int mid = (left + right) / 2; if (codepoint < special_large_starts[mid]) right = mid - 1; else if (codepoint > special_large_starts[mid] + special_large_counts[mid]) left = mid + 1; else return special_large_widths[mid]; } if (codepoint <= 0xffff) { uint32_t block = codepoint >> 7; if (!((bmp_filter[block >> 5] >> (block & 31u)) & 1u)) return 1; } left = 0; right = SPECIAL_SMALL_COUNT - 1; while (left <= right) { int mid = (left + right) / 2; uint32_t entry = special_small_ranges[mid]; uint32_t start = entry >> 11; if (codepoint < start) right = mid - 1; else if (codepoint > start + ((entry >> 1) & 0x3FF)) left = mid + 1; else return (entry & 1) ? 2 : 0; } return 1;}
int wcwidth(uint32_t codepoint) { if (codepoint >= 0x20 && codepoint <= 0x7e) return 1; if (codepoint >= 0xa0 && codepoint <= 0xff) return 1; if (codepoint < 0x20 || (codepoint > 0x7e && codepoint < 0xa0)) return codepoint == 0 ? 0 : -1; /* Surrogates are not scalar values; they only appear via malformed * UTF-8 and are never printable. */ if ((codepoint & 0xfffff800) == 0xd800) return -1; /* Noncharacters are permanently unassigned and never printable: * U+FDD0..U+FDEF and the last two codepoints of every plane * (U+nFFFE/U+nFFFF). */ if ((codepoint & 0xfffe) == 0xfffe || (codepoint >= 0xfdd0 && codepoint <= 0xfdef)) return -1; return codepoint_in_special(codepoint);}
int iswprint(uint32_t codepoint) { return wcwidth(codepoint) >= 0; }