1+ // From https://github.com/cyb70289/utf8/blob/master/lemire-neon.c
2+ // Adapted from https://github.com/lemire/fastvalidate-utf-8
3+
4+ #ifndef SIMDJSON_SIMDUTF8CHECK_NEON_H
5+ #define SIMDJSON_SIMDUTF8CHECK_NEON_H
6+
7+ #ifdef __aarch64__
8+
9+ #include <stdio.h>
10+ #include <stddef.h>
11+ #include <stdint.h>
12+ #include <string.h>
13+ #include <inttypes.h>
14+ #include <arm_neon.h>
15+
16+ /*
17+ * legal utf-8 byte sequence
18+ * http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
19+ *
20+ * Code Points 1st 2s 3s 4s
21+ * U+0000..U+007F 00..7F
22+ * U+0080..U+07FF C2..DF 80..BF
23+ * U+0800..U+0FFF E0 A0..BF 80..BF
24+ * U+1000..U+CFFF E1..EC 80..BF 80..BF
25+ * U+D000..U+D7FF ED 80..9F 80..BF
26+ * U+E000..U+FFFF EE..EF 80..BF 80..BF
27+ * U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
28+ * U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
29+ * U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
30+ *
31+ */
32+
33+ #if 0
34+ static void print128 (const char * s , const int8x16_t * v128 )
35+ {
36+ int8_t v8 [16 ];
37+ vst1q_s8 (v8 , * v128 );
38+
39+ if (s )
40+ printf ("%s:\t" , s );
41+ for (int i = 0 ; i < 16 ; ++ i )
42+ printf ("%02x " , (unsigned char )v8 [i ]);
43+ printf ("\n" );
44+ }
45+ #endif
46+
47+ // all byte values must be no larger than 0xF4
48+ static inline void checkSmallerThan0xF4 (int8x16_t current_bytes ,
49+ int8x16_t * has_error ) {
50+ // unsigned, saturates to 0 below max
51+ * has_error = vorrq_s8 (* has_error ,
52+ vreinterpretq_s8_u8 (vqsubq_u8 (vreinterpretq_u8_s8 (current_bytes ), vdupq_n_u8 (0xF4 ))));
53+ }
54+
55+ static const int8_t _nibbles [] = {
56+ 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , // 0xxx (ASCII)
57+ 0 , 0 , 0 , 0 , // 10xx (continuation)
58+ 2 , 2 , // 110x
59+ 3 , // 1110
60+ 4 , // 1111, next should be 0 (not checked here)
61+ };
62+
63+ static inline int8x16_t continuationLengths (int8x16_t high_nibbles ) {
64+ return vqtbl1q_s8 (vld1q_s8 (_nibbles ), vreinterpretq_u8_s8 (high_nibbles ));
65+ }
66+
67+ static inline int8x16_t carryContinuations (int8x16_t initial_lengths ,
68+ int8x16_t previous_carries ) {
69+
70+ int8x16_t right1 =
71+ vreinterpretq_s8_u8 (vqsubq_u8 (vreinterpretq_u8_s8 (vextq_s8 (previous_carries , initial_lengths , 16 - 1 )),
72+ vdupq_n_u8 (1 )));
73+ int8x16_t sum = vaddq_s8 (initial_lengths , right1 );
74+
75+ int8x16_t right2 = vreinterpretq_s8_u8 (vqsubq_u8 (vreinterpretq_u8_s8 (vextq_s8 (previous_carries , sum , 16 - 2 )),
76+ vdupq_n_u8 (2 )));
77+ return vaddq_s8 (sum , right2 );
78+ }
79+
80+ static inline void checkContinuations (int8x16_t initial_lengths , int8x16_t carries ,
81+ int8x16_t * has_error ) {
82+
83+ // overlap || underlap
84+ // carry > length && length > 0 || !(carry > length) && !(length > 0)
85+ // (carries > length) == (lengths > 0)
86+ uint8x16_t overunder =
87+ vceqq_u8 (vcgtq_s8 (carries , initial_lengths ),
88+ vcgtq_s8 (initial_lengths , vdupq_n_s8 (0 )));
89+
90+ * has_error = vorrq_s8 (* has_error , vreinterpretq_s8_u8 (overunder ));
91+ }
92+
93+ // when 0xED is found, next byte must be no larger than 0x9F
94+ // when 0xF4 is found, next byte must be no larger than 0x8F
95+ // next byte must be continuation, ie sign bit is set, so signed < is ok
96+ static inline void checkFirstContinuationMax (int8x16_t current_bytes ,
97+ int8x16_t off1_current_bytes ,
98+ int8x16_t * has_error ) {
99+ uint8x16_t maskED = vceqq_s8 (off1_current_bytes , vdupq_n_s8 (0xED ));
100+ uint8x16_t maskF4 = vceqq_s8 (off1_current_bytes , vdupq_n_s8 (0xF4 ));
101+
102+ uint8x16_t badfollowED =
103+ vandq_u8 (vcgtq_s8 (current_bytes , vdupq_n_s8 (0x9F )), maskED );
104+ uint8x16_t badfollowF4 =
105+ vandq_u8 (vcgtq_s8 (current_bytes , vdupq_n_s8 (0x8F )), maskF4 );
106+
107+ * has_error = vorrq_s8 (* has_error , vreinterpretq_s8_u8 (vorrq_u8 (badfollowED , badfollowF4 )));
108+ }
109+
110+ static const int8_t _initial_mins [] = {
111+ -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 ,
112+ -128 , -128 , // 10xx => false
113+ (int8_t ) 0xC2 , -128 , // 110x
114+ (int8_t ) 0xE1 , // 1110
115+ (int8_t ) 0xF1 ,
116+ };
117+
118+ static const int8_t _second_mins [] = {
119+ -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 , -128 ,
120+ -128 , -128 , // 10xx => false
121+ 127 , 127 , // 110x => true
122+ (int8_t ) 0xA0 , // 1110
123+ (int8_t ) 0x90 ,
124+ };
125+
126+ // map off1_hibits => error condition
127+ // hibits off1 cur
128+ // C => < C2 && true
129+ // E => < E1 && < A0
130+ // F => < F1 && < 90
131+ // else false && false
132+ static inline void checkOverlong (int8x16_t current_bytes ,
133+ int8x16_t off1_current_bytes , int8x16_t hibits ,
134+ int8x16_t previous_hibits , int8x16_t * has_error ) {
135+ int8x16_t off1_hibits = vextq_s8 (previous_hibits , hibits , 16 - 1 );
136+ int8x16_t initial_mins = vqtbl1q_s8 (vld1q_s8 (_initial_mins ), vreinterpretq_u8_s8 (off1_hibits ));
137+
138+ uint8x16_t initial_under = vcgtq_s8 (initial_mins , off1_current_bytes );
139+
140+ int8x16_t second_mins = vqtbl1q_s8 (vld1q_s8 (_second_mins ), vreinterpretq_u8_s8 (off1_hibits ));
141+ uint8x16_t second_under = vcgtq_s8 (second_mins , current_bytes );
142+ * has_error =
143+ vorrq_s8 (* has_error , vreinterpretq_s8_u8 (vandq_u8 (initial_under , second_under )));
144+ }
145+
146+ struct processed_utf_bytes {
147+ int8x16_t rawbytes ;
148+ int8x16_t high_nibbles ;
149+ int8x16_t carried_continuations ;
150+ };
151+
152+ static inline void count_nibbles (int8x16_t bytes ,
153+ struct processed_utf_bytes * answer ) {
154+ answer -> rawbytes = bytes ;
155+ answer -> high_nibbles =
156+ vreinterpretq_s8_u8 (vshrq_n_u8 (vreinterpretq_u8_s8 (bytes ), 4 ));
157+ }
158+
159+ // check whether the current bytes are valid UTF-8
160+ // at the end of the function, previous gets updated
161+ static inline struct processed_utf_bytes
162+ checkUTF8Bytes (int8x16_t current_bytes , struct processed_utf_bytes * previous ,
163+ int8x16_t * has_error ) {
164+ struct processed_utf_bytes pb ;
165+ count_nibbles (current_bytes , & pb );
166+
167+ checkSmallerThan0xF4 (current_bytes , has_error );
168+
169+ int8x16_t initial_lengths = continuationLengths (pb .high_nibbles );
170+
171+ pb .carried_continuations =
172+ carryContinuations (initial_lengths , previous -> carried_continuations );
173+
174+ checkContinuations (initial_lengths , pb .carried_continuations , has_error );
175+
176+ int8x16_t off1_current_bytes =
177+ vextq_s8 (previous -> rawbytes , pb .rawbytes , 16 - 1 );
178+ checkFirstContinuationMax (current_bytes , off1_current_bytes , has_error );
179+
180+ checkOverlong (current_bytes , off1_current_bytes , pb .high_nibbles ,
181+ previous -> high_nibbles , has_error );
182+ return pb ;
183+ }
184+
185+ #if 0
186+ static const int8_t _verror [] = {9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 9 , 1 };
187+
188+ /* Return 0 on success, -1 on error */
189+ int utf8_lemire (const unsigned char * src , int len ) {
190+ int i = 0 ;
191+ int8x16_t has_error = vdupq_n_s8 (0 );
192+ struct processed_utf_bytes previous = {.rawbytes = vdupq_n_s8 (0 ),
193+ .high_nibbles = vdupq_n_s8 (0 ),
194+ .carried_continuations =
195+ vdupq_n_s8 (0 )};
196+ if (len >= 16 ) {
197+ for (; i <= len - 16 ; i += 16 ) {
198+ int8x16_t current_bytes = vld1q_s8 ((int8_t * )(src + i ));
199+ previous = checkUTF8Bytes (current_bytes , & previous , & has_error );
200+ }
201+ }
202+
203+ // last part
204+ if (i < len ) {
205+ char buffer [16 ];
206+ memset (buffer , 0 , 16 );
207+ memcpy (buffer , src + i , len - i );
208+ int8x16_t current_bytes = vld1q_s8 ((int8_t * )buffer );
209+ previous = checkUTF8Bytes (current_bytes , & previous , & has_error );
210+ } else {
211+ has_error =
212+ vorrq_s8 (vreinterpretq_s8_u8 (vcgtq_s8 (previous .carried_continuations ,
213+ vld1q_s8 (_verror ))),
214+ has_error );
215+ }
216+
217+ return vmaxvq_u8 (vreinterpretq_u8_s8 (has_error )) == 0 ? 0 : -1 ;
218+ }
219+ #endif
220+
221+ #endif
222+ #endif
0 commit comments