1#[cfg(target_arch = "x86")]
2use core::arch::x86::*;
3#[cfg(target_arch = "x86_64")]
4use core::arch::x86_64::*;
5
6#[cfg(feature = "alloc")]
7use alloc::{string::String, vec};
8
9#[cfg(not(feature = "alloc"))]
10use heapless::{String, Vec};
11
12use crate::error::Error;
13
14static TABLE_LOWER: &[u8] = b"0123456789abcdef";
15static TABLE_UPPER: &[u8] = b"0123456789ABCDEF";
16
17#[cfg(feature = "alloc")]
18fn hex_string_custom_case(src: &[u8], upper_case: bool) -> String {
19 let mut buffer = vec![0; src.len() * 2];
20 if upper_case {
21 hex_encode_upper(src, &mut buffer).expect("hex_string");
22 } else {
23 hex_encode(src, &mut buffer).expect("hex_string");
24 }
25
26 if cfg!(debug_assertions) {
27 String::from_utf8(buffer).unwrap()
28 } else {
29 unsafe { String::from_utf8_unchecked(buffer) }
31 }
32}
33
34#[cfg(not(feature = "alloc"))]
35fn hex_string_custom_case<const N: usize>(src: &[u8], upper_case: bool) -> String<N> {
36 let mut buffer = Vec::<_, N>::new();
37 buffer
38 .resize(src.len() * 2, 0)
39 .expect("String<N> capacity too short");
40 if upper_case {
41 hex_encode_upper(src, &mut buffer).expect("hex_string");
42 } else {
43 hex_encode(src, &mut buffer).expect("hex_string");
44 }
45
46 if cfg!(debug_assertions) {
47 String::from_utf8(buffer).unwrap()
48 } else {
49 unsafe { String::from_utf8_unchecked(buffer) }
51 }
52}
53
54#[cfg(feature = "alloc")]
55pub fn hex_string(src: &[u8]) -> String {
56 hex_string_custom_case(src, false)
57}
58
59#[cfg(not(feature = "alloc"))]
60pub fn hex_string<const N: usize>(src: &[u8]) -> String<N> {
61 hex_string_custom_case(src, false)
62}
63
64#[cfg(feature = "alloc")]
65pub fn hex_string_upper(src: &[u8]) -> String {
66 hex_string_custom_case(src, true)
67}
68
69#[cfg(not(feature = "alloc"))]
70pub fn hex_string_upper<const N: usize>(src: &[u8]) -> String<N> {
71 hex_string_custom_case(src, true)
72}
73
74pub fn hex_encode_custom<'a>(
75 src: &[u8],
76 dst: &'a mut [u8],
77 upper_case: bool,
78) -> Result<&'a mut str, Error> {
79 unsafe fn mut_str(buffer: &mut [u8]) -> &mut str {
80 if cfg!(debug_assertions) {
81 core::str::from_utf8_mut(buffer).unwrap()
82 } else {
83 core::str::from_utf8_unchecked_mut(buffer)
84 }
85 }
86
87 let expect_dst_len = src
88 .len()
89 .checked_mul(2)
90 .ok_or(Error::InvalidLength(src.len()))?;
91 if dst.len() < expect_dst_len {
92 return Err(Error::InvalidLength(expect_dst_len));
93 }
94
95 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
96 {
97 match crate::vectorization_support() {
98 crate::Vectorization::AVX2 => unsafe { hex_encode_avx2(src, dst, upper_case) },
99 crate::Vectorization::SSE41 => unsafe { hex_encode_sse41(src, dst, upper_case) },
100 crate::Vectorization::None => hex_encode_custom_case_fallback(src, dst, upper_case),
101 }
102 return Ok(unsafe { mut_str(dst) });
104 }
105 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
106 {
107 hex_encode_custom_case_fallback(src, dst, upper_case);
108 Ok(unsafe { mut_str(dst) })
110 }
111}
112
113pub fn hex_encode<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a mut str, Error> {
116 hex_encode_custom(src, dst, false)
117}
118
119pub fn hex_encode_upper<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a mut str, Error> {
120 hex_encode_custom(src, dst, true)
121}
122
123#[deprecated(since = "0.3.0", note = "please use `hex_encode` instead")]
124pub fn hex_to(src: &[u8], dst: &mut [u8]) -> Result<(), Error> {
125 hex_encode(src, dst).map(|_| ())
126}
127
128#[target_feature(enable = "avx2")]
129#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
130unsafe fn hex_encode_avx2(mut src: &[u8], dst: &mut [u8], upper_case: bool) {
131 let ascii_zero = _mm256_set1_epi8(b'0' as i8);
132 let nines = _mm256_set1_epi8(9);
133 let ascii_a = if upper_case {
134 _mm256_set1_epi8((b'A' - 9 - 1) as i8)
135 } else {
136 _mm256_set1_epi8((b'a' - 9 - 1) as i8)
137 };
138 let and4bits = _mm256_set1_epi8(0xf);
139
140 let mut i = 0_isize;
141 while src.len() >= 32 {
142 let invec = _mm256_loadu_si256(src.as_ptr() as *const _);
144
145 let masked1 = _mm256_and_si256(invec, and4bits);
146 let masked2 = _mm256_and_si256(_mm256_srli_epi64(invec, 4), and4bits);
147
148 let cmpmask1 = _mm256_cmpgt_epi8(masked1, nines);
150 let cmpmask2 = _mm256_cmpgt_epi8(masked2, nines);
151
152 let masked1 = _mm256_add_epi8(masked1, _mm256_blendv_epi8(ascii_zero, ascii_a, cmpmask1));
154 let masked2 = _mm256_add_epi8(masked2, _mm256_blendv_epi8(ascii_zero, ascii_a, cmpmask2));
155
156 let res1 = _mm256_unpacklo_epi8(masked2, masked1);
158 let res2 = _mm256_unpackhi_epi8(masked2, masked1);
159
160 let base = dst.as_mut_ptr().offset(i * 2);
162 let base1 = base.offset(0) as *mut _;
163 let base2 = base.offset(16) as *mut _;
164 let base3 = base.offset(32) as *mut _;
165 let base4 = base.offset(48) as *mut _;
166 _mm256_storeu2_m128i(base3, base1, res1);
167 _mm256_storeu2_m128i(base4, base2, res2);
168 src = &src[32..];
169 i += 32;
170 }
171
172 let i = i as usize;
173 hex_encode_sse41(src, &mut dst[i * 2..], upper_case);
174}
175
176#[target_feature(enable = "sse4.1")]
178#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
179unsafe fn hex_encode_sse41(mut src: &[u8], dst: &mut [u8], upper_case: bool) {
180 let ascii_zero = _mm_set1_epi8(b'0' as i8);
181 let nines = _mm_set1_epi8(9);
182 let ascii_a = if upper_case {
183 _mm_set1_epi8((b'A' - 9 - 1) as i8)
184 } else {
185 _mm_set1_epi8((b'a' - 9 - 1) as i8)
186 };
187 let and4bits = _mm_set1_epi8(0xf);
188
189 let mut i = 0_isize;
190 while src.len() >= 16 {
191 let invec = _mm_loadu_si128(src.as_ptr() as *const _);
192
193 let masked1 = _mm_and_si128(invec, and4bits);
194 let masked2 = _mm_and_si128(_mm_srli_epi64(invec, 4), and4bits);
195
196 let cmpmask1 = _mm_cmpgt_epi8(masked1, nines);
198 let cmpmask2 = _mm_cmpgt_epi8(masked2, nines);
199
200 let masked1 = _mm_add_epi8(masked1, _mm_blendv_epi8(ascii_zero, ascii_a, cmpmask1));
202 let masked2 = _mm_add_epi8(masked2, _mm_blendv_epi8(ascii_zero, ascii_a, cmpmask2));
203
204 let res1 = _mm_unpacklo_epi8(masked2, masked1);
206 let res2 = _mm_unpackhi_epi8(masked2, masked1);
207
208 _mm_storeu_si128(dst.as_mut_ptr().offset(i * 2) as *mut _, res1);
209 _mm_storeu_si128(dst.as_mut_ptr().offset(i * 2 + 16) as *mut _, res2);
210 src = &src[16..];
211 i += 16;
212 }
213
214 let i = i as usize;
215 hex_encode_custom_case_fallback(src, &mut dst[i * 2..], upper_case);
216}
217
218#[inline]
219fn hex_lower(byte: u8) -> u8 {
220 TABLE_LOWER[byte as usize]
221}
222
223#[inline]
224fn hex_upper(byte: u8) -> u8 {
225 TABLE_UPPER[byte as usize]
226}
227
228fn hex_encode_custom_case_fallback(src: &[u8], dst: &mut [u8], upper_case: bool) {
229 if upper_case {
230 for (byte, slots) in src.iter().zip(dst.chunks_exact_mut(2)) {
231 slots[0] = hex_upper((*byte >> 4) & 0xf);
232 slots[1] = hex_upper(*byte & 0xf);
233 }
234 } else {
235 for (byte, slots) in src.iter().zip(dst.chunks_exact_mut(2)) {
236 slots[0] = hex_lower((*byte >> 4) & 0xf);
237 slots[1] = hex_lower(*byte & 0xf);
238 }
239 }
240}
241
242pub fn hex_encode_fallback(src: &[u8], dst: &mut [u8]) {
243 hex_encode_custom_case_fallback(src, dst, false)
244}
245
246pub fn hex_encode_upper_fallback(src: &[u8], dst: &mut [u8]) {
247 hex_encode_custom_case_fallback(src, dst, true)
248}
249
250#[cfg(test)]
251mod tests {
252 use crate::encode::{hex_encode, hex_encode_custom_case_fallback};
253
254 use crate::hex_encode_fallback;
255 use core::str;
256 use proptest::proptest;
257
258 fn _test_encode_fallback(s: &String, upper_case: bool) {
259 let mut buffer = vec![0; s.as_bytes().len() * 2];
260 hex_encode_custom_case_fallback(s.as_bytes(), &mut buffer, upper_case);
261
262 let encode = unsafe { str::from_utf8_unchecked(&buffer[..s.as_bytes().len() * 2]) };
263 if upper_case {
264 assert_eq!(encode, hex::encode_upper(s));
265 } else {
266 assert_eq!(encode, hex::encode(s));
267 }
268 }
269
270 proptest! {
271 #[test]
272 fn test_encode_fallback(ref s in ".*") {
273 _test_encode_fallback(s, true);
274 _test_encode_fallback(s, false);
275 }
276 }
277
278 #[test]
279 fn test_encode_zero_length_src_should_be_ok() {
280 let src = b"";
281 let mut dst = [0u8; 10];
282 assert!(hex_encode(src, &mut dst).is_ok());
283
284 hex_encode_fallback(src, &mut dst);
286 }
287}