A public domain branchless UTF-8 decoder by Christopher Wellons: https://github.com/skeeto/branchless-utf8 Decode the next character, c, from s, 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 e
| 577 | * error, but it will always advance at least one byte. |
| 578 | */ |
| 579 | FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) |
| 580 | -> const char* { |
| 581 | constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; |
| 582 | constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; |
| 583 | constexpr int shiftc[] = {0, 18, 12, 6, 0}; |
| 584 | constexpr int shifte[] = {0, 6, 4, 2, 0}; |
| 585 | |
| 586 | int len = "\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" |
| 587 | [static_cast<unsigned char>(*s) >> 3]; |
| 588 | // Compute the pointer to the next character early so that the next |
| 589 | // iteration can start working on the next character. Neither Clang |
| 590 | // nor GCC figure out this reordering on their own. |
| 591 | const char* next = s + len + !len; |
| 592 | |
| 593 | using uchar = unsigned char; |
| 594 | |
| 595 | // Assume a four-byte character and load four bytes. Unused bits are |
| 596 | // shifted out. |
| 597 | *c = uint32_t(uchar(s[0]) & masks[len]) << 18; |
| 598 | *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; |
| 599 | *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; |
| 600 | *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; |
| 601 | *c >>= shiftc[len]; |
| 602 | |
| 603 | // Accumulate the various error conditions. |
| 604 | *e = (*c < mins[len]) << 6; // non-canonical encoding |
| 605 | *e |= ((*c >> 11) == 0x1b) << 7; // surrogate half? |
| 606 | *e |= (*c > 0x10FFFF) << 8; // out of range? |
| 607 | *e |= (uchar(s[1]) & 0xc0) >> 2; |
| 608 | *e |= (uchar(s[2]) & 0xc0) >> 4; |
| 609 | *e |= uchar(s[3]) >> 6; |
| 610 | *e ^= 0x2a; // top two bits of each tail byte correct? |
| 611 | *e >>= shifte[len]; |
| 612 | |
| 613 | return next; |
| 614 | } |
| 615 | |
| 616 | constexpr FMT_INLINE_VARIABLE uint32_t invalid_code_point = ~uint32_t(); |
| 617 |
no outgoing calls