/rust/registry/src/index.crates.io-1949cf8c6b5b557f/faster-hex-0.10.0/src/encode.rs
Line | Count | Source |
1 | | #[cfg(target_arch = "x86")] |
2 | | use core::arch::x86::*; |
3 | | #[cfg(target_arch = "x86_64")] |
4 | | use core::arch::x86_64::*; |
5 | | |
6 | | #[cfg(feature = "alloc")] |
7 | | use alloc::{string::String, vec}; |
8 | | |
9 | | #[cfg(not(feature = "alloc"))] |
10 | | use heapless::{String, Vec}; |
11 | | |
12 | | use crate::error::Error; |
13 | | |
14 | | static TABLE_LOWER: &[u8] = b"0123456789abcdef"; |
15 | | static TABLE_UPPER: &[u8] = b"0123456789ABCDEF"; |
16 | | |
17 | | #[cfg(feature = "alloc")] |
18 | 0 | fn hex_string_custom_case(src: &[u8], upper_case: bool) -> String { |
19 | 0 | let mut buffer = vec![0; src.len() * 2]; |
20 | 0 | if upper_case { |
21 | 0 | hex_encode_upper(src, &mut buffer).expect("hex_string"); |
22 | 0 | } else { |
23 | 0 | hex_encode(src, &mut buffer).expect("hex_string"); |
24 | 0 | } |
25 | | |
26 | 0 | if cfg!(debug_assertions) { |
27 | 0 | String::from_utf8(buffer).unwrap() |
28 | | } else { |
29 | | // Saftey: We just wrote valid utf8 hex string into the dst |
30 | 0 | unsafe { String::from_utf8_unchecked(buffer) } |
31 | | } |
32 | 0 | } |
33 | | |
34 | | #[cfg(not(feature = "alloc"))] |
35 | | fn 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 | | // Saftey: We just wrote valid utf8 hex string into the dst |
50 | | unsafe { String::from_utf8_unchecked(buffer) } |
51 | | } |
52 | | } |
53 | | |
54 | | #[cfg(feature = "alloc")] |
55 | 0 | pub fn hex_string(src: &[u8]) -> String { |
56 | 0 | hex_string_custom_case(src, false) |
57 | 0 | } |
58 | | |
59 | | #[cfg(not(feature = "alloc"))] |
60 | | pub fn hex_string<const N: usize>(src: &[u8]) -> String<N> { |
61 | | hex_string_custom_case(src, false) |
62 | | } |
63 | | |
64 | | #[cfg(feature = "alloc")] |
65 | 0 | pub fn hex_string_upper(src: &[u8]) -> String { |
66 | 0 | hex_string_custom_case(src, true) |
67 | 0 | } |
68 | | |
69 | | #[cfg(not(feature = "alloc"))] |
70 | | pub fn hex_string_upper<const N: usize>(src: &[u8]) -> String<N> { |
71 | | hex_string_custom_case(src, true) |
72 | | } |
73 | | |
74 | 2.00k | pub fn hex_encode_custom<'a>( |
75 | 2.00k | src: &[u8], |
76 | 2.00k | dst: &'a mut [u8], |
77 | 2.00k | upper_case: bool, |
78 | 2.00k | ) -> Result<&'a mut str, Error> { |
79 | 2.00k | unsafe fn mut_str(buffer: &mut [u8]) -> &mut str { |
80 | 2.00k | if cfg!(debug_assertions) { |
81 | 2.00k | core::str::from_utf8_mut(buffer).unwrap() |
82 | | } else { |
83 | 0 | core::str::from_utf8_unchecked_mut(buffer) |
84 | | } |
85 | 2.00k | } |
86 | | |
87 | 2.00k | let expect_dst_len = src |
88 | 2.00k | .len() |
89 | 2.00k | .checked_mul(2) |
90 | 2.00k | .ok_or(Error::InvalidLength(src.len()))?; |
91 | 2.00k | if dst.len() < expect_dst_len { |
92 | 0 | return Err(Error::InvalidLength(expect_dst_len)); |
93 | 2.00k | } |
94 | | |
95 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
96 | | { |
97 | 2.00k | match crate::vectorization_support() { |
98 | 2.00k | crate::Vectorization::AVX2 => unsafe { hex_encode_avx2(src, dst, upper_case) }, |
99 | 0 | crate::Vectorization::SSE41 => unsafe { hex_encode_sse41(src, dst, upper_case) }, |
100 | 0 | crate::Vectorization::None => hex_encode_custom_case_fallback(src, dst, upper_case), |
101 | | } |
102 | | // Safety: We just wrote valid utf8 hex string into the dst |
103 | 2.00k | 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 | | // Saftey: We just wrote valid utf8 hex string into the dst |
109 | | Ok(unsafe { mut_str(dst) }) |
110 | | } |
111 | 2.00k | } |
112 | | |
113 | | /// Hex encode src into dst. |
114 | | /// The length of dst must be at least src.len() * 2. |
115 | 2.00k | pub fn hex_encode<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a mut str, Error> { |
116 | 2.00k | hex_encode_custom(src, dst, false) |
117 | 2.00k | } |
118 | | |
119 | 0 | pub fn hex_encode_upper<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a mut str, Error> { |
120 | 0 | hex_encode_custom(src, dst, true) |
121 | 0 | } |
122 | | |
123 | | #[deprecated(since = "0.3.0", note = "please use `hex_encode` instead")] |
124 | 0 | pub fn hex_to(src: &[u8], dst: &mut [u8]) -> Result<(), Error> { |
125 | 0 | hex_encode(src, dst).map(|_| ()) |
126 | 0 | } |
127 | | |
128 | | #[target_feature(enable = "avx2")] |
129 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
130 | 2.00k | unsafe fn hex_encode_avx2(mut src: &[u8], dst: &mut [u8], upper_case: bool) { |
131 | 2.00k | let ascii_zero = _mm256_set1_epi8(b'0' as i8); |
132 | 2.00k | let nines = _mm256_set1_epi8(9); |
133 | 2.00k | let ascii_a = if upper_case { |
134 | 0 | _mm256_set1_epi8((b'A' - 9 - 1) as i8) |
135 | | } else { |
136 | 2.00k | _mm256_set1_epi8((b'a' - 9 - 1) as i8) |
137 | | }; |
138 | 2.00k | let and4bits = _mm256_set1_epi8(0xf); |
139 | | |
140 | 2.00k | let mut i = 0_isize; |
141 | 2.00k | while src.len() >= 32 { |
142 | 0 | // https://stackoverflow.com/questions/47425851/whats-the-difference-between-mm256-lddqu-si256-and-mm256-loadu-si256 |
143 | 0 | let invec = _mm256_loadu_si256(src.as_ptr() as *const _); |
144 | 0 |
|
145 | 0 | let masked1 = _mm256_and_si256(invec, and4bits); |
146 | 0 | let masked2 = _mm256_and_si256(_mm256_srli_epi64(invec, 4), and4bits); |
147 | 0 |
|
148 | 0 | // return 0xff corresponding to the elements > 9, or 0x00 otherwise |
149 | 0 | let cmpmask1 = _mm256_cmpgt_epi8(masked1, nines); |
150 | 0 | let cmpmask2 = _mm256_cmpgt_epi8(masked2, nines); |
151 | 0 |
|
152 | 0 | // add '0' or the offset depending on the masks |
153 | 0 | let masked1 = _mm256_add_epi8(masked1, _mm256_blendv_epi8(ascii_zero, ascii_a, cmpmask1)); |
154 | 0 | let masked2 = _mm256_add_epi8(masked2, _mm256_blendv_epi8(ascii_zero, ascii_a, cmpmask2)); |
155 | 0 |
|
156 | 0 | // interleave masked1 and masked2 bytes |
157 | 0 | let res1 = _mm256_unpacklo_epi8(masked2, masked1); |
158 | 0 | let res2 = _mm256_unpackhi_epi8(masked2, masked1); |
159 | 0 |
|
160 | 0 | // Store everything into the right destination now |
161 | 0 | let base = dst.as_mut_ptr().offset(i * 2); |
162 | 0 | let base1 = base.offset(0) as *mut _; |
163 | 0 | let base2 = base.offset(16) as *mut _; |
164 | 0 | let base3 = base.offset(32) as *mut _; |
165 | 0 | let base4 = base.offset(48) as *mut _; |
166 | 0 | _mm256_storeu2_m128i(base3, base1, res1); |
167 | 0 | _mm256_storeu2_m128i(base4, base2, res2); |
168 | 0 | src = &src[32..]; |
169 | 0 | i += 32; |
170 | 0 | } |
171 | | |
172 | 2.00k | let i = i as usize; |
173 | 2.00k | hex_encode_sse41(src, &mut dst[i * 2..], upper_case); |
174 | 2.00k | } |
175 | | |
176 | | // copied from https://github.com/Matherunner/bin2hex-sse/blob/master/base16_sse4.cpp |
177 | | #[target_feature(enable = "sse4.1")] |
178 | | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
179 | 2.00k | unsafe fn hex_encode_sse41(mut src: &[u8], dst: &mut [u8], upper_case: bool) { |
180 | 2.00k | let ascii_zero = _mm_set1_epi8(b'0' as i8); |
181 | 2.00k | let nines = _mm_set1_epi8(9); |
182 | 2.00k | let ascii_a = if upper_case { |
183 | 0 | _mm_set1_epi8((b'A' - 9 - 1) as i8) |
184 | | } else { |
185 | 2.00k | _mm_set1_epi8((b'a' - 9 - 1) as i8) |
186 | | }; |
187 | 2.00k | let and4bits = _mm_set1_epi8(0xf); |
188 | | |
189 | 2.00k | let mut i = 0_isize; |
190 | 4.00k | while src.len() >= 16 { |
191 | 2.00k | let invec = _mm_loadu_si128(src.as_ptr() as *const _); |
192 | 2.00k | |
193 | 2.00k | let masked1 = _mm_and_si128(invec, and4bits); |
194 | 2.00k | let masked2 = _mm_and_si128(_mm_srli_epi64(invec, 4), and4bits); |
195 | 2.00k | |
196 | 2.00k | // return 0xff corresponding to the elements > 9, or 0x00 otherwise |
197 | 2.00k | let cmpmask1 = _mm_cmpgt_epi8(masked1, nines); |
198 | 2.00k | let cmpmask2 = _mm_cmpgt_epi8(masked2, nines); |
199 | 2.00k | |
200 | 2.00k | // add '0' or the offset depending on the masks |
201 | 2.00k | let masked1 = _mm_add_epi8(masked1, _mm_blendv_epi8(ascii_zero, ascii_a, cmpmask1)); |
202 | 2.00k | let masked2 = _mm_add_epi8(masked2, _mm_blendv_epi8(ascii_zero, ascii_a, cmpmask2)); |
203 | 2.00k | |
204 | 2.00k | // interleave masked1 and masked2 bytes |
205 | 2.00k | let res1 = _mm_unpacklo_epi8(masked2, masked1); |
206 | 2.00k | let res2 = _mm_unpackhi_epi8(masked2, masked1); |
207 | 2.00k | |
208 | 2.00k | _mm_storeu_si128(dst.as_mut_ptr().offset(i * 2) as *mut _, res1); |
209 | 2.00k | _mm_storeu_si128(dst.as_mut_ptr().offset(i * 2 + 16) as *mut _, res2); |
210 | 2.00k | src = &src[16..]; |
211 | 2.00k | i += 16; |
212 | 2.00k | } |
213 | | |
214 | 2.00k | let i = i as usize; |
215 | 2.00k | hex_encode_custom_case_fallback(src, &mut dst[i * 2..], upper_case); |
216 | 2.00k | } |
217 | | |
218 | | #[inline] |
219 | 16.0k | fn hex_lower(byte: u8) -> u8 { |
220 | 16.0k | TABLE_LOWER[byte as usize] |
221 | 16.0k | } |
222 | | |
223 | | #[inline] |
224 | 0 | fn hex_upper(byte: u8) -> u8 { |
225 | 0 | TABLE_UPPER[byte as usize] |
226 | 0 | } |
227 | | |
228 | 2.00k | fn hex_encode_custom_case_fallback(src: &[u8], dst: &mut [u8], upper_case: bool) { |
229 | 2.00k | if upper_case { |
230 | 0 | for (byte, slots) in src.iter().zip(dst.chunks_exact_mut(2)) { |
231 | 0 | slots[0] = hex_upper((*byte >> 4) & 0xf); |
232 | 0 | slots[1] = hex_upper(*byte & 0xf); |
233 | 0 | } |
234 | | } else { |
235 | 8.00k | for (byte, slots) in src.iter().zip(dst.chunks_exact_mut(2)) { |
236 | 8.00k | slots[0] = hex_lower((*byte >> 4) & 0xf); |
237 | 8.00k | slots[1] = hex_lower(*byte & 0xf); |
238 | 8.00k | } |
239 | | } |
240 | 2.00k | } |
241 | | |
242 | 0 | pub fn hex_encode_fallback(src: &[u8], dst: &mut [u8]) { |
243 | 0 | hex_encode_custom_case_fallback(src, dst, false) |
244 | 0 | } |
245 | | |
246 | 0 | pub fn hex_encode_upper_fallback(src: &[u8], dst: &mut [u8]) { |
247 | 0 | hex_encode_custom_case_fallback(src, dst, true) |
248 | 0 | } |
249 | | |
250 | | #[cfg(test)] |
251 | | mod 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 | | // this function have no return value, so we just execute it and expect no panic |
285 | | hex_encode_fallback(src, &mut dst); |
286 | | } |
287 | | } |