1#[cfg(target_arch = "aarch64")]
4use core::arch::aarch64::*;
5#[cfg(target_arch = "x86")]
6use core::arch::x86::*;
7#[cfg(target_arch = "x86_64")]
8use core::arch::x86_64::*;
9
10use crate::error::Error;
11
12const NIL: u8 = u8::MAX;
13
14#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
15const T_MASK: i32 = 65535;
16
17const fn init_unhex_array(check_case: CheckCase) -> [u8; 256] {
18 let mut arr = [0; 256];
19 let mut i = 0;
20 while i < 256 {
21 arr[i] = match i as u8 {
22 b'0'..=b'9' => i as u8 - b'0',
23 b'a'..=b'f' => match check_case {
24 CheckCase::Lower | CheckCase::None => i as u8 - b'a' + 10,
25 _ => NIL,
26 },
27 b'A'..=b'F' => match check_case {
28 CheckCase::Upper | CheckCase::None => i as u8 - b'A' + 10,
29 _ => NIL,
30 },
31 _ => NIL,
32 };
33 i += 1;
34 }
35 arr
36}
37
38const fn init_unhex4_array(check_case: CheckCase) -> [u8; 256] {
39 let unhex_arr = init_unhex_array(check_case);
40
41 let mut unhex4_arr = [NIL; 256];
42 let mut i = 0;
43 while i < 256 {
44 if unhex_arr[i] != NIL {
45 unhex4_arr[i] = unhex_arr[i] << 4;
46 }
47 i += 1;
48 }
49 unhex4_arr
50}
51
52pub(crate) static UNHEX: [u8; 256] = init_unhex_array(CheckCase::None);
54
55pub(crate) static UNHEX_LOWER: [u8; 256] = init_unhex_array(CheckCase::Lower);
57
58pub(crate) static UNHEX_UPPER: [u8; 256] = init_unhex_array(CheckCase::Upper);
60
61pub(crate) static UNHEX4: [u8; 256] = init_unhex4_array(CheckCase::None);
63
64const _0213: i32 = 0b11011000;
65
66#[inline]
68fn unhex_b(x: usize) -> u8 {
69 UNHEX[x]
70}
71
72#[inline]
74fn unhex_a(x: usize) -> u8 {
75 UNHEX4[x]
76}
77
78#[inline]
79#[target_feature(enable = "avx2")]
80#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
81unsafe fn unhex_avx2(value: __m256i) -> __m256i {
82 let sr6 = _mm256_srai_epi16(value, 6);
83 let and15 = _mm256_and_si256(value, _mm256_set1_epi16(0xf));
84 let mul = _mm256_maddubs_epi16(sr6, _mm256_set1_epi16(9));
85 _mm256_add_epi16(mul, and15)
86}
87
88#[inline]
90#[target_feature(enable = "avx2")]
91#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
92unsafe fn nib2byte_avx2(a1: __m256i, b1: __m256i, a2: __m256i, b2: __m256i) -> __m256i {
93 let a4_1 = _mm256_slli_epi16(a1, 4);
94 let a4_2 = _mm256_slli_epi16(a2, 4);
95 let a4orb_1 = _mm256_or_si256(a4_1, b1);
96 let a4orb_2 = _mm256_or_si256(a4_2, b2);
97 let pck1 = _mm256_packus_epi16(a4orb_1, a4orb_2);
98 _mm256_permute4x64_epi64(pck1, _0213)
99}
100
101pub fn hex_check(src: &[u8]) -> bool {
103 hex_check_with_case(src, CheckCase::None)
104}
105
106pub fn hex_check_with_case(src: &[u8], check_case: CheckCase) -> bool {
108 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
109 {
110 match crate::vectorization_support() {
111 crate::Vectorization::AVX2 | crate::Vectorization::SSE41 => unsafe {
112 hex_check_sse_with_case(src, check_case)
113 },
114 crate::Vectorization::None => hex_check_fallback_with_case(src, check_case),
115 }
116 }
117
118 #[cfg(target_arch = "aarch64")]
119 {
120 match crate::vectorization_support() {
121 crate::Vectorization::Neon => unsafe { hex_check_neon_with_case(src, check_case) },
122 crate::Vectorization::None => hex_check_fallback_with_case(src, check_case),
123 }
124 }
125
126 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
127 hex_check_fallback_with_case(src, check_case)
128}
129
130pub fn hex_check_fallback(src: &[u8]) -> bool {
132 hex_check_fallback_with_case(src, CheckCase::None)
133}
134
135pub fn hex_check_fallback_with_case(src: &[u8], check_case: CheckCase) -> bool {
137 match check_case {
138 CheckCase::None => src.iter().all(|&x| UNHEX[x as usize] != NIL),
139 CheckCase::Lower => src.iter().all(|&x| UNHEX_LOWER[x as usize] != NIL),
140 CheckCase::Upper => src.iter().all(|&x| UNHEX_UPPER[x as usize] != NIL),
141 }
142}
143
144#[target_feature(enable = "sse4.1")]
147#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
148pub unsafe fn hex_check_sse(src: &[u8]) -> bool {
149 hex_check_sse_with_case(src, CheckCase::None)
150}
151
152#[derive(Eq, PartialEq)]
153#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
154pub enum CheckCase {
155 None,
156 Lower,
157 Upper,
158}
159
160#[target_feature(enable = "sse4.1")]
163#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
164pub unsafe fn hex_check_sse_with_case(mut src: &[u8], check_case: CheckCase) -> bool {
165 let ascii_zero = _mm_set1_epi8((b'0' - 1) as i8);
166 let ascii_nine = _mm_set1_epi8((b'9' + 1) as i8);
167 let ascii_ua = _mm_set1_epi8((b'A' - 1) as i8);
168 let ascii_uf = _mm_set1_epi8((b'F' + 1) as i8);
169 let ascii_la = _mm_set1_epi8((b'a' - 1) as i8);
170 let ascii_lf = _mm_set1_epi8((b'f' + 1) as i8);
171
172 while src.len() >= 16 {
173 let unchecked = _mm_loadu_si128(src.as_ptr() as *const _);
174
175 let gt0 = _mm_cmpgt_epi8(unchecked, ascii_zero);
176 let lt9 = _mm_cmplt_epi8(unchecked, ascii_nine);
177 let valid_digit = _mm_and_si128(gt0, lt9);
178
179 let (valid_la_lf, valid_ua_uf) = match check_case {
180 CheckCase::None => {
181 let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
182 let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
183
184 let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
185 let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
186
187 (
188 Some(_mm_and_si128(gtla, ltlf)),
189 Some(_mm_and_si128(gtua, ltuf)),
190 )
191 }
192 CheckCase::Lower => {
193 let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
194 let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
195
196 (Some(_mm_and_si128(gtla, ltlf)), None)
197 }
198 CheckCase::Upper => {
199 let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
200 let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
201 (None, Some(_mm_and_si128(gtua, ltuf)))
202 }
203 };
204
205 let valid_letter = match (valid_la_lf, valid_ua_uf) {
206 (Some(valid_lower), Some(valid_upper)) => _mm_or_si128(valid_lower, valid_upper),
207 (Some(valid_lower), None) => valid_lower,
208 (None, Some(valid_upper)) => valid_upper,
209 _ => unreachable!(),
210 };
211
212 let ret = _mm_movemask_epi8(_mm_or_si128(valid_digit, valid_letter));
213
214 if ret != T_MASK {
215 return false;
216 }
217
218 src = &src[16..];
219 }
220 hex_check_fallback_with_case(src, check_case)
221}
222
223#[target_feature(enable = "neon")]
224#[cfg(target_arch = "aarch64")]
225pub unsafe fn hex_check_neon(src: &[u8]) -> bool {
226 hex_check_neon_with_case(src, CheckCase::None)
227}
228
229#[target_feature(enable = "neon")]
230#[cfg(target_arch = "aarch64")]
231pub unsafe fn hex_check_neon_with_case(mut src: &[u8], check_case: CheckCase) -> bool {
232 let ascii_zero = vdupq_n_u8(b'0' - 1);
233 let ascii_nine = vdupq_n_u8(b'9' + 1);
234 let ascii_ua = vdupq_n_u8(b'A' - 1);
235 let ascii_uf = vdupq_n_u8(b'F' + 1);
236 let ascii_la = vdupq_n_u8(b'a' - 1);
237 let ascii_lf = vdupq_n_u8(b'f' + 1);
238
239 while src.len() >= 16 {
240 let unchecked = vld1q_u8(src.as_ptr() as *const _);
241
242 let gt0 = vcgtq_u8(unchecked, ascii_zero);
243 let lt9 = vcltq_u8(unchecked, ascii_nine);
244 let valid_digit = vandq_u8(gt0, lt9);
245
246 let (valid_la_lf, valid_ua_uf) = match check_case {
247 CheckCase::None => {
248 let gtua = vcgtq_u8(unchecked, ascii_ua);
249 let ltuf = vcltq_u8(unchecked, ascii_uf);
250
251 let gtla = vcgtq_u8(unchecked, ascii_la);
252 let ltlf = vcltq_u8(unchecked, ascii_lf);
253
254 (Some(vandq_u8(gtla, ltlf)), Some(vandq_u8(gtua, ltuf)))
255 }
256 CheckCase::Lower => {
257 let gtla = vcgtq_u8(unchecked, ascii_la);
258 let ltlf = vcltq_u8(unchecked, ascii_lf);
259
260 (Some(vandq_u8(gtla, ltlf)), None)
261 }
262 CheckCase::Upper => {
263 let gtua = vcgtq_u8(unchecked, ascii_ua);
264 let ltuf = vcltq_u8(unchecked, ascii_uf);
265
266 (None, Some(vandq_u8(gtua, ltuf)))
267 }
268 };
269
270 let valid_letter = match (valid_la_lf, valid_ua_uf) {
271 (Some(valid_lower), Some(valid_upper)) => vorrq_u8(valid_lower, valid_upper),
272 (Some(valid_lower), None) => valid_lower,
273 (None, Some(valid_upper)) => valid_upper,
274 _ => unreachable!(),
275 };
276
277 let ret = vminvq_u8(vorrq_u8(valid_digit, valid_letter));
278
279 if ret == 0 {
280 return false;
281 }
282
283 src = &src[16..];
284 }
285
286 hex_check_fallback_with_case(src, check_case)
287}
288
289pub fn hex_decode(src: &[u8], dst: &mut [u8]) -> Result<(), Error> {
293 hex_decode_with_case(src, dst, CheckCase::None)
294}
295
296pub fn hex_decode_with_case(
303 src: &[u8],
304 dst: &mut [u8],
305 check_case: CheckCase,
306) -> Result<(), Error> {
307 let len = dst.len().checked_mul(2).ok_or(Error::Overflow)?;
308 if src.len() < len || ((src.len() & 1) != 0) {
309 return Err(Error::InvalidLength(len));
310 }
311
312 if !hex_check_with_case(src, check_case) {
313 return Err(Error::InvalidChar);
314 }
315 hex_decode_unchecked(src, dst);
316 Ok(())
317}
318
319pub fn hex_decode_unchecked(src: &[u8], dst: &mut [u8]) {
320 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
321 {
322 match crate::vectorization_support() {
323 crate::Vectorization::AVX2 => unsafe { hex_decode_avx2(src, dst) },
324 crate::Vectorization::None | crate::Vectorization::SSE41 => {
325 hex_decode_fallback(src, dst)
326 }
327 }
328 }
329 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
330 hex_decode_fallback(src, dst);
331}
332
333#[target_feature(enable = "avx2")]
334#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
335unsafe fn hex_decode_avx2(mut src: &[u8], mut dst: &mut [u8]) {
336 let mask_a = _mm256_setr_epi8(
339 0, -1, 2, -1, 4, -1, 6, -1, 8, -1, 10, -1, 12, -1, 14, -1, 0, -1, 2, -1, 4, -1, 6, -1, 8,
340 -1, 10, -1, 12, -1, 14, -1,
341 );
342
343 let mask_b = _mm256_setr_epi8(
346 1, -1, 3, -1, 5, -1, 7, -1, 9, -1, 11, -1, 13, -1, 15, -1, 1, -1, 3, -1, 5, -1, 7, -1, 9,
347 -1, 11, -1, 13, -1, 15, -1,
348 );
349
350 while src.len() >= 64 && dst.len() >= 32 {
351 let av1 = _mm256_loadu_si256(src.as_ptr() as *const _);
352 let av2 = _mm256_loadu_si256(src[32..].as_ptr() as *const _);
353
354 let mut a1 = _mm256_shuffle_epi8(av1, mask_a);
355 let mut b1 = _mm256_shuffle_epi8(av1, mask_b);
356 let mut a2 = _mm256_shuffle_epi8(av2, mask_a);
357 let mut b2 = _mm256_shuffle_epi8(av2, mask_b);
358
359 a1 = unhex_avx2(a1);
360 a2 = unhex_avx2(a2);
361 b1 = unhex_avx2(b1);
362 b2 = unhex_avx2(b2);
363
364 let bytes = nib2byte_avx2(a1, b1, a2, b2);
365
366 _mm256_storeu_si256(dst.as_mut_ptr() as *mut _, bytes);
368 dst = &mut dst[32..];
369 src = &src[64..];
370 }
371 hex_decode_fallback(src, dst)
372}
373
374pub fn hex_decode_fallback(src: &[u8], dst: &mut [u8]) {
375 for (slot, bytes) in dst.iter_mut().zip(src.chunks_exact(2)) {
376 let a = unhex_a(bytes[0] as usize);
377 let b = unhex_b(bytes[1] as usize);
378 *slot = a | b;
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use crate::decode::NIL;
385 use crate::{
386 decode::{
387 hex_check_fallback, hex_check_fallback_with_case, hex_decode_fallback, CheckCase,
388 },
389 encode::hex_string,
390 };
391 use proptest::proptest;
392
393 #[test]
394 fn test_unchecked_decode_stops_at_source_end() {
395 use crate::hex_decode_unchecked;
396
397 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
398 if cfg!(target_feature = "sse")
399 && !cfg!(target_env = "sgx")
400 && is_x86_feature_detected!("avx2")
401 {
402 assert_eq!(crate::vectorization_support(), crate::Vectorization::AVX2);
403 }
404
405 for &(src_len, dst_len) in &[
406 (0, 32),
407 (1, 32),
408 (31, 32),
409 (32, 32),
410 (63, 32),
411 (64, 33),
412 (96, 64),
413 (127, 64),
414 (128, 65),
415 ] {
416 let src = vec![b'a'; src_len];
417 let mut dst = vec![0x5a; dst_len];
418 let mut expected = dst.clone();
419 expected[..(src_len / 2).min(dst_len)].fill(0xaa);
420
421 hex_decode_unchecked(&src, &mut dst);
422 assert_eq!(dst, expected, "src_len={src_len}, dst_len={dst_len}");
423 }
424 }
425
426 #[cfg(not(feature = "alloc"))]
427 const CAPACITY: usize = 128;
428
429 fn _test_decode_fallback(s: &String) {
430 let len = s.as_bytes().len();
431 let mut dst = Vec::with_capacity(len);
432 dst.resize(len, 0);
433
434 #[cfg(feature = "alloc")]
435 let hex_string = hex_string(s.as_bytes());
436 #[cfg(not(feature = "alloc"))]
437 let hex_string = hex_string::<CAPACITY>(s.as_bytes());
438
439 hex_decode_fallback(hex_string.as_bytes(), &mut dst);
440
441 assert_eq!(&dst[..], s.as_bytes());
442 }
443
444 #[cfg(feature = "alloc")]
445 proptest! {
446 #[test]
447 fn test_decode_fallback(ref s in ".+") {
448 _test_decode_fallback(s);
449 }
450 }
451
452 #[cfg(not(feature = "alloc"))]
453 proptest! {
454 #[test]
455 fn test_decode_fallback(ref s in ".{1,16}") {
456 _test_decode_fallback(s);
457 }
458 }
459
460 fn _test_check_fallback_true(s: &String) {
461 assert!(hex_check_fallback(s.as_bytes()));
462 match (
463 s.contains(char::is_lowercase),
464 s.contains(char::is_uppercase),
465 ) {
466 (true, true) => {
467 assert!(!hex_check_fallback_with_case(
468 s.as_bytes(),
469 CheckCase::Lower
470 ));
471 assert!(!hex_check_fallback_with_case(
472 s.as_bytes(),
473 CheckCase::Upper
474 ));
475 }
476 (true, false) => {
477 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
478 assert!(!hex_check_fallback_with_case(
479 s.as_bytes(),
480 CheckCase::Upper
481 ));
482 }
483 (false, true) => {
484 assert!(!hex_check_fallback_with_case(
485 s.as_bytes(),
486 CheckCase::Lower
487 ));
488 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
489 }
490 (false, false) => {
491 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
492 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
493 }
494 }
495 }
496
497 proptest! {
498 #[test]
499 fn test_check_fallback_true(ref s in "[0-9a-fA-F]+") {
500 _test_check_fallback_true(s);
501 }
502 }
503
504 fn _test_check_fallback_false(s: &String) {
505 assert!(!hex_check_fallback(s.as_bytes()));
506 assert!(!hex_check_fallback_with_case(
507 s.as_bytes(),
508 CheckCase::Upper
509 ));
510 assert!(!hex_check_fallback_with_case(
511 s.as_bytes(),
512 CheckCase::Lower
513 ));
514 }
515
516 proptest! {
517 #[test]
518 fn test_check_fallback_false(ref s in ".{16}[^0-9a-fA-F]+") {
519 _test_check_fallback_false(s);
520 }
521 }
522
523 #[test]
524 fn test_init_static_array_is_right() {
525 static OLD_UNHEX: [u8; 256] = [
526 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
527 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
528 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 1, 2, 3, 4, 5,
529 6, 7, 8, 9, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL,
530 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
531 NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL, NIL, NIL, NIL,
532 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
533 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
534 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
535 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
536 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
537 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
538 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
539 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
540 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
541 ];
542
543 static OLD_UNHEX4: [u8; 256] = [
544 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
545 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
546 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 16, 32, 48,
547 64, 80, 96, 112, 128, 144, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224,
548 240, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
549 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224, 240, NIL,
550 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
551 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
552 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
553 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
554 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
555 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
556 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
557 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
558 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
559 ];
560
561 assert_eq!(OLD_UNHEX, crate::decode::UNHEX);
562 assert_eq!(OLD_UNHEX4, crate::decode::UNHEX4);
563 }
564}
565
566#[cfg(all(
567 test,
568 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
569))]
570mod test_simd {
571 use crate::decode::{
572 hex_check, hex_check_fallback, hex_check_fallback_with_case, hex_check_with_case,
573 hex_decode, hex_decode_unchecked, hex_decode_with_case, CheckCase,
574 };
575 #[cfg(target_arch = "aarch64")]
576 use crate::decode::{hex_check_neon, hex_check_neon_with_case};
577 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
578 use crate::decode::{hex_check_sse, hex_check_sse_with_case};
579 #[cfg(target_arch = "aarch64")]
580 use std::arch::is_aarch64_feature_detected;
581
582 use proptest::proptest;
583
584 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
585 fn _test_check_sse_with_case(s: &String, check_case: CheckCase, expect_result: bool) {
586 if is_x86_feature_detected!("sse4.1") {
587 assert_eq!(
588 unsafe { hex_check_sse_with_case(s.as_bytes(), check_case) },
589 expect_result
590 )
591 }
592 }
593
594 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
595 fn _test_check_sse_true(s: &String) {
596 if is_x86_feature_detected!("sse4.1") {
597 assert!(unsafe { hex_check_sse(s.as_bytes()) });
598 }
599 }
600
601 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
602 proptest! {
603 #[test]
604 fn test_check_sse_true(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
605 _test_check_sse_true(s);
606 _test_check_sse_with_case(s, CheckCase::None, true);
607 match (s.contains(char::is_lowercase), s.contains(char::is_uppercase)){
608 (true, true) => {
609 _test_check_sse_with_case(s, CheckCase::Lower, false);
610 _test_check_sse_with_case(s, CheckCase::Upper, false);
611 },
612 (true, false) => {
613 _test_check_sse_with_case(s, CheckCase::Lower, true);
614 _test_check_sse_with_case(s, CheckCase::Upper, false);
615 },
616 (false, true) => {
617 _test_check_sse_with_case(s, CheckCase::Lower, false);
618 _test_check_sse_with_case(s, CheckCase::Upper, true);
619 },
620 (false, false) => {
621 _test_check_sse_with_case(s, CheckCase::Lower, true);
622 _test_check_sse_with_case(s, CheckCase::Upper, true);
623 }
624 }
625 }
626 }
627
628 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
629 fn _test_check_sse_false(s: &String) {
630 if is_x86_feature_detected!("sse4.1") {
631 assert!(!unsafe { hex_check_sse(s.as_bytes()) });
632 }
633 }
634 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
635 proptest! {
636 #[test]
637 fn test_check_sse_false(ref s in ".{16}[^0-9a-fA-F]+") {
638 _test_check_sse_false(s);
639 _test_check_sse_with_case(s, CheckCase::None, false);
640 _test_check_sse_with_case(s, CheckCase::Lower, false);
641 _test_check_sse_with_case(s, CheckCase::Upper, false);
642 }
643 }
644
645 #[cfg(target_arch = "aarch64")]
646 fn _test_check_neon_with_case(s: &String, check_case: CheckCase, expect_result: bool) {
647 if is_aarch64_feature_detected!("neon") {
648 assert_eq!(
649 unsafe { hex_check_neon_with_case(s.as_bytes(), check_case) },
650 expect_result
651 )
652 }
653 }
654
655 #[cfg(target_arch = "aarch64")]
656 fn _test_check_neon_true(s: &String) {
657 if is_aarch64_feature_detected!("neon") {
658 assert!(unsafe { hex_check_neon(s.as_bytes()) });
659 }
660 }
661
662 #[cfg(target_arch = "aarch64")]
663 proptest! {
664 #[test]
665 fn test_check_neon_true(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
666 _test_check_neon_true(s);
667 _test_check_neon_with_case(s, CheckCase::None, true);
668 match (s.contains(char::is_lowercase), s.contains(char::is_uppercase)){
669 (true, true) => {
670 _test_check_neon_with_case(s, CheckCase::Lower, false);
671 _test_check_neon_with_case(s, CheckCase::Upper, false);
672 },
673 (true, false) => {
674 _test_check_neon_with_case(s, CheckCase::Lower, true);
675 _test_check_neon_with_case(s, CheckCase::Upper, false);
676 },
677 (false, true) => {
678 _test_check_neon_with_case(s, CheckCase::Lower, false);
679 _test_check_neon_with_case(s, CheckCase::Upper, true);
680 },
681 (false, false) => {
682 _test_check_neon_with_case(s, CheckCase::Lower, true);
683 _test_check_neon_with_case(s, CheckCase::Upper, true);
684 }
685 }
686 }
687 }
688
689 #[cfg(target_arch = "aarch64")]
690 fn _test_check_neon_false(s: &String) {
691 if is_aarch64_feature_detected!("neon") {
692 assert!(!unsafe { hex_check_neon(s.as_bytes()) });
693 }
694 }
695 #[cfg(target_arch = "aarch64")]
696 proptest! {
697 #[test]
698 fn test_check_neon_false(ref s in ".{16}[^0-9a-fA-F]+") {
699 _test_check_neon_false(s);
700 _test_check_neon_with_case(s, CheckCase::None, false);
701 _test_check_neon_with_case(s, CheckCase::Lower, false);
702 _test_check_neon_with_case(s, CheckCase::Upper, false);
703 }
704 }
705
706 #[test]
707 fn test_decode_zero_length_src_should_not_be_ok() {
708 let src = b"";
709 let mut dst = [0u8; 10];
710 assert!(
711 matches!(hex_decode(src, &mut dst), Err(crate::Error::InvalidLength(len)) if len == 20)
712 );
713 assert!(
714 matches!(hex_decode_with_case(src, &mut dst, CheckCase::None), Err(crate::Error::InvalidLength(len)) if len == 20)
715 );
716 assert!(hex_check(src));
717 assert!(hex_check_with_case(src, CheckCase::None));
718 assert!(hex_check_fallback(src));
719 assert!(hex_check_fallback_with_case(src, CheckCase::None));
720
721 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
722 if is_x86_feature_detected!("sse4.1") {
723 assert!(unsafe { hex_check_sse_with_case(src, CheckCase::None) });
724 assert!(unsafe { hex_check_sse(src) });
725 }
726
727 #[cfg(target_arch = "aarch64")]
728 if is_aarch64_feature_detected!("neon") {
729 assert!(unsafe { hex_check_neon_with_case(src, CheckCase::None) });
730 assert!(unsafe { hex_check_neon(src) });
731 }
732
733 hex_decode_unchecked(src, &mut dst);
735 }
736
737 #[test]
739 fn test_if_dst_len_gt_expect_len_should_return_error() {
740 let short_str = b"8e40af02265360d59f4ecf9ae9ebf8f00a3118408f5a9cdcbcc9c0f93642f3"; {
742 let mut dst = [0u8; 31];
743 let result = hex_decode(short_str.as_slice(), &mut dst);
744 assert!(result.is_ok());
745 }
746
747 {
748 let mut dst = [0u8; 32];
749 let result = hex_decode(short_str.as_slice(), &mut dst);
750 assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 64))
751 }
752
753 {
754 let mut dst = [0u8; 33];
755 let result = hex_decode(short_str.as_slice(), &mut dst);
756 assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 66))
757 }
758 }
759
760 #[test]
763 fn test_decode_zero_src() {
764 let zero_src = b"";
765 {
766 let mut zero_dst = [];
767 assert!(hex_decode(zero_src, &mut zero_dst).is_ok());
768 }
769
770 {
771 let mut non_zero_dst = [0u8; 1];
772 assert!(
773 matches!(hex_decode(zero_src, &mut non_zero_dst), Err(crate::Error::InvalidLength(len)) if len == 2)
774 );
775 }
776 }
777}