2.56 xs_unicode.h
  1/* copyright (c) 2022 - 2024 grunfink et al. / MIT license */
  2
  3#ifndef _XS_UNICODE_H
  4
  5#define _XS_UNICODE_H
  6
  7 int _xs_utf8_enc(char buf[4], unsigned int cpoint);
  8 int xs_is_utf8_cont_byte(char c);
  9 unsigned int xs_utf8_dec(const char **str);
 10 int xs_unicode_width(unsigned int cpoint);
 11 int xs_is_surrogate(unsigned int cpoint);
 12 unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2);
 13 unsigned int xs_surrogate_enc(unsigned int cpoint);
 14 unsigned int *_xs_unicode_upper_search(unsigned int cpoint);
 15 unsigned int *_xs_unicode_lower_search(unsigned int cpoint);
 16 #define xs_unicode_is_upper(cpoint) (!!_xs_unicode_upper_search(cpoint))
 17 #define xs_unicode_is_lower(cpoint) (!!_xs_unicode_lower_search(cpoint))
 18 unsigned int xs_unicode_to_upper(unsigned int cpoint);
 19 unsigned int xs_unicode_to_lower(unsigned int cpoint);
 20 int xs_unicode_nfd(unsigned int cpoint, unsigned int *base, unsigned int *diac);
 21 int xs_unicode_nfc(unsigned int base, unsigned int diac, unsigned int *cpoint);
 22 int xs_unicode_is_alpha(unsigned int cpoint);
 23
 24#ifdef _XS_H
 25 xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint);
 26#endif
 27
 28#ifdef XS_IMPLEMENTATION
 29
 30#ifndef xs_countof
 31#define xs_countof(a) (sizeof((a)) / sizeof((*a)))
 32#endif
 33
 34int _xs_utf8_enc(char buf[4], unsigned int cpoint)
 35/* encodes an Unicode codepoint to utf-8 into buf and returns the size in bytes */
 36{
 37    char *p = buf;
 38
 39    if (cpoint < 0x80) /* 1 byte char */
 40        *p++ = cpoint & 0xff;
 41    else {
 42        if (cpoint < 0x800) /* 2 byte char */
 43            *p++ = 0xc0 | (cpoint >> 6);
 44        else {
 45            if (cpoint < 0x10000) /* 3 byte char */
 46                *p++ = 0xe0 | (cpoint >> 12);
 47            else { /* 4 byte char */
 48                *p++ = 0xf0 | (cpoint >> 18);
 49                *p++ = 0x80 | ((cpoint >> 12) & 0x3f);
 50            }
 51
 52            *p++ = 0x80 | ((cpoint >> 6) & 0x3f);
 53        }
 54
 55        *p++ = 0x80 | (cpoint & 0x3f);
 56    }
 57
 58    return p - buf;
 59}
 60
 61
 62int xs_is_utf8_cont_byte(char c)
 63/* returns true if c is an utf8 continuation byte */
 64{
 65    return ((c & 0xc0) == 0x80);
 66}
 67
 68
 69unsigned int xs_utf8_dec(const char **str)
 70/* decodes an utf-8 char inside str and updates the pointer */
 71{
 72    const char *p = *str;
 73    unsigned int cpoint = 0;
 74    unsigned char c = *p++;
 75    int cb = 0;
 76
 77    if ((c & 0x80) == 0) { /* 1 byte char */
 78        cpoint = c;
 79    }
 80    else
 81    if ((c & 0xe0) == 0xc0) { /* 2 byte char */
 82        cpoint = (c & 0x1f) << 6;
 83        cb = 1;
 84    }
 85    else
 86    if ((c & 0xf0) == 0xe0) { /* 3 byte char */
 87        cpoint = (c & 0x0f) << 12;
 88        cb = 2;
 89    }
 90    else
 91    if ((c & 0xf8) == 0xf0) { /* 4 byte char */
 92        cpoint = (c & 0x07) << 18;
 93        cb = 3;
 94    }
 95
 96    /* process the continuation bytes */
 97    while (cb > 0 && *p && xs_is_utf8_cont_byte(*p))
 98        cpoint |= (*p++ & 0x3f) << (--cb * 6);
 99
100    /* incomplete or broken? */
101    if (cb)
102        cpoint = 0xfffd;
103
104    *str = p;
105    return cpoint;
106}
107
108
109/** Unicode character width: intentionally dead simple **/
110
111static unsigned int xs_unicode_width_table[] = {
112    0x300,      0x36f,      0,      /* diacritics */
113    0x1100,     0x11ff,     2,      /* Hangul */
114    0x2e80,     0xa4cf,     2,      /* CJK */
115    0xac00,     0xd7a3,     2,      /* more Hangul */
116    0xe000,     0xf8ff,     0,      /* private use */
117    0xf900,     0xfaff,     2,      /* CJK compatibility */
118    0xff00,     0xff60,     2,      /* full width things */
119    0xffdf,     0xffe6,     2,      /* full width things */
120    0x1f200,    0x1ffff,    2,      /* emojis */
121    0x20000,    0x2fffd,    2       /* more CJK */
122};
123
124int xs_unicode_width(unsigned int cpoint)
125/* returns the width in columns of a Unicode codepoint (somewhat simplified) */
126{
127    int b = 0;
128    int t = xs_countof(xs_unicode_width_table) / 3 - 1;
129
130    while (t >= b) {
131        int n = (b + t) / 2;
132        unsigned int *p = &xs_unicode_width_table[n * 3];
133
134        if (cpoint < p[0])
135            t = n - 1;
136        else
137        if (cpoint > p[1])
138            b = n + 1;
139        else
140            return p[2];
141    }
142
143    return 1;
144}
145
146
147/** surrogate pairs **/
148
149int xs_is_surrogate(unsigned int cpoint)
150/* checks if cpoint is the first element of a Unicode surrogate pair */
151{
152    return cpoint >= 0xd800 && cpoint <= 0xdfff;
153}
154
155
156unsigned int xs_surrogate_dec(unsigned int p1, unsigned int p2)
157/* "decodes" a surrogate pair into a codepoint */
158{
159    return 0x10000 | ((p1 & 0x3ff) << 10) | (p2 & 0x3ff);
160}
161
162
163unsigned int xs_surrogate_enc(unsigned int cpoint)
164/* "encodes" a Unicode into a surrogate pair (p1 in the MSB word) */
165{
166    unsigned int p1 = 0xd7c0 + (cpoint >> 10);
167    unsigned int p2 = 0xdc00 + (cpoint & 0x3ff);
168
169    return (p1 << 16) | p2;
170}
171
172
173#ifdef _XS_H
174
175xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint)
176/* encodes an Unicode codepoint to utf-8 into str */
177{
178    char tmp[4];
179
180    int c = _xs_utf8_enc(tmp, cpoint);
181
182    return xs_append_m(str, tmp, c);
183}
184
185#endif /* _XS_H */
186
187
188#ifdef _XS_UNICODE_TBL_H
189
190/* include xs_unicode_tbl.h before this one to use these functions */
191
192unsigned int *_xs_unicode_upper_search(unsigned int cpoint)
193/* searches for an uppercase codepoint in the case fold table */
194{
195    int b = 0;
196    int t = xs_countof(xs_unicode_case_fold_table) / 2 + 1;
197
198    while (t >= b) {
199        int n = (b + t) / 2;
200        unsigned int *p = &xs_unicode_case_fold_table[n * 2];
201
202        if (cpoint < p[0])
203            t = n - 1;
204        else
205        if (cpoint > p[0])
206            b = n + 1;
207        else
208            return p;
209    }
210
211    return NULL;
212}
213
214
215unsigned int *_xs_unicode_lower_search(unsigned int cpoint)
216/* searches for a lowercase codepoint in the case fold table */
217{
218    unsigned int *p = xs_unicode_case_fold_table;
219    unsigned int *e = p + xs_countof(xs_unicode_case_fold_table);
220
221    while (p < e) {
222        if (cpoint == p[1])
223            return p;
224
225        p += 2;
226    }
227
228    return NULL;
229}
230
231
232unsigned int xs_unicode_to_lower(unsigned int cpoint)
233/* returns the cpoint to lowercase */
234{
235    unsigned int *p = _xs_unicode_upper_search(cpoint);
236
237    return p == NULL ? cpoint : p[1];
238}
239
240
241unsigned int xs_unicode_to_upper(unsigned int cpoint)
242/* returns the cpoint to uppercase */
243{
244    unsigned int *p = _xs_unicode_lower_search(cpoint);
245
246    return p == NULL ? cpoint : p[0];
247}
248
249
250int xs_unicode_nfd(unsigned int cpoint, unsigned int *base, unsigned int *diac)
251/* applies unicode Normalization Form D */
252{
253    int b = 0;
254    int t = xs_countof(xs_unicode_nfd_table) / 3 - 1;
255
256    while (t >= b) {
257        int n = (b + t) / 2;
258        unsigned int *p = &xs_unicode_nfd_table[n * 3];
259
260        int c = cpoint - p[0];
261
262        if (c < 0)
263            t = n - 1;
264        else
265        if (c > 0)
266            b = n + 1;
267        else {
268            *base = p[1];
269            *diac = p[2];
270            return 1;
271        }
272    }
273
274    return 0;
275}
276
277
278int xs_unicode_nfc(unsigned int base, unsigned int diac, unsigned int *cpoint)
279/* applies unicode Normalization Form C */
280{
281    unsigned int *p = xs_unicode_nfd_table;
282    unsigned int *e = p + xs_countof(xs_unicode_nfd_table);
283
284    while (p < e) {
285        if (p[1] == base && p[2] == diac) {
286            *cpoint = p[0];
287            return 1;
288        }
289
290        p += 3;
291    }
292
293    return 0;
294}
295
296
297int xs_unicode_is_alpha(unsigned int cpoint)
298/* checks if a codepoint is an alpha (i.e. a letter) */
299{
300    int b = 0;
301    int t = xs_countof(xs_unicode_alpha_table) / 2 - 1;
302
303    while (t >= b) {
304        int n = (b + t) / 2;
305        unsigned int *p = &xs_unicode_alpha_table[n * 2];
306
307        if (cpoint < p[0])
308            t = n - 1;
309        else
310        if (cpoint > p[1])
311            b = n + 1;
312        else
313            return 1;
314    }
315
316    return 0;
317}
318
319
320#endif /* _XS_UNICODE_TBL_H */
321
322#endif /* XS_IMPLEMENTATION */
323
324#endif /* _XS_UNICODE_H */