|
7 | 7 | #if defined(_ARM_NEON) || defined(__aarch64__) || \ |
8 | 8 | (defined(_MSC_VER) && defined(_M_ARM64)) |
9 | 9 |
|
| 10 | +#include "simdjson/simdutf8check.h" |
10 | 11 | #include <arm_neon.h> |
11 | 12 | #include <cinttypes> |
12 | 13 | #include <cstddef> |
@@ -175,6 +176,64 @@ check_utf8_bytes(int8x16_t current_bytes, struct processed_utf_bytes *previous, |
175 | 176 | previous->high_nibbles, has_error); |
176 | 177 | return pb; |
177 | 178 | } |
| 179 | + |
| 180 | +template <> |
| 181 | +struct utf8_checking_state<Architecture::ARM64> { |
| 182 | + int8x16_t has_error{}; |
| 183 | + processed_utf_bytes previous{}; |
| 184 | +}; |
| 185 | + |
| 186 | +// Checks that all bytes are ascii |
| 187 | +really_inline bool check_ascii_neon(simd_input<Architecture::ARM64> in) { |
| 188 | + // checking if the most significant bit is always equal to 0. |
| 189 | + uint8x16_t high_bit = vdupq_n_u8(0x80); |
| 190 | + uint8x16_t t0 = vorrq_u8(in.i0, in.i1); |
| 191 | + uint8x16_t t1 = vorrq_u8(in.i2, in.i3); |
| 192 | + uint8x16_t t3 = vorrq_u8(t0, t1); |
| 193 | + uint8x16_t t4 = vandq_u8(t3, high_bit); |
| 194 | + uint64x2_t v64 = vreinterpretq_u64_u8(t4); |
| 195 | + uint32x2_t v32 = vqmovn_u64(v64); |
| 196 | + uint64x1_t result = vreinterpret_u64_u32(v32); |
| 197 | + return vget_lane_u64(result, 0) == 0; |
| 198 | +} |
| 199 | + |
| 200 | +template <> |
| 201 | +really_inline void check_utf8<Architecture::ARM64>( |
| 202 | + simd_input<Architecture::ARM64> in, |
| 203 | + utf8_checking_state<Architecture::ARM64> &state) { |
| 204 | + if (check_ascii_neon(in)) { |
| 205 | + // All bytes are ascii. Therefore the byte that was just before must be |
| 206 | + // ascii too. We only check the byte that was just before simd_input. Nines |
| 207 | + // are arbitrary values. |
| 208 | + const int8x16_t verror = |
| 209 | + (int8x16_t){9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 1}; |
| 210 | + state.has_error = |
| 211 | + vorrq_s8(vreinterpretq_s8_u8( |
| 212 | + vcgtq_s8(state.previous.carried_continuations, verror)), |
| 213 | + state.has_error); |
| 214 | + } else { |
| 215 | + // it is not ascii so we have to do heavy work |
| 216 | + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i0), |
| 217 | + &(state.previous), &(state.has_error)); |
| 218 | + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i1), |
| 219 | + &(state.previous), &(state.has_error)); |
| 220 | + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i2), |
| 221 | + &(state.previous), &(state.has_error)); |
| 222 | + state.previous = check_utf8_bytes(vreinterpretq_s8_u8(in.i3), |
| 223 | + &(state.previous), &(state.has_error)); |
| 224 | + } |
| 225 | +} |
| 226 | + |
| 227 | +template <> |
| 228 | +really_inline ErrorValues check_utf8_errors<Architecture::ARM64>( |
| 229 | + utf8_checking_state<Architecture::ARM64> &state) { |
| 230 | + uint64x2_t v64 = vreinterpretq_u64_s8(state.has_error); |
| 231 | + uint32x2_t v32 = vqmovn_u64(v64); |
| 232 | + uint64x1_t result = vreinterpret_u64_u32(v32); |
| 233 | + return vget_lane_u64(result, 0) != 0 ? simdjson::UTF8_ERROR |
| 234 | + : simdjson::SUCCESS; |
| 235 | +} |
| 236 | + |
178 | 237 | } // namespace simdjson |
179 | 238 | #endif |
180 | 239 | #endif |
0 commit comments