| 82 | } |
| 83 | |
| 84 | static inline OffsetPt utf8DecodeCheck(const char* str, std::string::size_type i) { |
| 85 | uint32_t b0; |
| 86 | uint32_t b1; |
| 87 | uint32_t b2; |
| 88 | uint32_t b3; |
| 89 | |
| 90 | b0 = static_cast<unsigned char>(str[i]); |
| 91 | |
| 92 | if (b0 < 0x80) { |
| 93 | // 1-byte character |
| 94 | return {1, b0}; |
| 95 | } else if (b0 < 0xC0) { |
| 96 | // Unexpected continuation byte |
| 97 | return kInvalidPt; |
| 98 | } else if (b0 < 0xE0) { |
| 99 | // 2-byte character |
| 100 | if (((b1 = str[i + 1]) & 0xC0) != 0x80) |
| 101 | return kInvalidPt; |
| 102 | |
| 103 | char32_t pt = (b0 & 0x1F) << 6 | (b1 & 0x3F); |
| 104 | if (pt < 0x80) |
| 105 | return kInvalidPt; |
| 106 | |
| 107 | return {2, pt}; |
| 108 | } else if (b0 < 0xF0) { |
| 109 | // 3-byte character |
| 110 | if (((b1 = str[i + 1]) & 0xC0) != 0x80) |
| 111 | return kInvalidPt; |
| 112 | if (((b2 = str[i + 2]) & 0xC0) != 0x80) |
| 113 | return kInvalidPt; |
| 114 | |
| 115 | char32_t pt = (b0 & 0x0F) << 12 | (b1 & 0x3F) << 6 | (b2 & 0x3F); |
| 116 | if (pt < 0x800) |
| 117 | return kInvalidPt; |
| 118 | |
| 119 | return {3, pt}; |
| 120 | } else if (b0 < 0xF8) { |
| 121 | // 4-byte character |
| 122 | if (((b1 = str[i + 1]) & 0xC0) != 0x80) |
| 123 | return kInvalidPt; |
| 124 | if (((b2 = str[i + 2]) & 0xC0) != 0x80) |
| 125 | return kInvalidPt; |
| 126 | if (((b3 = str[i + 3]) & 0xC0) != 0x80) |
| 127 | return kInvalidPt; |
| 128 | |
| 129 | char32_t pt = (b0 & 0x0F) << 18 | (b1 & 0x3F) << 12 | (b2 & 0x3F) << 6 | (b3 & 0x3F); |
| 130 | if (pt < 0x10000 || pt >= 0x110000) |
| 131 | return kInvalidPt; |
| 132 | |
| 133 | return {4, pt}; |
| 134 | } else { |
| 135 | // Codepoint out of range |
| 136 | return kInvalidPt; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | static inline char32_t utf8Decode(const char* str, std::string::size_type& i) { |
| 141 | OffsetPt res = utf8DecodeCheck(str, i); |