diff options
-rw-r--r-- | sxiv.h | 1 | ||||
-rw-r--r-- | utf8.h | 68 | ||||
-rw-r--r-- | util.c | 28 | ||||
-rw-r--r-- | window.c | 12 |
4 files changed, 75 insertions, 34 deletions
@@ -361,7 +361,6 @@ int r_opendir(r_dir_t*, const char*, bool); int r_closedir(r_dir_t*); char* r_readdir(r_dir_t*); int r_mkdir(char*); -void* utf8codepoint(const void * __restrict__ str, long * __restrict__ out_codepoint); /* window.c */ @@ -0,0 +1,68 @@ +/* Branchless UTF-8 decoder + * + * This is free and unencumbered software released into the public domain. + */ +#ifndef UTF8_H +#define UTF8_H + +#include <stdint.h> + +/* Decode the next character, C, from BUF, reporting errors in E. + * + * Since this is a branchless decoder, four bytes will be read from the + * buffer regardless of the actual length of the next character. This + * means the buffer _must_ have at least three bytes of zero padding + * following the end of the data stream. + * + * Errors are reported in E, which will be non-zero if the parsed + * character was somehow invalid: invalid byte sequence, non-canonical + * encoding, or a surrogate half. + * + * The function returns a pointer to the next character. When an error + * occurs, this pointer will be a guess that depends on the particular + * error, but it will always advance at least one byte. + */ +static void * +utf8_decode(void *buf, uint32_t *c, int *e) +{ + static const char lengths[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0 + }; + static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + static const int shiftc[] = {0, 18, 12, 6, 0}; + static const int shifte[] = {0, 6, 4, 2, 0}; + + unsigned char *s = buf; + int len = lengths[s[0] >> 3]; + + /* Compute the pointer to the next character early so that the next + * iteration can start working on the next character. Neither Clang + * nor GCC figure out this reordering on their own. + */ + unsigned char *next = s + len + !len; + + /* Assume a four-byte character and load four bytes. Unused bits are + * shifted out. + */ + *c = (uint32_t)(s[0] & masks[len]) << 18; + *c |= (uint32_t)(s[1] & 0x3f) << 12; + *c |= (uint32_t)(s[2] & 0x3f) << 6; + *c |= (uint32_t)(s[3] & 0x3f) << 0; + *c >>= shiftc[len]; + + /* Accumulate the various error conditions. */ + *e = (*c < mins[len]) << 6; // non-canonical encoding + *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? + *e |= (*c > 0x10FFFF) << 8; // out of range? + *e |= (s[1] & 0xc0) >> 2; + *e |= (s[2] & 0xc0) >> 4; + *e |= (s[3] ) >> 6; + *e ^= 0x2a; // top two bits of each tail byte correct? + *e >>= shifte[len]; + + return next; +} + +#endif @@ -205,31 +205,3 @@ int r_mkdir(char *path) return 0; } -/* copied from sheredom's utf8.h (public domain) https://github.com/sheredom/utf8.h */ - -void* utf8codepoint(const void* __restrict__ str, long* __restrict__ out_codepoint) -{ - const char *s = (const char *)str; - - if (0xf0 == (0xf8 & s[0])) { - // 4 byte utf8 codepoint - *out_codepoint = ((0x07 & s[0]) << 18) | ((0x3f & s[1]) << 12) | - ((0x3f & s[2]) << 6) | (0x3f & s[3]); - s += 4; - } else if (0xe0 == (0xf0 & s[0])) { - // 3 byte utf8 codepoint - *out_codepoint = ((0x0f & s[0]) << 12) | ((0x3f & s[1]) << 6) | (0x3f & s[2]); - s += 3; - } else if (0xc0 == (0xe0 & s[0])) { - // 2 byte utf8 codepoint - *out_codepoint = ((0x1f & s[0]) << 6) | (0x3f & s[1]); - s += 2; - } else { - // 1 byte utf8 codepoint otherwise - *out_codepoint = s[0]; - s += 1; - } - - return (void *)s; -} - @@ -20,6 +20,7 @@ #define _WINDOW_CONFIG #include "config.h" #include "icon/data.h" +#include "utf8.h" #include <stdlib.h> #include <string.h> @@ -131,8 +132,9 @@ void win_init(win_t *win) win->bar.l.size = BAR_L_LEN; win->bar.r.size = BAR_R_LEN; - win->bar.l.buf = emalloc(win->bar.l.size); - win->bar.r.buf = emalloc(win->bar.r.size); + /* 3 padding bytes needed by utf8_decode */ + win->bar.l.buf = emalloc(win->bar.l.size + 3); + win->bar.r.buf = emalloc(win->bar.r.size + 3); win->bar.h = options->hide_bar ? 0 : barheight; INIT_ATOM_(WM_DELETE_WINDOW); @@ -371,14 +373,14 @@ int win_textwidth(const win_env_t *e, const char *text, unsigned int len, bool w void win_draw_bar_text(win_t *win, XftDraw *d, XftColor *color, XftFont *font, int x, int y, char *text, int maxlen, int maximum_x) { size_t len = 0; - int xshift = 0, newshift; - long codep; + int err, xshift = 0, newshift; + uint32_t codep; char *p, *nextp; FcCharSet* fccharset; XftFont* fallback = NULL; for (p = text; *p && (len < maxlen); p = nextp, len++) { - nextp = utf8codepoint(p, &codep); + nextp = utf8_decode(p, &codep, &err); if (!XftCharExists(win->env.dpy, font, codep)) { fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, codep); |