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