1#[cfg(all(feature = "der", feature = "hybrid-array"))]
4mod der;
5
6#[cfg(feature = "rlp")]
7mod rlp;
8
9#[cfg(feature = "alloc")]
10use alloc::{string::String, vec::Vec};
11
12use super::Uint;
13use crate::{DecodeError, Limb, Word};
14
15#[cfg(feature = "alloc")]
16use super::boxed::div::div_rem_vartime_in_place;
17#[cfg(feature = "alloc")]
18use super::div_limb::{div2by1, Reciprocal};
19#[cfg(feature = "alloc")]
20use crate::{NonZero, WideWord};
21
22#[cfg(feature = "hybrid-array")]
23use crate::Encoding;
24
25#[cfg(feature = "alloc")]
26const RADIX_ENCODING_LIMBS_LARGE: usize = 32;
27
28const RADIX_ENCODING_MIN: u32 = 2;
29const RADIX_ENCODING_MAX: u32 = 36;
30
31impl<const LIMBS: usize> Uint<LIMBS> {
32 pub const fn from_be_slice(bytes: &[u8]) -> Self {
34 assert!(
35 bytes.len() == Limb::BYTES * LIMBS,
36 "bytes are not the expected size"
37 );
38
39 let mut res = [Limb::ZERO; LIMBS];
40 let mut buf = [0u8; Limb::BYTES];
41 let mut i = 0;
42
43 while i < LIMBS {
44 let mut j = 0;
45 while j < Limb::BYTES {
46 buf[j] = bytes[i * Limb::BYTES + j];
47 j += 1;
48 }
49 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
50 i += 1;
51 }
52
53 Uint::new(res)
54 }
55
56 pub const fn from_be_hex(hex: &str) -> Self {
60 let bytes = hex.as_bytes();
61
62 assert!(
63 bytes.len() == Limb::BYTES * LIMBS * 2,
64 "hex string is not the expected size"
65 );
66
67 let mut res = [Limb::ZERO; LIMBS];
68 let mut buf = [0u8; Limb::BYTES];
69 let mut i = 0;
70 let mut err = 0;
71
72 while i < LIMBS {
73 let mut j = 0;
74 while j < Limb::BYTES {
75 let offset = (i * Limb::BYTES + j) * 2;
76 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
77 err |= byte_err;
78 buf[j] = result;
79 j += 1;
80 }
81 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
82 i += 1;
83 }
84
85 assert!(err == 0, "invalid hex byte");
86
87 Uint::new(res)
88 }
89
90 pub const fn from_le_slice(bytes: &[u8]) -> Self {
92 assert!(
93 bytes.len() == Limb::BYTES * LIMBS,
94 "bytes are not the expected size"
95 );
96
97 let mut res = [Limb::ZERO; LIMBS];
98 let mut buf = [0u8; Limb::BYTES];
99 let mut i = 0;
100
101 while i < LIMBS {
102 let mut j = 0;
103 while j < Limb::BYTES {
104 buf[j] = bytes[i * Limb::BYTES + j];
105 j += 1;
106 }
107 res[i] = Limb(Word::from_le_bytes(buf));
108 i += 1;
109 }
110
111 Uint::new(res)
112 }
113
114 pub const fn from_le_hex(hex: &str) -> Self {
118 let bytes = hex.as_bytes();
119
120 assert!(
121 bytes.len() == Limb::BYTES * LIMBS * 2,
122 "bytes are not the expected size"
123 );
124
125 let mut res = [Limb::ZERO; LIMBS];
126 let mut buf = [0u8; Limb::BYTES];
127 let mut i = 0;
128 let mut err = 0;
129
130 while i < LIMBS {
131 let mut j = 0;
132 while j < Limb::BYTES {
133 let offset = (i * Limb::BYTES + j) * 2;
134 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
135 err |= byte_err;
136 buf[j] = result;
137 j += 1;
138 }
139 res[i] = Limb(Word::from_le_bytes(buf));
140 i += 1;
141 }
142
143 assert!(err == 0, "invalid hex byte");
144
145 Uint::new(res)
146 }
147
148 #[cfg(feature = "hybrid-array")]
151 #[inline]
152 pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
153 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
154
155 for (src, dst) in self
156 .limbs
157 .iter()
158 .rev()
159 .cloned()
160 .zip(out.chunks_exact_mut(Limb::BYTES))
161 {
162 dst.copy_from_slice(&src.to_be_bytes());
163 }
164 }
165
166 #[cfg(feature = "hybrid-array")]
169 #[inline]
170 pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
171 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
172
173 for (src, dst) in self
174 .limbs
175 .iter()
176 .cloned()
177 .zip(out.chunks_exact_mut(Limb::BYTES))
178 {
179 dst.copy_from_slice(&src.to_le_bytes());
180 }
181 }
182
183 pub fn from_str_radix_vartime(src: &str, radix: u32) -> Result<Self, DecodeError> {
194 let mut slf = Self::ZERO;
195 radix_decode_str(src, radix, &mut SliceDecodeByLimb::new(&mut slf.limbs))?;
196 Ok(slf)
197 }
198
199 #[cfg(feature = "alloc")]
203 pub fn to_string_radix_vartime(&self, radix: u32) -> String {
204 let mut buf = *self;
205 radix_encode_limbs_mut_to_string(radix, buf.as_limbs_mut())
206 }
207}
208
209pub(crate) const fn uint_to_be_bytes<const LIMBS: usize, const BYTES: usize>(
211 uint: &Uint<LIMBS>,
212) -> [u8; BYTES] {
213 if BYTES != LIMBS * Limb::BYTES {
214 panic!("BYTES != LIMBS * Limb::BYTES");
215 }
216
217 let mut ret = [0u8; BYTES];
218 let mut i = 0;
219
220 while i < LIMBS {
221 let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes();
222 let mut j = 0;
223
224 while j < Limb::BYTES {
225 ret[i * Limb::BYTES + j] = limb_bytes[j];
226 j += 1;
227 }
228
229 i += 1;
230 }
231
232 ret
233}
234
235pub(crate) const fn uint_to_le_bytes<const LIMBS: usize, const BYTES: usize>(
237 uint: &Uint<LIMBS>,
238) -> [u8; BYTES] {
239 if BYTES != LIMBS * Limb::BYTES {
240 panic!("BYTES != LIMBS * Limb::BYTES");
241 }
242
243 let mut ret = [0u8; BYTES];
244 let mut i = 0;
245
246 while i < LIMBS {
247 let limb_bytes = uint.limbs[i].0.to_le_bytes();
248 let mut j = 0;
249
250 while j < Limb::BYTES {
251 ret[i * Limb::BYTES + j] = limb_bytes[j];
252 j += 1;
253 }
254
255 i += 1;
256 }
257
258 ret
259}
260
261#[inline(always)]
263const fn decode_nibble(src: u8) -> u16 {
264 let byte = src as i16;
265 let mut ret: i16 = -1;
266
267 ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
270 ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
273 ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
276
277 ret as u16
278}
279
280#[inline(always)]
284pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
285 let hi = decode_nibble(bytes[0]);
286 let lo = decode_nibble(bytes[1]);
287 let byte = (hi << 4) | lo;
288 let err = byte >> 8;
289 let result = byte as u8;
290 (result, err)
291}
292
293pub(crate) trait DecodeByLimb {
295 fn limbs_mut(&mut self) -> &mut [Limb];
297
298 fn push_limb(&mut self, limb: Limb) -> bool;
300}
301
302pub(crate) struct SliceDecodeByLimb<'de> {
304 limbs: &'de mut [Limb],
305 len: usize,
306}
307
308impl<'de> SliceDecodeByLimb<'de> {
309 #[inline]
310 pub fn new(limbs: &'de mut [Limb]) -> Self {
311 Self { limbs, len: 0 }
312 }
313}
314
315impl DecodeByLimb for SliceDecodeByLimb<'_> {
316 #[inline]
317 fn push_limb(&mut self, limb: Limb) -> bool {
318 if self.len < self.limbs.len() {
319 self.limbs[self.len] = limb;
320 self.len += 1;
321 true
322 } else {
323 false
324 }
325 }
326
327 #[inline]
328 fn limbs_mut(&mut self) -> &mut [Limb] {
329 &mut self.limbs[..self.len]
330 }
331}
332
333pub(crate) fn radix_decode_str<D: DecodeByLimb>(
338 src: &str,
339 radix: u32,
340 out: &mut D,
341) -> Result<(), DecodeError> {
342 if !(RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix) {
343 panic!("unsupported radix");
344 }
345 if radix == 2 || radix == 4 || radix == 16 {
346 radix_decode_str_aligned_digits(src, radix as u8, out)
347 } else {
348 radix_decode_str_digits(src, radix as u8, out)
349 }
350}
351
352#[inline(always)]
353fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
355 let src_b = src.as_bytes();
357 let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
358
359 if digits.is_empty() {
360 Err(DecodeError::Empty)
362 } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
363 Err(DecodeError::InvalidDigit)
365 } else {
366 while digits[0] == b'0' || digits[0] == b'_' {
368 digits = &digits[1..];
369 if digits.is_empty() {
370 break;
371 }
372 }
373 Ok(digits)
374 }
375}
376
377fn radix_decode_str_digits<D: DecodeByLimb>(
379 src: &str,
380 radix: u8,
381 out: &mut D,
382) -> Result<(), DecodeError> {
383 let digits = radix_preprocess_str(src)?;
384 let mut buf = [0u8; Limb::BITS as _];
385 let mut limb_digits = Word::MAX.ilog(radix as _) as usize;
386 let mut limb_max = Limb(Word::pow(radix as _, limb_digits as _));
387 let mut digits_pos = 0;
388 let mut buf_pos = 0;
389
390 while digits_pos < digits.len() {
391 loop {
393 let digit = match digits[digits_pos] {
394 b @ b'0'..=b'9' => b - b'0',
395 b @ b'a'..=b'z' => b + 10 - b'a',
396 b @ b'A'..=b'Z' => b + 10 - b'A',
397 b'_' => {
398 digits_pos += 1;
399 continue;
400 }
401 _ => radix,
402 };
403 if digit >= radix {
404 return Err(DecodeError::InvalidDigit);
405 }
406 buf[buf_pos] = digit;
407 buf_pos += 1;
408 digits_pos += 1;
409
410 if digits_pos == digits.len() || buf_pos == limb_digits {
411 break;
412 }
413 }
414
415 if buf_pos < limb_digits {
417 limb_digits = buf_pos;
418 limb_max = Limb(Word::pow(radix as _, limb_digits as _));
419 }
420
421 let mut carry = Limb::ZERO;
423 for c in buf[..limb_digits].iter().copied() {
424 carry = Limb(carry.0 * (radix as Word) + (c as Word));
425 }
426 for limb in out.limbs_mut().iter_mut() {
429 (*limb, carry) = Limb::ZERO.mac(*limb, limb_max, carry);
430 }
431 if carry.0 != 0 && !out.push_limb(carry) {
433 return Err(DecodeError::InputSize);
434 }
435
436 buf_pos = 0;
437 buf[..limb_digits].fill(0);
438 }
439
440 Ok(())
441}
442
443fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
446 src: &str,
447 radix: u8,
448 out: &mut D,
449) -> Result<(), DecodeError> {
450 debug_assert!(radix == 2 || radix == 4 || radix == 16);
451
452 let digits = radix_preprocess_str(src)?;
453 let shift = radix.trailing_zeros();
454 let limb_digits = (Limb::BITS / shift) as usize;
455 let mut buf = [0u8; Limb::BITS as _];
456 let mut buf_pos = 0;
457 let mut digits_pos = digits.len();
458
459 while digits_pos > 0 {
460 loop {
462 digits_pos -= 1;
463
464 let digit = match digits[digits_pos] {
465 b @ b'0'..=b'9' => b - b'0',
466 b @ b'a'..=b'z' => b + 10 - b'a',
467 b @ b'A'..=b'Z' => b + 10 - b'A',
468 b'_' => {
469 continue;
471 }
472 _ => radix,
473 };
474 if digit >= radix {
475 return Err(DecodeError::InvalidDigit);
476 }
477 buf[buf_pos] = digit;
478 buf_pos += 1;
479
480 if digits_pos == 0 || buf_pos == limb_digits {
481 break;
482 }
483 }
484
485 if buf_pos > 0 {
486 let mut w: Word = 0;
488 for c in buf[..buf_pos].iter().rev().copied() {
489 w = (w << shift) | (c as Word);
490 }
491 if !out.push_limb(Limb(w)) {
493 return Err(DecodeError::InputSize);
494 }
495
496 buf_pos = 0;
497 buf[..limb_digits].fill(0);
498 }
499 }
500
501 Ok(())
502}
503
504#[cfg(feature = "alloc")]
508pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
509 let mut array_buf = [Limb::ZERO; 128];
510 let mut vec_buf = Vec::new();
511 let limb_count = limbs.len();
512 let buf = if limb_count <= array_buf.len() {
513 array_buf[..limb_count].copy_from_slice(limbs);
514 &mut array_buf[..limb_count]
515 } else {
516 vec_buf.extend_from_slice(limbs);
517 &mut vec_buf[..limb_count]
518 };
519 radix_encode_limbs_mut_to_string(radix, buf)
520}
521
522#[cfg(feature = "alloc")]
527pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut [Limb]) -> String {
528 if !(RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix) {
529 panic!("unsupported radix");
530 }
531
532 let mut out;
533 if radix.is_power_of_two() {
534 let bits = radix.trailing_zeros() as usize;
535 let size = (limbs.len() * Limb::BITS as usize).div_ceil(bits);
536 out = vec![0u8; size];
537 radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
538 } else {
539 let params = RadixDivisionParams::for_radix(radix);
540 let size = params.encoded_size(limbs.len());
541 out = vec![0u8; size];
542 params.encode_limbs(limbs, &mut out[..]);
543 }
544 let size = out.len();
545 let mut skip = 0;
546 while skip + 1 < size && out[skip] == b'0' {
547 skip += 1;
548 }
549 if skip > 0 {
550 out.copy_within(skip..size, 0);
551 out.truncate(size - skip);
552 }
553 String::from_utf8(out).expect("utf-8 decoding error")
554}
555
556#[cfg(feature = "alloc")]
561fn radix_encode_limbs_by_shifting(radix: u32, limbs: &mut [Limb], out: &mut [u8]) {
562 debug_assert!(radix.is_power_of_two());
563 debug_assert!(!out.is_empty());
564
565 let radix_bits = radix.trailing_zeros();
566 let mask = (radix - 1) as u8;
567 let mut out_idx = out.len();
568 let mut digits: WideWord = 0;
569 let mut digits_bits = 0;
570 let mut digit;
571
572 for limb in limbs.iter().chain([&Limb::ZERO]) {
573 digits_bits += Limb::BITS;
574 digits |= (limb.0 as WideWord) << (digits_bits % Limb::BITS);
575 for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
576 out_idx -= 1;
577 (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
578 out[out_idx] = if digit < 10 {
579 b'0' + digit
580 } else {
581 b'a' + (digit - 10)
582 };
583 digits_bits -= radix_bits;
584 }
585 }
586
587 out[0..out_idx].fill(b'0');
588}
589
590#[cfg(feature = "alloc")]
592#[derive(Debug, Clone, Copy)]
593pub(crate) struct RadixDivisionParams {
594 radix: u32,
595 digits_limb: usize,
596 reciprocal: Reciprocal,
597 digits_large: usize,
598 div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
599}
600
601#[cfg(feature = "alloc")]
602impl RadixDivisionParams {
603 #[allow(trivial_numeric_casts)]
605 const ALL: [Self; 31] = {
606 let mut res = [Self {
607 radix: 0,
608 digits_limb: 0,
609 reciprocal: Reciprocal::default(),
610 digits_large: 0,
611 div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
612 }; 31];
613 let mut radix: u32 = 3;
614 let mut i: usize = 0;
615 while radix <= RADIX_ENCODING_MAX {
616 if radix.is_power_of_two() {
617 radix += 1;
618 continue;
619 }
620 let digits_limb = Word::MAX.ilog(radix as Word);
621 let div_limb = NonZero(Limb((radix as Word).pow(digits_limb)));
622 let (div_large, digits_large) =
623 radix_large_divisor(radix, div_limb, digits_limb as usize);
624 res[i] = Self {
625 radix,
626 digits_limb: digits_limb as usize,
627 reciprocal: Reciprocal::new(div_limb),
628 digits_large,
629 div_large,
630 };
631 radix += 1;
632 i += 1;
633 }
634 res
635 };
636
637 #[allow(trivial_numeric_casts)]
638 pub const fn for_radix(radix: u32) -> Self {
639 if radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX {
640 panic!("invalid radix for division");
641 }
642 let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
643 if ret.radix != radix {
644 panic!("radix lookup failure");
645 }
646 ret
647 }
648
649 pub const fn encoded_size(&self, limb_count: usize) -> usize {
651 limb_count * (self.digits_limb + 1)
653 }
654
655 #[allow(trivial_numeric_casts)]
659 fn encode_limbs(&self, limbs: &mut [Limb], out: &mut [u8]) {
660 debug_assert!(!limbs.is_empty());
661
662 let radix = self.radix as Word;
663 let div_limb = self.reciprocal.divisor().0;
664 let mut limb_count = limbs.len();
665 let mut out_idx = out.len();
666
667 if limb_count > RADIX_ENCODING_LIMBS_LARGE {
668 let mut remain;
670 while limb_count >= RADIX_ENCODING_LIMBS_LARGE {
671 remain = self.div_large;
672 div_rem_vartime_in_place(&mut limbs[..limb_count], &mut remain);
673 limb_count = limb_count + 1 - RADIX_ENCODING_LIMBS_LARGE;
674 if limbs[limb_count - 1] == Limb::ZERO {
675 limb_count -= 1;
676 }
677 let next_idx = out_idx.saturating_sub(self.digits_large);
678 self.encode_limbs(&mut remain, &mut out[next_idx..out_idx]);
679 out_idx = next_idx;
680 }
681 }
682
683 let lshift = self.reciprocal.shift();
684 let rshift = (Limb::BITS - lshift) % Limb::BITS;
685 let mut hi = Limb::ZERO;
686 let mut digits_word;
687 let mut digit;
688
689 loop {
690 digits_word = if limb_count > 0 {
691 let mut carry = Limb::ZERO;
692
693 if lshift > 0 {
696 for limb in limbs[..limb_count].iter_mut() {
697 (*limb, carry) = ((*limb << lshift) | carry, *limb >> rshift);
698 }
699 carry |= hi << lshift;
700 } else {
701 carry = hi;
702 }
703
704 for limb in limbs[..limb_count].iter_mut().rev() {
706 (limb.0, carry.0) = div2by1(carry.0, limb.0, &self.reciprocal);
707 }
708 if limbs[limb_count - 1] << lshift < div_limb {
709 hi = limbs[limb_count - 1];
710 limb_count -= 1;
711 } else {
712 hi = Limb::ZERO
713 }
714
715 carry.0 >> lshift
717 } else {
718 let res = hi.0;
720 hi = Limb::ZERO;
721 res
722 };
723
724 for _ in 0..self.digits_limb.min(out_idx) {
726 out_idx -= 1;
727 (digits_word, digit) = (digits_word / radix, (digits_word % radix) as u8);
728 out[out_idx] = if digit < 10 {
729 b'0' + digit
730 } else {
731 b'a' + (digit - 10)
732 };
733 }
734
735 if out_idx == 0 {
737 break;
738 }
739 }
740 }
741}
742
743#[cfg(feature = "alloc")]
748#[allow(trivial_numeric_casts)]
749const fn radix_large_divisor(
750 radix: u32,
751 div_limb: NonZero<Limb>,
752 digits_limb: usize,
753) -> ([Limb; RADIX_ENCODING_LIMBS_LARGE], usize) {
754 let mut out = [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE];
755 let mut digits_large = digits_limb;
756 let mut top = 1;
757 out[0] = div_limb.0;
758 while top < RADIX_ENCODING_LIMBS_LARGE {
760 let mut carry = Limb::ZERO;
761 let mut j = 0;
762 while j < top {
763 (out[j], carry) = Limb::ZERO.mac(out[j], div_limb.0, carry);
764 j += 1;
765 }
766 if carry.0 != 0 {
767 out[top] = carry;
768 top += 1;
769 }
770 digits_large += digits_limb;
771 }
772 let mut out_test = out;
774 loop {
775 let mut carry = Limb::ZERO;
776 let mut j = 0;
777 while j < RADIX_ENCODING_LIMBS_LARGE {
778 (out_test[j], carry) = Limb::ZERO.mac(out[j], Limb(radix as Word), carry);
779 j += 1;
780 }
781 if carry.0 == 0 {
782 out = out_test;
783 digits_large += 1;
784 } else {
785 break;
786 }
787 }
788 (out, digits_large)
789}
790
791#[cfg(test)]
792mod tests {
793 use crate::{DecodeError, Limb, Zero, U128, U64};
794 use hex_literal::hex;
795
796 #[cfg(feature = "alloc")]
797 use alloc::format;
798
799 #[cfg(target_pointer_width = "32")]
800 use crate::U64 as UintEx;
801
802 #[cfg(target_pointer_width = "64")]
803 use crate::U128 as UintEx;
804
805 #[cfg(feature = "alloc")]
806 use super::radix_encode_limbs_to_string;
807
808 #[test]
809 #[cfg(target_pointer_width = "32")]
810 fn from_be_slice() {
811 let bytes = hex!("0011223344556677");
812 let n = UintEx::from_be_slice(&bytes);
813 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
814 }
815
816 #[test]
817 #[cfg(target_pointer_width = "64")]
818 fn from_be_slice() {
819 let bytes = hex!("00112233445566778899aabbccddeeff");
820 let n = UintEx::from_be_slice(&bytes);
821 assert_eq!(
822 n.as_limbs(),
823 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
824 );
825 }
826
827 #[test]
828 #[cfg(target_pointer_width = "32")]
829 fn from_le_slice() {
830 let bytes = hex!("7766554433221100");
831 let n = UintEx::from_le_slice(&bytes);
832 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
833 }
834
835 #[test]
836 #[cfg(target_pointer_width = "64")]
837 fn from_le_slice() {
838 let bytes = hex!("ffeeddccbbaa99887766554433221100");
839 let n = UintEx::from_le_slice(&bytes);
840 assert_eq!(
841 n.as_limbs(),
842 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
843 );
844 }
845
846 #[test]
847 #[cfg(target_pointer_width = "32")]
848 fn from_be_hex() {
849 let n = UintEx::from_be_hex("0011223344556677");
850 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
851 }
852
853 #[test]
854 #[cfg(target_pointer_width = "64")]
855 fn from_be_hex() {
856 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
857 assert_eq!(
858 n.as_limbs(),
859 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
860 );
861 }
862
863 #[test]
864 #[cfg(target_pointer_width = "32")]
865 fn from_le_hex() {
866 let n = UintEx::from_le_hex("7766554433221100");
867 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
868 }
869
870 #[test]
871 #[cfg(target_pointer_width = "64")]
872 fn from_le_hex() {
873 let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
874 assert_eq!(
875 n.as_limbs(),
876 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
877 );
878 }
879
880 #[cfg(feature = "alloc")]
881 #[test]
882 fn hex_upper() {
883 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
884 let n = U128::from_be_hex(hex);
885 assert_eq!(hex, format!("{:X}", n));
886 }
887
888 #[cfg(feature = "alloc")]
889 #[test]
890 fn hex_lower() {
891 let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
892 let n = U128::from_be_hex(hex);
893 assert_eq!(hex, format!("{:x}", n));
894 }
895
896 #[cfg(feature = "alloc")]
897 #[test]
898 fn fmt_binary() {
899 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
900 let n = U128::from_be_hex(hex);
901 let expect = "\
902 1010101010101010101010101010101010111011101110111011101110111011\
903 1100110011001100110011001100110011011101110111011101110111011101";
904 assert_eq!(expect, format!("{:b}", n));
905 }
906
907 #[test]
908 fn from_str_radix_disallowed() {
909 let tests = [
910 ("", 10, DecodeError::Empty),
911 ("+", 10, DecodeError::Empty),
912 ("_", 10, DecodeError::InvalidDigit),
913 ("0_", 10, DecodeError::InvalidDigit),
914 ("0_", 10, DecodeError::InvalidDigit),
915 ("a", 10, DecodeError::InvalidDigit),
916 (".", 10, DecodeError::InvalidDigit),
917 (
918 "99999999999999999999999999999999",
919 10,
920 DecodeError::InputSize,
921 ),
922 ];
923 for (input, radix, expect) in tests {
924 assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
925 }
926 }
927
928 #[test]
929 fn from_str_radix_2() {
930 let buf = &[b'1'; 128];
931 let radix = U128::from_u64(2);
932 let radix_max = U128::from_u64(1);
933 let mut last: Option<U128> = None;
934 for idx in (1..buf.len()).rev() {
935 let res = U128::from_str_radix_vartime(
936 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
937 2,
938 )
939 .expect("error decoding");
940 assert!(!bool::from(res.is_zero()));
941 if let Some(prev) = last {
942 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
943 }
944 last = Some(res);
945 }
946 assert_eq!(last, Some(radix_max));
947 }
948
949 #[test]
950 fn from_str_radix_5() {
951 let buf = &[b'4'; 55];
952 let radix = U128::from_u64(5);
953 let radix_max = U128::from_u64(4);
954 let mut last: Option<U128> = None;
955 for idx in (1..buf.len()).rev() {
956 let res = U128::from_str_radix_vartime(
957 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
958 5,
959 )
960 .expect("error decoding");
961 assert!(!bool::from(res.is_zero()));
962 if let Some(prev) = last {
963 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
964 }
965 last = Some(res);
966 }
967 assert_eq!(last, Some(radix_max));
968 }
969
970 #[test]
971 fn from_str_radix_10() {
972 let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
973 let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
974 assert_eq!(res, U128::MAX);
975 }
976
977 #[cfg(feature = "alloc")]
978 #[test]
979 fn from_str_radix_16() {
980 let hex = "fedcba9876543210fedcba9876543210";
981 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
982 assert_eq!(hex, format!("{res:x}"));
983 }
984
985 #[cfg(feature = "alloc")]
986 #[test]
987 fn encode_radix_8() {
988 assert_eq!(
989 &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
990 "3777777777777777777777777777777777777777777"
991 );
992 assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
993 assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
994
995 let hex = "1234567123456765432107654321";
996 let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
997 let out = radix_encode_limbs_to_string(8, res.as_limbs());
998 assert_eq!(&out, hex);
999 }
1000
1001 #[cfg(feature = "alloc")]
1002 #[test]
1003 fn encode_radix_10() {
1004 assert_eq!(
1005 &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1006 "340282366920938463463374607431768211455"
1007 );
1008 assert_eq!(
1009 &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1010 "0"
1011 );
1012 assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1013 }
1014
1015 #[cfg(feature = "alloc")]
1016 #[test]
1017 fn encode_radix_16() {
1018 let hex = "fedcba9876543210fedcba9876543210";
1019 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1020 let out = radix_encode_limbs_to_string(16, res.as_limbs());
1021 assert_eq!(&out, hex);
1022 }
1023
1024 #[cfg(all(feature = "rand", feature = "alloc"))]
1025 #[test]
1026 fn encode_radix_round_trip() {
1027 use crate::{Random, U256};
1028 use rand_core::SeedableRng;
1029 let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);
1030
1031 for _ in 0..100 {
1032 let uint = U256::random(&mut rng);
1033 for radix in 2..=36 {
1034 let enc = uint.to_string_radix_vartime(radix);
1035 let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1036 assert_eq!(
1037 res, uint,
1038 "round trip failure: radix {radix} encoded {uint} as {enc}"
1039 );
1040 }
1041 }
1042 }
1043}