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 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 #[cfg(not(feature = "alloc"))]
394 const CAPACITY: usize = 128;
395
396 fn _test_decode_fallback(s: &String) {
397 let len = s.as_bytes().len();
398 let mut dst = Vec::with_capacity(len);
399 dst.resize(len, 0);
400
401 #[cfg(feature = "alloc")]
402 let hex_string = hex_string(s.as_bytes());
403 #[cfg(not(feature = "alloc"))]
404 let hex_string = hex_string::<CAPACITY>(s.as_bytes());
405
406 hex_decode_fallback(hex_string.as_bytes(), &mut dst);
407
408 assert_eq!(&dst[..], s.as_bytes());
409 }
410
411 #[cfg(feature = "alloc")]
412 proptest! {
413 #[test]
414 fn test_decode_fallback(ref s in ".+") {
415 _test_decode_fallback(s);
416 }
417 }
418
419 #[cfg(not(feature = "alloc"))]
420 proptest! {
421 #[test]
422 fn test_decode_fallback(ref s in ".{1,16}") {
423 _test_decode_fallback(s);
424 }
425 }
426
427 fn _test_check_fallback_true(s: &String) {
428 assert!(hex_check_fallback(s.as_bytes()));
429 match (
430 s.contains(char::is_lowercase),
431 s.contains(char::is_uppercase),
432 ) {
433 (true, true) => {
434 assert!(!hex_check_fallback_with_case(
435 s.as_bytes(),
436 CheckCase::Lower
437 ));
438 assert!(!hex_check_fallback_with_case(
439 s.as_bytes(),
440 CheckCase::Upper
441 ));
442 }
443 (true, false) => {
444 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
445 assert!(!hex_check_fallback_with_case(
446 s.as_bytes(),
447 CheckCase::Upper
448 ));
449 }
450 (false, true) => {
451 assert!(!hex_check_fallback_with_case(
452 s.as_bytes(),
453 CheckCase::Lower
454 ));
455 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
456 }
457 (false, false) => {
458 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
459 assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
460 }
461 }
462 }
463
464 proptest! {
465 #[test]
466 fn test_check_fallback_true(ref s in "[0-9a-fA-F]+") {
467 _test_check_fallback_true(s);
468 }
469 }
470
471 fn _test_check_fallback_false(s: &String) {
472 assert!(!hex_check_fallback(s.as_bytes()));
473 assert!(!hex_check_fallback_with_case(
474 s.as_bytes(),
475 CheckCase::Upper
476 ));
477 assert!(!hex_check_fallback_with_case(
478 s.as_bytes(),
479 CheckCase::Lower
480 ));
481 }
482
483 proptest! {
484 #[test]
485 fn test_check_fallback_false(ref s in ".{16}[^0-9a-fA-F]+") {
486 _test_check_fallback_false(s);
487 }
488 }
489
490 #[test]
491 fn test_init_static_array_is_right() {
492 static OLD_UNHEX: [u8; 256] = [
493 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
494 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
495 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 1, 2, 3, 4, 5,
496 6, 7, 8, 9, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL,
497 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
498 NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL, NIL, NIL, NIL,
499 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
500 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
501 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
502 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
503 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
504 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
505 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
506 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
507 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
508 ];
509
510 static OLD_UNHEX4: [u8; 256] = [
511 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
512 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
513 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 16, 32, 48,
514 64, 80, 96, 112, 128, 144, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224,
515 240, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
516 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224, 240, NIL,
517 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
518 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
519 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
520 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
521 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
522 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
523 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
524 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
525 NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
526 ];
527
528 assert_eq!(OLD_UNHEX, crate::decode::UNHEX);
529 assert_eq!(OLD_UNHEX4, crate::decode::UNHEX4);
530 }
531}
532
533#[cfg(all(
534 test,
535 any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
536))]
537mod test_simd {
538 use crate::decode::{
539 hex_check, hex_check_fallback, hex_check_fallback_with_case, hex_check_with_case,
540 hex_decode, hex_decode_unchecked, hex_decode_with_case, CheckCase,
541 };
542 #[cfg(target_arch = "aarch64")]
543 use crate::decode::{hex_check_neon, hex_check_neon_with_case};
544 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
545 use crate::decode::{hex_check_sse, hex_check_sse_with_case};
546 #[cfg(target_arch = "aarch64")]
547 use std::arch::is_aarch64_feature_detected;
548
549 use proptest::proptest;
550
551 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
552 fn _test_check_sse_with_case(s: &String, check_case: CheckCase, expect_result: bool) {
553 if is_x86_feature_detected!("sse4.1") {
554 assert_eq!(
555 unsafe { hex_check_sse_with_case(s.as_bytes(), check_case) },
556 expect_result
557 )
558 }
559 }
560
561 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
562 fn _test_check_sse_true(s: &String) {
563 if is_x86_feature_detected!("sse4.1") {
564 assert!(unsafe { hex_check_sse(s.as_bytes()) });
565 }
566 }
567
568 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
569 proptest! {
570 #[test]
571 fn test_check_sse_true(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
572 _test_check_sse_true(s);
573 _test_check_sse_with_case(s, CheckCase::None, true);
574 match (s.contains(char::is_lowercase), s.contains(char::is_uppercase)){
575 (true, true) => {
576 _test_check_sse_with_case(s, CheckCase::Lower, false);
577 _test_check_sse_with_case(s, CheckCase::Upper, false);
578 },
579 (true, false) => {
580 _test_check_sse_with_case(s, CheckCase::Lower, true);
581 _test_check_sse_with_case(s, CheckCase::Upper, false);
582 },
583 (false, true) => {
584 _test_check_sse_with_case(s, CheckCase::Lower, false);
585 _test_check_sse_with_case(s, CheckCase::Upper, true);
586 },
587 (false, false) => {
588 _test_check_sse_with_case(s, CheckCase::Lower, true);
589 _test_check_sse_with_case(s, CheckCase::Upper, true);
590 }
591 }
592 }
593 }
594
595 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
596 fn _test_check_sse_false(s: &String) {
597 if is_x86_feature_detected!("sse4.1") {
598 assert!(!unsafe { hex_check_sse(s.as_bytes()) });
599 }
600 }
601 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
602 proptest! {
603 #[test]
604 fn test_check_sse_false(ref s in ".{16}[^0-9a-fA-F]+") {
605 _test_check_sse_false(s);
606 _test_check_sse_with_case(s, CheckCase::None, false);
607 _test_check_sse_with_case(s, CheckCase::Lower, false);
608 _test_check_sse_with_case(s, CheckCase::Upper, false);
609 }
610 }
611
612 #[cfg(target_arch = "aarch64")]
613 fn _test_check_neon_with_case(s: &String, check_case: CheckCase, expect_result: bool) {
614 if is_aarch64_feature_detected!("neon") {
615 assert_eq!(
616 unsafe { hex_check_neon_with_case(s.as_bytes(), check_case) },
617 expect_result
618 )
619 }
620 }
621
622 #[cfg(target_arch = "aarch64")]
623 fn _test_check_neon_true(s: &String) {
624 if is_aarch64_feature_detected!("neon") {
625 assert!(unsafe { hex_check_neon(s.as_bytes()) });
626 }
627 }
628
629 #[cfg(target_arch = "aarch64")]
630 proptest! {
631 #[test]
632 fn test_check_neon_true(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
633 _test_check_neon_true(s);
634 _test_check_neon_with_case(s, CheckCase::None, true);
635 match (s.contains(char::is_lowercase), s.contains(char::is_uppercase)){
636 (true, true) => {
637 _test_check_neon_with_case(s, CheckCase::Lower, false);
638 _test_check_neon_with_case(s, CheckCase::Upper, false);
639 },
640 (true, false) => {
641 _test_check_neon_with_case(s, CheckCase::Lower, true);
642 _test_check_neon_with_case(s, CheckCase::Upper, false);
643 },
644 (false, true) => {
645 _test_check_neon_with_case(s, CheckCase::Lower, false);
646 _test_check_neon_with_case(s, CheckCase::Upper, true);
647 },
648 (false, false) => {
649 _test_check_neon_with_case(s, CheckCase::Lower, true);
650 _test_check_neon_with_case(s, CheckCase::Upper, true);
651 }
652 }
653 }
654 }
655
656 #[cfg(target_arch = "aarch64")]
657 fn _test_check_neon_false(s: &String) {
658 if is_aarch64_feature_detected!("neon") {
659 assert!(!unsafe { hex_check_neon(s.as_bytes()) });
660 }
661 }
662 #[cfg(target_arch = "aarch64")]
663 proptest! {
664 #[test]
665 fn test_check_neon_false(ref s in ".{16}[^0-9a-fA-F]+") {
666 _test_check_neon_false(s);
667 _test_check_neon_with_case(s, CheckCase::None, false);
668 _test_check_neon_with_case(s, CheckCase::Lower, false);
669 _test_check_neon_with_case(s, CheckCase::Upper, false);
670 }
671 }
672
673 #[test]
674 fn test_decode_zero_length_src_should_not_be_ok() {
675 let src = b"";
676 let mut dst = [0u8; 10];
677 assert!(
678 matches!(hex_decode(src, &mut dst), Err(crate::Error::InvalidLength(len)) if len == 20)
679 );
680 assert!(
681 matches!(hex_decode_with_case(src, &mut dst, CheckCase::None), Err(crate::Error::InvalidLength(len)) if len == 20)
682 );
683 assert!(hex_check(src));
684 assert!(hex_check_with_case(src, CheckCase::None));
685 assert!(hex_check_fallback(src));
686 assert!(hex_check_fallback_with_case(src, CheckCase::None));
687
688 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
689 if is_x86_feature_detected!("sse4.1") {
690 assert!(unsafe { hex_check_sse_with_case(src, CheckCase::None) });
691 assert!(unsafe { hex_check_sse(src) });
692 }
693
694 #[cfg(target_arch = "aarch64")]
695 if is_aarch64_feature_detected!("neon") {
696 assert!(unsafe { hex_check_neon_with_case(src, CheckCase::None) });
697 assert!(unsafe { hex_check_neon(src) });
698 }
699
700 hex_decode_unchecked(src, &mut dst);
702 }
703
704 #[test]
706 fn test_if_dst_len_gt_expect_len_should_return_error() {
707 let short_str = b"8e40af02265360d59f4ecf9ae9ebf8f00a3118408f5a9cdcbcc9c0f93642f3"; {
709 let mut dst = [0u8; 31];
710 let result = hex_decode(short_str.as_slice(), &mut dst);
711 assert!(result.is_ok());
712 }
713
714 {
715 let mut dst = [0u8; 32];
716 let result = hex_decode(short_str.as_slice(), &mut dst);
717 assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 64))
718 }
719
720 {
721 let mut dst = [0u8; 33];
722 let result = hex_decode(short_str.as_slice(), &mut dst);
723 assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 66))
724 }
725 }
726
727 #[test]
730 fn test_decode_zero_src() {
731 let zero_src = b"";
732 {
733 let mut zero_dst = [];
734 assert!(hex_decode(zero_src, &mut zero_dst).is_ok());
735 }
736
737 {
738 let mut non_zero_dst = [0u8; 1];
739 assert!(
740 matches!(hex_decode(zero_src, &mut non_zero_dst), Err(crate::Error::InvalidLength(len)) if len == 2)
741 );
742 }
743 }
744}