From cbb31e6a7c4cfd1841daf97b3b10bc94e30835d8 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 30 Mar 2024 22:50:12 +0900 Subject: [PATCH] string.c (mrb_utf8_strlen): use SWAR technique for performance --- src/string.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/string.c b/src/string.c index 4c93d2ab2..8fb9dcb78 100644 --- a/src/string.c +++ b/src/string.c @@ -272,6 +272,14 @@ mrb_gc_free_str(mrb_state *mrb, struct RString *str) # define ALIGNED_WORD_ACCESS 1 #endif +#ifdef MRB_64BIT +#define bitint uint64_t +#define MASK1 0x0101010101010101ull +#else +#define bitint uint32_t +#define MASK1 0x01010101ul +#endif + #ifdef MRB_UTF8_STRING #define NOASCII(c) ((c) & 0x80) @@ -421,15 +429,46 @@ mrb_utf8len(const char* p, const char* e) return len; } +#if defined(__GNUC__) || __has_builtin(__builtin_popcount) +# ifdef MRB_64BIT +# define popcount(x) __builtin_popcountll(x) +# else +# define popcount(x) __builtin_popcountl(x) +# endif +#else +static inline uint32_t popcount(bitint x) +{ + x = (x & (MASK1*0x55)) + ((x >> 1) & (MASK1*0x55)); + x = (x & (MASK1*0x33)) + ((x >> 2) & (MASK1*0x33)); + x = (x & (MASK1*0x0F)) + ((x >> 4) & (MASK1*0x0F)); + return (x * MASK1) >> 56; +} +#endif + mrb_int mrb_utf8_strlen(const char *str, mrb_int byte_len) { mrb_int len = 0; + const char *p = str; - const char *e = p + byte_len; - while (p < e) { - if (utf8_islead(*p)) len++; - p++; + const char *be = p + sizeof(bitint) * (byte_len / sizeof(bitint)); + for (; p < be; p+=sizeof(bitint)) { + bitint t0; + + memcpy(&t0, p, sizeof(bitint)); + const bitint t1 = t0 & (MASK1*0xc0); + const bitint t2 = t1 + (MASK1*0x40); + const bitint t3 = t1 & t2; + len += popcount(t3); + } + len = sizeof(bitint) * (byte_len / sizeof(bitint)) - len; + + if (byte_len % sizeof(bitint)) { + const char *e = str + byte_len; + while (p < e) { + if (utf8_islead(*p)) len++; + p++; + } } return len; }