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#[allow(unsafe_code)]
388const fn cast_slice(limbs: &[Word]) -> &[u8] {
389 let new_len = size_of_val(limbs);
390
391 unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
396}
397
398#[allow(unsafe_code)]
399const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
400 let new_len = size_of_val(limbs);
401
402 unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), new_len) }
407}
408
409impl<const LIMBS: usize> EncodedUint<LIMBS> {
410 #[must_use]
412 pub const fn as_slice(&self) -> &[u8] {
413 cast_slice(&self.0)
414 }
415
416 pub const fn as_mut_slice(&mut self) -> &mut [u8] {
418 cast_slice_mut(&mut self.0)
419 }
420
421 const fn new_le(value: &Uint<LIMBS>) -> Self {
422 let mut buffer = [0; LIMBS];
423 let mut i = 0;
424
425 while i < LIMBS {
426 let src_bytes = &value.limbs[i].0.to_le_bytes();
427
428 let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
431
432 let mut j = 0;
434 while j < Limb::BYTES {
435 dst_bytes[j] = src_bytes[j];
436 j += 1;
437 }
438
439 i += 1;
440 }
441 Self(buffer)
442 }
443
444 const fn new_be(value: &Uint<LIMBS>) -> Self {
445 let mut buffer = [0; LIMBS];
446 let mut i = 0;
447 while i < LIMBS {
448 let src_bytes = &value.limbs[i].0.to_be_bytes();
449
450 let dst_bytes: &mut [u8] =
453 cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
454
455 let mut j = 0;
457 while j < Limb::BYTES {
458 dst_bytes[j] = src_bytes[j];
459 j += 1;
460 }
461
462 i += 1;
463 }
464 Self(buffer)
465 }
466}
467
468impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
469 fn default() -> Self {
470 Self([0; LIMBS])
471 }
472}
473
474impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
475 fn as_ref(&self) -> &[u8] {
476 self.as_slice()
477 }
478}
479
480impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
481 fn as_mut(&mut self) -> &mut [u8] {
482 self.as_mut_slice()
483 }
484}
485
486impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
487 type Target = [u8];
488 fn deref(&self) -> &Self::Target {
489 self.as_ref()
490 }
491}
492
493impl<const BYTES: usize, const LIMBS: usize> From<EncodedUint<LIMBS>> for [u8; BYTES]
494where
495 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
496{
497 #[inline]
498 fn from(input: EncodedUint<LIMBS>) -> Self {
499 Self::from(&input)
500 }
501}
502
503impl<const BYTES: usize, const LIMBS: usize> From<&EncodedUint<LIMBS>> for [u8; BYTES]
504where
505 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
506{
507 #[inline]
508 fn from(input: &EncodedUint<LIMBS>) -> Self {
509 let mut output = [0u8; BYTES];
510 output.as_mut_slice().copy_from_slice(input.as_ref());
511 output
512 }
513}
514
515impl<const BYTES: usize, const LIMBS: usize> From<[u8; BYTES]> for EncodedUint<LIMBS>
516where
517 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
518{
519 #[inline]
520 fn from(input: [u8; BYTES]) -> Self {
521 Self::from(&input)
522 }
523}
524
525impl<const BYTES: usize, const LIMBS: usize> From<&[u8; BYTES]> for EncodedUint<LIMBS>
526where
527 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
528{
529 #[inline]
530 fn from(input: &[u8; BYTES]) -> Self {
531 let mut output = Self::default();
532 output.as_mut().copy_from_slice(input.as_ref());
533 output
534 }
535}
536
537#[derive(Clone, Copy, Debug, PartialEq, Eq)]
539pub struct TryFromSliceError;
540
541impl fmt::Display for TryFromSliceError {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
543 write!(f, "TryFromSliceError")
544 }
545}
546
547impl core::error::Error for TryFromSliceError {}
548
549impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
550 type Error = TryFromSliceError;
551
552 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
553 if bytes.len() != Uint::<LIMBS>::BYTES {
554 return Err(TryFromSliceError);
555 }
556 let mut result = Self::default();
557 result.as_mut().copy_from_slice(bytes);
558 Ok(result)
559 }
560}
561
562impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
563 type Repr = EncodedUint<LIMBS>;
564
565 #[inline]
566 fn from_be_bytes(bytes: Self::Repr) -> Self {
567 Self::from_be_slice(bytes.as_ref())
568 }
569
570 #[inline]
571 fn from_le_bytes(bytes: Self::Repr) -> Self {
572 Self::from_le_slice(bytes.as_ref())
573 }
574
575 #[inline]
576 fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
577 Self::from_be_slice_truncated(bytes, bits_precision)
578 }
579
580 #[inline]
581 fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
582 Self::from_le_slice_truncated(bytes, bits_precision)
583 }
584
585 #[inline]
586 fn to_be_bytes(&self) -> Self::Repr {
587 self.to_be_bytes()
588 }
589
590 #[inline]
591 fn to_le_bytes(&self) -> Self::Repr {
592 self.to_le_bytes()
593 }
594}
595
596#[inline(always)]
598#[allow(clippy::cast_sign_loss)]
599const fn decode_nibble(src: u8) -> u16 {
600 let byte = src as i16;
601 let mut ret: i16 = -1;
602
603 ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
606 ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
609 ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
612
613 ret as u16
614}
615
616#[inline(always)]
620#[allow(clippy::cast_possible_truncation)]
621pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
622 let hi = decode_nibble(bytes[0]);
623 let lo = decode_nibble(bytes[1]);
624 let byte = (hi << 4) | lo;
625 let err = byte >> 8;
626 let result = byte as u8;
627 (result, err)
628}
629
630pub(crate) trait DecodeByLimb {
632 fn limbs_mut(&mut self) -> &mut [Limb];
634
635 fn push_limb(&mut self, limb: Limb) -> bool;
637}
638
639pub(crate) struct SliceDecodeByLimb<'de> {
641 limbs: &'de mut [Limb],
642 len: usize,
643}
644
645impl<'de> SliceDecodeByLimb<'de> {
646 #[inline]
647 pub fn new(limbs: &'de mut [Limb]) -> Self {
648 Self { limbs, len: 0 }
649 }
650}
651
652impl DecodeByLimb for SliceDecodeByLimb<'_> {
653 #[inline]
654 fn push_limb(&mut self, limb: Limb) -> bool {
655 if self.len < self.limbs.len() {
656 self.limbs[self.len] = limb;
657 self.len += 1;
658 true
659 } else {
660 false
661 }
662 }
663
664 #[inline]
665 fn limbs_mut(&mut self) -> &mut [Limb] {
666 &mut self.limbs[..self.len]
667 }
668}
669
670#[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
678pub(crate) fn radix_decode_str<D: DecodeByLimb>(
679 src: &str,
680 radix: u32,
681 out: &mut D,
682) -> Result<(), DecodeError> {
683 assert!(
684 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
685 "unsupported radix"
686 );
687 if radix == 2 || radix == 4 || radix == 16 {
688 radix_decode_str_aligned_digits(src, radix as u8, out)
689 } else {
690 radix_decode_str_digits(src, radix as u8, out)
691 }
692}
693
694#[inline(always)]
695fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
697 let src_b = src.as_bytes();
699 let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
700
701 if digits.is_empty() {
702 Err(DecodeError::Empty)
704 } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
705 Err(DecodeError::InvalidDigit)
707 } else {
708 while digits[0] == b'0' || digits[0] == b'_' {
710 digits = &digits[1..];
711 if digits.is_empty() {
712 break;
713 }
714 }
715 Ok(digits)
716 }
717}
718
719#[allow(clippy::cast_possible_truncation)]
721fn radix_decode_str_digits<D: DecodeByLimb>(
722 src: &str,
723 radix: u8,
724 out: &mut D,
725) -> Result<(), DecodeError> {
726 let digits = radix_preprocess_str(src)?;
727 let mut buf = [0u8; Limb::BITS as _];
728 let mut limb_digits = Word::MAX.ilog(radix.into()) as usize;
729 let mut limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
730 let mut digits_pos = 0;
731 let mut buf_pos = 0;
732
733 while digits_pos < digits.len() {
734 loop {
736 let digit = match digits[digits_pos] {
737 b @ b'0'..=b'9' => b - b'0',
738 b @ b'a'..=b'z' => b + 10 - b'a',
739 b @ b'A'..=b'Z' => b + 10 - b'A',
740 b'_' => {
741 digits_pos += 1;
742 continue;
743 }
744 _ => radix,
745 };
746 if digit >= radix {
747 return Err(DecodeError::InvalidDigit);
748 }
749 buf[buf_pos] = digit;
750 buf_pos += 1;
751 digits_pos += 1;
752
753 if digits_pos == digits.len() || buf_pos == limb_digits {
754 break;
755 }
756 }
757
758 if buf_pos < limb_digits {
760 limb_digits = buf_pos;
761 limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
762 }
763
764 let mut carry = Limb::ZERO;
766 for c in buf[..limb_digits].iter().copied() {
767 carry = Limb(carry.0 * Word::from(radix) + Word::from(c));
768 }
769 for limb in out.limbs_mut().iter_mut() {
772 (*limb, carry) = limb.carrying_mul_add(limb_max, carry, Limb::ZERO);
773 }
774 if carry.0 != 0 && !out.push_limb(carry) {
776 return Err(DecodeError::InputSize);
777 }
778
779 buf_pos = 0;
780 buf[..limb_digits].fill(0);
781 }
782
783 Ok(())
784}
785
786#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
789fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
790 src: &str,
791 radix: u8,
792 out: &mut D,
793) -> Result<(), DecodeError> {
794 debug_assert!(radix == 2 || radix == 4 || radix == 16);
795
796 let digits = radix_preprocess_str(src)?;
797 let shift = radix.trailing_zeros();
798 let limb_digits = (Limb::BITS / shift) as usize;
799 let mut buf = [0u8; Limb::BITS as _];
800 let mut buf_pos = 0;
801 let mut digits_pos = digits.len();
802
803 while digits_pos > 0 {
804 loop {
806 digits_pos -= 1;
807
808 let digit = match digits[digits_pos] {
809 b @ b'0'..=b'9' => b - b'0',
810 b @ b'a'..=b'z' => b + 10 - b'a',
811 b @ b'A'..=b'Z' => b + 10 - b'A',
812 b'_' => {
813 continue;
815 }
816 _ => radix,
817 };
818 if digit >= radix {
819 return Err(DecodeError::InvalidDigit);
820 }
821 buf[buf_pos] = digit;
822 buf_pos += 1;
823
824 if digits_pos == 0 || buf_pos == limb_digits {
825 break;
826 }
827 }
828
829 if buf_pos > 0 {
830 let mut w: Word = 0;
832 for c in buf[..buf_pos].iter().rev().copied() {
833 w = (w << shift) | Word::from(c);
834 }
835 if !out.push_limb(Limb(w)) {
837 return Err(DecodeError::InputSize);
838 }
839
840 buf_pos = 0;
841 buf[..limb_digits].fill(0);
842 }
843 }
844
845 Ok(())
846}
847
848#[cfg(feature = "alloc")]
854pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
855 let mut array_buf = [Limb::ZERO; 128];
856 let mut vec_buf = Vec::new();
857 let limb_count = limbs.len();
858 let buf = if limb_count <= array_buf.len() {
859 array_buf[..limb_count].copy_from_slice(limbs);
860 &mut array_buf[..limb_count]
861 } else {
862 vec_buf.extend_from_slice(limbs);
863 &mut vec_buf[..limb_count]
864 };
865 radix_encode_limbs_mut_to_string(radix, UintRef::new_mut(buf))
866}
867
868#[cfg(feature = "alloc")]
875pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut UintRef) -> String {
876 assert!(
877 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
878 "unsupported radix"
879 );
880
881 let mut out;
882 if radix.is_power_of_two() {
883 let size = bitlen::from_limbs(limbs.nlimbs()).div_ceil(radix.trailing_zeros()) as usize;
884 out = vec![0u8; size];
885 radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
886 } else {
887 let params = RadixDivisionParams::for_radix(radix);
888 let size = params.encoded_size(limbs.nlimbs());
889 out = vec![0u8; size];
890 params.encode_limbs(limbs, &mut out[..]);
891 }
892 let size = out.len();
893 let mut skip = 0;
894 while skip + 1 < size && out[skip] == b'0' {
895 skip += 1;
896 }
897 if skip > 0 {
898 out.copy_within(skip..size, 0);
899 out.truncate(size - skip);
900 }
901 String::from_utf8(out).expect("utf-8 decoding error")
902}
903
904#[cfg(feature = "alloc")]
909#[allow(clippy::cast_possible_truncation, reason = "needs triage")]
910#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
911fn radix_encode_limbs_by_shifting(radix: u32, limbs: &UintRef, out: &mut [u8]) {
912 debug_assert!(radix.is_power_of_two());
913 debug_assert!(!out.is_empty());
914
915 let radix_bits = radix.trailing_zeros();
916 let mask = (radix - 1) as u8;
917 let mut out_idx = out.len();
918 let mut digits: WideWord = 0;
919 let mut digits_bits = 0;
920 let mut digit;
921
922 for limb in limbs.iter().chain([&Limb::ZERO]) {
923 digits_bits += Limb::BITS;
924 digits |= WideWord::from(limb.0) << (digits_bits % Limb::BITS);
925 for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
926 out_idx -= 1;
927 (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
928 out[out_idx] = if digit < 10 {
929 b'0' + digit
930 } else {
931 b'a' + (digit - 10)
932 };
933 digits_bits -= radix_bits;
934 }
935 }
936
937 out[0..out_idx].fill(b'0');
938}
939
940#[cfg(feature = "alloc")]
942#[derive(Debug, Clone, Copy)]
943pub(crate) struct RadixDivisionParams {
944 radix: u32,
945 digits_per_limb: usize,
946 bits_per_limb: u32,
947 recip_limb: Reciprocal,
948 digits_large: usize,
949 div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
950 recip_large: Reciprocal,
951 shift_large: u32,
952}
953
954#[cfg(feature = "alloc")]
955impl RadixDivisionParams {
956 #[allow(trivial_numeric_casts)]
958 const ALL: [Self; 31] = {
959 let mut res = [Self {
960 radix: 0,
961 digits_per_limb: 0,
962 bits_per_limb: 0,
963 recip_limb: Reciprocal::default(),
964 digits_large: 0,
965 div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
966 shift_large: 0,
967 recip_large: Reciprocal::default(),
968 }; 31];
969 let mut radix: u32 = 3;
970 let mut i: usize = 0;
971 while radix <= RADIX_ENCODING_MAX {
972 if radix.is_power_of_two() {
973 radix += 1;
974 continue;
975 }
976 let digits_limb = Word::MAX.ilog(radix as Word);
977 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
978 let bits_limb = Limb::BITS - div_limb.as_ref().leading_zeros() - 1;
979 let (div_large, digits_large, shift_large) =
980 radix_large_divisor(radix, div_limb, digits_limb as usize);
981 let recip_large = Reciprocal::new(
982 div_large[RADIX_ENCODING_LIMBS_LARGE - 1]
983 .to_nz()
984 .expect_copied("zero divisor"),
985 );
986 res[i] = Self {
987 radix,
988 digits_per_limb: digits_limb as usize,
989 bits_per_limb: bits_limb,
990 recip_limb: Reciprocal::new(div_limb),
991 digits_large,
992 div_large,
993 shift_large,
994 recip_large,
995 };
996 radix += 1;
997 i += 1;
998 }
999 res
1000 };
1001
1002 #[allow(trivial_numeric_casts)]
1003 pub const fn for_radix(radix: u32) -> Self {
1004 assert!(
1005 !(radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX),
1006 "invalid radix for division"
1007 );
1008 let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
1009 assert!(ret.radix == radix, "radix lookup failure");
1010 ret
1011 }
1012
1013 pub const fn encoded_size(&self, limb_count: usize) -> usize {
1015 limb_count * (self.digits_per_limb + 1)
1017 }
1018
1019 #[allow(clippy::cast_possible_truncation)]
1023 pub fn encode_limbs(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1024 let mut out_idx = out.len();
1025 let mut remain = Uint::<RADIX_ENCODING_LIMBS_LARGE>::ZERO;
1026
1027 while out_idx > 0 {
1028 let (digits, next_idx) = if limbs.nlimbs() >= RADIX_ENCODING_THRESHOLD_LARGE {
1029 let div_large = UintRef::new(&self.div_large);
1030 let remain_mut = remain.as_mut_uint_ref();
1031
1032 let limbs_hi = limbs.shl_assign_limb_vartime(self.shift_large);
1034 let rem_high = limbs.div_rem_large_shifted::<true>(
1035 limbs_hi,
1036 div_large,
1037 RADIX_ENCODING_LIMBS_LARGE as u32,
1038 self.recip_large,
1039 );
1040 let limbs_rem;
1041 (limbs_rem, limbs) = limbs.split_at_mut(RADIX_ENCODING_LIMBS_LARGE - 1);
1044
1045 remain_mut
1047 .leading_mut(RADIX_ENCODING_LIMBS_LARGE - 1)
1048 .copy_from(limbs_rem);
1049 remain_mut.limbs[RADIX_ENCODING_LIMBS_LARGE - 1] = rem_high;
1050 remain_mut.shr_assign_limb_vartime(self.shift_large);
1051
1052 (remain_mut, out_idx.saturating_sub(self.digits_large))
1053 } else {
1054 (core::mem::replace(&mut limbs, UintRef::new_mut(&mut [])), 0)
1055 };
1056
1057 self.encode_limbs_small(digits, &mut out[next_idx..out_idx]);
1059 out_idx = next_idx;
1060 }
1061 }
1062
1063 #[allow(trivial_numeric_casts, reason = "needs triage")]
1064 #[allow(clippy::cast_possible_truncation, reason = "needs triage")]
1065 #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
1066 fn encode_limbs_small(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1067 const DIGITS: &[u8; 36] = b"0123456789abcdefghijklmnopqrstuvwxyz";
1068
1069 let radix = Word::from(self.radix);
1070 let mut out_idx = out.len();
1071 let mut bits_acc = 0;
1072 let (mut digit, mut digits_word);
1073
1074 while out_idx > 0 {
1075 if limbs.is_empty() {
1076 out[0..out_idx].fill(b'0');
1077 break;
1078 }
1079
1080 let limbs_hi = limbs.shl_assign_limb_vartime(self.recip_limb.shift());
1082 digits_word = limbs
1083 .div_rem_limb_with_reciprocal_shifted(limbs_hi, &self.recip_limb)
1084 .0;
1085
1086 bits_acc += self.bits_per_limb;
1088 if bits_acc >= Limb::BITS {
1089 bits_acc -= Limb::BITS;
1090 limbs = limbs.leading_mut(limbs.nlimbs().saturating_sub(1));
1091 }
1092
1093 for _ in 0..self.digits_per_limb.min(out_idx) {
1095 out_idx -= 1;
1096 (digits_word, digit) = (digits_word / radix, (digits_word % radix) as usize);
1097 out[out_idx] = DIGITS[digit];
1098 }
1099 }
1100 }
1101}
1102
1103#[cfg(feature = "alloc")]
1108#[allow(trivial_numeric_casts)]
1109const fn radix_large_divisor<const LIMBS: usize>(
1110 radix: u32,
1111 div_limb: NonZero<Limb>,
1112 digits_limb: usize,
1113) -> ([Limb; LIMBS], usize, u32) {
1114 let mut out = [Limb::ZERO; LIMBS];
1115 let mut digits_large = digits_limb;
1116 let mut top = 1;
1117 out[0] = div_limb.get_copy();
1118 while top < LIMBS {
1120 let mut carry = Limb::ZERO;
1121 let mut j = 0;
1122 while j < top {
1123 (out[j], carry) = out[j].carrying_mul_add(div_limb.get_copy(), carry, Limb::ZERO);
1124 j += 1;
1125 }
1126 if carry.0 != 0 {
1127 out[top] = carry;
1128 top += 1;
1129 }
1130 digits_large += digits_limb;
1131 }
1132 let mut out_test = out;
1134 loop {
1135 let mut carry = Limb::ZERO;
1136 let mut j = 0;
1137 while j < LIMBS {
1138 (out_test[j], carry) = out[j].carrying_mul_add(Limb(radix as Word), carry, Limb::ZERO);
1139 j += 1;
1140 }
1141 if carry.0 == 0 {
1142 out = out_test;
1143 digits_large += 1;
1144 } else {
1145 break;
1146 }
1147 }
1148
1149 let out_mut = UintRef::new_mut(&mut out);
1150 let shift = out_mut.leading_zeros();
1151 out_mut.shl_assign_limb_vartime(shift);
1152 (out, digits_large, shift)
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157 use crate::{DecodeError, EncodedUint, Limb, U64, U128};
1158 use hex_literal::hex;
1159
1160 #[cfg(feature = "alloc")]
1161 use {
1162 super::{radix_encode_limbs_to_string, radix_large_divisor},
1163 crate::{NonZero, Uint, Word},
1164 alloc::format,
1165 };
1166
1167 cpubits::cpubits! {
1168 32 => {
1169 use crate::U64 as UintEx;
1170
1171 #[test]
1172 fn from_be_slice() {
1173 let bytes = hex!("0011223344556677");
1174 let n = UintEx::from_be_slice(&bytes);
1175 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1176 }
1177
1178 #[test]
1179 fn from_le_slice() {
1180 let bytes = hex!("7766554433221100");
1181 let n = UintEx::from_le_slice(&bytes);
1182 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1183 }
1184
1185 #[test]
1186 fn from_be_slice_truncated() {
1187 let bytes = hex!("0011223344556677");
1188 let n = UintEx::from_be_slice_truncated(&bytes, 64);
1189 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1190 }
1191
1192 #[test]
1193 fn from_le_slice_truncated() {
1194 let bytes = hex!("7766554433221100");
1195 let n = UintEx::from_le_slice_truncated(&bytes, 64);
1196 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1197 }
1198
1199 #[test]
1200 fn from_be_hex() {
1201 let n = UintEx::from_be_hex("0011223344556677");
1202 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1203 }
1204
1205 #[test]
1206 fn from_le_hex() {
1207 let n = UintEx::from_le_hex("7766554433221100");
1208 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1209 }
1210 }
1211 64 => {
1212 use crate::U128 as UintEx;
1213
1214 #[test]
1215 fn from_be_slice() {
1216 let bytes = hex!("00112233445566778899aabbccddeeff");
1217 let n = UintEx::from_be_slice(&bytes);
1218 assert_eq!(
1219 n.as_limbs(),
1220 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1221 );
1222 }
1223
1224 #[test]
1225 fn from_le_slice() {
1226 let bytes = hex!("ffeeddccbbaa99887766554433221100");
1227 let n = UintEx::from_le_slice(&bytes);
1228 assert_eq!(
1229 n.as_limbs(),
1230 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1231 );
1232 }
1233
1234 #[test]
1235 fn from_be_slice_truncated() {
1236 let bytes = hex!("112233445566778899aabbccddeeff");
1237 let n = UintEx::from_be_slice_truncated(&bytes, 128);
1238 assert_eq!(
1239 n.as_limbs(),
1240 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1241 );
1242 }
1243
1244 #[test]
1245 fn from_le_slice_truncated() {
1246 let bytes = hex!("ffeeddccbbaa998877665544332211");
1247 let n = UintEx::from_le_slice_truncated(&bytes, 128);
1248 assert_eq!(
1249 n.as_limbs(),
1250 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1251 );
1252 }
1253
1254 #[test]
1255 fn from_be_hex() {
1256 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1257 assert_eq!(
1258 n.as_limbs(),
1259 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1260 );
1261 }
1262
1263 #[test]
1264 fn from_le_hex() {
1265 let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
1266 assert_eq!(
1267 n.as_limbs(),
1268 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1269 );
1270 }
1271 }
1272 }
1273
1274 #[test]
1275 fn fill_limbs_from_be_slice_truncated_empty() {
1276 let mut limbs = [];
1277 assert!(super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 0).is_ok());
1278 assert_eq!(
1279 super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 1),
1280 Err(DecodeError::Precision)
1281 );
1282 }
1283
1284 #[test]
1285 fn fill_limbs_from_le_slice_truncated_empty() {
1286 let mut limbs = [];
1287 assert!(super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 0).is_ok());
1288 assert_eq!(
1289 super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 1),
1290 Err(DecodeError::Precision)
1291 );
1292 }
1293
1294 #[cfg(feature = "alloc")]
1295 #[test]
1296 fn hex_upper() {
1297 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1298 let n = U128::from_be_hex(hex);
1299 assert_eq!(hex, format!("{n:X}"));
1300 }
1301
1302 #[cfg(feature = "alloc")]
1303 #[test]
1304 fn hex_lower() {
1305 let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
1306 let n = U128::from_be_hex(hex);
1307 assert_eq!(hex, format!("{n:x}"));
1308 }
1309
1310 #[cfg(feature = "alloc")]
1311 #[test]
1312 fn fmt_binary() {
1313 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1314 let n = U128::from_be_hex(hex);
1315 let expect = "\
1316 1010101010101010101010101010101010111011101110111011101110111011\
1317 1100110011001100110011001100110011011101110111011101110111011101";
1318 assert_eq!(expect, format!("{n:b}"));
1319 }
1320
1321 #[test]
1322 fn from_str_radix_disallowed() {
1323 let tests = [
1324 ("", 10, DecodeError::Empty),
1325 ("+", 10, DecodeError::Empty),
1326 ("_", 10, DecodeError::InvalidDigit),
1327 ("0_", 10, DecodeError::InvalidDigit),
1328 ("0_", 10, DecodeError::InvalidDigit),
1329 ("a", 10, DecodeError::InvalidDigit),
1330 (".", 10, DecodeError::InvalidDigit),
1331 (
1332 "99999999999999999999999999999999",
1333 10,
1334 DecodeError::InputSize,
1335 ),
1336 ];
1337 for (input, radix, expect) in tests {
1338 assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
1339 }
1340 }
1341
1342 #[test]
1343 fn from_str_radix_2() {
1344 let buf = &[b'1'; 128];
1345 let radix = U128::from_u64(2);
1346 let radix_max = U128::from_u64(1);
1347 let mut last: Option<U128> = None;
1348 for idx in (1..buf.len()).rev() {
1349 let res = U128::from_str_radix_vartime(
1350 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1351 2,
1352 )
1353 .expect("error decoding");
1354 assert!(!bool::from(res.is_zero()));
1355 if let Some(prev) = last {
1356 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1357 }
1358 last = Some(res);
1359 }
1360 assert_eq!(last, Some(radix_max));
1361 }
1362
1363 #[test]
1364 fn from_str_radix_5() {
1365 let buf = &[b'4'; 55];
1366 let radix = U128::from_u64(5);
1367 let radix_max = U128::from_u64(4);
1368 let mut last: Option<U128> = None;
1369 for idx in (1..buf.len()).rev() {
1370 let res = U128::from_str_radix_vartime(
1371 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1372 5,
1373 )
1374 .expect("error decoding");
1375 assert!(!bool::from(res.is_zero()));
1376 if let Some(prev) = last {
1377 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1378 }
1379 last = Some(res);
1380 }
1381 assert_eq!(last, Some(radix_max));
1382 }
1383
1384 #[test]
1385 fn from_str_radix_10() {
1386 let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
1387 let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
1388 assert_eq!(res, U128::MAX);
1389 }
1390
1391 #[cfg(feature = "alloc")]
1392 #[test]
1393 fn from_str_radix_16() {
1394 let hex = "fedcba9876543210fedcba9876543210";
1395 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1396 assert_eq!(hex, format!("{res:x}"));
1397 }
1398
1399 #[cfg(feature = "alloc")]
1400 #[test]
1401 fn encode_radix_8() {
1402 assert_eq!(
1403 &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
1404 "3777777777777777777777777777777777777777777"
1405 );
1406 assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
1407 assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
1408
1409 let hex = "1234567123456765432107654321";
1410 let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
1411 let out = radix_encode_limbs_to_string(8, res.as_limbs());
1412 assert_eq!(&out, hex);
1413 }
1414
1415 #[cfg(feature = "alloc")]
1416 #[test]
1417 fn encode_radix_10() {
1418 assert_eq!(
1419 &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1420 "340282366920938463463374607431768211455"
1421 );
1422 assert_eq!(
1423 &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1424 "0"
1425 );
1426 assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1427 }
1428
1429 #[cfg(feature = "alloc")]
1430 #[test]
1431 fn encode_radix_16() {
1432 let hex = "fedcba9876543210fedcba9876543210";
1433 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1434 let out = radix_encode_limbs_to_string(16, res.as_limbs());
1435 assert_eq!(&out, hex);
1436 }
1437
1438 #[cfg(all(feature = "rand_core", feature = "alloc"))]
1439 #[test]
1440 fn encode_radix_round_trip() {
1441 use crate::{Random, U256};
1442 use rand_core::SeedableRng;
1443 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
1444
1445 let rounds = if cfg!(miri) { 10 } else { 100 };
1446 for _ in 0..rounds {
1447 let uint = U256::random_from_rng(&mut rng);
1448 for radix in 2..=36 {
1449 let enc = uint.to_string_radix_vartime(radix);
1450 let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1451 assert_eq!(
1452 res, uint,
1453 "round trip failure: radix {radix} encoded {uint} as {enc}"
1454 );
1455 }
1456 }
1457 }
1458
1459 cpubits::cpubits! {
1460 32 => {
1461 #[test]
1462 fn encode_be_hex() {
1463 let n = UintEx::from_be_hex("0011223344556677");
1464
1465 let bytes = n.to_be_bytes();
1466 assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
1467
1468 #[cfg(feature = "der")]
1469 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
1470 }
1471 }
1472 64 => {
1473 #[test]
1474 fn encode_be_hex() {
1475 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1476
1477 let bytes = n.to_be_bytes();
1478 assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
1479
1480 #[cfg(feature = "der")]
1481 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);
1482 }
1483 }
1484 }
1485
1486 #[test]
1487 fn infer_sizes() {
1488 let bytes = b"0011223344556677";
1489
1490 let n = EncodedUint::from(bytes);
1491 assert_eq!(n.as_slice(), bytes);
1492
1493 let n = EncodedUint::from(*bytes);
1494 assert_eq!(n.as_slice(), bytes);
1495
1496 let n: [u8; 16] = EncodedUint::from(bytes).into();
1497 assert_eq!(n.as_slice(), bytes);
1498
1499 let n: [u8; 16] = (&EncodedUint::from(bytes)).into();
1500 assert_eq!(n.as_slice(), bytes);
1501 }
1502
1503 #[allow(clippy::cast_lossless)]
1504 #[allow(trivial_numeric_casts)]
1505 #[cfg(feature = "alloc")]
1506 #[test]
1507 fn test_radix_large_divisor() {
1508 let radix = 5u32;
1509 let digits_limb = Word::MAX.ilog(radix as Word);
1510 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
1511 let (div_large, _digits_large, _shift_large) =
1512 radix_large_divisor::<4>(radix, div_limb, digits_limb as usize);
1513 assert!(
1514 Uint::new(div_large)
1515 .checked_mul(&Uint::<1>::from_u32(radix))
1516 .is_none()
1517 .to_bool()
1518 );
1519 }
1520}