faster_hex/
decode.rs

1// avx2 decode modified from https://github.com/zbjornson/fast-hex/blob/master/src/hex.cc
2
3#[cfg(target_arch = "x86")]
4use core::arch::x86::*;
5#[cfg(target_arch = "x86_64")]
6use core::arch::x86_64::*;
7
8use crate::error::Error;
9
10const NIL: u8 = u8::MAX;
11
12#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
13const T_MASK: i32 = 65535;
14
15const fn init_unhex_array(check_case: CheckCase) -> [u8; 256] {
16    let mut arr = [0; 256];
17    let mut i = 0;
18    while i < 256 {
19        arr[i] = match i as u8 {
20            b'0'..=b'9' => i as u8 - b'0',
21            b'a'..=b'f' => match check_case {
22                CheckCase::Lower | CheckCase::None => i as u8 - b'a' + 10,
23                _ => NIL,
24            },
25            b'A'..=b'F' => match check_case {
26                CheckCase::Upper | CheckCase::None => i as u8 - b'A' + 10,
27                _ => NIL,
28            },
29            _ => NIL,
30        };
31        i += 1;
32    }
33    arr
34}
35
36const fn init_unhex4_array(check_case: CheckCase) -> [u8; 256] {
37    let unhex_arr = init_unhex_array(check_case);
38
39    let mut unhex4_arr = [NIL; 256];
40    let mut i = 0;
41    while i < 256 {
42        if unhex_arr[i] != NIL {
43            unhex4_arr[i] = unhex_arr[i] << 4;
44        }
45        i += 1;
46    }
47    unhex4_arr
48}
49
50// ASCII -> hex
51pub(crate) static UNHEX: [u8; 256] = init_unhex_array(CheckCase::None);
52
53// ASCII -> hex, lower case
54pub(crate) static UNHEX_LOWER: [u8; 256] = init_unhex_array(CheckCase::Lower);
55
56// ASCII -> hex, upper case
57pub(crate) static UNHEX_UPPER: [u8; 256] = init_unhex_array(CheckCase::Upper);
58
59// ASCII -> hex << 4
60pub(crate) static UNHEX4: [u8; 256] = init_unhex4_array(CheckCase::None);
61
62const _0213: i32 = 0b11011000;
63
64// lower nibble
65#[inline]
66fn unhex_b(x: usize) -> u8 {
67    UNHEX[x]
68}
69
70// upper nibble, logically equivalent to unhex_b(x) << 4
71#[inline]
72fn unhex_a(x: usize) -> u8 {
73    UNHEX4[x]
74}
75
76#[inline]
77#[target_feature(enable = "avx2")]
78#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
79unsafe fn unhex_avx2(value: __m256i) -> __m256i {
80    let sr6 = _mm256_srai_epi16(value, 6);
81    let and15 = _mm256_and_si256(value, _mm256_set1_epi16(0xf));
82    let mul = _mm256_maddubs_epi16(sr6, _mm256_set1_epi16(9));
83    _mm256_add_epi16(mul, and15)
84}
85
86// (a << 4) | b;
87#[inline]
88#[target_feature(enable = "avx2")]
89#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
90unsafe fn nib2byte_avx2(a1: __m256i, b1: __m256i, a2: __m256i, b2: __m256i) -> __m256i {
91    let a4_1 = _mm256_slli_epi16(a1, 4);
92    let a4_2 = _mm256_slli_epi16(a2, 4);
93    let a4orb_1 = _mm256_or_si256(a4_1, b1);
94    let a4orb_2 = _mm256_or_si256(a4_2, b2);
95    let pck1 = _mm256_packus_epi16(a4orb_1, a4orb_2);
96    _mm256_permute4x64_epi64(pck1, _0213)
97}
98
99/// Check if the input is valid hex bytes slice
100pub fn hex_check(src: &[u8]) -> bool {
101    hex_check_with_case(src, CheckCase::None)
102}
103
104/// Check if the input is valid hex bytes slice with case check
105pub fn hex_check_with_case(src: &[u8], check_case: CheckCase) -> bool {
106    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
107    {
108        match crate::vectorization_support() {
109            crate::Vectorization::AVX2 | crate::Vectorization::SSE41 => unsafe {
110                hex_check_sse_with_case(src, check_case)
111            },
112            crate::Vectorization::None => hex_check_fallback_with_case(src, check_case),
113        }
114    }
115
116    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
117    hex_check_fallback_with_case(src, check_case)
118}
119
120/// Check if the input is valid hex bytes slice
121pub fn hex_check_fallback(src: &[u8]) -> bool {
122    hex_check_fallback_with_case(src, CheckCase::None)
123}
124
125/// Check if the input is valid hex bytes slice with case check
126pub fn hex_check_fallback_with_case(src: &[u8], check_case: CheckCase) -> bool {
127    match check_case {
128        CheckCase::None => src.iter().all(|&x| UNHEX[x as usize] != NIL),
129        CheckCase::Lower => src.iter().all(|&x| UNHEX_LOWER[x as usize] != NIL),
130        CheckCase::Upper => src.iter().all(|&x| UNHEX_UPPER[x as usize] != NIL),
131    }
132}
133
134/// # Safety
135/// Check if a byte slice is valid.
136#[target_feature(enable = "sse4.1")]
137#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
138pub unsafe fn hex_check_sse(src: &[u8]) -> bool {
139    hex_check_sse_with_case(src, CheckCase::None)
140}
141
142#[derive(Eq, PartialEq)]
143pub enum CheckCase {
144    None,
145    Lower,
146    Upper,
147}
148
149/// # Safety
150/// Check if a byte slice is valid on given check_case.
151#[target_feature(enable = "sse4.1")]
152#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
153pub unsafe fn hex_check_sse_with_case(mut src: &[u8], check_case: CheckCase) -> bool {
154    let ascii_zero = _mm_set1_epi8((b'0' - 1) as i8);
155    let ascii_nine = _mm_set1_epi8((b'9' + 1) as i8);
156    let ascii_ua = _mm_set1_epi8((b'A' - 1) as i8);
157    let ascii_uf = _mm_set1_epi8((b'F' + 1) as i8);
158    let ascii_la = _mm_set1_epi8((b'a' - 1) as i8);
159    let ascii_lf = _mm_set1_epi8((b'f' + 1) as i8);
160
161    while src.len() >= 16 {
162        let unchecked = _mm_loadu_si128(src.as_ptr() as *const _);
163
164        let gt0 = _mm_cmpgt_epi8(unchecked, ascii_zero);
165        let lt9 = _mm_cmplt_epi8(unchecked, ascii_nine);
166        let valid_digit = _mm_and_si128(gt0, lt9);
167
168        let (valid_la_lf, valid_ua_uf) = match check_case {
169            CheckCase::None => {
170                let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
171                let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
172
173                let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
174                let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
175
176                (
177                    Some(_mm_and_si128(gtla, ltlf)),
178                    Some(_mm_and_si128(gtua, ltuf)),
179                )
180            }
181            CheckCase::Lower => {
182                let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
183                let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
184
185                (Some(_mm_and_si128(gtla, ltlf)), None)
186            }
187            CheckCase::Upper => {
188                let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
189                let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
190                (None, Some(_mm_and_si128(gtua, ltuf)))
191            }
192        };
193
194        let valid_letter = match (valid_la_lf, valid_ua_uf) {
195            (Some(valid_lower), Some(valid_upper)) => _mm_or_si128(valid_lower, valid_upper),
196            (Some(valid_lower), None) => valid_lower,
197            (None, Some(valid_upper)) => valid_upper,
198            _ => unreachable!(),
199        };
200
201        let ret = _mm_movemask_epi8(_mm_or_si128(valid_digit, valid_letter));
202
203        if ret != T_MASK {
204            return false;
205        }
206
207        src = &src[16..];
208    }
209    hex_check_fallback_with_case(src, check_case)
210}
211
212/// Hex decode src into dst.
213/// The length of src must be even, and it's allowed to decode a zero length src.
214/// The length of dst must be at least src.len() / 2.
215pub fn hex_decode(src: &[u8], dst: &mut [u8]) -> Result<(), Error> {
216    hex_decode_with_case(src, dst, CheckCase::None)
217}
218
219/// Hex decode src into dst.
220/// The length of src must be even, and it's allowed to decode a zero length src.
221/// The length of dst must be at least src.len() / 2.
222/// when check_case is CheckCase::Lower, the hex string must be lower case.
223/// when check_case is CheckCase::Upper, the hex string must be upper case.
224/// when check_case is CheckCase::None, the hex string can be lower case or upper case.
225pub fn hex_decode_with_case(
226    src: &[u8],
227    dst: &mut [u8],
228    check_case: CheckCase,
229) -> Result<(), Error> {
230    let len = dst.len().checked_mul(2).ok_or(Error::Overflow)?;
231    if src.len() < len || ((src.len() & 1) != 0) {
232        return Err(Error::InvalidLength(len));
233    }
234
235    if !hex_check_with_case(src, check_case) {
236        return Err(Error::InvalidChar);
237    }
238    hex_decode_unchecked(src, dst);
239    Ok(())
240}
241
242pub fn hex_decode_unchecked(src: &[u8], dst: &mut [u8]) {
243    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
244    {
245        match crate::vectorization_support() {
246            crate::Vectorization::AVX2 => unsafe { hex_decode_avx2(src, dst) },
247            crate::Vectorization::None | crate::Vectorization::SSE41 => {
248                hex_decode_fallback(src, dst)
249            }
250        }
251    }
252    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
253    hex_decode_fallback(src, dst);
254}
255
256#[target_feature(enable = "avx2")]
257#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
258unsafe fn hex_decode_avx2(mut src: &[u8], mut dst: &mut [u8]) {
259    // 0, -1, 2, -1, 4, -1, 6, -1, 8, -1, 10, -1, 12, -1, 14, -1,
260    // 0, -1, 2, -1, 4, -1, 6, -1, 8, -1, 10, -1, 12, -1, 14, -1
261    let mask_a = _mm256_setr_epi8(
262        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,
263        -1, 10, -1, 12, -1, 14, -1,
264    );
265
266    // 1, -1, 3, -1, 5, -1, 7, -1, 9, -1, 11, -1, 13, -1, 15, -1,
267    // 1, -1, 3, -1, 5, -1, 7, -1, 9, -1, 11, -1, 13, -1, 15, -1
268    let mask_b = _mm256_setr_epi8(
269        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,
270        -1, 11, -1, 13, -1, 15, -1,
271    );
272
273    while dst.len() >= 32 {
274        let av1 = _mm256_loadu_si256(src.as_ptr() as *const _);
275        let av2 = _mm256_loadu_si256(src[32..].as_ptr() as *const _);
276
277        let mut a1 = _mm256_shuffle_epi8(av1, mask_a);
278        let mut b1 = _mm256_shuffle_epi8(av1, mask_b);
279        let mut a2 = _mm256_shuffle_epi8(av2, mask_a);
280        let mut b2 = _mm256_shuffle_epi8(av2, mask_b);
281
282        a1 = unhex_avx2(a1);
283        a2 = unhex_avx2(a2);
284        b1 = unhex_avx2(b1);
285        b2 = unhex_avx2(b2);
286
287        let bytes = nib2byte_avx2(a1, b1, a2, b2);
288
289        //dst does not need to be aligned on any particular boundary
290        _mm256_storeu_si256(dst.as_mut_ptr() as *mut _, bytes);
291        dst = &mut dst[32..];
292        src = &src[64..];
293    }
294    hex_decode_fallback(src, dst)
295}
296
297pub fn hex_decode_fallback(src: &[u8], dst: &mut [u8]) {
298    for (slot, bytes) in dst.iter_mut().zip(src.chunks_exact(2)) {
299        let a = unhex_a(bytes[0] as usize);
300        let b = unhex_b(bytes[1] as usize);
301        *slot = a | b;
302    }
303}
304
305#[cfg(test)]
306mod tests {
307    use crate::decode::NIL;
308    use crate::{
309        decode::{
310            hex_check_fallback, hex_check_fallback_with_case, hex_decode_fallback, CheckCase,
311        },
312        encode::hex_string,
313    };
314    use proptest::proptest;
315
316    #[cfg(not(feature = "alloc"))]
317    const CAPACITY: usize = 128;
318
319    fn _test_decode_fallback(s: &String) {
320        let len = s.as_bytes().len();
321        let mut dst = Vec::with_capacity(len);
322        dst.resize(len, 0);
323
324        #[cfg(feature = "alloc")]
325        let hex_string = hex_string(s.as_bytes());
326        #[cfg(not(feature = "alloc"))]
327        let hex_string = hex_string::<CAPACITY>(s.as_bytes());
328
329        hex_decode_fallback(hex_string.as_bytes(), &mut dst);
330
331        assert_eq!(&dst[..], s.as_bytes());
332    }
333
334    #[cfg(feature = "alloc")]
335    proptest! {
336        #[test]
337        fn test_decode_fallback(ref s in ".+") {
338            _test_decode_fallback(s);
339        }
340    }
341
342    #[cfg(not(feature = "alloc"))]
343    proptest! {
344        #[test]
345        fn test_decode_fallback(ref s in ".{1,16}") {
346            _test_decode_fallback(s);
347        }
348    }
349
350    fn _test_check_fallback_true(s: &String) {
351        assert!(hex_check_fallback(s.as_bytes()));
352        match (
353            s.contains(char::is_lowercase),
354            s.contains(char::is_uppercase),
355        ) {
356            (true, true) => {
357                assert!(!hex_check_fallback_with_case(
358                    s.as_bytes(),
359                    CheckCase::Lower
360                ));
361                assert!(!hex_check_fallback_with_case(
362                    s.as_bytes(),
363                    CheckCase::Upper
364                ));
365            }
366            (true, false) => {
367                assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
368                assert!(!hex_check_fallback_with_case(
369                    s.as_bytes(),
370                    CheckCase::Upper
371                ));
372            }
373            (false, true) => {
374                assert!(!hex_check_fallback_with_case(
375                    s.as_bytes(),
376                    CheckCase::Lower
377                ));
378                assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
379            }
380            (false, false) => {
381                assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Lower));
382                assert!(hex_check_fallback_with_case(s.as_bytes(), CheckCase::Upper));
383            }
384        }
385    }
386
387    proptest! {
388    #[test]
389        fn test_check_fallback_true(ref s in "[0-9a-fA-F]+") {
390            _test_check_fallback_true(s);
391        }
392    }
393
394    fn _test_check_fallback_false(s: &String) {
395        assert!(!hex_check_fallback(s.as_bytes()));
396        assert!(!hex_check_fallback_with_case(
397            s.as_bytes(),
398            CheckCase::Upper
399        ));
400        assert!(!hex_check_fallback_with_case(
401            s.as_bytes(),
402            CheckCase::Lower
403        ));
404    }
405
406    proptest! {
407        #[test]
408        fn test_check_fallback_false(ref s in ".{16}[^0-9a-fA-F]+") {
409            _test_check_fallback_false(s);
410        }
411    }
412
413    #[test]
414    fn test_init_static_array_is_right() {
415        static OLD_UNHEX: [u8; 256] = [
416            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
417            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
418            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 1, 2, 3, 4, 5,
419            6, 7, 8, 9, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL,
420            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
421            NIL, NIL, NIL, NIL, NIL, NIL, 10, 11, 12, 13, 14, 15, NIL, NIL, NIL, NIL, NIL, NIL,
422            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
423            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
424            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
425            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
426            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
427            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
428            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
429            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
430            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
431        ];
432
433        static OLD_UNHEX4: [u8; 256] = [
434            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
435            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
436            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 0, 16, 32, 48,
437            64, 80, 96, 112, 128, 144, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224,
438            240, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
439            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, 160, 176, 192, 208, 224, 240, NIL,
440            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
441            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
442            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
443            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
444            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
445            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
446            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
447            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
448            NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
449        ];
450
451        assert_eq!(OLD_UNHEX, crate::decode::UNHEX);
452        assert_eq!(OLD_UNHEX4, crate::decode::UNHEX4);
453    }
454}
455
456#[cfg(all(test, any(target_arch = "x86", target_arch = "x86_64")))]
457mod test_sse {
458    use crate::decode::{
459        hex_check, hex_check_fallback, hex_check_fallback_with_case, hex_check_sse,
460        hex_check_sse_with_case, hex_check_with_case, hex_decode, hex_decode_unchecked,
461        hex_decode_with_case, CheckCase,
462    };
463    use proptest::proptest;
464
465    fn _test_check_sse_with_case(s: &String, check_case: CheckCase, expect_result: bool) {
466        if is_x86_feature_detected!("sse4.1") {
467            assert_eq!(
468                unsafe { hex_check_sse_with_case(s.as_bytes(), check_case) },
469                expect_result
470            )
471        }
472    }
473
474    fn _test_check_sse_true(s: &String) {
475        if is_x86_feature_detected!("sse4.1") {
476            assert!(unsafe { hex_check_sse(s.as_bytes()) });
477        }
478    }
479
480    proptest! {
481    #[test]
482    fn test_check_sse_true(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
483            _test_check_sse_true(s);
484            _test_check_sse_with_case(s, CheckCase::None, true);
485            match (s.contains(char::is_lowercase), s.contains(char::is_uppercase)){
486                (true, true) => {
487                    _test_check_sse_with_case(s, CheckCase::Lower, false);
488                    _test_check_sse_with_case(s, CheckCase::Upper, false);
489                },
490                (true, false) => {
491                    _test_check_sse_with_case(s, CheckCase::Lower, true);
492                    _test_check_sse_with_case(s, CheckCase::Upper, false);
493                },
494                (false, true) => {
495                    _test_check_sse_with_case(s, CheckCase::Lower, false);
496                    _test_check_sse_with_case(s, CheckCase::Upper, true);
497                },
498                (false, false) => {
499                    _test_check_sse_with_case(s, CheckCase::Lower, true);
500                    _test_check_sse_with_case(s, CheckCase::Upper, true);
501                }
502            }
503        }
504    }
505
506    fn _test_check_sse_false(s: &String) {
507        if is_x86_feature_detected!("sse4.1") {
508            assert!(!unsafe { hex_check_sse(s.as_bytes()) });
509        }
510    }
511
512    proptest! {
513        #[test]
514        fn test_check_sse_false(ref s in ".{16}[^0-9a-fA-F]+") {
515            _test_check_sse_false(s);
516            _test_check_sse_with_case(s, CheckCase::None, false);
517            _test_check_sse_with_case(s, CheckCase::Lower, false);
518            _test_check_sse_with_case(s, CheckCase::Upper, false);
519        }
520    }
521
522    #[test]
523    fn test_decode_zero_length_src_should_not_be_ok() {
524        let src = b"";
525        let mut dst = [0u8; 10];
526        assert!(
527            matches!(hex_decode(src, &mut dst), Err(crate::Error::InvalidLength(len)) if len == 20)
528        );
529        assert!(
530            matches!(hex_decode_with_case(src, &mut dst, CheckCase::None), Err(crate::Error::InvalidLength(len)) if len == 20)
531        );
532        assert!(hex_check(src));
533        assert!(hex_check_with_case(src, CheckCase::None));
534        assert!(hex_check_fallback(src));
535        assert!(hex_check_fallback_with_case(src, CheckCase::None));
536
537        if is_x86_feature_detected!("sse4.1") {
538            assert!(unsafe { hex_check_sse_with_case(src, CheckCase::None) });
539            assert!(unsafe { hex_check_sse(src) });
540        }
541
542        // this function have no return value, so we just execute it and expect no panic
543        hex_decode_unchecked(src, &mut dst);
544    }
545
546    // If `dst's length` is greater than `src's length * 2`, `hex_decode` should return error
547    #[test]
548    fn test_if_dst_len_gt_expect_len_should_return_error() {
549        let short_str = b"8e40af02265360d59f4ecf9ae9ebf8f00a3118408f5a9cdcbcc9c0f93642f3"; // 62 bytes
550        {
551            let mut dst = [0u8; 31];
552            let result = hex_decode(short_str.as_slice(), &mut dst);
553            assert!(result.is_ok());
554        }
555
556        {
557            let mut dst = [0u8; 32];
558            let result = hex_decode(short_str.as_slice(), &mut dst);
559            assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 64))
560        }
561
562        {
563            let mut dst = [0u8; 33];
564            let result = hex_decode(short_str.as_slice(), &mut dst);
565            assert!(matches!(result, Err(crate::Error::InvalidLength(len)) if len == 66))
566        }
567    }
568
569    // if both `src` and `dst` are empty, it's ok
570    // if `src` is empty, but `dst` is not empty, it should be reported as error
571    #[test]
572    fn test_decode_zero_src() {
573        let zero_src = b"";
574        {
575            let mut zero_dst = [];
576            assert!(hex_decode(zero_src, &mut zero_dst).is_ok());
577        }
578
579        {
580            let mut non_zero_dst = [0u8; 1];
581            assert!(
582                matches!(hex_decode(zero_src, &mut non_zero_dst), Err(crate::Error::InvalidLength(len)) if len == 2)
583            );
584        }
585    }
586}