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