1#![cfg_attr(not(any(test, feature = "std")), no_std)]
2
3#[cfg(feature = "alloc")]
4extern crate alloc;
5
6mod decode;
7mod encode;
8mod error;
9
10#[cfg(feature = "serde")]
11mod serde;
12
13pub use crate::decode::{
14 hex_check, hex_check_fallback, hex_check_with_case, hex_decode, hex_decode_fallback,
15 hex_decode_unchecked,
16};
17pub use crate::encode::{
18 hex_encode, hex_encode_fallback, hex_encode_upper, hex_encode_upper_fallback, hex_string,
19 hex_string_upper,
20};
21
22pub use crate::error::Error;
23
24#[cfg(feature = "serde")]
25pub use crate::serde::{
26 deserialize, nopfx_ignorecase, nopfx_lowercase, nopfx_uppercase, serialize, withpfx_ignorecase,
27 withpfx_lowercase, withpfx_uppercase,
28};
29
30#[allow(deprecated)]
31pub use crate::encode::hex_to;
32
33#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
34pub use crate::decode::{hex_check_sse, hex_check_sse_with_case};
35
36#[derive(Copy, Clone, PartialEq, Eq, Debug)]
37pub(crate) enum Vectorization {
38 None = 0,
39 SSE41 = 1,
40 AVX2 = 2,
41}
42
43#[inline(always)]
44pub(crate) fn vectorization_support() -> Vectorization {
45 #[cfg(all(
46 any(target_arch = "x86", target_arch = "x86_64"),
47 target_feature = "sse"
48 ))]
49 {
50 use core::sync::atomic::{AtomicU8, Ordering};
51 static FLAGS: AtomicU8 = AtomicU8::new(u8::MAX);
52
53 let current_flags = FLAGS.load(Ordering::Relaxed);
55 if current_flags != u8::MAX {
57 return match current_flags {
58 0 => Vectorization::None,
59 1 => Vectorization::SSE41,
60 2 => Vectorization::AVX2,
61 _ => unreachable!(),
62 };
63 }
64
65 let val = vectorization_support_no_cache_x86();
66
67 FLAGS.store(val as u8, Ordering::Relaxed);
68 return val;
69 }
70 #[allow(unreachable_code)]
71 Vectorization::None
72}
73
74#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
75#[cold]
76fn vectorization_support_no_cache_x86() -> Vectorization {
77 #[cfg(target_arch = "x86")]
78 use core::arch::x86::__cpuid_count;
79 #[cfg(target_arch = "x86_64")]
80 use core::arch::x86_64::__cpuid_count;
81
82 if cfg!(target_env = "sgx") || !cfg!(target_feature = "sse") {
85 return Vectorization::None;
86 }
87
88 let proc_info_ecx = unsafe { __cpuid_count(1, 0) }.ecx;
89 let have_sse4 = (proc_info_ecx >> 19) & 1 == 1;
90 if !have_sse4 {
92 return Vectorization::None;
93 }
94
95 let have_xsave = (proc_info_ecx >> 26) & 1 == 1;
96 let have_osxsave = (proc_info_ecx >> 27) & 1 == 1;
97 let have_avx = (proc_info_ecx >> 27) & 1 == 1;
98 if have_xsave && have_osxsave && have_avx {
99 if unsafe { avx2_support_no_cache_x86() } {
101 return Vectorization::AVX2;
102 }
103 }
104 Vectorization::SSE41
105}
106
107#[target_feature(enable = "xsave")]
110#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
111#[cold]
112unsafe fn avx2_support_no_cache_x86() -> bool {
113 #[cfg(target_arch = "x86")]
114 use core::arch::x86::{__cpuid_count, _xgetbv};
115 #[cfg(target_arch = "x86_64")]
116 use core::arch::x86_64::{__cpuid_count, _xgetbv};
117
118 let xcr0 = _xgetbv(0);
119 let os_avx_support = xcr0 & 6 == 6;
120 if os_avx_support {
121 let extended_features_ebx = __cpuid_count(7, 0).ebx;
122 let have_avx2 = (extended_features_ebx >> 5) & 1 == 1;
123 if have_avx2 {
124 return true;
125 }
126 }
127 false
128}
129
130#[cfg(test)]
131mod tests {
132 use crate::decode::{hex_decode, hex_decode_with_case, CheckCase};
133 use crate::encode::{hex_encode, hex_string};
134 use crate::{hex_encode_upper, hex_string_upper, vectorization_support, Vectorization};
135 use proptest::proptest;
136
137 #[cfg(not(feature = "alloc"))]
138 const CAPACITY: usize = 128;
139
140 #[test]
141 fn test_feature_detection() {
142 let vector_support = vectorization_support();
143 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
144 {
145 match vector_support {
146 Vectorization::AVX2 => assert!(is_x86_feature_detected!("avx2")),
147 Vectorization::SSE41 => assert!(is_x86_feature_detected!("sse4.1")),
148 Vectorization::None => assert!(
149 !cfg!(target_feature = "sse")
150 || !is_x86_feature_detected!("avx2") && !is_x86_feature_detected!("sse4.1")
151 ),
152 }
153 }
154 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
155 assert_eq!(vector_support, Vectorization::None);
156 }
157
158 fn _test_hex_encode(s: &String) {
159 let mut buffer = vec![0; s.as_bytes().len() * 2];
160 {
161 let encode = &*hex_encode(s.as_bytes(), &mut buffer).unwrap();
162
163 #[cfg(feature = "alloc")]
164 let hex_string = hex_string(s.as_bytes());
165 #[cfg(not(feature = "alloc"))]
166 let hex_string = hex_string::<CAPACITY>(s.as_bytes());
167
168 assert_eq!(encode, hex::encode(s));
169 assert_eq!(hex_string.as_str(), hex::encode(s));
170 }
171
172 {
173 let encode_upper = &*hex_encode_upper(s.as_bytes(), &mut buffer).unwrap();
174
175 #[cfg(feature = "alloc")]
176 let hex_string_upper = hex_string_upper(s.as_bytes());
177 #[cfg(not(feature = "alloc"))]
178 let hex_string_upper = hex_string_upper::<CAPACITY>(s.as_bytes());
179
180 assert_eq!(encode_upper, hex::encode_upper(s));
181 assert_eq!(hex_string_upper.as_str(), hex::encode_upper(s));
182 }
183 }
184
185 #[cfg(feature = "alloc")]
186 proptest! {
187 #[test]
188 fn test_hex_encode(ref s in ".*") {
189 _test_hex_encode(s);
190 }
191 }
192
193 #[cfg(not(feature = "alloc"))]
194 proptest! {
195 #[test]
196 fn test_hex_encode(ref s in ".{0,16}") {
197 _test_hex_encode(s);
198 }
199 }
200
201 fn _test_hex_decode(s: &String) {
202 let len = s.as_bytes().len();
203 {
204 let mut dst = Vec::with_capacity(len);
205 dst.resize(len, 0);
206 #[cfg(feature = "alloc")]
207 let hex_string = hex_string(s.as_bytes());
208 #[cfg(not(feature = "alloc"))]
209 let hex_string = hex_string::<CAPACITY>(s.as_bytes());
210
211 hex_decode(hex_string.as_bytes(), &mut dst).unwrap();
212
213 hex_decode_with_case(hex_string.as_bytes(), &mut dst, CheckCase::Lower).unwrap();
214
215 assert_eq!(&dst[..], s.as_bytes());
216 }
217 {
218 let mut dst = Vec::with_capacity(len);
219 dst.resize(len, 0);
220 #[cfg(feature = "alloc")]
221 let hex_string_upper = hex_string_upper(s.as_bytes());
222 #[cfg(not(feature = "alloc"))]
223 let hex_string_upper = hex_string_upper::<CAPACITY>(s.as_bytes());
224
225 hex_decode_with_case(hex_string_upper.as_bytes(), &mut dst, CheckCase::Upper).unwrap();
226
227 assert_eq!(&dst[..], s.as_bytes());
228 }
229 }
230
231 #[cfg(feature = "alloc")]
232 proptest! {
233 #[test]
234 fn test_hex_decode(ref s in ".+") {
235 _test_hex_decode(s);
236 }
237 }
238
239 #[cfg(not(feature = "alloc"))]
240 proptest! {
241 #[test]
242 fn test_hex_decode(ref s in ".{1,16}") {
243 _test_hex_decode(s);
244 }
245 }
246
247 fn _test_hex_decode_check(s: &String, ok: bool) {
248 let len = s.as_bytes().len();
249 let mut dst = Vec::with_capacity(len / 2);
250 dst.resize(len / 2, 0);
251 assert!(hex_decode(s.as_bytes(), &mut dst).is_ok() == ok);
252 }
253
254 proptest! {
255 #[test]
256 fn test_hex_decode_check(ref s in "([0-9a-fA-F][0-9a-fA-F])+") {
257 _test_hex_decode_check(s, true);
258 }
259 }
260
261 proptest! {
262 #[test]
263 fn test_hex_decode_check_odd(ref s in "[0-9a-fA-F]{11}") {
264 _test_hex_decode_check(s, false);
265 }
266 }
267}