From 3c7d6f3528432a433cd368605e4dc6f0fecaa898 Mon Sep 17 00:00:00 2001 From: Bert Münnich Date: Thu, 7 Dec 2017 14:57:02 +0100 Subject: Replace utf8codepoint with Chris Wellons' utf8_decode Code under a different license should be kept in a separate file. This implemention is a single header file with ~65 lines, so it better fits this requirement. --- sxiv.h | 1 - utf8.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.c | 28 -------------------------- window.c | 12 +++++++----- 4 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 utf8.h diff --git a/sxiv.h b/sxiv.h index 29bdb2b..4e384c8 100644 --- a/sxiv.h +++ b/sxiv.h @@ -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 */ diff --git a/utf8.h b/utf8.h new file mode 100644 index 0000000..8c6a7a0 --- /dev/null +++ b/utf8.h @@ -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 + +/* 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 diff --git a/util.c b/util.c index 973bc80..8f41d78 100644 --- a/util.c +++ b/util.c @@ -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; -} - diff --git a/window.c b/window.c index d1a96fb..b2d72d1 100644 --- a/window.c +++ b/window.c @@ -20,6 +20,7 @@ #define _WINDOW_CONFIG #include "config.h" #include "icon/data.h" +#include "utf8.h" #include #include @@ -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); -- cgit v1.2.3-54-g00ecf