1#![cfg_attr(not(any(feature = "std", test)), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(any(feature = "alloc", test))]
59extern crate alloc;
60
61#[cfg(all(feature = "serde", any(feature = "alloc", test)))]
62mod serde;
63
64#[cfg(target_arch = "aarch64")]
65mod base32_neon;
66
67#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
68mod base32_avx2;
69
70const PAD: u8 = b'=';
71
72const Z32_DECODE_TABLE: [u8; 256] = [
73 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
74 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
75 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x12, 0x20, 0x19, 0x1a, 0x1b, 0x1e, 0x1d, 0x07,
76 0x1f, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
77 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
78 0x20, 0x20, 0x18, 0x01, 0x0c, 0x03, 0x08, 0x05, 0x06, 0x1c, 0x15, 0x09, 0x0a, 0x20, 0x0b, 0x02, 0x10, 0x0d, 0x0e,
79 0x04, 0x16, 0x11, 0x13, 0x20, 0x14, 0x0f, 0x00, 0x17, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
80 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
81 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
82 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
83 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
84 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
85 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
86 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
87];
88
89const Z32_ENCODE_TABLE: [u8; 32] = *b"ybndrfg8ejkmcpqxot1uwisza345h769";
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub enum Alphabet {
93 Crockford,
94 Rfc4648,
95 Rfc4648NoPadding,
96 Rfc4648Lower,
97 Rfc4648LowerNoPadding,
98 Rfc4648Hex,
99 Rfc4648HexNoPadding,
100 Rfc4648HexLower,
101 Rfc4648HexLowerNoPadding,
102 Z32,
103}
104
105impl Alphabet {
106 #[inline]
107 const fn is_padded(&self) -> bool {
108 match self {
109 Alphabet::Crockford => false,
110 Alphabet::Rfc4648 => true,
111 Alphabet::Rfc4648NoPadding => false,
112 Alphabet::Rfc4648Lower => true,
113 Alphabet::Rfc4648LowerNoPadding => false,
114 Alphabet::Rfc4648Hex => true,
115 Alphabet::Rfc4648HexNoPadding => false,
116 Alphabet::Rfc4648HexLower => true,
117 Alphabet::Rfc4648HexLowerNoPadding => false,
118 Alphabet::Z32 => false,
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum EncodeError {
125 InvalidOutputLength,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub enum DecodeError {
130 InvalidInput,
131 InvalidLength,
132 InvalidPadding,
133}
134
135impl core::fmt::Display for EncodeError {
136 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137 match self {
138 Self::InvalidOutputLength => f.write_str("output buffer size is not valid"),
139 }
140 }
141}
142
143impl core::fmt::Display for DecodeError {
144 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
145 match self {
146 Self::InvalidInput => f.write_str("invalid base32 character"),
147 Self::InvalidLength => f.write_str("invalid base32 length"),
148 Self::InvalidPadding => f.write_str("invalid base32 padding"),
149 }
150 }
151}
152
153#[cfg(feature = "std")]
154impl std::error::Error for EncodeError {}
155
156#[cfg(feature = "std")]
157impl std::error::Error for DecodeError {}
158
159pub const fn encoded_length(bytes_len: usize, padding: bool) -> Option<usize> {
171 if bytes_len == 0 {
172 return Some(0);
173 }
174 let complete_chunks = bytes_len / 5;
175 let base = match complete_chunks.checked_mul(8) {
176 Some(v) => v,
177 None => return None,
178 };
179 let rem = bytes_len % 5;
180 if rem == 0 {
181 Some(base)
182 } else if padding {
183 base.checked_add(8)
184 } else {
185 let bits = match bytes_len.checked_mul(8) {
186 Some(v) => v,
187 None => return None,
188 };
189 match bits.checked_add(4) {
190 Some(v) => Some(v / 5),
191 None => None,
192 }
193 }
194}
195
196#[cfg(feature = "alloc")]
209pub fn encode(data: impl AsRef<[u8]>, alphabet: Alphabet) -> alloc::string::String {
210 let data = data.as_ref();
211 let padding = alphabet.is_padded();
212 let len = encoded_length(data.len(), padding).expect("encoded length overflow");
213 let mut output = alloc::vec![0u8; len];
214 encode_into(&mut output, data, alphabet).expect("output buffer sized correctly");
215 unsafe { alloc::string::String::from_utf8_unchecked(output) }
216}
217
218pub const fn encode_array<const OUT: usize>(data: &[u8], alphabet: Alphabet) -> [u8; OUT] {
231 match encoded_length(data.len(), alphabet.is_padded()) {
232 Some(len) if len == OUT => {}
233 _ => panic!("encode_array: output array length is invalid"),
234 }
235 let mut result = [0u8; OUT];
236 match encode_into_constant_time(&mut result, data, alphabet) {
237 Ok(()) => result,
238 Err(_) => panic!("encode_array: output array length is invalid"),
239 }
240}
241
242pub fn encode_into(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
262 let padding = alphabet.is_padded();
263 let expected = encoded_length(data.len(), padding).expect("encoded length overflow");
264 if output.len() < expected {
265 return Err(EncodeError::InvalidOutputLength);
266 }
267
268 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
269 if data.len() >= 40 {
270 return unsafe { base32_neon::encode_into(output, data, alphabet) };
271 }
272
273 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
274 if data.len() >= 40 {
275 return unsafe { base32_avx2::encode_into(output, data, alphabet) };
276 }
277
278 encode_into_constant_time(output, data, alphabet)
279}
280
281pub const fn encode_into_constant_time(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
296 let padding = alphabet.is_padded();
297 let expected = encoded_length(data.len(), padding).expect("encoded length overflow");
298 if output.len() < expected {
299 return Err(EncodeError::InvalidOutputLength);
300 }
301
302 let len = data.len();
303 let mut i = 0;
304
305 while i + 40 <= len {
306 encode_8blocks(output, alphabet, data, i);
307 i += 40;
308 }
309
310 while i + 5 <= len {
311 let b0 = data[i];
312 let b1 = data[i + 1];
313 let b2 = data[i + 2];
314 let b3 = data[i + 3];
315 let b4 = data[i + 4];
316
317 let q0 = b0 >> 3;
318 let q1 = ((b0 & 0x07) << 2) | (b1 >> 6);
319 let q2 = (b1 >> 1) & 0x1F;
320 let q3 = ((b1 & 0x01) << 4) | (b2 >> 4);
321 let q4 = ((b2 & 0x0F) << 1) | (b3 >> 7);
322 let q5 = (b3 >> 2) & 0x1F;
323 let q6 = ((b3 & 0x03) << 3) | (b4 >> 5);
324 let q7 = b4 & 0x1F;
325
326 let o = (i / 5) * 8;
327 output[o] = quintet_to_char(q0, alphabet);
328 output[o + 1] = quintet_to_char(q1, alphabet);
329 output[o + 2] = quintet_to_char(q2, alphabet);
330 output[o + 3] = quintet_to_char(q3, alphabet);
331 output[o + 4] = quintet_to_char(q4, alphabet);
332 output[o + 5] = quintet_to_char(q5, alphabet);
333 output[o + 6] = quintet_to_char(q6, alphabet);
334 output[o + 7] = quintet_to_char(q7, alphabet);
335
336 i += 5;
337 }
338
339 let rem = len - i;
340 if rem > 0 {
341 let o = (i / 5) * 8;
342 let b0 = data[i];
343 let q0 = b0 >> 3;
344 let q1 = (b0 & 0x07) << 2;
345 output[o] = quintet_to_char(q0, alphabet);
346 output[o + 1] = quintet_to_char(q1, alphabet);
347
348 if rem >= 2 {
349 let b1 = data[i + 1];
350 let q1 = ((b0 & 0x07) << 2) | (b1 >> 6);
351 let q2 = (b1 >> 1) & 0x1F;
352 let q3 = (b1 & 0x01) << 4;
353 output[o + 1] = quintet_to_char(q1, alphabet);
354 output[o + 2] = quintet_to_char(q2, alphabet);
355 output[o + 3] = quintet_to_char(q3, alphabet);
356
357 if rem >= 3 {
358 let b2 = data[i + 2];
359 let q3 = ((b1 & 0x01) << 4) | (b2 >> 4);
360 let q4 = (b2 & 0x0F) << 1;
361 output[o + 3] = quintet_to_char(q3, alphabet);
362 output[o + 4] = quintet_to_char(q4, alphabet);
363
364 if rem == 4 {
365 let b3 = data[i + 3];
366 let q4 = ((b2 & 0x0F) << 1) | (b3 >> 7);
367 let q5 = (b3 >> 2) & 0x1F;
368 let q6 = (b3 & 0x03) << 3;
369 output[o + 4] = quintet_to_char(q4, alphabet);
370 output[o + 5] = quintet_to_char(q5, alphabet);
371 output[o + 6] = quintet_to_char(q6, alphabet);
372 }
373 }
374 }
375
376 if padding {
377 let pad_start = match rem {
378 1 => o + 2,
379 2 => o + 4,
380 3 => o + 5,
381 4 => o + 7,
382 _ => unreachable!(),
383 };
384 let pad_end = o + 8;
385 let mut p = pad_start;
386 while p < pad_end {
387 output[p] = PAD;
388 p += 1;
389 }
390 }
391 }
392 Ok(())
393}
394
395#[cfg(feature = "alloc")]
405pub fn encode_into_string(output: &mut alloc::string::String, data: &[u8], alphabet: Alphabet) {
406 let encoded_length = encoded_length(data.len(), alphabet.is_padded()).expect("output length overflow");
407 if encoded_length <= 256 {
408 let mut buf = [0u8; 256];
410 let mut buf = &mut buf[..encoded_length];
411 encode_into(&mut buf, data, alphabet).unwrap();
412 output.push_str(unsafe { core::str::from_utf8_unchecked(&buf) });
414 } else {
415 let mut buf = alloc::vec![0u8; encoded_length];
416 encode_into(&mut buf, data, alphabet).unwrap();
417 output.push_str(unsafe { core::str::from_utf8_unchecked(&buf) });
419 }
420}
421
422#[inline]
425const fn not_in_range(v: u8, lo: u8, hi: u8) -> u8 {
426 (((v.wrapping_sub(lo) as i8) | (hi.wrapping_sub(v) as i8)) >> 7) as u8
427}
428
429#[inline]
432const fn check_trailing_bits(value: u8, max_pad: u8) -> u8 {
433 let mask = (1u8 << max_pad).wrapping_sub(1);
434 let pad_bits = value & mask;
435 (!not_in_range(pad_bits, 1, mask)) & 0x20
436}
437
438#[inline]
439const fn encode_8blocks(output: &mut [u8], alphabet: Alphabet, data: &[u8], start: usize) {
440 let mut n = 0;
441 while n < 8 {
442 let i = start + n * 5;
443 let b0 = data[i];
444 let b1 = data[i + 1];
445 let b2 = data[i + 2];
446 let b3 = data[i + 3];
447 let b4 = data[i + 4];
448
449 let q0 = b0 >> 3;
450 let q1 = ((b0 & 0x07) << 2) | (b1 >> 6);
451 let q2 = (b1 >> 1) & 0x1F;
452 let q3 = ((b1 & 0x01) << 4) | (b2 >> 4);
453 let q4 = ((b2 & 0x0F) << 1) | (b3 >> 7);
454 let q5 = (b3 >> 2) & 0x1F;
455 let q6 = ((b3 & 0x03) << 3) | (b4 >> 5);
456 let q7 = b4 & 0x1F;
457
458 let o = (start / 5) * 8 + n * 8;
459 output[o] = quintet_to_char(q0, alphabet);
460 output[o + 1] = quintet_to_char(q1, alphabet);
461 output[o + 2] = quintet_to_char(q2, alphabet);
462 output[o + 3] = quintet_to_char(q3, alphabet);
463 output[o + 4] = quintet_to_char(q4, alphabet);
464 output[o + 5] = quintet_to_char(q5, alphabet);
465 output[o + 6] = quintet_to_char(q6, alphabet);
466 output[o + 7] = quintet_to_char(q7, alphabet);
467
468 n += 1;
469 }
470}
471
472#[inline]
475const fn quintet_to_char(v: u8, alphabet: Alphabet) -> u8 {
476 match alphabet {
477 Alphabet::Crockford => quintet_to_crockford(v),
478 Alphabet::Rfc4648 | Alphabet::Rfc4648NoPadding => {
479 let not_upper = not_in_range(v, 0, 25);
480 let not_digit = not_in_range(v, 26, 31);
481 (v + b'A') & !not_upper | (v.wrapping_sub(26).wrapping_add(b'2')) & !not_digit
482 }
483 Alphabet::Rfc4648Lower | Alphabet::Rfc4648LowerNoPadding => {
484 let not_lower = not_in_range(v, 0, 25);
485 let not_digit = not_in_range(v, 26, 31);
486 (v + b'a') & !not_lower | (v.wrapping_sub(26).wrapping_add(b'2')) & !not_digit
487 }
488 Alphabet::Rfc4648Hex | Alphabet::Rfc4648HexNoPadding => {
489 let not_digit = not_in_range(v, 0, 9);
490 let not_upper = not_in_range(v, 10, 31);
491 (v + b'0') & !not_digit | (v.wrapping_sub(10).wrapping_add(b'A')) & !not_upper
492 }
493 Alphabet::Rfc4648HexLower | Alphabet::Rfc4648HexLowerNoPadding => {
494 let not_digit = not_in_range(v, 0, 9);
495 let not_lower = not_in_range(v, 10, 31);
496 (v + b'0') & !not_digit | (v.wrapping_sub(10).wrapping_add(b'a')) & !not_lower
497 }
498 Alphabet::Z32 => Z32_ENCODE_TABLE[v as usize],
499 }
500}
501
502#[inline]
504const fn quintet_to_crockford(v: u8) -> u8 {
505 let not_0_9 = not_in_range(v, 0, 9);
506 let not_10_17 = not_in_range(v, 10, 17);
507 let not_18_19 = not_in_range(v, 18, 19);
508 let not_20_21 = not_in_range(v, 20, 21);
509 let not_22_26 = not_in_range(v, 22, 26);
510 let not_27_31 = not_in_range(v, 27, 31);
511 (v + b'0') & !not_0_9
512 | (v + 55) & !not_10_17
513 | (v + 56) & !not_18_19
514 | (v + 57) & !not_20_21
515 | (v + 58) & !not_22_26
516 | (v + 59) & !not_27_31
517}
518
519#[inline]
524const fn decoded_length(encoded_content_len: usize) -> Result<usize, DecodeError> {
525 let full_blocks = encoded_content_len / 8;
526 let rem = encoded_content_len % 8;
527
528 let base = full_blocks * 5;
529
530 match rem {
531 0 => Ok(base),
532 2 => Ok(base + 1),
533 4 => Ok(base + 2),
534 5 => Ok(base + 3),
535 7 => Ok(base + 4),
536 _ => Err(DecodeError::InvalidLength),
537 }
538}
539
540#[cfg(feature = "alloc")]
554pub fn decode(data: impl AsRef<[u8]>, alphabet: Alphabet) -> Result<alloc::vec::Vec<u8>, DecodeError> {
555 let data = data.as_ref();
556 let padding = alphabet.is_padded();
557 let (content_len, _) = strip_padding_info(data, padding)?;
558 let output_len = decoded_length(content_len)?;
559 let mut output = alloc::vec![0u8; output_len];
560 decode_into(&mut output, data, alphabet)?;
561 Ok(output)
562}
563
564pub const fn decode_array<const OUT: usize>(encoded_data: &[u8], alphabet: Alphabet) -> Result<[u8; OUT], DecodeError> {
577 let mut result = [0u8; OUT];
578 match decode_into_constant_time(&mut result, encoded_data, alphabet) {
579 Ok(()) => Ok(result),
580 Err(err) => Err(err),
581 }
582}
583
584pub fn decode_into(output: &mut [u8], encoded_data: &[u8], alphabet: Alphabet) -> Result<(), DecodeError> {
605 let padding = alphabet.is_padded();
606 let (content_len, _) = strip_padding_info(encoded_data, padding)?;
607 let computed_output = decoded_length(content_len)?;
608 if output.len() < computed_output {
609 return Err(DecodeError::InvalidLength);
610 }
611
612 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
613 if content_len >= 64 {
614 let content = &encoded_data[..content_len];
615 return unsafe { base32_neon::decode_into(output, content, alphabet) };
616 }
617
618 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
619 if content_len >= 32 {
620 let content = &encoded_data[..content_len];
621 return unsafe { base32_avx2::decode_into(output, content, alphabet) };
622 }
623
624 decode_into_constant_time(output, encoded_data, alphabet)
625}
626
627pub const fn decode_into_constant_time(
642 output: &mut [u8],
643 encoded_data: &[u8],
644 alphabet: Alphabet,
645) -> Result<(), DecodeError> {
646 let in_len = encoded_data.len();
647 let padding = alphabet.is_padded();
648
649 if in_len == 0 {
650 return Ok(());
651 }
652
653 let (content_len, _padding_len) = match strip_padding_info(encoded_data, padding) {
654 Ok(info) => info,
655 Err(e) => return Err(e),
656 };
657
658 if content_len == 0 {
659 return Ok(());
660 }
661
662 let computed_output = match decoded_length(content_len) {
663 Ok(len) => len,
664 Err(e) => return Err(e),
665 };
666
667 if output.len() < computed_output {
668 return Err(DecodeError::InvalidLength);
669 }
670
671 let mut err: u8 = 0;
672 let mut i = 0;
673 let mut o = 0;
674
675 while i + 64 <= content_len {
676 decode_8quads(output, alphabet, encoded_data, &mut i, &mut o, &mut err);
677 }
678
679 while i + 8 <= content_len {
680 decode_1quad(output, alphabet, encoded_data, &mut i, &mut o, &mut err);
681 }
682
683 if i < content_len {
684 let remaining = content_len - i;
685 match remaining {
686 2 => {
687 let v0 = char_to_quintet(encoded_data[i], alphabet);
688 let v1 = char_to_quintet(encoded_data[i + 1], alphabet);
689 err |= v0 | v1;
690 err |= check_trailing_bits(v1, 2);
691 output[o] = (v0 << 3) | (v1 >> 2);
692 }
693 4 => {
694 let v0 = char_to_quintet(encoded_data[i], alphabet);
695 let v1 = char_to_quintet(encoded_data[i + 1], alphabet);
696 let v2 = char_to_quintet(encoded_data[i + 2], alphabet);
697 let v3 = char_to_quintet(encoded_data[i + 3], alphabet);
698 err |= v0 | v1 | v2 | v3;
699 err |= check_trailing_bits(v3, 4);
700 output[o] = (v0 << 3) | (v1 >> 2);
701 output[o + 1] = (v1.wrapping_shl(6)) | (v2 << 1) | (v3 >> 4);
702 }
703 5 => {
704 let v0 = char_to_quintet(encoded_data[i], alphabet);
705 let v1 = char_to_quintet(encoded_data[i + 1], alphabet);
706 let v2 = char_to_quintet(encoded_data[i + 2], alphabet);
707 let v3 = char_to_quintet(encoded_data[i + 3], alphabet);
708 let v4 = char_to_quintet(encoded_data[i + 4], alphabet);
709 err |= v0 | v1 | v2 | v3 | v4;
710 err |= check_trailing_bits(v4, 1);
711 output[o] = (v0 << 3) | (v1 >> 2);
712 output[o + 1] = (v1.wrapping_shl(6)) | (v2 << 1) | (v3 >> 4);
713 output[o + 2] = (v3.wrapping_shl(4)) | (v4 >> 1);
714 }
715 7 => {
716 let v0 = char_to_quintet(encoded_data[i], alphabet);
717 let v1 = char_to_quintet(encoded_data[i + 1], alphabet);
718 let v2 = char_to_quintet(encoded_data[i + 2], alphabet);
719 let v3 = char_to_quintet(encoded_data[i + 3], alphabet);
720 let v4 = char_to_quintet(encoded_data[i + 4], alphabet);
721 let v5 = char_to_quintet(encoded_data[i + 5], alphabet);
722 let v6 = char_to_quintet(encoded_data[i + 6], alphabet);
723 err |= v0 | v1 | v2 | v3 | v4 | v5 | v6;
724 err |= check_trailing_bits(v6, 3);
725 output[o] = (v0 << 3) | (v1 >> 2);
726 output[o + 1] = (v1.wrapping_shl(6)) | (v2 << 1) | (v3 >> 4);
727 output[o + 2] = (v3.wrapping_shl(4)) | (v4 >> 1);
728 output[o + 3] = (v4.wrapping_shl(7)) | (v5 << 2) | (v6 >> 3);
729 }
730 _ => return Err(DecodeError::InvalidLength),
731 }
732 }
733
734 if err >= 32 {
735 return Err(DecodeError::InvalidInput);
736 }
737
738 Ok(())
739}
740
741#[inline]
742const fn decode_1quad(output: &mut [u8], alphabet: Alphabet, data: &[u8], i: &mut usize, o: &mut usize, err: &mut u8) {
743 let v0 = char_to_quintet(data[*i], alphabet);
744 let v1 = char_to_quintet(data[*i + 1], alphabet);
745 let v2 = char_to_quintet(data[*i + 2], alphabet);
746 let v3 = char_to_quintet(data[*i + 3], alphabet);
747 let v4 = char_to_quintet(data[*i + 4], alphabet);
748 let v5 = char_to_quintet(data[*i + 5], alphabet);
749 let v6 = char_to_quintet(data[*i + 6], alphabet);
750 let v7 = char_to_quintet(data[*i + 7], alphabet);
751 *err |= v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7;
752 output[*o] = (v0 << 3) | (v1 >> 2);
753 output[*o + 1] = (v1.wrapping_shl(6)) | (v2 << 1) | (v3 >> 4);
754 output[*o + 2] = (v3.wrapping_shl(4)) | (v4 >> 1);
755 output[*o + 3] = (v4.wrapping_shl(7)) | (v5 << 2) | (v6 >> 3);
756 output[*o + 4] = (v6.wrapping_shl(5)) | v7;
757 *i += 8;
758 *o += 5;
759}
760
761#[inline]
762const fn decode_8quads(output: &mut [u8], alphabet: Alphabet, data: &[u8], i: &mut usize, o: &mut usize, err: &mut u8) {
763 let mut n = 0;
764 while n < 8 {
765 let v0 = char_to_quintet(data[*i], alphabet);
766 let v1 = char_to_quintet(data[*i + 1], alphabet);
767 let v2 = char_to_quintet(data[*i + 2], alphabet);
768 let v3 = char_to_quintet(data[*i + 3], alphabet);
769 let v4 = char_to_quintet(data[*i + 4], alphabet);
770 let v5 = char_to_quintet(data[*i + 5], alphabet);
771 let v6 = char_to_quintet(data[*i + 6], alphabet);
772 let v7 = char_to_quintet(data[*i + 7], alphabet);
773 *err |= v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7;
774 output[*o] = (v0 << 3) | (v1 >> 2);
775 output[*o + 1] = (v1.wrapping_shl(6)) | (v2 << 1) | (v3 >> 4);
776 output[*o + 2] = (v3.wrapping_shl(4)) | (v4 >> 1);
777 output[*o + 3] = (v4.wrapping_shl(7)) | (v5 << 2) | (v6 >> 3);
778 output[*o + 4] = (v6.wrapping_shl(5)) | v7;
779 *i += 8;
780 *o += 5;
781 n += 1;
782 }
783}
784
785#[inline]
786const fn strip_padding_info(data: &[u8], expect_padding: bool) -> Result<(usize, usize), DecodeError> {
787 let in_len = data.len();
788
789 if expect_padding {
790 if in_len == 0 {
791 return Ok((0, 0));
792 }
793
794 let count = count_trailing_padding(data);
795 let content_len = in_len - count;
796
797 let err = (count > 0 && in_len % 8 != 0)
798 || count > 6
799 || (count > 0
800 && match count {
801 6 => content_len % 8 != 2,
802 4 => content_len % 8 != 4,
803 3 => content_len % 8 != 5,
804 1 => content_len % 8 != 7,
805 _ => true,
806 });
807
808 if err {
809 return Err(DecodeError::InvalidPadding);
810 }
811
812 Ok((content_len, count))
813 } else {
814 if in_len > 0 && data[in_len - 1] == PAD {
815 return Err(DecodeError::InvalidPadding);
816 }
817 Ok((in_len, 0))
818 }
819}
820
821const fn count_trailing_padding(data: &[u8]) -> usize {
825 let len = data.len();
826 if len == 0 {
827 return 0;
828 }
829 let max_check = if len < 7 { len } else { 7 };
830 let mut count: usize = 0;
831 let mut all_pad: u8 = 0xFF;
832
833 let mut k = 0;
834 while k < max_check {
835 let idx = len - 1 - k;
836 let is_pad = if data[idx] == PAD { 0xFFu8 } else { 0x00u8 };
837 all_pad = all_pad & is_pad;
838 let all_pad_ext = (all_pad as i8 >> 7) as usize;
839 count = ((k + 1) as usize) & all_pad_ext | count & !all_pad_ext;
840 k += 1;
841 }
842
843 count
844}
845
846#[inline]
849const fn char_to_quintet(c: u8, alphabet: Alphabet) -> u8 {
850 match alphabet {
851 Alphabet::Crockford => crockford_to_quintet(c),
852 Alphabet::Rfc4648 | Alphabet::Rfc4648NoPadding => {
853 let not_upper = not_in_range(c, b'A', b'Z');
854 let not_digit = not_in_range(c, b'2', b'7');
855 let value = (c.wrapping_sub(b'A')) & !not_upper | (c.wrapping_sub(b'2').wrapping_add(26)) & !not_digit;
856 let invalid = not_upper & not_digit;
857 value | (invalid & 0x20)
858 }
859 Alphabet::Rfc4648Lower | Alphabet::Rfc4648LowerNoPadding => {
860 let not_lower = not_in_range(c, b'a', b'z');
861 let not_digit = not_in_range(c, b'2', b'7');
862 let value = (c.wrapping_sub(b'a')) & !not_lower | (c.wrapping_sub(b'2').wrapping_add(26)) & !not_digit;
863 let invalid = not_lower & not_digit;
864 value | (invalid & 0x20)
865 }
866 Alphabet::Rfc4648Hex | Alphabet::Rfc4648HexNoPadding => {
867 let not_digit = not_in_range(c, b'0', b'9');
868 let not_upper = not_in_range(c, b'A', b'V');
869 let value = (c.wrapping_sub(b'0')) & !not_digit | (c.wrapping_sub(b'A').wrapping_add(10)) & !not_upper;
870 let invalid = not_digit & not_upper;
871 value | (invalid & 0x20)
872 }
873 Alphabet::Rfc4648HexLower | Alphabet::Rfc4648HexLowerNoPadding => {
874 let not_digit = not_in_range(c, b'0', b'9');
875 let not_lower = not_in_range(c, b'a', b'v');
876 let value = (c.wrapping_sub(b'0')) & !not_digit | (c.wrapping_sub(b'a').wrapping_add(10)) & !not_lower;
877 let invalid = not_digit & not_lower;
878 value | (invalid & 0x20)
879 }
880 Alphabet::Z32 => Z32_DECODE_TABLE[c as usize],
881 }
882}
883
884#[inline]
886const fn crockford_to_quintet(c: u8) -> u8 {
887 let not_0_9 = not_in_range(c, b'0', b'9');
888 let not_a_h = not_in_range(c, b'A', b'H');
889 let not_j_k = not_in_range(c, b'J', b'K');
890 let not_m_n = not_in_range(c, b'M', b'N');
891 let not_p_t = not_in_range(c, b'P', b'T');
892 let not_v_z = not_in_range(c, b'V', b'Z');
893 let value = (c.wrapping_sub(b'0')) & !not_0_9
894 | (c.wrapping_sub(b'A').wrapping_add(10)) & !not_a_h
895 | (c.wrapping_sub(b'J').wrapping_add(18)) & !not_j_k
896 | (c.wrapping_sub(b'M').wrapping_add(20)) & !not_m_n
897 | (c.wrapping_sub(b'P').wrapping_add(22)) & !not_p_t
898 | (c.wrapping_sub(b'V').wrapping_add(27)) & !not_v_z;
899 let invalid = not_0_9 & not_a_h & not_j_k & not_m_n & not_p_t & not_v_z;
900 value | (invalid & 0x20)
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906
907 const ENCODE_VECTORS: &[(&[u8], Alphabet, &str, &str)] = &[
909 (b"", Alphabet::Rfc4648, "", "RFC4648 padded: empty"),
910 (b"", Alphabet::Rfc4648NoPadding, "", "RFC4648 unpadded: empty"),
911 (b"\x00", Alphabet::Rfc4648, "AA======", "RFC4648 padded: 0x00"),
912 (b"\xFF", Alphabet::Rfc4648, "74======", "RFC4648 padded: 0xFF"),
913 (b"\xAB", Alphabet::Rfc4648, "VM======", "RFC4648 padded: 0xAB"),
914 (b"fo", Alphabet::Rfc4648, "MZXQ====", "RFC4648 padded: 'fo'"),
915 (b"foo", Alphabet::Rfc4648, "MZXW6===", "RFC4648 padded: 'foo'"),
916 (b"foob", Alphabet::Rfc4648, "MZXW6YQ=", "RFC4648 padded: 'foob'"),
917 (b"fooba", Alphabet::Rfc4648, "MZXW6YTB", "RFC4648 padded: 'fooba'"),
918 (b"foobar", Alphabet::Rfc4648, "MZXW6YTBOI======", "RFC4648 padded: 'foobar'"),
919 (b"hello", Alphabet::Rfc4648, "NBSWY3DP", "RFC4648 padded: 'hello'"),
920 (b"hello", Alphabet::Rfc4648NoPadding, "NBSWY3DP", "RFC4648 unpadded: 'hello'"),
921 (b"h", Alphabet::Rfc4648NoPadding, "NA", "RFC4648 unpadded: 'h'"),
922 (b"he", Alphabet::Rfc4648NoPadding, "NBSQ", "RFC4648 unpadded: 'he'"),
923 (b"hel", Alphabet::Rfc4648NoPadding, "NBSWY", "RFC4648 unpadded: 'hel'"),
924 (b"hell", Alphabet::Rfc4648NoPadding, "NBSWY3A", "RFC4648 unpadded: 'hell'"),
925 (b"hello", Alphabet::Rfc4648Lower, "nbswy3dp", "RFC4648 lower: 'hello'"),
926 (b"hello", Alphabet::Rfc4648Hex, "D1IMOR3F", "RFC4648 hex: 'hello'"),
927 (b"hello", Alphabet::Rfc4648HexLower, "d1imor3f", "RFC4648 hex lower: 'hello'"),
928 (b"hello", Alphabet::Crockford, "D1JPRV3F", "Crockford: 'hello'"),
929 (b"f", Alphabet::Rfc4648Hex, "CO======", "RFC4648 hex: 'f'"),
931 (b"fo", Alphabet::Rfc4648Hex, "CPNG====", "RFC4648 hex: 'fo'"),
932 (b"foo", Alphabet::Rfc4648Hex, "CPNMU===", "RFC4648 hex: 'foo'"),
933 (b"foob", Alphabet::Rfc4648Hex, "CPNMUOG=", "RFC4648 hex: 'foob'"),
934 (b"fooba", Alphabet::Rfc4648Hex, "CPNMUOJ1", "RFC4648 hex: 'fooba'"),
935 (b"foobar", Alphabet::Rfc4648Hex, "CPNMUOJ1E8======", "RFC4648 hex: 'foobar'"),
936 (b"", Alphabet::Z32, "", "Z32: empty"),
938 (b"\x00", Alphabet::Z32, "yy", "Z32: 0x00"),
939 (b"\xff", Alphabet::Z32, "9h", "Z32: 0xFF"),
940 (b"\xab", Alphabet::Z32, "ic", "Z32: 0xAB"),
941 (b"fo", Alphabet::Z32, "c3zo", "Z32: fo"),
942 (b"foo", Alphabet::Z32, "c3zs6", "Z32: foo"),
943 (b"foob", Alphabet::Z32, "c3zs6ao", "Z32: foob"),
944 (b"fooba", Alphabet::Z32, "c3zs6aub", "Z32: fooba"),
945 (b"foobar", Alphabet::Z32, "c3zs6aubqe", "Z32: foobar"),
946 (b"hello", Alphabet::Z32, "pb1sa5dx", "Z32: hello"),
947 (b"h", Alphabet::Z32, "py", "Z32: h"),
948 (b"he", Alphabet::Z32, "pb1o", "Z32: he"),
949 (b"hel", Alphabet::Z32, "pb1sa", "Z32: hel"),
950 (b"hell", Alphabet::Z32, "pb1sa5y", "Z32: hell"),
951 ];
952
953 const DECODE_VECTORS: &[(&[u8], Alphabet, &[u8], &str)] = &[
955 (b"", Alphabet::Rfc4648, b"", "RFC4648 padded: empty"),
956 (b"AA======", Alphabet::Rfc4648, b"\x00", "RFC4648 padded: 0x00"),
957 (b"AE======", Alphabet::Rfc4648, b"\x01", "RFC4648 padded: 0x01"),
958 (b"MZXQ====", Alphabet::Rfc4648, b"fo", "RFC4648 padded: 'fo'"),
959 (b"MZXW6===", Alphabet::Rfc4648, b"foo", "RFC4648 padded: 'foo'"),
960 (b"MZXW6YQ=", Alphabet::Rfc4648, b"foob", "RFC4648 padded: 'foob'"),
961 (b"MZXW6YTB", Alphabet::Rfc4648, b"fooba", "RFC4648 padded: 'fooba'"),
962 (b"NA", Alphabet::Rfc4648NoPadding, b"h", "RFC4648 unpadded: 'h'"),
963 (b"NBSQ", Alphabet::Rfc4648NoPadding, b"he", "RFC4648 unpadded: 'he'"),
964 (b"NBSWY", Alphabet::Rfc4648NoPadding, b"hel", "RFC4648 unpadded: 'hel'"),
965 (b"NBSWY3A", Alphabet::Rfc4648NoPadding, b"hell", "RFC4648 unpadded: 'hell'"),
966 (b"nbswy3dp", Alphabet::Rfc4648Lower, b"hello", "RFC4648 lower: 'hello'"),
967 (b"D1IMOR3F", Alphabet::Rfc4648Hex, b"hello", "RFC4648 hex: 'hello'"),
968 (b"D1JPRV3F", Alphabet::Crockford, b"hello", "Crockford: 'hello'"),
969 (b"", Alphabet::Z32, b"", "Z32: empty"),
971 (b"yy", Alphabet::Z32, b"\x00", "Z32: 0x00"),
972 (b"9h", Alphabet::Z32, b"\xff", "Z32: 0xFF"),
973 (b"ic", Alphabet::Z32, b"\xab", "Z32: 0xAB"),
974 (b"c3zo", Alphabet::Z32, b"fo", "Z32: fo"),
975 (b"c3zs6", Alphabet::Z32, b"foo", "Z32: foo"),
976 (b"c3zs6ao", Alphabet::Z32, b"foob", "Z32: foob"),
977 (b"c3zs6aub", Alphabet::Z32, b"fooba", "Z32: fooba"),
978 (b"c3zs6aubqe", Alphabet::Z32, b"foobar", "Z32: foobar"),
979 (b"pb1sa5dx", Alphabet::Z32, b"hello", "Z32: hello"),
980 (b"py", Alphabet::Z32, b"h", "Z32: h"),
981 (b"pb1o", Alphabet::Z32, b"he", "Z32: he"),
982 (b"pb1sa", Alphabet::Z32, b"hel", "Z32: hel"),
983 (b"pb1sa5y", Alphabet::Z32, b"hell", "Z32: hell"),
984 ];
985
986 const DECODE_ERROR_VECTORS: &[(&[u8], Alphabet, DecodeError, &str)] = &[
988 (b"!!!!====", Alphabet::Rfc4648, DecodeError::InvalidInput, "4 invalid chars"),
989 (
990 b"AAA=====",
991 Alphabet::Rfc4648,
992 DecodeError::InvalidPadding,
993 "wrong padding position",
994 ),
995 (
996 b"AA==========",
997 Alphabet::Rfc4648,
998 DecodeError::InvalidPadding,
999 "too many padding chars",
1000 ),
1001 (
1002 b"AA======",
1003 Alphabet::Rfc4648NoPadding,
1004 DecodeError::InvalidPadding,
1005 "no-pad rejects padding",
1006 ),
1007 (b"A", Alphabet::Rfc4648, DecodeError::InvalidLength, "single char"),
1008 (
1009 b"D1JPRV!!",
1010 Alphabet::Crockford,
1011 DecodeError::InvalidInput,
1012 "Crockford invalid chars",
1013 ),
1014 (b"!!!!", Alphabet::Z32, DecodeError::InvalidInput, "Z32 invalid chars"),
1015 ];
1016
1017 const ENCODED_LENGTH_VECTORS: &[(usize, bool, Option<usize>, &str)] = &[
1019 (0, true, Some(0), "empty padded"),
1020 (1, true, Some(8), "1 byte padded"),
1021 (5, true, Some(8), "5 bytes padded"),
1022 (6, true, Some(16), "6 bytes padded"),
1023 (10, true, Some(16), "10 bytes padded"),
1024 (0, false, Some(0), "empty unpadded"),
1025 (1, false, Some(2), "1 byte unpadded"),
1026 (5, false, Some(8), "5 bytes unpadded"),
1027 (6, false, Some(10), "6 bytes unpadded"),
1028 ];
1029
1030 const ENCODE_INTO_STRING_VECTORS: &[(&str, &[u8], Alphabet, &str, &str)] = &[
1032 ("", b"", Alphabet::Rfc4648, "", "empty"),
1033 ("prefix", b"", Alphabet::Rfc4648, "prefix", "empty data with prefix"),
1034 (
1035 "",
1036 b"hello world",
1037 Alphabet::Rfc4648,
1038 "NBSWY3DPEB3W64TMMQ======",
1039 "hello world padded",
1040 ),
1041 (
1042 "",
1043 b"hello world",
1044 Alphabet::Rfc4648NoPadding,
1045 "NBSWY3DPEB3W64TMMQ",
1046 "hello world unpadded",
1047 ),
1048 (
1049 "data: ",
1050 b"hello world",
1051 Alphabet::Rfc4648,
1052 "data: NBSWY3DPEB3W64TMMQ======",
1053 "append to prefix",
1054 ),
1055 ("", b"foobar", Alphabet::Rfc4648Hex, "CPNMUOJ1E8======", "hex alphabet"),
1056 ];
1057
1058 const ALL_ALPHABETS: &[Alphabet] = &[
1059 Alphabet::Rfc4648,
1060 Alphabet::Rfc4648NoPadding,
1061 Alphabet::Rfc4648Lower,
1062 Alphabet::Rfc4648Hex,
1063 Alphabet::Rfc4648HexLower,
1064 Alphabet::Crockford,
1065 Alphabet::Z32,
1066 ];
1067
1068 const ROUNDTRIP_SIZES: &[usize] = &[
1069 0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129,
1070 ];
1071
1072 const SIMD_BOUNDARY_SIZES: &[usize] = &[38, 39, 40, 41, 42, 45, 50, 62, 63, 64, 65, 66, 84, 85, 100];
1073
1074 #[test]
1075 fn test_encode() {
1076 for &(input, alphabet, expected, desc) in ENCODE_VECTORS {
1077 let result = encode(input, alphabet);
1078 assert_eq!(result, expected, "encode: {desc}");
1079 }
1080 }
1081
1082 #[test]
1083 fn test_decode() {
1084 for &(encoded, alphabet, expected, desc) in DECODE_VECTORS {
1085 let result = decode(encoded, alphabet).unwrap();
1086 assert_eq!(&result, expected, "decode: {desc}");
1087 }
1088 }
1089
1090 #[test]
1091 fn test_decode_error() {
1092 for &(encoded, alphabet, expected_err, desc) in DECODE_ERROR_VECTORS {
1093 let result = decode(encoded, alphabet);
1094 assert_eq!(result, Err(expected_err), "decode error: {desc}");
1095 }
1096
1097 let mut data = alloc::vec![b'A'; 256];
1099 data[255] = b'!';
1100 assert_eq!(decode(&data, Alphabet::Rfc4648), Err(DecodeError::InvalidInput));
1101 }
1102
1103 #[test]
1104 fn test_encoded_length() {
1105 for &(data_len, padding, expected, desc) in ENCODED_LENGTH_VECTORS {
1106 let result = encoded_length(data_len, padding);
1107 assert_eq!(result, expected, "encoded_length: {desc}");
1108 }
1109 }
1110
1111 #[test]
1112 fn test_encode_into_string() {
1113 for &(initial, input, alphabet, expected, desc) in ENCODE_INTO_STRING_VECTORS {
1114 let mut s = alloc::string::String::from(initial);
1115 encode_into_string(&mut s, input, alphabet);
1116 assert_eq!(s, expected, "encode_into_string: {desc}");
1117 }
1118
1119 let mut s = alloc::string::String::from("~~");
1121 encode_into_string(&mut s, b"hello", Alphabet::Rfc4648);
1122 assert_eq!(s, "~~NBSWY3DP");
1123 encode_into_string(&mut s, b"foo", Alphabet::Rfc4648);
1124 assert_eq!(s, "~~NBSWY3DPMZXW6===");
1125
1126 for alphabet in ALL_ALPHABETS {
1128 let expected = encode(b"hello world", *alphabet);
1129 let mut s = alloc::string::String::new();
1130 encode_into_string(&mut s, b"hello world", *alphabet);
1131 assert_eq!(s, expected, "encode_into_string alphabet {alphabet:?}");
1132 }
1133 }
1134
1135 #[test]
1136 fn test_roundtrip() {
1137 for &len in ROUNDTRIP_SIZES {
1138 let data: Vec<u8> = (0..len as u8).collect();
1139 for alphabet in ALL_ALPHABETS {
1140 let encoded = encode(&data, *alphabet);
1141 let decoded = decode(encoded.as_bytes(), *alphabet).unwrap();
1142 assert_eq!(decoded, data, "roundtrip len={len} alphabet={alphabet:?}");
1143 }
1144 }
1145 }
1146
1147 #[test]
1148 fn test_roundtrip_large() {
1149 let size = 4096;
1150
1151 let data = alloc::vec![0x00u8; size];
1152 let elen = encoded_length(size, true).expect("encoded_len overflow");
1153 let mut encoded = alloc::vec![0u8; elen];
1154 encode_into_constant_time(&mut encoded, &data, Alphabet::Rfc4648).unwrap();
1155 let mut decoded = alloc::vec![0u8; size];
1156 decode_into_constant_time(&mut decoded, &encoded, Alphabet::Rfc4648).unwrap();
1157 assert_eq!(decoded, data, "4096 zeroes constant-time");
1158
1159 let data = alloc::vec![0xFFu8; size];
1160 let mut encoded = alloc::vec![0u8; elen];
1161 encode_into_constant_time(&mut encoded, &data, Alphabet::Rfc4648).unwrap();
1162 decode_into_constant_time(&mut decoded, &encoded, Alphabet::Rfc4648).unwrap();
1163 assert_eq!(decoded, data, "4096 0xFF constant-time");
1164
1165 let data: Vec<u8> = (0..=255).cycle().take(size).collect();
1166 let encoded = encode(&data, Alphabet::Rfc4648);
1167 let decoded = decode(encoded.as_bytes(), Alphabet::Rfc4648).unwrap();
1168 assert_eq!(decoded, data, "4096 cycle dispatch");
1169
1170 let data: Vec<u8> = (0..=255).collect();
1171 let mut s = alloc::string::String::new();
1172 encode_into_string(&mut s, &data, Alphabet::Rfc4648);
1173 let decoded = decode(s.as_bytes(), Alphabet::Rfc4648).unwrap();
1174 assert_eq!(decoded, data, "256-byte encode_into_string roundtrip");
1175
1176 let data: Vec<u8> = (0..255).cycle().take(4096).collect();
1177 let expected = encode(&data, Alphabet::Rfc4648);
1178 let mut s = alloc::string::String::new();
1179 encode_into_string(&mut s, &data, Alphabet::Rfc4648);
1180 assert_eq!(s, expected, "4096-byte encode_into_string");
1181 }
1182
1183 #[test]
1184 fn test_encode_all_single_bytes() {
1185 for byte in 0..=255u8 {
1186 for alphabet in &[
1187 Alphabet::Rfc4648,
1188 Alphabet::Rfc4648Lower,
1189 Alphabet::Rfc4648Hex,
1190 Alphabet::Rfc4648HexLower,
1191 Alphabet::Crockford,
1192 Alphabet::Z32,
1193 ] {
1194 let padding = alphabet.is_padded();
1195 let elen = encoded_length(1, padding).unwrap();
1196 let mut encoded = alloc::vec![0u8; elen];
1197 encode_into_constant_time(&mut encoded, &[byte], *alphabet).unwrap();
1198 let mut decoded = [0u8; 1];
1199 decode_into_constant_time(&mut decoded, &encoded, *alphabet).unwrap();
1200 assert_eq!(decoded[0], byte, "single byte roundtrip {byte:#04x} alphabet={alphabet:?}");
1201 }
1202 }
1203 }
1204
1205 #[test]
1206 fn test_decode_invalid_char_every_position() {
1207 let mut out = [0u8; 128];
1208
1209 for pos in 0..8 {
1211 let mut input = [b'A'; 8];
1212 input[pos] = b'!';
1213 assert_eq!(
1214 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648),
1215 Err(DecodeError::InvalidInput),
1216 "invalid char at position {pos} in 8-char input"
1217 );
1218 }
1219
1220 for pos in 0..64 {
1222 let mut input = [b'A'; 64];
1223 input[pos] = b'!';
1224 assert_eq!(
1225 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648),
1226 Err(DecodeError::InvalidInput),
1227 "invalid char at position {pos} in 64-char input"
1228 );
1229 }
1230
1231 for pos in 0..72 {
1233 let mut input = [b'A'; 72];
1234 input[pos] = b'!';
1235 assert_eq!(
1236 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648),
1237 Err(DecodeError::InvalidInput),
1238 "invalid char at position {pos} in 72-char input"
1239 );
1240 }
1241
1242 for pos in 0..8 {
1244 let mut input = [b'a'; 8];
1245 input[pos] = b'!';
1246 assert_eq!(
1247 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648Lower),
1248 Err(DecodeError::InvalidInput)
1249 );
1250 }
1251 for pos in 0..8 {
1252 let mut input = [b'0'; 8];
1253 input[pos] = b'!';
1254 assert_eq!(
1255 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648Hex),
1256 Err(DecodeError::InvalidInput)
1257 );
1258 }
1259 for pos in 0..8 {
1260 let mut input = [b'0'; 8];
1261 input[pos] = b'!';
1262 assert_eq!(
1263 decode_into_constant_time(&mut out, &input, Alphabet::Rfc4648HexLower),
1264 Err(DecodeError::InvalidInput)
1265 );
1266 }
1267 for pos in 0..8 {
1268 let mut input = [b'0'; 8];
1269 input[pos] = b'!';
1270 assert_eq!(
1271 decode_into_constant_time(&mut out, &input, Alphabet::Crockford),
1272 Err(DecodeError::InvalidInput)
1273 );
1274 }
1275 for pos in 0..8 {
1276 let mut input = [b'y'; 8];
1277 input[pos] = b'!';
1278 assert_eq!(
1279 decode_into_constant_time(&mut out, &input, Alphabet::Z32),
1280 Err(DecodeError::InvalidInput)
1281 );
1282 }
1283 }
1284
1285 #[test]
1286 fn test_decode_non_canonical_trailing_bits() {
1287 let mut out = [0u8; 8];
1288
1289 for &(input, expected) in &[
1291 (b"AA======" as &[u8], Ok(())),
1292 (b"AB======" as &[u8], Err(DecodeError::InvalidInput)),
1293 (b"AC======" as &[u8], Err(DecodeError::InvalidInput)),
1294 (b"AD======" as &[u8], Err(DecodeError::InvalidInput)),
1295 ] {
1296 assert_eq!(
1297 decode_into_constant_time(&mut out, input, Alphabet::Rfc4648),
1298 expected,
1299 "non-canonical (rem=2): {:?}",
1300 core::str::from_utf8(input)
1301 );
1302 }
1303
1304 for &(input, expected) in &[
1306 (b"MZXQ====" as &[u8], Ok(())),
1307 (b"MZXR====" as &[u8], Err(DecodeError::InvalidInput)),
1308 ] {
1309 assert_eq!(
1310 decode_into_constant_time(&mut out, input, Alphabet::Rfc4648),
1311 expected,
1312 "non-canonical (rem=4): {:?}",
1313 core::str::from_utf8(input)
1314 );
1315 }
1316
1317 for &(input, expected) in &[
1319 (b"MZXW6===" as &[u8], Ok(())),
1320 (b"MZXW7===" as &[u8], Err(DecodeError::InvalidInput)),
1321 ] {
1322 assert_eq!(
1323 decode_into_constant_time(&mut out, input, Alphabet::Rfc4648),
1324 expected,
1325 "non-canonical (rem=5): {:?}",
1326 core::str::from_utf8(input)
1327 );
1328 }
1329
1330 assert_eq!(decode_into_constant_time(&mut out, b"NBSWY3DP", Alphabet::Rfc4648), Ok(()));
1332 }
1333
1334 #[test]
1335 fn test_decode_rejects_interior_padding() {
1336 let mut out = [0u8; 8];
1337 assert_eq!(
1338 decode_into_constant_time(&mut out, b"=AAA====", Alphabet::Rfc4648),
1339 Err(DecodeError::InvalidInput)
1340 );
1341 assert_eq!(
1342 decode_into_constant_time(&mut out, b"A=AA====", Alphabet::Rfc4648),
1343 Err(DecodeError::InvalidInput)
1344 );
1345 assert_eq!(
1346 decode_into_constant_time(&mut out, b"AA=A====", Alphabet::Rfc4648),
1347 Err(DecodeError::InvalidInput)
1348 );
1349 assert_eq!(
1350 decode_into_constant_time(&mut out, b"AAA=====", Alphabet::Rfc4648),
1351 Err(DecodeError::InvalidPadding)
1352 );
1353 }
1354
1355 #[test]
1356 fn test_roundtrip_simd_boundary_sizes() {
1357 let mut data_buf = Vec::new();
1358 let mut enc_buf = Vec::new();
1359
1360 for &input_len in SIMD_BOUNDARY_SIZES {
1361 data_buf.clear();
1362 for b in 0..input_len {
1363 data_buf.push(b as u8);
1364 }
1365
1366 for alphabet in &[Alphabet::Rfc4648, Alphabet::Rfc4648NoPadding] {
1367 let padding = alphabet.is_padded();
1368 let elen = encoded_length(input_len, padding).expect("encoded_len overflow");
1369 enc_buf.resize(elen, 0);
1370 encode_into_constant_time(&mut enc_buf, &data_buf, *alphabet).unwrap();
1371
1372 let mut decoded = alloc::vec![0u8; input_len];
1373 assert_eq!(
1374 decode_into_constant_time(&mut decoded, &enc_buf, *alphabet),
1375 Ok(()),
1376 "decode failed len={input_len} alphabet={alphabet:?}"
1377 );
1378 assert_eq!(&decoded, &data_buf, "roundtrip mismatch len={input_len} alphabet={alphabet:?}");
1379 }
1380 }
1381 }
1382
1383 #[test]
1384 fn test_const_encode() {
1385 const RESULT: [u8; 8] = encode_array::<8>(b"hello", Alphabet::Rfc4648);
1386 assert_eq!(&RESULT, b"NBSWY3DP");
1387
1388 const RESULT_EMPTY: [u8; 0] = encode_array::<0>(b"", Alphabet::Rfc4648);
1389 assert_eq!(RESULT_EMPTY.len(), 0);
1390
1391 const RESULT_CROCKFORD: [u8; 8] = encode_array::<8>(b"hello", Alphabet::Crockford);
1392 assert_eq!(&RESULT_CROCKFORD, b"D1JPRV3F");
1393
1394 const RESULT_Z32: [u8; 8] = encode_array::<8>(b"hello", Alphabet::Z32);
1395 assert_eq!(&RESULT_Z32, b"pb1sa5dx");
1396 }
1397
1398 #[test]
1399 fn test_const_decode() {
1400 const RESULT: Result<[u8; 5], DecodeError> = decode_array::<5>(b"NBSWY3DP", Alphabet::Rfc4648);
1401 assert_eq!(RESULT.unwrap(), *b"hello");
1402
1403 const RESULT_EMPTY: Result<[u8; 0], DecodeError> = decode_array::<0>(b"", Alphabet::Rfc4648);
1404 assert_eq!(RESULT_EMPTY.unwrap().len(), 0);
1405
1406 const RESULT_Z32: Result<[u8; 5], DecodeError> = decode_array::<5>(b"pb1sa5dx", Alphabet::Z32);
1407 assert_eq!(RESULT_Z32.unwrap(), *b"hello");
1408 }
1409
1410 #[test]
1411 fn test_const_decode_error() {
1412 const ERR_INVALID: Result<[u8; 5], DecodeError> = decode_array::<5>(b"D1JPRV!!", Alphabet::Crockford);
1413 assert_eq!(ERR_INVALID, Err(DecodeError::InvalidInput));
1414
1415 const ERR_Z32: Result<[u8; 5], DecodeError> = decode_array::<5>(b"pb1sa!!x", Alphabet::Z32);
1416 assert_eq!(ERR_Z32, Err(DecodeError::InvalidInput));
1417 }
1418
1419 #[test]
1420 fn test_buffer_management() {
1421 let mut out = [0u8; 1];
1422 assert_eq!(
1423 encode_into(&mut out, b"hello", Alphabet::Rfc4648),
1424 Err(EncodeError::InvalidOutputLength)
1425 );
1426
1427 let mut out = [0u8; 5];
1428 decode_into(&mut out, b"NBSWY3DP", Alphabet::Rfc4648).unwrap();
1429 assert_eq!(&out, b"hello");
1430
1431 let mut out = [0u8; 1];
1432 assert_eq!(
1433 decode_into(&mut out, b"NBSWY3DP", Alphabet::Rfc4648),
1434 Err(DecodeError::InvalidLength)
1435 );
1436
1437 let mut remainders = [0u8; 80];
1439 let mut output = [0u8; 80];
1440 for len in 1..=80 {
1441 for i in 0..len {
1442 remainders[i] = (i * 7 + 3) as u8;
1443 }
1444 let encoded = encode(&remainders[..len], Alphabet::Rfc4648);
1445 let expected_output_len = len;
1446 let r = decode_into(&mut output[..expected_output_len], encoded.as_bytes(), Alphabet::Rfc4648);
1447 assert!(r.is_ok(), "decode_into failed at len {}", len);
1448 assert_eq!(&output[..expected_output_len], &remainders[..len], "mismatch at len {}", len);
1449 }
1450 }
1451
1452 #[test]
1453 fn test_display_error() {
1454 assert_eq!(format!("{}", DecodeError::InvalidInput), "invalid base32 character");
1455 assert_eq!(format!("{}", DecodeError::InvalidLength), "invalid base32 length");
1456 assert_eq!(format!("{}", DecodeError::InvalidPadding), "invalid base32 padding");
1457 assert_eq!(
1458 format!("{}", EncodeError::InvalidOutputLength),
1459 "output buffer size is not valid"
1460 );
1461 }
1462
1463 #[cfg(feature = "serde")]
1464 #[test]
1465 fn test_serde() {
1466 #[derive(::serde::Serialize, ::serde::Deserialize)]
1467 struct Data(#[serde(with = "crate::serde")] Vec<u8>);
1468
1469 let data = Data(b"hello world".to_vec());
1470 let json = ::serde_json::to_string(&data).unwrap();
1471 assert_eq!(json, "\"NBSWY3DPEB3W64TMMQ======\"");
1472 let deserialized: Data = ::serde_json::from_str(&json).unwrap();
1473 assert_eq!(deserialized.0, b"hello world");
1474 }
1475}