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