1#![cfg_attr(not(any(feature = "std", test)), no_std)]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4#[cfg(any(feature = "alloc", test))]
44extern crate alloc;
45
46#[cfg(all(feature = "serde", any(feature = "alloc", test)))]
47mod serde;
48
49#[cfg(target_arch = "aarch64")]
50mod hex_neon;
51
52#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
53mod hex_avx2;
54
55const ALPHABET_LOWER: [u8; 16] = *b"0123456789abcdef";
56const ALPHABET_UPPER: [u8; 16] = *b"0123456789ABCDEF";
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Alphabet {
78 Lower,
79 Upper,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum DecodeError {
85 InvalidInput,
88 InvalidInputLength,
90 InvalidOutputLength,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum EncodeError {
97 InvalidOutputLength,
99}
100
101impl core::fmt::Display for DecodeError {
102 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103 match self {
104 Self::InvalidInput => f.write_str("invalid hex character"),
105 Self::InvalidInputLength => f.write_str("odd number of hex characters"),
106 Self::InvalidOutputLength => f.write_str("output buffer size must be equal to input.len() / 2"),
107 }
108 }
109}
110
111impl core::fmt::Display for EncodeError {
112 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113 match self {
114 Self::InvalidOutputLength => f.write_str("output buffer size must be equal to input.len() * 2"),
115 }
116 }
117}
118
119#[cfg(feature = "std")]
120impl std::error::Error for DecodeError {}
121
122#[cfg(feature = "std")]
123impl std::error::Error for EncodeError {}
124
125#[cfg(any(feature = "alloc", test))]
136#[inline]
137pub fn encode(data: impl AsRef<[u8]>) -> alloc::string::String {
138 encode_with_alphabet(data.as_ref(), Alphabet::Lower)
139}
140
141#[cfg(any(feature = "alloc", test))]
149#[inline]
150pub fn encode_with_alphabet(data: impl AsRef<[u8]>, alphabet: Alphabet) -> alloc::string::String {
151 let data = data.as_ref();
152 let mut output = alloc::vec![0u8; data.len() * 2];
153 encode_into(&mut output, data, alphabet).unwrap();
154 unsafe { alloc::string::String::from_utf8_unchecked(output) }
155}
156
157pub const fn encode_array<const OUT: usize>(data: &[u8], alphabet: Alphabet) -> [u8; OUT] {
170 let mut result = [0u8; OUT];
171 match encode_into_constant_time(&mut result, data, alphabet) {
172 Ok(_) => {}
173 Err(_) => panic!("output buffer size is not valid"),
174 };
175 result
176}
177
178#[inline]
197pub fn encode_into(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
198 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
199 if data.len() >= 16 {
200 check_encode_output_length(data.len(), output.len())?;
201 return unsafe { hex_neon::encode_into(output, data, alphabet) };
202 }
203
204 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
205 if data.len() >= 32 {
206 check_encode_output_length(data.len(), output.len())?;
207 return unsafe { hex_avx2::encode_into(output, data, alphabet) };
208 }
209
210 return encode_into_constant_time(output, data, alphabet);
211}
212
213pub const fn encode_into_constant_time(output: &mut [u8], data: &[u8], alphabet: Alphabet) -> Result<(), EncodeError> {
228 match check_encode_output_length(data.len(), output.len()) {
229 Ok(_) => {}
230 Err(err) => return Err(err),
231 };
232
233 let mut i = 0;
234 let len = data.len();
235
236 while i + 16 <= len {
237 let b0 = data[i];
238 let b1 = data[i + 1];
239 let b2 = data[i + 2];
240 let b3 = data[i + 3];
241 let b4 = data[i + 4];
242 let b5 = data[i + 5];
243 let b6 = data[i + 6];
244 let b7 = data[i + 7];
245 let b8 = data[i + 8];
246 let b9 = data[i + 9];
247 let b10 = data[i + 10];
248 let b11 = data[i + 11];
249 let b12 = data[i + 12];
250 let b13 = data[i + 13];
251 let b14 = data[i + 14];
252 let b15 = data[i + 15];
253
254 let o = i * 2;
255 output[o] = nibble_to_hex(b0 >> 4, alphabet);
256 output[o + 1] = nibble_to_hex(b0 & 0x0F, alphabet);
257 output[o + 2] = nibble_to_hex(b1 >> 4, alphabet);
258 output[o + 3] = nibble_to_hex(b1 & 0x0F, alphabet);
259 output[o + 4] = nibble_to_hex(b2 >> 4, alphabet);
260 output[o + 5] = nibble_to_hex(b2 & 0x0F, alphabet);
261 output[o + 6] = nibble_to_hex(b3 >> 4, alphabet);
262 output[o + 7] = nibble_to_hex(b3 & 0x0F, alphabet);
263 output[o + 8] = nibble_to_hex(b4 >> 4, alphabet);
264 output[o + 9] = nibble_to_hex(b4 & 0x0F, alphabet);
265 output[o + 10] = nibble_to_hex(b5 >> 4, alphabet);
266 output[o + 11] = nibble_to_hex(b5 & 0x0F, alphabet);
267 output[o + 12] = nibble_to_hex(b6 >> 4, alphabet);
268 output[o + 13] = nibble_to_hex(b6 & 0x0F, alphabet);
269 output[o + 14] = nibble_to_hex(b7 >> 4, alphabet);
270 output[o + 15] = nibble_to_hex(b7 & 0x0F, alphabet);
271 output[o + 16] = nibble_to_hex(b8 >> 4, alphabet);
272 output[o + 17] = nibble_to_hex(b8 & 0x0F, alphabet);
273 output[o + 18] = nibble_to_hex(b9 >> 4, alphabet);
274 output[o + 19] = nibble_to_hex(b9 & 0x0F, alphabet);
275 output[o + 20] = nibble_to_hex(b10 >> 4, alphabet);
276 output[o + 21] = nibble_to_hex(b10 & 0x0F, alphabet);
277 output[o + 22] = nibble_to_hex(b11 >> 4, alphabet);
278 output[o + 23] = nibble_to_hex(b11 & 0x0F, alphabet);
279 output[o + 24] = nibble_to_hex(b12 >> 4, alphabet);
280 output[o + 25] = nibble_to_hex(b12 & 0x0F, alphabet);
281 output[o + 26] = nibble_to_hex(b13 >> 4, alphabet);
282 output[o + 27] = nibble_to_hex(b13 & 0x0F, alphabet);
283 output[o + 28] = nibble_to_hex(b14 >> 4, alphabet);
284 output[o + 29] = nibble_to_hex(b14 & 0x0F, alphabet);
285 output[o + 30] = nibble_to_hex(b15 >> 4, alphabet);
286 output[o + 31] = nibble_to_hex(b15 & 0x0F, alphabet);
287
288 i += 16;
289 }
290
291 while i < len {
292 let b = data[i];
293 let o = i * 2;
294 output[o] = nibble_to_hex(b >> 4, alphabet);
295 output[o + 1] = nibble_to_hex(b & 0x0F, alphabet);
296 i += 1;
297 }
298
299 Ok(())
300}
301
302#[inline]
303const fn nibble_to_hex(nibble: u8, alphabet: Alphabet) -> u8 {
304 let nibble = nibble & 0x0F;
305 let digit_mask = (((nibble as i16) - 10) >> 8) as u8;
306
307 let digit_val = b'0' + nibble;
308 let letter_val = b'a' + nibble - 10;
309 let upper_val = b'A' + nibble - 10;
310
311 let lower_result = (digit_val & digit_mask) | (letter_val & !digit_mask);
312 let upper_result = (digit_val & digit_mask) | (upper_val & !digit_mask);
313
314 match alphabet {
315 Alphabet::Lower => lower_result,
316 Alphabet::Upper => upper_result,
317 }
318}
319
320#[inline]
321const fn check_encode_output_length(data_length: usize, output_length: usize) -> Result<(), EncodeError> {
322 if data_length * 2 != output_length {
323 return Err(EncodeError::InvalidOutputLength);
324 }
325 Ok(())
326}
327
328#[cfg(feature = "alloc")]
338pub fn encode_into_string(output: &mut alloc::string::String, data: &[u8], alphabet: Alphabet) {
339 let encoded_length = data.len() * 2;
340 if encoded_length <= 256 {
341 let mut buf = [0u8; 256];
343 let mut buf = &mut buf[..encoded_length];
344 encode_into(&mut buf, data, alphabet).unwrap();
345 output.push_str(unsafe { core::str::from_utf8_unchecked(&buf) });
347 } else {
348 let mut buf = alloc::vec![0u8; encoded_length];
349 encode_into(&mut buf, data, alphabet).unwrap();
350 output.push_str(unsafe { core::str::from_utf8_unchecked(&buf) });
352 }
353}
354
355#[cfg(any(feature = "alloc", test))]
375pub fn decode(data: impl AsRef<[u8]>) -> Result<alloc::vec::Vec<u8>, DecodeError> {
376 let data = data.as_ref();
377 let mut output = alloc::vec![0u8; data.len() / 2];
378 decode_into(&mut output, data)?;
379 Ok(output)
380}
381
382pub const fn decode_array<const OUT: usize>(encoded_data: &[u8]) -> Result<[u8; OUT], DecodeError> {
395 let mut result = [0u8; OUT];
396 match decode_into_constant_time(&mut result, encoded_data) {
397 Ok(_) => {}
398 Err(err) => return Err(err),
399 }
400 Ok(result)
401}
402
403pub fn decode_into(output: &mut [u8], encoded_data: &[u8]) -> Result<(), DecodeError> {
425 #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
426 if encoded_data.len() >= 32 {
427 check_decode_input_and_output_length(encoded_data.len(), output.len())?;
428 return unsafe { hex_neon::decode_into(output, encoded_data) };
429 }
430
431 #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "avx2"))]
432 if encoded_data.len() >= 32 {
433 check_decode_input_and_output_length(encoded_data.len(), output.len())?;
434 return unsafe { hex_avx2::decode_into(output, encoded_data) };
435 }
436
437 decode_into_constant_time(output, encoded_data)
438}
439
440pub const fn decode_into_constant_time(output: &mut [u8], encoded_data: &[u8]) -> Result<(), DecodeError> {
459 match check_decode_input_and_output_length(encoded_data.len(), output.len()) {
460 Ok(_) => {}
461 Err(err) => return Err(err),
462 };
463
464 let in_len = encoded_data.len();
465 let mut i = 0;
466 let mut err: u8 = 0;
467
468 while i + 32 <= in_len {
469 let h0 = nibble_from_hex(encoded_data[i]);
470 let l0 = nibble_from_hex(encoded_data[i + 1]);
471 err |= h0 | l0;
472 output[i / 2] = (h0 << 4) | l0;
473
474 let h1 = nibble_from_hex(encoded_data[i + 2]);
475 let l1 = nibble_from_hex(encoded_data[i + 3]);
476 err |= h1 | l1;
477 output[i / 2 + 1] = (h1 << 4) | l1;
478
479 let h2 = nibble_from_hex(encoded_data[i + 4]);
480 let l2 = nibble_from_hex(encoded_data[i + 5]);
481 err |= h2 | l2;
482 output[i / 2 + 2] = (h2 << 4) | l2;
483
484 let h3 = nibble_from_hex(encoded_data[i + 6]);
485 let l3 = nibble_from_hex(encoded_data[i + 7]);
486 err |= h3 | l3;
487 output[i / 2 + 3] = (h3 << 4) | l3;
488
489 let h4 = nibble_from_hex(encoded_data[i + 8]);
490 let l4 = nibble_from_hex(encoded_data[i + 9]);
491 err |= h4 | l4;
492 output[i / 2 + 4] = (h4 << 4) | l4;
493
494 let h5 = nibble_from_hex(encoded_data[i + 10]);
495 let l5 = nibble_from_hex(encoded_data[i + 11]);
496 err |= h5 | l5;
497 output[i / 2 + 5] = (h5 << 4) | l5;
498
499 let h6 = nibble_from_hex(encoded_data[i + 12]);
500 let l6 = nibble_from_hex(encoded_data[i + 13]);
501 err |= h6 | l6;
502 output[i / 2 + 6] = (h6 << 4) | l6;
503
504 let h7 = nibble_from_hex(encoded_data[i + 14]);
505 let l7 = nibble_from_hex(encoded_data[i + 15]);
506 err |= h7 | l7;
507 output[i / 2 + 7] = (h7 << 4) | l7;
508
509 let h8 = nibble_from_hex(encoded_data[i + 16]);
510 let l8 = nibble_from_hex(encoded_data[i + 17]);
511 err |= h8 | l8;
512 output[i / 2 + 8] = (h8 << 4) | l8;
513
514 let h9 = nibble_from_hex(encoded_data[i + 18]);
515 let l9 = nibble_from_hex(encoded_data[i + 19]);
516 err |= h9 | l9;
517 output[i / 2 + 9] = (h9 << 4) | l9;
518
519 let h10 = nibble_from_hex(encoded_data[i + 20]);
520 let l10 = nibble_from_hex(encoded_data[i + 21]);
521 err |= h10 | l10;
522 output[i / 2 + 10] = (h10 << 4) | l10;
523
524 let h11 = nibble_from_hex(encoded_data[i + 22]);
525 let l11 = nibble_from_hex(encoded_data[i + 23]);
526 err |= h11 | l11;
527 output[i / 2 + 11] = (h11 << 4) | l11;
528
529 let h12 = nibble_from_hex(encoded_data[i + 24]);
530 let l12 = nibble_from_hex(encoded_data[i + 25]);
531 err |= h12 | l12;
532 output[i / 2 + 12] = (h12 << 4) | l12;
533
534 let h13 = nibble_from_hex(encoded_data[i + 26]);
535 let l13 = nibble_from_hex(encoded_data[i + 27]);
536 err |= h13 | l13;
537 output[i / 2 + 13] = (h13 << 4) | l13;
538
539 let h14 = nibble_from_hex(encoded_data[i + 28]);
540 let l14 = nibble_from_hex(encoded_data[i + 29]);
541 err |= h14 | l14;
542 output[i / 2 + 14] = (h14 << 4) | l14;
543
544 let h15 = nibble_from_hex(encoded_data[i + 30]);
545 let l15 = nibble_from_hex(encoded_data[i + 31]);
546 err |= h15 | l15;
547 output[i / 2 + 15] = (h15 << 4) | l15;
548
549 i += 32;
550 }
551
552 while i < in_len {
553 let h = nibble_from_hex(encoded_data[i]);
554 let l = nibble_from_hex(encoded_data[i + 1]);
555 err |= h | l;
556 output[i / 2] = (h << 4) | l;
557 i += 2;
558 }
559
560 if err & 0xF0 != 0 {
561 return Err(DecodeError::InvalidInput);
562 }
563
564 Ok(())
565}
566
567#[inline]
568const fn nibble_from_hex(c: u8) -> u8 {
569 let is_digit = ((((c as i16) - (b'0' as i16)) | ((b'9' as i16) - (c as i16))) >> 8) as u8;
570 let is_lower = ((((c as i16) - (b'a' as i16)) | ((b'f' as i16) - (c as i16))) >> 8) as u8;
571 let is_upper = ((((c as i16) - (b'A' as i16)) | ((b'F' as i16) - (c as i16))) >> 8) as u8;
572
573 let digit_val = c.wrapping_sub(b'0');
574 let lower_val = c.wrapping_sub(b'a').wrapping_add(10);
575 let upper_val = c.wrapping_sub(b'A').wrapping_add(10);
576
577 let value = (digit_val & !is_digit) | (lower_val & !is_lower) | (upper_val & !is_upper);
578 let invalid = is_digit & is_lower & is_upper;
579
580 value | (invalid & 0xF0)
581}
582
583#[inline]
584const fn check_decode_input_and_output_length(encoded_length: usize, output_length: usize) -> Result<(), DecodeError> {
585 if encoded_length % 2 != 0 {
586 return Err(DecodeError::InvalidInputLength);
587 }
588 if output_length != encoded_length / 2 {
589 return Err(DecodeError::InvalidOutputLength);
590 }
591
592 Ok(())
593}
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[test]
600 fn encode_empty() {
601 assert_eq!(encode(b""), "");
602 let mut out = [0u8; 0];
603 encode_into(&mut out, b"", Alphabet::Lower).unwrap();
604 }
605
606 #[test]
607 fn encode_single_byte() {
608 assert_eq!(encode(b"\x00"), "00");
609 assert_eq!(encode(b"\xFF"), "ff");
610 assert_eq!(encode(b"\xAB"), "ab");
611 assert_eq!(encode_with_alphabet(b"\xAB", Alphabet::Upper), "AB");
612 }
613
614 #[test]
615 fn encode_multiple_bytes() {
616 assert_eq!(encode(b"hello"), "68656c6c6f");
617 assert_eq!(encode_with_alphabet(b"hello", Alphabet::Upper), "68656C6C6F");
618 assert_eq!(
619 encode(b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"),
620 "00112233445566778899aabbccddeeff"
621 );
622 }
623
624 #[test]
625 fn encode_all_bytes() {
626 let data: Vec<u8> = (0..=255).collect();
627 let hex = encode(&data);
628 assert_eq!(hex.len(), 512);
629 for (i, &b) in data.iter().enumerate() {
630 let hi = ALPHABET_LOWER[(b >> 4) as usize];
631 let lo = ALPHABET_LOWER[(b & 0x0F) as usize];
632 assert_eq!(hex.as_bytes()[i * 2], hi);
633 assert_eq!(hex.as_bytes()[i * 2 + 1], lo);
634 }
635 }
636
637 #[test]
638 fn encode_into_exact_buffer() {
639 let mut out = [0u8; 4];
640 encode_into(&mut out, b"\xDE\xAD", Alphabet::Upper).unwrap();
641 assert_eq!(&out, b"DEAD");
642 }
643
644 #[test]
645 fn decode_empty() {
646 assert_eq!(decode(b"").unwrap(), b"");
647 }
648
649 #[test]
650 fn decode_single_byte() {
651 assert_eq!(decode(b"00").unwrap(), b"\x00");
652 assert_eq!(decode(b"ff").unwrap(), b"\xFF");
653 assert_eq!(decode(b"FF").unwrap(), b"\xFF");
654 assert_eq!(decode(b"ab").unwrap(), b"\xAB");
655 assert_eq!(decode(b"AB").unwrap(), b"\xAB");
656 }
657
658 #[test]
659 fn decode_multiple_bytes() {
660 assert_eq!(decode(b"68656c6c6f").unwrap(), b"hello");
661 assert_eq!(
662 decode(b"00112233445566778899AABBCCDDEEFF").unwrap(),
663 b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
664 );
665 }
666
667 #[test]
668 fn decode_into_exact_buffer() {
669 let mut out = [0u8; 2];
670 decode_into(&mut out, b"DEAD").unwrap();
671 assert_eq!(&out, b"\xDE\xAD");
672 }
673
674 #[test]
675 fn decode_invalid_character() {
676 assert_eq!(decode(b"0g"), Err(DecodeError::InvalidInput));
677 assert_eq!(decode(b"GG"), Err(DecodeError::InvalidInput));
678 assert_eq!(decode(b" "), Err(DecodeError::InvalidInput));
679 }
680
681 #[test]
682 fn decode_odd_length() {
683 assert_eq!(decode(b"0"), Err(DecodeError::InvalidInputLength));
684 assert_eq!(decode(b"abc"), Err(DecodeError::InvalidInputLength));
685 }
686
687 #[test]
688 fn decode_trailing_invalid_in_large_buffer() {
689 let mut input = alloc::vec![b'0'; 64];
690 input[63] = b'g';
691 assert_eq!(decode(&input), Err(DecodeError::InvalidInput));
692 }
693
694 #[test]
695 fn roundtrip() {
696 let data: Vec<u8> = (0..=255).cycle().take(1024).collect();
697 let hex = encode(&data);
698 let decoded = decode(hex.as_bytes()).unwrap();
699 assert_eq!(decoded, data);
700 }
701
702 #[test]
703 fn roundtrip_upper() {
704 let data: Vec<u8> = (0..=255).cycle().take(1024).collect();
705 let hex = encode_with_alphabet(&data, Alphabet::Upper);
706 let decoded = decode(&hex).unwrap();
707 assert_eq!(decoded, data);
708 }
709
710 #[test]
711 fn roundtrip_various_sizes() {
712 for len in [0, 1, 2, 3, 4, 5, 15, 16, 17, 31, 32, 33, 63, 64, 65, 95, 96] {
713 let data: Vec<u8> = (0..len as u8).collect();
714 let hex = encode(&data);
715 let decoded = decode(hex.as_bytes()).unwrap();
716 assert_eq!(decoded, data, "roundtrip failed for len={}", len);
717 }
718 }
719
720 #[test]
721 fn decode_case_insensitivity() {
722 assert_eq!(decode(b"abcdef"), decode(b"ABCDEF"));
723 assert_eq!(decode(b"AbCdEf"), decode(b"aBcDeF"));
724 }
725
726 #[test]
727 fn rfc4648_test_vectors_encode() {
728 let vectors = [
729 (b"" as &[u8], ""),
730 (b"f", "66"),
731 (b"fo", "666F"),
732 (b"foo", "666F6F"),
733 (b"foob", "666F6F62"),
734 (b"fooba", "666F6F6261"),
735 (b"foobar", "666F6F626172"),
736 ];
737 for (input, expected) in &vectors {
738 assert_eq!(encode_with_alphabet(input, Alphabet::Upper), *expected);
739 }
740 }
741
742 #[test]
743 fn rfc4648_test_vectors_decode() {
744 let vectors = [
745 ("", b"" as &[u8]),
746 ("66", b"f"),
747 ("666F", b"fo"),
748 ("666F6F", b"foo"),
749 ("666F6F62", b"foob"),
750 ("666F6F6261", b"fooba"),
751 ("666F6F626172", b"foobar"),
752 ];
753 for (hex_str, expected) in &vectors {
754 assert_eq!(decode(hex_str.as_bytes()).unwrap(), *expected);
755 }
756 }
757
758 #[test]
759 fn rfc4648_test_vectors_lowercase() {
760 let vectors = [
761 ("66", b"f" as &[u8]),
762 ("666f", b"fo" as &[u8]),
763 ("666f6f", b"foo" as &[u8]),
764 ("666f6f62", b"foob" as &[u8]),
765 ("666f6f6261", b"fooba" as &[u8]),
766 ("666f6f626172", b"foobar" as &[u8]),
767 ];
768 for (hex_str, expected) in &vectors {
769 assert_eq!(decode(hex_str.as_bytes()).unwrap(), *expected);
770 }
771 }
772
773 #[test]
774 fn simd_boundary_nonuniform() {
775 let sizes = [
776 0, 1, 2, 3, 15, 16, 17, 31, 32, 33, 63, 64, 65, 95, 96, 127, 128, 129, 255, 256, 257,
777 ];
778 for &len in &sizes {
779 let data: Vec<u8> = (0..len)
780 .map(|i: usize| (i.wrapping_mul(17).wrapping_add(0xAB)) as u8)
781 .collect();
782 let hex = encode(&data);
783 let decoded = decode(hex.as_bytes()).unwrap();
784 assert_eq!(decoded, data, "non-uniform roundtrip failed for len={}", len);
785 }
786 }
787
788 #[test]
789 fn decode_into_too_small() {
790 let mut out = [0u8; 1];
791 assert_eq!(decode_into(&mut out, b"0000"), Err(DecodeError::InvalidOutputLength));
792 }
793
794 #[test]
795 fn encode_into_panics_on_too_small() {
796 use std::panic::{AssertUnwindSafe, catch_unwind};
797 let mut out = [0u8; 1];
798 let result = catch_unwind(AssertUnwindSafe(|| {
799 encode_into(&mut out, b"hello", Alphabet::Lower).unwrap();
800 }));
801 assert!(result.is_err());
802 }
803
804 #[cfg(feature = "serde")]
805 #[test]
806 fn serde_roundtrip() {
807 #[derive(::serde::Serialize, ::serde::Deserialize)]
808 struct Data(#[serde(with = "crate::serde")] Vec<u8>);
809
810 let data = Data(b"hello world".to_vec());
811 let json = ::serde_json::to_string(&data).unwrap();
812 assert_eq!(json, "\"68656c6c6f20776f726c64\"");
813 let deserialized: Data = ::serde_json::from_str(&json).unwrap();
814 assert_eq!(deserialized.0, b"hello world");
815 }
816
817 #[test]
818 fn const_encode() {
819 const DATA: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF];
820 const HEX: [u8; 8] = encode_array::<8>(&DATA, Alphabet::Lower);
821 const HEX_UPPER: [u8; 8] = encode_array::<8>(&DATA, Alphabet::Upper);
822 assert_eq!(&HEX, b"deadbeef");
823 assert_eq!(&HEX_UPPER, b"DEADBEEF");
824 }
825
826 #[test]
827 fn const_encode_empty() {
828 const HEX: [u8; 0] = encode_array::<0>(b"", Alphabet::Lower);
829 assert_eq!(HEX.len(), 0);
830 }
831
832 #[test]
833 fn const_decode() {
834 const RESULT: Result<[u8; 4], DecodeError> = decode_array::<4>(b"deadbeef");
835 assert_eq!(RESULT.unwrap(), [0xDE, 0xAD, 0xBE, 0xEF]);
836 }
837
838 #[test]
839 fn const_decode_empty() {
840 const RESULT: Result<[u8; 0], DecodeError> = decode_array::<0>(b"");
841 assert_eq!(RESULT.unwrap().len(), 0);
842 }
843
844 #[test]
845 fn const_decode_upper() {
846 const RESULT: Result<[u8; 4], DecodeError> = decode_array::<4>(b"DEADBEEF");
847 assert_eq!(RESULT.unwrap(), [0xDE, 0xAD, 0xBE, 0xEF]);
848 }
849
850 #[test]
851 fn const_decode_invalid_character() {
852 const ERR: Result<[u8; 1], DecodeError> = decode_array::<1>(b"0g");
853 assert_eq!(ERR, Err(DecodeError::InvalidInput));
854 }
855
856 #[test]
857 fn const_decode_odd_length() {
858 const ERR: Result<[u8; 0], DecodeError> = decode_array::<0>(b"0");
859 assert_eq!(ERR, Err(DecodeError::InvalidInputLength));
860 }
861
862 #[test]
863 fn const_decode_wrong_output_size() {
864 const ERR: Result<[u8; 2], DecodeError> = decode_array::<2>(b"00");
865 assert_eq!(ERR, Err(DecodeError::InvalidOutputLength));
866 }
867
868 #[test]
869 fn encode_into_string_empty() {
870 let mut s = alloc::string::String::new();
871 encode_into_string(&mut s, b"", Alphabet::Lower);
872 assert_eq!(s, "");
873 }
874
875 #[test]
876 fn encode_into_string_empty_data_nonempty_output() {
877 let mut s = alloc::string::String::from("prefix");
878 encode_into_string(&mut s, b"", Alphabet::Lower);
879 assert_eq!(s, "prefix");
880 }
881
882 #[test]
883 fn encode_into_string_single_byte() {
884 let mut s = alloc::string::String::new();
885 encode_into_string(&mut s, b"\x00", Alphabet::Lower);
886 assert_eq!(s, "00");
887 let mut s = alloc::string::String::new();
888 encode_into_string(&mut s, b"\xFF", Alphabet::Upper);
889 assert_eq!(s, "FF");
890 }
891
892 #[test]
893 fn encode_into_string_multiple_bytes() {
894 let mut s = alloc::string::String::new();
895 encode_into_string(&mut s, b"hello", Alphabet::Lower);
896 assert_eq!(s, "68656c6c6f");
897 let mut s = alloc::string::String::new();
898 encode_into_string(&mut s, b"hello", Alphabet::Upper);
899 assert_eq!(s, "68656C6C6F");
900 }
901
902 #[test]
903 fn encode_into_string_append() {
904 let mut s = alloc::string::String::from("~~");
905 encode_into_string(&mut s, b"\xDE\xAD", Alphabet::Lower);
906 assert_eq!(s, "~~dead");
907 encode_into_string(&mut s, b"\xBE\xEF", Alphabet::Lower);
908 assert_eq!(s, "~~deadbeef");
909 }
910
911 #[test]
912 fn encode_into_string_large() {
913 let data: Vec<u8> = (0..255).cycle().take(4096).collect();
914 let expected = encode_with_alphabet(&data, Alphabet::Lower);
915 let mut s = alloc::string::String::new();
916 encode_into_string(&mut s, &data, Alphabet::Lower);
917 assert_eq!(s, expected);
918 }
919
920 #[test]
921 fn encode_into_string_roundtrip() {
922 let data: Vec<u8> = (0..=255).collect();
923 let mut s = alloc::string::String::new();
924 encode_into_string(&mut s, &data, Alphabet::Lower);
925 let decoded = decode(s.as_bytes()).unwrap();
926 assert_eq!(decoded, data);
927 }
928
929 #[test]
930 fn encode_into_string_small_boundary() {
931 let mut s = alloc::string::String::new();
932 encode_into_string(
933 &mut s,
934 b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
935 Alphabet::Lower,
936 );
937 assert_eq!(s, "000102030405060708090a0b0c0d0e0f");
938 }
939
940 #[test]
941 fn encode_into_string_all_alphabets() {
942 let data = b"hello world";
943 for alphabet in &[Alphabet::Lower, Alphabet::Upper] {
944 let expected = encode_with_alphabet(data, *alphabet);
945 let mut s = alloc::string::String::new();
946 encode_into_string(&mut s, data, *alphabet);
947 assert_eq!(s, expected, "mismatch for alphabet {alphabet:?}");
948 }
949 }
950
951 #[test]
952 fn encode_into_string_rfc4648_vectors() {
953 let vectors = [
954 (b"" as &[u8], "", ""),
955 (b"f", "66", "66"),
956 (b"fo", "666f", "666F"),
957 (b"foo", "666f6f", "666F6F"),
958 (b"foob", "666f6f62", "666F6F62"),
959 (b"fooba", "666f6f6261", "666F6F6261"),
960 (b"foobar", "666f6f626172", "666F6F626172"),
961 ];
962 for (input, expected_lower, expected_upper) in &vectors {
963 let mut s = alloc::string::String::new();
964 encode_into_string(&mut s, input, Alphabet::Lower);
965 assert_eq!(s, *expected_lower);
966 let mut s = alloc::string::String::new();
967 encode_into_string(&mut s, input, Alphabet::Upper);
968 assert_eq!(s, *expected_upper);
969 }
970 }
971
972 #[test]
973 fn encode_into_string_exact_stack_capacity() {
974 let data: Vec<u8> = (0..128).collect();
975 let expected = encode(&data);
976 let mut s = alloc::string::String::new();
977 encode_into_string(&mut s, &data, Alphabet::Lower);
978 assert_eq!(s, expected);
979 }
980
981 #[test]
982 fn encode_into_string_exceeds_stack_capacity() {
983 let data: Vec<u8> = (0..129).collect();
984 let expected = encode(&data);
985 let mut s = alloc::string::String::new();
986 encode_into_string(&mut s, &data, Alphabet::Lower);
987 assert_eq!(s, expected);
988 }
989}