1#![no_std]
2#![forbid(unsafe_code)]
3
4#[cfg(test)]
21mod tests;
22
23#[derive(Debug, PartialEq)]
25pub enum ConvertError {
26 InvalidInputLength,
28
29 InvalidOutputLength,
31
32 InvalidInput,
34}
35
36#[cfg(feature = "decode")]
57pub fn hex2bin<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
58 if input.len() % 2 != 0 {
59 return Err(ConvertError::InvalidInputLength);
60 }
61
62 if input.len() / 2 > output.len() {
63 return Err(ConvertError::InvalidOutputLength);
64 }
65
66 for block_num in 0..(input.len() / 2) {
67 let mut num = 0u8;
68 for &digit in &input[(block_num * 2)..(block_num * 2 + 2)] {
69 let val = match digit {
70 b'a'..=b'f' => digit - b'a' + 10,
71 b'A'..=b'F' => digit - b'A' + 10,
72 b'0'..=b'9' => digit - b'0',
73 _ => return Err(ConvertError::InvalidInput),
74 };
75
76 num = (num << 4) | val;
77 }
78
79 output[block_num] = num;
80 }
81
82 Ok(&mut output[..(input.len() / 2)])
83}
84
85#[cfg(feature = "encode")]
101pub fn bin2hex<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
102 const DIGITS: &[u8] = &[
103 b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a' , b'b',
104 b'c', b'd', b'e', b'f'
105 ];
106
107 if output.len() < input.len() * 2 {
108 return Err(ConvertError::InvalidOutputLength);
109 }
110
111 for (idx, &byte) in input.iter().enumerate() {
112 output[idx * 2 + 0] = DIGITS[((byte >> 4) & 0x0f) as usize];
113 output[idx * 2 + 1] = DIGITS[((byte >> 0) & 0x0f) as usize];
114 }
115
116 Ok(&mut output[..(input.len() * 2)])
117}
118
119#[cfg(feature = "encode")]
124pub fn b32encode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
125 const DIGITS: &[u8] = &[
126 b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
127 b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
128 b'Y', b'Z', b'2', b'3', b'4', b'5', b'6', b'7'
129 ];
130
131 let data_len = input.len() * 8 / 5;
132 let pad_len = 8 - (data_len % 8);
133 let total_len = data_len + if pad_len == 8 { 0 } else { pad_len };
134
135 if total_len == 0 {
136 return Ok(&mut output[0..0]);
137 }
138
139 if total_len > output.len() {
140 return Err(ConvertError::InvalidOutputLength);
141 }
142
143 for block_idx in 0..(1 + input.len() / 5) {
144 let max_block_len = if input.len() > block_idx * 5 + 5 { block_idx * 5 + 5 } else { input.len() };
145 let block = &input[block_idx * 5..max_block_len];
146
147 let mut num = 0u64;
148 for i in 0..block.len() {
149 num |= (block[i] as u64) << (64 - 8 - i*8);
150 }
151
152 for i in 0..8 {
153 let digit_idx = (num >> (64 - 5 - i*5)) & 0b11111;
154 output[block_idx * 8 + i] = DIGITS[digit_idx as usize];
155 }
156 }
157
158 for idx in data_len + 1..total_len {
159 output[idx] = b'=';
160 }
161
162 Ok(&mut output[..total_len])
163}
164
165#[cfg(feature = "decode")]
172pub fn b32decode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
173 let padding = 8 - input.len() % 8;
174 let input_len = input.len() + if padding != 8 { padding } else { 0 };
175
176 let mut output_len = input_len * 5 / 8;
177 if output_len > output.len() {
178 return Err(ConvertError::InvalidOutputLength);
179 }
180
181 let mut eof = false;
182
183 for block_idx in 0..(1 + input.len() / 8) {
184 let block_end = if input.len() > block_idx * 8 + 8 { block_idx * 8 + 8 } else { input.len() };
185 let block = &input[(block_idx * 8)..block_end];
186
187 let mut num = 0u64;
188 for idx in 0..block.len() {
189 let ch = match block[idx] {
190 b'=' => { eof = true; continue },
191 _ if eof => return Err(ConvertError::InvalidInput),
193 b'1' => b'I',
194 b'0' => b'O',
195 c => c,
196 };
197
198 let c_val = match ch {
199 b'A'..=b'Z' => ch - b'A',
200 b'a'..=b'z' => ch - b'a',
201 b'2'..=b'7' => ch - b'2' + 26,
202 _ => return Err(ConvertError::InvalidInput)
203 };
204
205 num |= (c_val as u64) << (64 - 5 - idx * 5);
206 output_len = block_idx * 5 + (idx * 5 / 8) + 1;
207 }
208
209 if block_idx * 5 + 5 > output.len() {
210 return Err(ConvertError::InvalidOutputLength);
211 }
212
213 for i in 0..5 {
214 output[block_idx * 5 + i] = ((num >> (64 - 8 - i * 8)) & 0xff) as u8;
215 }
216 }
217
218 Ok(&mut output[..output_len])
219}
220
221#[cfg(feature = "encode")]
226pub fn b64encode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
227 const DIGITS: &[u8] = &[
228 b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L',
229 b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X',
230 b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j',
231 b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v',
232 b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7',
233 b'8', b'9', b'+', b'/'
234 ];
235
236 let data_len = input.len() * 4 / 3;
237 let pad_len = (4 - (data_len % 4)) % 4;
238 let required_len = data_len + pad_len;
239 if required_len > output.len() {
240 return Err(ConvertError::InvalidOutputLength);
241 }
242
243 for block_idx in 0..(input.len() / 3 + 1) {
244 let block_end = core::cmp::min(block_idx * 3 + 3, input.len());
245 let block = &input[block_idx * 3..block_end];
246 if block.len() == 0 {
247 break;
248 }
249
250 let mut raw_num = 0u32;
252 for i in 0..block.len() {
253 raw_num |= (block[i] as u32) << (16 - (i * 8));
254 }
255
256 for i in 0..4 {
257 let di = (raw_num >> (18 - (6 * i))) & 0b111111;
258 output[block_idx * 4 + i] = DIGITS[di as usize];
259 }
260 }
261
262 for ch in &mut output[(data_len + 1)..] {
263 *ch = b'=';
264 }
265
266 Ok(&mut output[..required_len])
267}
268
269#[cfg(feature = "decode")]
277pub fn b64decode<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a mut [u8], ConvertError> {
278 if input.len() % 4 != 0 {
279 return Err(ConvertError::InvalidInputLength);
280 }
281
282 let mut output_length = input.len() / 4 * 3;
283 if output_length > output.len() {
284 return Err(ConvertError::InvalidOutputLength);
285 }
286
287 for block_idx in 0..(input.len() / 4) {
288 let block = &input[block_idx * 4..(block_idx * 4 + 4)];
289
290 let mut num = 0u32;
291 for i in 0..4 {
292 let ch = block[i];
293 if ch == b'=' {
294 if i < 2 {
295 return Err(ConvertError::InvalidInput);
296 }
297
298 if i == 2 {
300 if block[3] != b'=' { return Err(ConvertError::InvalidInput); }
304 if num & 0x00ffffff != 0 { return Err(ConvertError::InvalidInput); }
305 } else if i == 3 {
306 if num & 0x0000ffff != 0 { return Err(ConvertError::InvalidInput); }
310 }
311
312 output_length = block_idx * 3 + i - 1;
313 break;
314 }
315
316 let c_val = match ch {
317 b'A'..=b'Z' => ch - b'A',
318 b'a'..=b'z' => ch - b'a' + 26,
319 b'0'..=b'9' => ch - b'0' + 52,
320 b'+' => 62,
321 b'/' => 63,
322 _ => return Err(ConvertError::InvalidInput),
323 };
324
325 num |= (c_val as u32) << (26 - 6 * i);
326 }
327
328 for i in 0..3 {
329 output[block_idx * 3 + i] = ((num >> (24 - i * 8)) & 0xff) as u8;
330 }
331 }
332
333 Ok(&mut output[..output_length])
334}