-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathwcwidth.c
More file actions
199 lines (190 loc) · 11.1 KB
/
Copy pathwcwidth.c
File metadata and controls
199 lines (190 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* 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; }