Coverage Report

Created: 2026-02-14 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/faster-hex-0.10.0/src/decode.rs
Line
Count
Source
1
// avx2 decode modified from https://github.com/zbjornson/fast-hex/blob/master/src/hex.cc
2
3
#[cfg(target_arch = "x86")]
4
use core::arch::x86::*;
5
#[cfg(target_arch = "x86_64")]
6
use core::arch::x86_64::*;
7
8
use crate::error::Error;
9
10
const NIL: u8 = u8::MAX;
11
12
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
13
const T_MASK: i32 = 65535;
14
15
0
const fn init_unhex_array(check_case: CheckCase) -> [u8; 256] {
16
0
    let mut arr = [0; 256];
17
0
    let mut i = 0;
18
0
    while i < 256 {
19
0
        arr[i] = match i as u8 {
20
0
            b'0'..=b'9' => i as u8 - b'0',
21
0
            b'a'..=b'f' => match check_case {
22
0
                CheckCase::Lower | CheckCase::None => i as u8 - b'a' + 10,
23
0
                _ => NIL,
24
            },
25
0
            b'A'..=b'F' => match check_case {
26
0
                CheckCase::Upper | CheckCase::None => i as u8 - b'A' + 10,
27
0
                _ => NIL,
28
            },
29
0
            _ => NIL,
30
        };
31
0
        i += 1;
32
    }
33
0
    arr
34
0
}
35
36
0
const fn init_unhex4_array(check_case: CheckCase) -> [u8; 256] {
37
0
    let unhex_arr = init_unhex_array(check_case);
38
39
0
    let mut unhex4_arr = [NIL; 256];
40
0
    let mut i = 0;
41
0
    while i < 256 {
42
0
        if unhex_arr[i] != NIL {
43
0
            unhex4_arr[i] = unhex_arr[i] << 4;
44
0
        }
45
0
        i += 1;
46
    }
47
0
    unhex4_arr
48
0
}
49
50
// ASCII -> hex
51
pub(crate) static UNHEX: [u8; 256] = init_unhex_array(CheckCase::None);
52
53
// ASCII -> hex, lower case
54
pub(crate) static UNHEX_LOWER: [u8; 256] = init_unhex_array(CheckCase::Lower);
55
56
// ASCII -> hex, upper case
57
pub(crate) static UNHEX_UPPER: [u8; 256] = init_unhex_array(CheckCase::Upper);
58
59
// ASCII -> hex << 4
60
pub(crate) static UNHEX4: [u8; 256] = init_unhex4_array(CheckCase::None);
61
62
const _0213: i32 = 0b11011000;
63
64
// lower nibble
65
#[inline]
66
1.16M
fn unhex_b(x: usize) -> u8 {
67
1.16M
    UNHEX[x]
68
1.16M
}
69
70
// upper nibble, logically equivalent to unhex_b(x) << 4
71
#[inline]
72
1.16M
fn unhex_a(x: usize) -> u8 {
73
1.16M
    UNHEX4[x]
74
1.16M
}
75
76
#[inline]
77
#[target_feature(enable = "avx2")]
78
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
79
0
unsafe fn unhex_avx2(value: __m256i) -> __m256i {
80
0
    let sr6 = _mm256_srai_epi16(value, 6);
81
0
    let and15 = _mm256_and_si256(value, _mm256_set1_epi16(0xf));
82
0
    let mul = _mm256_maddubs_epi16(sr6, _mm256_set1_epi16(9));
83
0
    _mm256_add_epi16(mul, and15)
84
0
}
85
86
// (a << 4) | b;
87
#[inline]
88
#[target_feature(enable = "avx2")]
89
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
90
0
unsafe fn nib2byte_avx2(a1: __m256i, b1: __m256i, a2: __m256i, b2: __m256i) -> __m256i {
91
0
    let a4_1 = _mm256_slli_epi16(a1, 4);
92
0
    let a4_2 = _mm256_slli_epi16(a2, 4);
93
0
    let a4orb_1 = _mm256_or_si256(a4_1, b1);
94
0
    let a4orb_2 = _mm256_or_si256(a4_2, b2);
95
0
    let pck1 = _mm256_packus_epi16(a4orb_1, a4orb_2);
96
0
    _mm256_permute4x64_epi64(pck1, _0213)
97
0
}
98
99
/// Check if the input is valid hex bytes slice
100
0
pub fn hex_check(src: &[u8]) -> bool {
101
0
    hex_check_with_case(src, CheckCase::None)
102
0
}
103
104
/// Check if the input is valid hex bytes slice with case check
105
58.9k
pub fn hex_check_with_case(src: &[u8], check_case: CheckCase) -> bool {
106
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
107
    {
108
58.9k
        match crate::vectorization_support() {
109
            crate::Vectorization::AVX2 | crate::Vectorization::SSE41 => unsafe {
110
58.9k
                hex_check_sse_with_case(src, check_case)
111
            },
112
0
            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
58.9k
}
119
120
/// Check if the input is valid hex bytes slice
121
0
pub fn hex_check_fallback(src: &[u8]) -> bool {
122
0
    hex_check_fallback_with_case(src, CheckCase::None)
123
0
}
124
125
/// Check if the input is valid hex bytes slice with case check
126
58.9k
pub fn hex_check_fallback_with_case(src: &[u8], check_case: CheckCase) -> bool {
127
58.9k
    match check_case {
128
469k
        CheckCase::None => src.iter().all(|&x| UNHEX[x as usize] != NIL),
129
0
        CheckCase::Lower => src.iter().all(|&x| UNHEX_LOWER[x as usize] != NIL),
130
0
        CheckCase::Upper => src.iter().all(|&x| UNHEX_UPPER[x as usize] != NIL),
131
    }
132
58.9k
}
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"))]
138
0
pub unsafe fn hex_check_sse(src: &[u8]) -> bool {
139
0
    hex_check_sse_with_case(src, CheckCase::None)
140
0
}
141
142
#[derive(Eq, PartialEq)]
143
pub 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"))]
153
58.9k
pub unsafe fn hex_check_sse_with_case(mut src: &[u8], check_case: CheckCase) -> bool {
154
58.9k
    let ascii_zero = _mm_set1_epi8((b'0' - 1) as i8);
155
58.9k
    let ascii_nine = _mm_set1_epi8((b'9' + 1) as i8);
156
58.9k
    let ascii_ua = _mm_set1_epi8((b'A' - 1) as i8);
157
58.9k
    let ascii_uf = _mm_set1_epi8((b'F' + 1) as i8);
158
58.9k
    let ascii_la = _mm_set1_epi8((b'a' - 1) as i8);
159
58.9k
    let ascii_lf = _mm_set1_epi8((b'f' + 1) as i8);
160
161
175k
    while src.len() >= 16 {
162
116k
        let unchecked = _mm_loadu_si128(src.as_ptr() as *const _);
163
164
116k
        let gt0 = _mm_cmpgt_epi8(unchecked, ascii_zero);
165
116k
        let lt9 = _mm_cmplt_epi8(unchecked, ascii_nine);
166
116k
        let valid_digit = _mm_and_si128(gt0, lt9);
167
168
116k
        let (valid_la_lf, valid_ua_uf) = match check_case {
169
            CheckCase::None => {
170
116k
                let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
171
116k
                let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
172
173
116k
                let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
174
116k
                let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
175
176
116k
                (
177
116k
                    Some(_mm_and_si128(gtla, ltlf)),
178
116k
                    Some(_mm_and_si128(gtua, ltuf)),
179
116k
                )
180
            }
181
            CheckCase::Lower => {
182
0
                let gtla = _mm_cmpgt_epi8(unchecked, ascii_la);
183
0
                let ltlf = _mm_cmplt_epi8(unchecked, ascii_lf);
184
185
0
                (Some(_mm_and_si128(gtla, ltlf)), None)
186
            }
187
            CheckCase::Upper => {
188
0
                let gtua = _mm_cmpgt_epi8(unchecked, ascii_ua);
189
0
                let ltuf = _mm_cmplt_epi8(unchecked, ascii_uf);
190
0
                (None, Some(_mm_and_si128(gtua, ltuf)))
191
            }
192
        };
193
194
116k
        let valid_letter = match (valid_la_lf, valid_ua_uf) {
195
116k
            (Some(valid_lower), Some(valid_upper)) => _mm_or_si128(valid_lower, valid_upper),
196
0
            (Some(valid_lower), None) => valid_lower,
197
0
            (None, Some(valid_upper)) => valid_upper,
198
0
            _ => unreachable!(),
199
        };
200
201
116k
        let ret = _mm_movemask_epi8(_mm_or_si128(valid_digit, valid_letter));
202
203
116k
        if ret != T_MASK {
204
54
            return false;
205
116k
        }
206
207
116k
        src = &src[16..];
208
    }
209
58.9k
    hex_check_fallback_with_case(src, check_case)
210
58.9k
}
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.
215
58.9k
pub fn hex_decode(src: &[u8], dst: &mut [u8]) -> Result<(), Error> {
216
58.9k
    hex_decode_with_case(src, dst, CheckCase::None)
217
58.9k
}
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.
225
58.9k
pub fn hex_decode_with_case(
226
58.9k
    src: &[u8],
227
58.9k
    dst: &mut [u8],
228
58.9k
    check_case: CheckCase,
229
58.9k
) -> Result<(), Error> {
230
58.9k
    let len = dst.len().checked_mul(2).ok_or(Error::Overflow)?;
231
58.9k
    if src.len() < len || ((src.len() & 1) != 0) {
232
0
        return Err(Error::InvalidLength(len));
233
58.9k
    }
234
235
58.9k
    if !hex_check_with_case(src, check_case) {
236
112
        return Err(Error::InvalidChar);
237
58.8k
    }
238
58.8k
    hex_decode_unchecked(src, dst);
239
58.8k
    Ok(())
240
58.9k
}
241
242
58.8k
pub fn hex_decode_unchecked(src: &[u8], dst: &mut [u8]) {
243
    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
244
    {
245
58.8k
        match crate::vectorization_support() {
246
58.8k
            crate::Vectorization::AVX2 => unsafe { hex_decode_avx2(src, dst) },
247
            crate::Vectorization::None | crate::Vectorization::SSE41 => {
248
0
                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
58.8k
}
255
256
#[target_feature(enable = "avx2")]
257
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
258
58.8k
unsafe 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
58.8k
    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
58.8k
    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
58.8k
    while dst.len() >= 32 {
274
0
        let av1 = _mm256_loadu_si256(src.as_ptr() as *const _);
275
0
        let av2 = _mm256_loadu_si256(src[32..].as_ptr() as *const _);
276
0
277
0
        let mut a1 = _mm256_shuffle_epi8(av1, mask_a);
278
0
        let mut b1 = _mm256_shuffle_epi8(av1, mask_b);
279
0
        let mut a2 = _mm256_shuffle_epi8(av2, mask_a);
280
0
        let mut b2 = _mm256_shuffle_epi8(av2, mask_b);
281
0
282
0
        a1 = unhex_avx2(a1);
283
0
        a2 = unhex_avx2(a2);
284
0
        b1 = unhex_avx2(b1);
285
0
        b2 = unhex_avx2(b2);
286
0
287
0
        let bytes = nib2byte_avx2(a1, b1, a2, b2);
288
0
289
0
        //dst does not need to be aligned on any particular boundary
290
0
        _mm256_storeu_si256(dst.as_mut_ptr() as *mut _, bytes);
291
0
        dst = &mut dst[32..];
292
0
        src = &src[64..];
293
0
    }
294
58.8k
    hex_decode_fallback(src, dst)
295
58.8k
}
296
297
58.8k
pub fn hex_decode_fallback(src: &[u8], dst: &mut [u8]) {
298
1.16M
    for (slot, bytes) in dst.iter_mut().zip(src.chunks_exact(2)) {
299
1.16M
        let a = unhex_a(bytes[0] as usize);
300
1.16M
        let b = unhex_b(bytes[1] as usize);
301
1.16M
        *slot = a | b;
302
1.16M
    }
303
58.8k
}
304
305
#[cfg(test)]
306
mod 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")))]
457
mod 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
}