diff --git a/src/common/wchar.c b/src/common/wchar.c index 6e7d731e02..12b3a5e1a2 100644 --- a/src/common/wchar.c +++ b/src/common/wchar.c @@ -13,6 +13,10 @@ #include "c.h" #include "mb/pg_wchar.h" +#include "port/pg_bitutils.h" + +/* FIXME -- should go in src/include/port */ +#include /* @@ -1762,6 +1766,80 @@ pg_utf8_verifystr(const unsigned char *s, int len) { const unsigned char *start = s; +#ifdef __x86_64__ + + + const __m128i zero = _mm_setzero_si128(); + __m128i chunk, + cmp; + + const int chunk_size = sizeof(__m128i); + int zero_mask, + highbit_mask, + ascii_count, + remainder; + + while (len >= chunk_size) + { + /* load next chunk */ + chunk = _mm_loadu_si128((const __m128i *) s); + + /* first detect any zero bytes */ + cmp = _mm_cmpeq_epi8(chunk, zero); + zero_mask = _mm_movemask_epi8(cmp); + + /* if there is a zero byte, let the slow path encounter it */ + if (zero_mask) + break; + + /* now check for non-ascii bytes */ + highbit_mask = _mm_movemask_epi8(chunk); + + if (!highbit_mask) + { + /* all ascii, so advance to the next chunk */ + s += chunk_size; + len -= chunk_size; + continue; + } + + /* + * if not all ascii, maybe there is a solid block of ascii + * at the beginning of the chunk. if so, skip it + */ + ascii_count = pg_rightmost_one_pos32(highbit_mask); + + s += ascii_count; + len -= ascii_count; + remainder = chunk_size - ascii_count; + + /* found non-ascii, so handle the remainder in the normal way */ + while (remainder > 0) + { + int l; + + /* + * fast path for ASCII-subset characters + * we already know they're non-zero + */ + if (!IS_HIGHBIT_SET(*s)) + l = 1; + else + { + l = pg_utf8_verifychar(s, len); + if (l == -1) + goto finish; + } + s += l; + len -= l; + remainder -= l; + + } + } + +#endif /* __x86_64__ */ + + /* handle last few bytes */ while (len > 0) { int l; @@ -1770,19 +1848,20 @@ pg_utf8_verifystr(const unsigned char *s, int len) if (!IS_HIGHBIT_SET(*s)) { if (*s == '\0') - break; + goto finish; l = 1; } else { l = pg_utf8_verifychar(s, len); if (l == -1) - break; + goto finish; } s += l; len -= l; } +finish: return s - start; }