1#[cfg(feature = "der")]
4mod der;
5#[cfg(feature = "rlp")]
6mod rlp;
7
8use super::Uint;
9use crate::{
10 ByteOrder, DecodeError, EncodedSize, Encoding, Limb, Word, bitlen,
11 encoding::{truncate_be, truncate_le},
12};
13use core::{fmt, ops::Deref};
14
15#[cfg(feature = "alloc")]
16use crate::{NonZero, Reciprocal, UintRef, WideWord};
17#[cfg(feature = "alloc")]
18use alloc::{string::String, vec::Vec};
19
20#[cfg(feature = "alloc")]
21const RADIX_ENCODING_LIMBS_LARGE: usize = 16;
22#[cfg(feature = "alloc")]
23const RADIX_ENCODING_THRESHOLD_LARGE: usize = 24;
24
25const RADIX_ENCODING_MIN: u32 = 2;
26const RADIX_ENCODING_MAX: u32 = 36;
27
28impl<const LIMBS: usize> Uint<LIMBS> {
29 #[must_use]
35 #[track_caller]
36 pub const fn from_be_slice(bytes: &[u8]) -> Self {
37 assert!(
38 bytes.len() == Limb::BYTES * LIMBS,
39 "bytes are not the expected size"
40 );
41
42 let mut res = [Limb::ZERO; LIMBS];
43 let mut buf = [0u8; Limb::BYTES];
44 let mut i = 0;
45
46 while i < LIMBS {
47 let mut j = 0;
48 while j < Limb::BYTES {
49 buf[j] = bytes[i * Limb::BYTES + j];
50 j += 1;
51 }
52 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
53 i += 1;
54 }
55
56 Uint::new(res)
57 }
58
59 #[must_use]
65 #[track_caller]
66 pub const fn from_le_slice(bytes: &[u8]) -> Self {
67 assert!(
68 bytes.len() == Limb::BYTES * LIMBS,
69 "bytes are not the expected size"
70 );
71
72 let mut res = [Limb::ZERO; LIMBS];
73 let mut buf = [0u8; Limb::BYTES];
74 let mut i = 0;
75
76 while i < LIMBS {
77 let mut j = 0;
78 while j < Limb::BYTES {
79 buf[j] = bytes[i * Limb::BYTES + j];
80 j += 1;
81 }
82 res[i] = Limb(Word::from_le_bytes(buf));
83 i += 1;
84 }
85
86 Uint::new(res)
87 }
88
89 #[must_use]
96 #[track_caller]
97 pub fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
98 let mut ret = Self::ZERO;
99 assert!(
100 fill_limbs_from_be_slice_truncated(bytes, &mut ret.limbs, bits_precision).is_ok(),
101 "U{} is too small to store requested {} bits",
102 Self::BITS,
103 bits_precision
104 );
105 ret
106 }
107
108 #[must_use]
115 #[track_caller]
116 pub fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
117 let mut ret = Self::ZERO;
118 assert!(
119 fill_limbs_from_le_slice_truncated(bytes, &mut ret.limbs, bits_precision).is_ok(),
120 "U{} is too small to store requested {} bits",
121 Self::BITS,
122 bits_precision
123 );
124 ret
125 }
126
127 #[must_use]
134 #[track_caller]
135 pub const fn from_slice(bytes: &[u8], byte_order: ByteOrder) -> Self {
136 match byte_order {
137 ByteOrder::BigEndian => Self::from_be_slice(bytes),
138 ByteOrder::LittleEndian => Self::from_le_slice(bytes),
139 }
140 }
141
142 #[must_use]
151 #[track_caller]
152 pub fn from_slice_truncated(bytes: &[u8], bits_precision: u32, byte_order: ByteOrder) -> Self {
153 match byte_order {
154 ByteOrder::BigEndian => Self::from_be_slice_truncated(bytes, bits_precision),
155 ByteOrder::LittleEndian => Self::from_le_slice_truncated(bytes, bits_precision),
156 }
157 }
158
159 #[must_use]
164 #[track_caller]
165 pub const fn from_be_hex(hex: &str) -> Self {
166 let bytes = hex.as_bytes();
167
168 assert!(
169 bytes.len() == Limb::BYTES * LIMBS * 2,
170 "hex string is not the expected size"
171 );
172
173 let mut res = [Limb::ZERO; LIMBS];
174 let mut buf = [0u8; Limb::BYTES];
175 let mut i = 0;
176 let mut err = 0;
177
178 while i < LIMBS {
179 let mut j = 0;
180 while j < Limb::BYTES {
181 let offset = (i * Limb::BYTES + j) * 2;
182 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
183 err |= byte_err;
184 buf[j] = result;
185 j += 1;
186 }
187 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
188 i += 1;
189 }
190
191 assert!(err == 0, "invalid hex byte");
192
193 Uint::new(res)
194 }
195
196 #[must_use]
201 #[track_caller]
202 pub const fn from_le_hex(hex: &str) -> Self {
203 let bytes = hex.as_bytes();
204
205 assert!(
206 bytes.len() == Limb::BYTES * LIMBS * 2,
207 "bytes are not the expected size"
208 );
209
210 let mut res = [Limb::ZERO; LIMBS];
211 let mut buf = [0u8; Limb::BYTES];
212 let mut i = 0;
213 let mut err = 0;
214
215 while i < LIMBS {
216 let mut j = 0;
217 while j < Limb::BYTES {
218 let offset = (i * Limb::BYTES + j) * 2;
219 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
220 err |= byte_err;
221 buf[j] = result;
222 j += 1;
223 }
224 res[i] = Limb(Word::from_le_bytes(buf));
225 i += 1;
226 }
227
228 assert!(err == 0, "invalid hex byte");
229
230 Uint::new(res)
231 }
232
233 #[must_use]
239 #[track_caller]
240 pub const fn from_hex(hex: &str, byte_order: ByteOrder) -> Self {
241 match byte_order {
242 ByteOrder::BigEndian => Self::from_be_hex(hex),
243 ByteOrder::LittleEndian => Self::from_le_hex(hex),
244 }
245 }
246
247 #[cfg(feature = "hybrid-array")]
250 #[inline]
251 pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
252 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
253
254 for (src, dst) in self
255 .limbs
256 .iter()
257 .rev()
258 .cloned()
259 .zip(out.chunks_exact_mut(Limb::BYTES))
260 {
261 dst.copy_from_slice(&src.to_be_bytes());
262 }
263 }
264
265 #[cfg(feature = "hybrid-array")]
268 #[inline]
269 pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
270 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
271
272 for (src, dst) in self
273 .limbs
274 .iter()
275 .cloned()
276 .zip(out.chunks_exact_mut(Limb::BYTES))
277 {
278 dst.copy_from_slice(&src.to_le_bytes());
279 }
280 }
281
282 pub fn from_str_radix_vartime(src: &str, radix: u32) -> Result<Self, DecodeError> {
296 let mut slf = Self::ZERO;
297 radix_decode_str(src, radix, &mut SliceDecodeByLimb::new(&mut slf.limbs))?;
298 Ok(slf)
299 }
300
301 #[cfg(feature = "alloc")]
306 #[must_use]
307 pub fn to_string_radix_vartime(&self, radix: u32) -> String {
308 let mut buf = *self;
309 radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref())
310 }
311
312 #[must_use]
314 pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
315 EncodedUint::new_be(self)
316 }
317
318 #[must_use]
320 pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
321 EncodedUint::new_le(self)
322 }
323}
324
325pub(crate) fn fill_limbs_from_be_slice_truncated(
330 bytes: &[u8],
331 limbs: &mut [Limb],
332 bits_precision: u32,
333) -> Result<(), DecodeError> {
334 if bitlen::from_limbs(limbs.len()) < bits_precision {
335 return Err(DecodeError::Precision);
336 }
337
338 let bytes = truncate_be(bytes, bits_precision);
339 for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(limbs.iter_mut()) {
340 *limb = Limb::from_be_slice(chunk);
341 }
342
343 mask_high_limb(limbs, bits_precision);
344 Ok(())
345}
346
347pub(crate) fn fill_limbs_from_le_slice_truncated(
352 bytes: &[u8],
353 limbs: &mut [Limb],
354 bits_precision: u32,
355) -> Result<(), DecodeError> {
356 if bitlen::from_limbs(limbs.len()) < bits_precision {
357 return Err(DecodeError::Precision);
358 }
359
360 let bytes = truncate_le(bytes, bits_precision);
361 for (chunk, limb) in bytes.chunks(Limb::BYTES).zip(limbs.iter_mut()) {
362 *limb = Limb::from_le_slice(chunk);
363 }
364
365 mask_high_limb(limbs, bits_precision);
366 Ok(())
367}
368
369#[inline(always)]
371fn mask_high_limb(limbs: &mut [Limb], bits_precision: u32) {
372 debug_assert!(bitlen::from_limbs(limbs.len()) >= bits_precision);
373 let unaligned_bits = bits_precision & (Limb::BITS - 1);
374 if unaligned_bits != 0 {
375 limbs[bitlen::to_limbs(bits_precision) - 1].mask_to_precision(unaligned_bits);
376 }
377}
378
379#[derive(Copy, Clone, Debug, PartialEq, Eq)]
385pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);
386
387#[cfg(feature = "zeroize")]
388impl<const LIMBS: usize> zeroize::Zeroize for EncodedUint<LIMBS> {
389 fn zeroize(&mut self) {
390 self.0.zeroize();
391 }
392}
393
394#[allow(unsafe_code)]
395const fn cast_slice(limbs: &[Word]) -> &[u8] {
396 let new_len = size_of_val(limbs);
397
398 unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
403}
404
405#[allow(unsafe_code)]
406const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
407 let new_len = size_of_val(limbs);
408
409 unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), new_len) }
414}
415
416impl<const LIMBS: usize> EncodedUint<LIMBS> {
417 #[must_use]
419 pub const fn as_slice(&self) -> &[u8] {
420 cast_slice(&self.0)
421 }
422
423 pub const fn as_mut_slice(&mut self) -> &mut [u8] {
425 cast_slice_mut(&mut self.0)
426 }
427
428 const fn new_le(value: &Uint<LIMBS>) -> Self {
429 let mut buffer = [0; LIMBS];
430 let mut i = 0;
431
432 while i < LIMBS {
433 let src_bytes = &value.limbs[i].0.to_le_bytes();
434
435 let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
438
439 let mut j = 0;
441 while j < Limb::BYTES {
442 dst_bytes[j] = src_bytes[j];
443 j += 1;
444 }
445
446 i += 1;
447 }
448 Self(buffer)
449 }
450
451 const fn new_be(value: &Uint<LIMBS>) -> Self {
452 let mut buffer = [0; LIMBS];
453 let mut i = 0;
454 while i < LIMBS {
455 let src_bytes = &value.limbs[i].0.to_be_bytes();
456
457 let dst_bytes: &mut [u8] =
460 cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
461
462 let mut j = 0;
464 while j < Limb::BYTES {
465 dst_bytes[j] = src_bytes[j];
466 j += 1;
467 }
468
469 i += 1;
470 }
471 Self(buffer)
472 }
473}
474
475impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
476 fn default() -> Self {
477 Self([0; LIMBS])
478 }
479}
480
481impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
482 fn as_ref(&self) -> &[u8] {
483 self.as_slice()
484 }
485}
486
487impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
488 fn as_mut(&mut self) -> &mut [u8] {
489 self.as_mut_slice()
490 }
491}
492
493impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
494 type Target = [u8];
495 fn deref(&self) -> &Self::Target {
496 self.as_ref()
497 }
498}
499
500impl<const BYTES: usize, const LIMBS: usize> From<EncodedUint<LIMBS>> for [u8; BYTES]
501where
502 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
503{
504 #[inline]
505 fn from(input: EncodedUint<LIMBS>) -> Self {
506 Self::from(&input)
507 }
508}
509
510impl<const BYTES: usize, const LIMBS: usize> From<&EncodedUint<LIMBS>> for [u8; BYTES]
511where
512 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
513{
514 #[inline]
515 fn from(input: &EncodedUint<LIMBS>) -> Self {
516 let mut output = [0u8; BYTES];
517 output.as_mut_slice().copy_from_slice(input.as_ref());
518 output
519 }
520}
521
522impl<const BYTES: usize, const LIMBS: usize> From<[u8; BYTES]> for EncodedUint<LIMBS>
523where
524 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
525{
526 #[inline]
527 fn from(input: [u8; BYTES]) -> Self {
528 Self::from(&input)
529 }
530}
531
532impl<const BYTES: usize, const LIMBS: usize> From<&[u8; BYTES]> for EncodedUint<LIMBS>
533where
534 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
535{
536 #[inline]
537 fn from(input: &[u8; BYTES]) -> Self {
538 let mut output = Self::default();
539 output.as_mut().copy_from_slice(input.as_ref());
540 output
541 }
542}
543
544#[derive(Clone, Copy, Debug, PartialEq, Eq)]
546pub struct TryFromSliceError;
547
548impl fmt::Display for TryFromSliceError {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
550 write!(f, "TryFromSliceError")
551 }
552}
553
554impl core::error::Error for TryFromSliceError {}
555
556impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
557 type Error = TryFromSliceError;
558
559 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
560 if bytes.len() != Uint::<LIMBS>::BYTES {
561 return Err(TryFromSliceError);
562 }
563 let mut result = Self::default();
564 result.as_mut().copy_from_slice(bytes);
565 Ok(result)
566 }
567}
568
569impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
570 type Repr = EncodedUint<LIMBS>;
571
572 #[inline]
573 fn from_be_bytes(bytes: Self::Repr) -> Self {
574 Self::from_be_slice(bytes.as_ref())
575 }
576
577 #[inline]
578 fn from_le_bytes(bytes: Self::Repr) -> Self {
579 Self::from_le_slice(bytes.as_ref())
580 }
581
582 #[inline]
583 fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
584 Self::from_be_slice_truncated(bytes, bits_precision)
585 }
586
587 #[inline]
588 fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
589 Self::from_le_slice_truncated(bytes, bits_precision)
590 }
591
592 #[inline]
593 fn to_be_bytes(&self) -> Self::Repr {
594 self.to_be_bytes()
595 }
596
597 #[inline]
598 fn to_le_bytes(&self) -> Self::Repr {
599 self.to_le_bytes()
600 }
601}
602
603#[inline(always)]
605#[allow(clippy::cast_sign_loss)]
606const fn decode_nibble(src: u8) -> u16 {
607 let byte = src as i16;
608 let mut ret: i16 = -1;
609
610 ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
613 ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
616 ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
619
620 ret as u16
621}
622
623#[inline(always)]
627#[allow(clippy::cast_possible_truncation)]
628pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
629 let hi = decode_nibble(bytes[0]);
630 let lo = decode_nibble(bytes[1]);
631 let byte = (hi << 4) | lo;
632 let err = byte >> 8;
633 let result = byte as u8;
634 (result, err)
635}
636
637pub(crate) trait DecodeByLimb {
639 fn limbs_mut(&mut self) -> &mut [Limb];
641
642 fn push_limb(&mut self, limb: Limb) -> bool;
644}
645
646pub(crate) struct SliceDecodeByLimb<'de> {
648 limbs: &'de mut [Limb],
649 len: usize,
650}
651
652impl<'de> SliceDecodeByLimb<'de> {
653 #[inline]
654 pub fn new(limbs: &'de mut [Limb]) -> Self {
655 Self { limbs, len: 0 }
656 }
657}
658
659impl DecodeByLimb for SliceDecodeByLimb<'_> {
660 #[inline]
661 fn push_limb(&mut self, limb: Limb) -> bool {
662 if self.len < self.limbs.len() {
663 self.limbs[self.len] = limb;
664 self.len += 1;
665 true
666 } else {
667 false
668 }
669 }
670
671 #[inline]
672 fn limbs_mut(&mut self) -> &mut [Limb] {
673 &mut self.limbs[..self.len]
674 }
675}
676
677#[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
685pub(crate) fn radix_decode_str<D: DecodeByLimb>(
686 src: &str,
687 radix: u32,
688 out: &mut D,
689) -> Result<(), DecodeError> {
690 assert!(
691 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
692 "unsupported radix"
693 );
694 if radix == 2 || radix == 4 || radix == 16 {
695 radix_decode_str_aligned_digits(src, radix as u8, out)
696 } else {
697 radix_decode_str_digits(src, radix as u8, out)
698 }
699}
700
701#[inline(always)]
702fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
704 let src_b = src.as_bytes();
706 let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
707
708 if digits.is_empty() {
709 Err(DecodeError::Empty)
711 } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
712 Err(DecodeError::InvalidDigit)
714 } else {
715 while digits[0] == b'0' || digits[0] == b'_' {
717 digits = &digits[1..];
718 if digits.is_empty() {
719 break;
720 }
721 }
722 Ok(digits)
723 }
724}
725
726#[allow(clippy::cast_possible_truncation)]
728fn radix_decode_str_digits<D: DecodeByLimb>(
729 src: &str,
730 radix: u8,
731 out: &mut D,
732) -> Result<(), DecodeError> {
733 let digits = radix_preprocess_str(src)?;
734 let mut buf = [0u8; Limb::BITS as _];
735 let mut limb_digits = Word::MAX.ilog(radix.into()) as usize;
736 let mut limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
737 let mut digits_pos = 0;
738 let mut buf_pos = 0;
739
740 while digits_pos < digits.len() {
741 loop {
743 let digit = match digits[digits_pos] {
744 b @ b'0'..=b'9' => b - b'0',
745 b @ b'a'..=b'z' => b + 10 - b'a',
746 b @ b'A'..=b'Z' => b + 10 - b'A',
747 b'_' => {
748 digits_pos += 1;
749 continue;
750 }
751 _ => radix,
752 };
753 if digit >= radix {
754 return Err(DecodeError::InvalidDigit);
755 }
756 buf[buf_pos] = digit;
757 buf_pos += 1;
758 digits_pos += 1;
759
760 if digits_pos == digits.len() || buf_pos == limb_digits {
761 break;
762 }
763 }
764
765 if buf_pos < limb_digits {
767 limb_digits = buf_pos;
768 limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
769 }
770
771 let mut carry = Limb::ZERO;
773 for c in buf[..limb_digits].iter().copied() {
774 carry = Limb(carry.0 * Word::from(radix) + Word::from(c));
775 }
776 for limb in out.limbs_mut().iter_mut() {
779 (*limb, carry) = limb.carrying_mul_add(limb_max, carry, Limb::ZERO);
780 }
781 if carry.0 != 0 && !out.push_limb(carry) {
783 return Err(DecodeError::InputSize);
784 }
785
786 buf_pos = 0;
787 buf[..limb_digits].fill(0);
788 }
789
790 Ok(())
791}
792
793#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
796fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
797 src: &str,
798 radix: u8,
799 out: &mut D,
800) -> Result<(), DecodeError> {
801 debug_assert!(radix == 2 || radix == 4 || radix == 16);
802
803 let digits = radix_preprocess_str(src)?;
804 let shift = radix.trailing_zeros();
805 let limb_digits = (Limb::BITS / shift) as usize;
806 let mut buf = [0u8; Limb::BITS as _];
807 let mut buf_pos = 0;
808 let mut digits_pos = digits.len();
809
810 while digits_pos > 0 {
811 loop {
813 digits_pos -= 1;
814
815 let digit = match digits[digits_pos] {
816 b @ b'0'..=b'9' => b - b'0',
817 b @ b'a'..=b'z' => b + 10 - b'a',
818 b @ b'A'..=b'Z' => b + 10 - b'A',
819 b'_' => {
820 continue;
822 }
823 _ => radix,
824 };
825 if digit >= radix {
826 return Err(DecodeError::InvalidDigit);
827 }
828 buf[buf_pos] = digit;
829 buf_pos += 1;
830
831 if digits_pos == 0 || buf_pos == limb_digits {
832 break;
833 }
834 }
835
836 if buf_pos > 0 {
837 let mut w: Word = 0;
839 for c in buf[..buf_pos].iter().rev().copied() {
840 w = (w << shift) | Word::from(c);
841 }
842 if !out.push_limb(Limb(w)) {
844 return Err(DecodeError::InputSize);
845 }
846
847 buf_pos = 0;
848 buf[..limb_digits].fill(0);
849 }
850 }
851
852 Ok(())
853}
854
855#[cfg(feature = "alloc")]
861pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
862 let mut array_buf = [Limb::ZERO; 128];
863 let mut vec_buf = Vec::new();
864 let limb_count = limbs.len();
865 let buf = if limb_count <= array_buf.len() {
866 array_buf[..limb_count].copy_from_slice(limbs);
867 &mut array_buf[..limb_count]
868 } else {
869 vec_buf.extend_from_slice(limbs);
870 &mut vec_buf[..limb_count]
871 };
872 radix_encode_limbs_mut_to_string(radix, UintRef::new_mut(buf))
873}
874
875#[cfg(feature = "alloc")]
882pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut UintRef) -> String {
883 assert!(
884 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
885 "unsupported radix"
886 );
887
888 let mut out;
889 if radix.is_power_of_two() {
890 let size = bitlen::from_limbs(limbs.nlimbs()).div_ceil(radix.trailing_zeros()) as usize;
891 out = vec![0u8; size];
892 radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
893 } else {
894 let params = RadixDivisionParams::for_radix(radix);
895 let size = params.encoded_size(limbs.nlimbs());
896 out = vec![0u8; size];
897 params.encode_limbs(limbs, &mut out[..]);
898 }
899 let size = out.len();
900 let mut skip = 0;
901 while skip + 1 < size && out[skip] == b'0' {
902 skip += 1;
903 }
904 if skip > 0 {
905 out.copy_within(skip..size, 0);
906 out.truncate(size - skip);
907 }
908 String::from_utf8(out).expect("utf-8 decoding error")
909}
910
911#[cfg(feature = "alloc")]
916#[allow(clippy::cast_possible_truncation, reason = "needs triage")]
917#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
918fn radix_encode_limbs_by_shifting(radix: u32, limbs: &UintRef, out: &mut [u8]) {
919 debug_assert!(radix.is_power_of_two());
920 debug_assert!(!out.is_empty());
921
922 let radix_bits = radix.trailing_zeros();
923 let mask = (radix - 1) as u8;
924 let mut out_idx = out.len();
925 let mut digits: WideWord = 0;
926 let mut digits_bits = 0;
927 let mut digit;
928
929 for limb in limbs.iter().chain([&Limb::ZERO]) {
930 digits_bits += Limb::BITS;
931 digits |= WideWord::from(limb.0) << (digits_bits % Limb::BITS);
932 for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
933 out_idx -= 1;
934 (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
935 out[out_idx] = if digit < 10 {
936 b'0' + digit
937 } else {
938 b'a' + (digit - 10)
939 };
940 digits_bits -= radix_bits;
941 }
942 }
943
944 out[0..out_idx].fill(b'0');
945}
946
947#[cfg(feature = "alloc")]
949#[derive(Debug, Clone, Copy)]
950pub(crate) struct RadixDivisionParams {
951 radix: u32,
952 digits_per_limb: usize,
953 bits_per_limb: u32,
954 recip_limb: Reciprocal,
955 digits_large: usize,
956 div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
957 recip_large: Reciprocal,
958 shift_large: u32,
959}
960
961#[cfg(feature = "alloc")]
962impl RadixDivisionParams {
963 #[allow(trivial_numeric_casts)]
965 const ALL: [Self; 31] = {
966 let mut res = [Self {
967 radix: 0,
968 digits_per_limb: 0,
969 bits_per_limb: 0,
970 recip_limb: Reciprocal::default(),
971 digits_large: 0,
972 div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
973 shift_large: 0,
974 recip_large: Reciprocal::default(),
975 }; 31];
976 let mut radix: u32 = 3;
977 let mut i: usize = 0;
978 while radix <= RADIX_ENCODING_MAX {
979 if radix.is_power_of_two() {
980 radix += 1;
981 continue;
982 }
983 let digits_limb = Word::MAX.ilog(radix as Word);
984 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
985 let bits_limb = Limb::BITS - div_limb.as_ref().leading_zeros() - 1;
986 let (div_large, digits_large, shift_large) =
987 radix_large_divisor(radix, div_limb, digits_limb as usize);
988 let recip_large = Reciprocal::new(
989 div_large[RADIX_ENCODING_LIMBS_LARGE - 1]
990 .to_nz()
991 .expect_copied("zero divisor"),
992 );
993 res[i] = Self {
994 radix,
995 digits_per_limb: digits_limb as usize,
996 bits_per_limb: bits_limb,
997 recip_limb: Reciprocal::new(div_limb),
998 digits_large,
999 div_large,
1000 shift_large,
1001 recip_large,
1002 };
1003 radix += 1;
1004 i += 1;
1005 }
1006 res
1007 };
1008
1009 #[allow(trivial_numeric_casts)]
1010 pub const fn for_radix(radix: u32) -> Self {
1011 assert!(
1012 !(radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX),
1013 "invalid radix for division"
1014 );
1015 let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
1016 assert!(ret.radix == radix, "radix lookup failure");
1017 ret
1018 }
1019
1020 pub const fn encoded_size(&self, limb_count: usize) -> usize {
1022 limb_count * (self.digits_per_limb + 1)
1024 }
1025
1026 #[allow(clippy::cast_possible_truncation)]
1030 pub fn encode_limbs(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1031 let mut out_idx = out.len();
1032 let mut remain = Uint::<RADIX_ENCODING_LIMBS_LARGE>::ZERO;
1033
1034 while out_idx > 0 {
1035 let (digits, next_idx) = if limbs.nlimbs() >= RADIX_ENCODING_THRESHOLD_LARGE {
1036 let div_large = UintRef::new(&self.div_large);
1037 let remain_mut = remain.as_mut_uint_ref();
1038
1039 let limbs_hi = limbs.shl_assign_limb_vartime(self.shift_large);
1041 let rem_high = limbs.div_rem_large_shifted::<true>(
1042 limbs_hi,
1043 div_large,
1044 RADIX_ENCODING_LIMBS_LARGE as u32,
1045 self.recip_large,
1046 );
1047 let limbs_rem;
1048 (limbs_rem, limbs) = limbs.split_at_mut(RADIX_ENCODING_LIMBS_LARGE - 1);
1051
1052 remain_mut
1054 .leading_mut(RADIX_ENCODING_LIMBS_LARGE - 1)
1055 .copy_from(limbs_rem);
1056 remain_mut.limbs[RADIX_ENCODING_LIMBS_LARGE - 1] = rem_high;
1057 remain_mut.shr_assign_limb_vartime(self.shift_large);
1058
1059 (remain_mut, out_idx.saturating_sub(self.digits_large))
1060 } else {
1061 (core::mem::replace(&mut limbs, UintRef::new_mut(&mut [])), 0)
1062 };
1063
1064 self.encode_limbs_small(digits, &mut out[next_idx..out_idx]);
1066 out_idx = next_idx;
1067 }
1068 }
1069
1070 #[allow(trivial_numeric_casts, reason = "needs triage")]
1071 #[allow(clippy::cast_possible_truncation, reason = "needs triage")]
1072 #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
1073 fn encode_limbs_small(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1074 const DIGITS: &[u8; 36] = b"0123456789abcdefghijklmnopqrstuvwxyz";
1075
1076 let radix = Word::from(self.radix);
1077 let mut out_idx = out.len();
1078 let mut bits_acc = 0;
1079 let (mut digit, mut digits_word);
1080
1081 while out_idx > 0 {
1082 if limbs.is_empty() {
1083 out[0..out_idx].fill(b'0');
1084 break;
1085 }
1086
1087 let limbs_hi = limbs.shl_assign_limb_vartime(self.recip_limb.shift());
1089 digits_word = limbs
1090 .div_rem_limb_with_reciprocal_shifted(limbs_hi, &self.recip_limb)
1091 .0;
1092
1093 bits_acc += self.bits_per_limb;
1095 if bits_acc >= Limb::BITS {
1096 bits_acc -= Limb::BITS;
1097 limbs = limbs.leading_mut(limbs.nlimbs().saturating_sub(1));
1098 }
1099
1100 for _ in 0..self.digits_per_limb.min(out_idx) {
1102 out_idx -= 1;
1103 (digits_word, digit) = (digits_word / radix, (digits_word % radix) as usize);
1104 out[out_idx] = DIGITS[digit];
1105 }
1106 }
1107 }
1108}
1109
1110#[cfg(feature = "alloc")]
1115#[allow(trivial_numeric_casts)]
1116const fn radix_large_divisor<const LIMBS: usize>(
1117 radix: u32,
1118 div_limb: NonZero<Limb>,
1119 digits_limb: usize,
1120) -> ([Limb; LIMBS], usize, u32) {
1121 let mut out = [Limb::ZERO; LIMBS];
1122 let mut digits_large = digits_limb;
1123 let mut top = 1;
1124 out[0] = div_limb.get_copy();
1125 while top < LIMBS {
1127 let mut carry = Limb::ZERO;
1128 let mut j = 0;
1129 while j < top {
1130 (out[j], carry) = out[j].carrying_mul_add(div_limb.get_copy(), carry, Limb::ZERO);
1131 j += 1;
1132 }
1133 if carry.0 != 0 {
1134 out[top] = carry;
1135 top += 1;
1136 }
1137 digits_large += digits_limb;
1138 }
1139 let mut out_test = out;
1141 loop {
1142 let mut carry = Limb::ZERO;
1143 let mut j = 0;
1144 while j < LIMBS {
1145 (out_test[j], carry) = out[j].carrying_mul_add(Limb(radix as Word), carry, Limb::ZERO);
1146 j += 1;
1147 }
1148 if carry.0 == 0 {
1149 out = out_test;
1150 digits_large += 1;
1151 } else {
1152 break;
1153 }
1154 }
1155
1156 let out_mut = UintRef::new_mut(&mut out);
1157 let shift = out_mut.leading_zeros();
1158 out_mut.shl_assign_limb_vartime(shift);
1159 (out, digits_large, shift)
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164 use crate::{DecodeError, EncodedUint, Limb, U64, U128};
1165 use hex_literal::hex;
1166
1167 #[cfg(feature = "alloc")]
1168 use {
1169 super::{radix_encode_limbs_to_string, radix_large_divisor},
1170 crate::{NonZero, Uint, Word},
1171 alloc::format,
1172 };
1173
1174 cpubits::cpubits! {
1175 32 => {
1176 use crate::U64 as UintEx;
1177
1178 #[test]
1179 fn from_be_slice() {
1180 let bytes = hex!("0011223344556677");
1181 let n = UintEx::from_be_slice(&bytes);
1182 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1183 }
1184
1185 #[test]
1186 fn from_le_slice() {
1187 let bytes = hex!("7766554433221100");
1188 let n = UintEx::from_le_slice(&bytes);
1189 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1190 }
1191
1192 #[test]
1193 fn from_be_slice_truncated() {
1194 let bytes = hex!("0011223344556677");
1195 let n = UintEx::from_be_slice_truncated(&bytes, 64);
1196 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1197 }
1198
1199 #[test]
1200 fn from_le_slice_truncated() {
1201 let bytes = hex!("7766554433221100");
1202 let n = UintEx::from_le_slice_truncated(&bytes, 64);
1203 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1204 }
1205
1206 #[test]
1207 fn from_be_hex() {
1208 let n = UintEx::from_be_hex("0011223344556677");
1209 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1210 }
1211
1212 #[test]
1213 fn from_le_hex() {
1214 let n = UintEx::from_le_hex("7766554433221100");
1215 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1216 }
1217 }
1218 64 => {
1219 use crate::U128 as UintEx;
1220
1221 #[test]
1222 fn from_be_slice() {
1223 let bytes = hex!("00112233445566778899aabbccddeeff");
1224 let n = UintEx::from_be_slice(&bytes);
1225 assert_eq!(
1226 n.as_limbs(),
1227 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1228 );
1229 }
1230
1231 #[test]
1232 fn from_le_slice() {
1233 let bytes = hex!("ffeeddccbbaa99887766554433221100");
1234 let n = UintEx::from_le_slice(&bytes);
1235 assert_eq!(
1236 n.as_limbs(),
1237 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1238 );
1239 }
1240
1241 #[test]
1242 fn from_be_slice_truncated() {
1243 let bytes = hex!("112233445566778899aabbccddeeff");
1244 let n = UintEx::from_be_slice_truncated(&bytes, 128);
1245 assert_eq!(
1246 n.as_limbs(),
1247 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1248 );
1249 }
1250
1251 #[test]
1252 fn from_le_slice_truncated() {
1253 let bytes = hex!("ffeeddccbbaa998877665544332211");
1254 let n = UintEx::from_le_slice_truncated(&bytes, 128);
1255 assert_eq!(
1256 n.as_limbs(),
1257 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1258 );
1259 }
1260
1261 #[test]
1262 fn from_be_hex() {
1263 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1264 assert_eq!(
1265 n.as_limbs(),
1266 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1267 );
1268 }
1269
1270 #[test]
1271 fn from_le_hex() {
1272 let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
1273 assert_eq!(
1274 n.as_limbs(),
1275 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1276 );
1277 }
1278 }
1279 }
1280
1281 #[test]
1282 fn fill_limbs_from_be_slice_truncated_empty() {
1283 let mut limbs = [];
1284 assert!(super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 0).is_ok());
1285 assert_eq!(
1286 super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 1),
1287 Err(DecodeError::Precision)
1288 );
1289 }
1290
1291 #[test]
1292 fn fill_limbs_from_le_slice_truncated_empty() {
1293 let mut limbs = [];
1294 assert!(super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 0).is_ok());
1295 assert_eq!(
1296 super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 1),
1297 Err(DecodeError::Precision)
1298 );
1299 }
1300
1301 #[cfg(feature = "alloc")]
1302 #[test]
1303 fn hex_upper() {
1304 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1305 let n = U128::from_be_hex(hex);
1306 assert_eq!(hex, format!("{n:X}"));
1307 }
1308
1309 #[cfg(feature = "alloc")]
1310 #[test]
1311 fn hex_lower() {
1312 let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
1313 let n = U128::from_be_hex(hex);
1314 assert_eq!(hex, format!("{n:x}"));
1315 }
1316
1317 #[cfg(feature = "alloc")]
1318 #[test]
1319 fn fmt_binary() {
1320 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1321 let n = U128::from_be_hex(hex);
1322 let expect = "\
1323 1010101010101010101010101010101010111011101110111011101110111011\
1324 1100110011001100110011001100110011011101110111011101110111011101";
1325 assert_eq!(expect, format!("{n:b}"));
1326 }
1327
1328 #[test]
1329 fn from_str_radix_disallowed() {
1330 let tests = [
1331 ("", 10, DecodeError::Empty),
1332 ("+", 10, DecodeError::Empty),
1333 ("_", 10, DecodeError::InvalidDigit),
1334 ("0_", 10, DecodeError::InvalidDigit),
1335 ("0_", 10, DecodeError::InvalidDigit),
1336 ("a", 10, DecodeError::InvalidDigit),
1337 (".", 10, DecodeError::InvalidDigit),
1338 (
1339 "99999999999999999999999999999999",
1340 10,
1341 DecodeError::InputSize,
1342 ),
1343 ];
1344 for (input, radix, expect) in tests {
1345 assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
1346 }
1347 }
1348
1349 #[test]
1350 fn from_str_radix_2() {
1351 let buf = &[b'1'; 128];
1352 let radix = U128::from_u64(2);
1353 let radix_max = U128::from_u64(1);
1354 let mut last: Option<U128> = None;
1355 for idx in (1..buf.len()).rev() {
1356 let res = U128::from_str_radix_vartime(
1357 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1358 2,
1359 )
1360 .expect("error decoding");
1361 assert!(!bool::from(res.is_zero()));
1362 if let Some(prev) = last {
1363 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1364 }
1365 last = Some(res);
1366 }
1367 assert_eq!(last, Some(radix_max));
1368 }
1369
1370 #[test]
1371 fn from_str_radix_5() {
1372 let buf = &[b'4'; 55];
1373 let radix = U128::from_u64(5);
1374 let radix_max = U128::from_u64(4);
1375 let mut last: Option<U128> = None;
1376 for idx in (1..buf.len()).rev() {
1377 let res = U128::from_str_radix_vartime(
1378 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1379 5,
1380 )
1381 .expect("error decoding");
1382 assert!(!bool::from(res.is_zero()));
1383 if let Some(prev) = last {
1384 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1385 }
1386 last = Some(res);
1387 }
1388 assert_eq!(last, Some(radix_max));
1389 }
1390
1391 #[test]
1392 fn from_str_radix_10() {
1393 let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
1394 let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
1395 assert_eq!(res, U128::MAX);
1396 }
1397
1398 #[cfg(feature = "alloc")]
1399 #[test]
1400 fn from_str_radix_16() {
1401 let hex = "fedcba9876543210fedcba9876543210";
1402 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1403 assert_eq!(hex, format!("{res:x}"));
1404 }
1405
1406 #[cfg(feature = "alloc")]
1407 #[test]
1408 fn encode_radix_8() {
1409 assert_eq!(
1410 &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
1411 "3777777777777777777777777777777777777777777"
1412 );
1413 assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
1414 assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
1415
1416 let hex = "1234567123456765432107654321";
1417 let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
1418 let out = radix_encode_limbs_to_string(8, res.as_limbs());
1419 assert_eq!(&out, hex);
1420 }
1421
1422 #[cfg(feature = "alloc")]
1423 #[test]
1424 fn encode_radix_10() {
1425 assert_eq!(
1426 &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1427 "340282366920938463463374607431768211455"
1428 );
1429 assert_eq!(
1430 &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1431 "0"
1432 );
1433 assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1434 }
1435
1436 #[cfg(feature = "alloc")]
1437 #[test]
1438 fn encode_radix_16() {
1439 let hex = "fedcba9876543210fedcba9876543210";
1440 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1441 let out = radix_encode_limbs_to_string(16, res.as_limbs());
1442 assert_eq!(&out, hex);
1443 }
1444
1445 #[cfg(all(feature = "rand_core", feature = "alloc"))]
1446 #[test]
1447 fn encode_radix_round_trip() {
1448 use crate::{Random, U256};
1449 use rand_core::SeedableRng;
1450 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
1451
1452 let rounds = if cfg!(miri) { 10 } else { 100 };
1453 for _ in 0..rounds {
1454 let uint = U256::random_from_rng(&mut rng);
1455 for radix in 2..=36 {
1456 let enc = uint.to_string_radix_vartime(radix);
1457 let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1458 assert_eq!(
1459 res, uint,
1460 "round trip failure: radix {radix} encoded {uint} as {enc}"
1461 );
1462 }
1463 }
1464 }
1465
1466 cpubits::cpubits! {
1467 32 => {
1468 #[test]
1469 fn encode_be_hex() {
1470 let n = UintEx::from_be_hex("0011223344556677");
1471
1472 let bytes = n.to_be_bytes();
1473 assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
1474
1475 #[cfg(feature = "der")]
1476 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
1477 }
1478 }
1479 64 => {
1480 #[test]
1481 fn encode_be_hex() {
1482 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1483
1484 let bytes = n.to_be_bytes();
1485 assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
1486
1487 #[cfg(feature = "der")]
1488 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);
1489 }
1490 }
1491 }
1492
1493 #[test]
1494 fn infer_sizes() {
1495 let bytes = b"0011223344556677";
1496
1497 let n = EncodedUint::from(bytes);
1498 assert_eq!(n.as_slice(), bytes);
1499
1500 let n = EncodedUint::from(*bytes);
1501 assert_eq!(n.as_slice(), bytes);
1502
1503 let n: [u8; 16] = EncodedUint::from(bytes).into();
1504 assert_eq!(n.as_slice(), bytes);
1505
1506 let n: [u8; 16] = (&EncodedUint::from(bytes)).into();
1507 assert_eq!(n.as_slice(), bytes);
1508 }
1509
1510 #[allow(clippy::cast_lossless)]
1511 #[allow(trivial_numeric_casts)]
1512 #[cfg(feature = "alloc")]
1513 #[test]
1514 fn test_radix_large_divisor() {
1515 let radix = 5u32;
1516 let digits_limb = Word::MAX.ilog(radix as Word);
1517 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
1518 let (div_large, _digits_large, _shift_large) =
1519 radix_large_divisor::<4>(radix, div_limb, digits_limb as usize);
1520 assert!(
1521 Uint::new(div_large)
1522 .checked_mul(&Uint::<1>::from_u32(radix))
1523 .is_none()
1524 .to_bool()
1525 );
1526 }
1527}