/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 | | } |