1#[cfg(feature = "der")]
4mod der;
5#[cfg(feature = "rlp")]
6mod rlp;
7
8use super::Uint;
9use crate::{ByteOrder, DecodeError, EncodedSize, Encoding, Limb, Word};
10use core::{fmt, ops::Deref};
11
12#[cfg(feature = "alloc")]
13use crate::{NonZero, Reciprocal, UintRef, WideWord, bitlen};
14#[cfg(feature = "alloc")]
15use alloc::{string::String, vec::Vec};
16
17#[cfg(feature = "alloc")]
18const RADIX_ENCODING_LIMBS_LARGE: usize = 16;
19#[cfg(feature = "alloc")]
20const RADIX_ENCODING_THRESHOLD_LARGE: usize = 24;
21
22const RADIX_ENCODING_MIN: u32 = 2;
23const RADIX_ENCODING_MAX: u32 = 36;
24
25impl<const LIMBS: usize> Uint<LIMBS> {
26 #[must_use]
31 pub const fn from_be_slice(bytes: &[u8]) -> Self {
32 assert!(
33 bytes.len() == Limb::BYTES * LIMBS,
34 "bytes are not the expected size"
35 );
36
37 let mut res = [Limb::ZERO; LIMBS];
38 let mut buf = [0u8; Limb::BYTES];
39 let mut i = 0;
40
41 while i < LIMBS {
42 let mut j = 0;
43 while j < Limb::BYTES {
44 buf[j] = bytes[i * Limb::BYTES + j];
45 j += 1;
46 }
47 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
48 i += 1;
49 }
50
51 Uint::new(res)
52 }
53
54 #[must_use]
59 pub const fn from_le_slice(bytes: &[u8]) -> Self {
60 assert!(
61 bytes.len() == Limb::BYTES * LIMBS,
62 "bytes are not the expected size"
63 );
64
65 let mut res = [Limb::ZERO; LIMBS];
66 let mut buf = [0u8; Limb::BYTES];
67 let mut i = 0;
68
69 while i < LIMBS {
70 let mut j = 0;
71 while j < Limb::BYTES {
72 buf[j] = bytes[i * Limb::BYTES + j];
73 j += 1;
74 }
75 res[i] = Limb(Word::from_le_bytes(buf));
76 i += 1;
77 }
78
79 Uint::new(res)
80 }
81
82 #[must_use]
88 pub const fn from_slice(bytes: &[u8], byte_order: ByteOrder) -> Self {
89 match byte_order {
90 ByteOrder::BigEndian => Self::from_be_slice(bytes),
91 ByteOrder::LittleEndian => Self::from_le_slice(bytes),
92 }
93 }
94
95 #[must_use]
100 pub const fn from_be_hex(hex: &str) -> Self {
101 let bytes = hex.as_bytes();
102
103 assert!(
104 bytes.len() == Limb::BYTES * LIMBS * 2,
105 "hex string is not the expected size"
106 );
107
108 let mut res = [Limb::ZERO; LIMBS];
109 let mut buf = [0u8; Limb::BYTES];
110 let mut i = 0;
111 let mut err = 0;
112
113 while i < LIMBS {
114 let mut j = 0;
115 while j < Limb::BYTES {
116 let offset = (i * Limb::BYTES + j) * 2;
117 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
118 err |= byte_err;
119 buf[j] = result;
120 j += 1;
121 }
122 res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
123 i += 1;
124 }
125
126 assert!(err == 0, "invalid hex byte");
127
128 Uint::new(res)
129 }
130
131 #[must_use]
136 pub const fn from_le_hex(hex: &str) -> Self {
137 let bytes = hex.as_bytes();
138
139 assert!(
140 bytes.len() == Limb::BYTES * LIMBS * 2,
141 "bytes are not the expected size"
142 );
143
144 let mut res = [Limb::ZERO; LIMBS];
145 let mut buf = [0u8; Limb::BYTES];
146 let mut i = 0;
147 let mut err = 0;
148
149 while i < LIMBS {
150 let mut j = 0;
151 while j < Limb::BYTES {
152 let offset = (i * Limb::BYTES + j) * 2;
153 let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
154 err |= byte_err;
155 buf[j] = result;
156 j += 1;
157 }
158 res[i] = Limb(Word::from_le_bytes(buf));
159 i += 1;
160 }
161
162 assert!(err == 0, "invalid hex byte");
163
164 Uint::new(res)
165 }
166
167 #[must_use]
173 pub const fn from_hex(hex: &str, byte_order: ByteOrder) -> Self {
174 match byte_order {
175 ByteOrder::BigEndian => Self::from_be_hex(hex),
176 ByteOrder::LittleEndian => Self::from_le_hex(hex),
177 }
178 }
179
180 #[cfg(feature = "hybrid-array")]
183 #[inline]
184 pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
185 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
186
187 for (src, dst) in self
188 .limbs
189 .iter()
190 .rev()
191 .cloned()
192 .zip(out.chunks_exact_mut(Limb::BYTES))
193 {
194 dst.copy_from_slice(&src.to_be_bytes());
195 }
196 }
197
198 #[cfg(feature = "hybrid-array")]
201 #[inline]
202 pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
203 debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
204
205 for (src, dst) in self
206 .limbs
207 .iter()
208 .cloned()
209 .zip(out.chunks_exact_mut(Limb::BYTES))
210 {
211 dst.copy_from_slice(&src.to_le_bytes());
212 }
213 }
214
215 pub fn from_str_radix_vartime(src: &str, radix: u32) -> Result<Self, DecodeError> {
229 let mut slf = Self::ZERO;
230 radix_decode_str(src, radix, &mut SliceDecodeByLimb::new(&mut slf.limbs))?;
231 Ok(slf)
232 }
233
234 #[cfg(feature = "alloc")]
239 #[must_use]
240 pub fn to_string_radix_vartime(&self, radix: u32) -> String {
241 let mut buf = *self;
242 radix_encode_limbs_mut_to_string(radix, buf.as_mut_uint_ref())
243 }
244
245 #[must_use]
247 pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
248 EncodedUint::new_be(self)
249 }
250
251 #[must_use]
253 pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
254 EncodedUint::new_le(self)
255 }
256}
257
258#[derive(Copy, Clone, Debug, PartialEq, Eq)]
264pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);
265
266#[allow(unsafe_code)]
267const fn cast_slice(limbs: &[Word]) -> &[u8] {
268 let new_len = size_of_val(limbs);
269
270 unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
275}
276
277#[allow(unsafe_code)]
278const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
279 let new_len = size_of_val(limbs);
280
281 unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), new_len) }
286}
287
288impl<const LIMBS: usize> EncodedUint<LIMBS> {
289 #[must_use]
291 pub const fn as_slice(&self) -> &[u8] {
292 cast_slice(&self.0)
293 }
294
295 pub const fn as_mut_slice(&mut self) -> &mut [u8] {
297 cast_slice_mut(&mut self.0)
298 }
299
300 const fn new_le(value: &Uint<LIMBS>) -> Self {
301 let mut buffer = [0; LIMBS];
302 let mut i = 0;
303
304 while i < LIMBS {
305 let src_bytes = &value.limbs[i].0.to_le_bytes();
306
307 let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
310
311 let mut j = 0;
313 while j < Limb::BYTES {
314 dst_bytes[j] = src_bytes[j];
315 j += 1;
316 }
317
318 i += 1;
319 }
320 Self(buffer)
321 }
322
323 const fn new_be(value: &Uint<LIMBS>) -> Self {
324 let mut buffer = [0; LIMBS];
325 let mut i = 0;
326 while i < LIMBS {
327 let src_bytes = &value.limbs[i].0.to_be_bytes();
328
329 let dst_bytes: &mut [u8] =
332 cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
333
334 let mut j = 0;
336 while j < Limb::BYTES {
337 dst_bytes[j] = src_bytes[j];
338 j += 1;
339 }
340
341 i += 1;
342 }
343 Self(buffer)
344 }
345}
346
347impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
348 fn default() -> Self {
349 Self([0; LIMBS])
350 }
351}
352
353impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
354 fn as_ref(&self) -> &[u8] {
355 self.as_slice()
356 }
357}
358
359impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
360 fn as_mut(&mut self) -> &mut [u8] {
361 self.as_mut_slice()
362 }
363}
364
365impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
366 type Target = [u8];
367 fn deref(&self) -> &Self::Target {
368 self.as_ref()
369 }
370}
371
372impl<const BYTES: usize, const LIMBS: usize> From<EncodedUint<LIMBS>> for [u8; BYTES]
373where
374 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
375{
376 #[inline]
377 fn from(input: EncodedUint<LIMBS>) -> Self {
378 Self::from(&input)
379 }
380}
381
382impl<const BYTES: usize, const LIMBS: usize> From<&EncodedUint<LIMBS>> for [u8; BYTES]
383where
384 EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
385{
386 #[inline]
387 fn from(input: &EncodedUint<LIMBS>) -> Self {
388 let mut output = [0u8; BYTES];
389 output.as_mut_slice().copy_from_slice(input.as_ref());
390 output
391 }
392}
393
394impl<const BYTES: usize, const LIMBS: usize> From<[u8; BYTES]> for EncodedUint<LIMBS>
395where
396 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
397{
398 #[inline]
399 fn from(input: [u8; BYTES]) -> Self {
400 Self::from(&input)
401 }
402}
403
404impl<const BYTES: usize, const LIMBS: usize> From<&[u8; BYTES]> for EncodedUint<LIMBS>
405where
406 [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
407{
408 #[inline]
409 fn from(input: &[u8; BYTES]) -> Self {
410 let mut output = Self::default();
411 output.as_mut().copy_from_slice(input.as_ref());
412 output
413 }
414}
415
416#[derive(Clone, Copy, Debug, PartialEq, Eq)]
418pub struct TryFromSliceError;
419
420impl fmt::Display for TryFromSliceError {
421 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
422 write!(f, "TryFromSliceError")
423 }
424}
425
426impl core::error::Error for TryFromSliceError {}
427
428impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
429 type Error = TryFromSliceError;
430
431 fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
432 if bytes.len() != Uint::<LIMBS>::BYTES {
433 return Err(TryFromSliceError);
434 }
435 let mut result = Self::default();
436 result.as_mut().copy_from_slice(bytes);
437 Ok(result)
438 }
439}
440
441impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
442 type Repr = EncodedUint<LIMBS>;
443
444 #[inline]
445 fn from_be_bytes(bytes: Self::Repr) -> Self {
446 Self::from_be_slice(bytes.as_ref())
447 }
448
449 #[inline]
450 fn from_le_bytes(bytes: Self::Repr) -> Self {
451 Self::from_le_slice(bytes.as_ref())
452 }
453
454 #[inline]
455 fn to_be_bytes(&self) -> Self::Repr {
456 self.to_be_bytes()
457 }
458
459 #[inline]
460 fn to_le_bytes(&self) -> Self::Repr {
461 self.to_le_bytes()
462 }
463}
464
465#[inline(always)]
467#[allow(clippy::cast_sign_loss)]
468const fn decode_nibble(src: u8) -> u16 {
469 let byte = src as i16;
470 let mut ret: i16 = -1;
471
472 ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
475 ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
478 ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
481
482 ret as u16
483}
484
485#[inline(always)]
489#[allow(clippy::cast_possible_truncation)]
490pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
491 let hi = decode_nibble(bytes[0]);
492 let lo = decode_nibble(bytes[1]);
493 let byte = (hi << 4) | lo;
494 let err = byte >> 8;
495 let result = byte as u8;
496 (result, err)
497}
498
499pub(crate) trait DecodeByLimb {
501 fn limbs_mut(&mut self) -> &mut [Limb];
503
504 fn push_limb(&mut self, limb: Limb) -> bool;
506}
507
508pub(crate) struct SliceDecodeByLimb<'de> {
510 limbs: &'de mut [Limb],
511 len: usize,
512}
513
514impl<'de> SliceDecodeByLimb<'de> {
515 #[inline]
516 pub fn new(limbs: &'de mut [Limb]) -> Self {
517 Self { limbs, len: 0 }
518 }
519}
520
521impl DecodeByLimb for SliceDecodeByLimb<'_> {
522 #[inline]
523 fn push_limb(&mut self, limb: Limb) -> bool {
524 if self.len < self.limbs.len() {
525 self.limbs[self.len] = limb;
526 self.len += 1;
527 true
528 } else {
529 false
530 }
531 }
532
533 #[inline]
534 fn limbs_mut(&mut self) -> &mut [Limb] {
535 &mut self.limbs[..self.len]
536 }
537}
538
539#[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
547pub(crate) fn radix_decode_str<D: DecodeByLimb>(
548 src: &str,
549 radix: u32,
550 out: &mut D,
551) -> Result<(), DecodeError> {
552 assert!(
553 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
554 "unsupported radix"
555 );
556 if radix == 2 || radix == 4 || radix == 16 {
557 radix_decode_str_aligned_digits(src, radix as u8, out)
558 } else {
559 radix_decode_str_digits(src, radix as u8, out)
560 }
561}
562
563#[inline(always)]
564fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
566 let src_b = src.as_bytes();
568 let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
569
570 if digits.is_empty() {
571 Err(DecodeError::Empty)
573 } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
574 Err(DecodeError::InvalidDigit)
576 } else {
577 while digits[0] == b'0' || digits[0] == b'_' {
579 digits = &digits[1..];
580 if digits.is_empty() {
581 break;
582 }
583 }
584 Ok(digits)
585 }
586}
587
588#[allow(clippy::cast_possible_truncation)]
590fn radix_decode_str_digits<D: DecodeByLimb>(
591 src: &str,
592 radix: u8,
593 out: &mut D,
594) -> Result<(), DecodeError> {
595 let digits = radix_preprocess_str(src)?;
596 let mut buf = [0u8; Limb::BITS as _];
597 let mut limb_digits = Word::MAX.ilog(radix.into()) as usize;
598 let mut limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
599 let mut digits_pos = 0;
600 let mut buf_pos = 0;
601
602 while digits_pos < digits.len() {
603 loop {
605 let digit = match digits[digits_pos] {
606 b @ b'0'..=b'9' => b - b'0',
607 b @ b'a'..=b'z' => b + 10 - b'a',
608 b @ b'A'..=b'Z' => b + 10 - b'A',
609 b'_' => {
610 digits_pos += 1;
611 continue;
612 }
613 _ => radix,
614 };
615 if digit >= radix {
616 return Err(DecodeError::InvalidDigit);
617 }
618 buf[buf_pos] = digit;
619 buf_pos += 1;
620 digits_pos += 1;
621
622 if digits_pos == digits.len() || buf_pos == limb_digits {
623 break;
624 }
625 }
626
627 if buf_pos < limb_digits {
629 limb_digits = buf_pos;
630 limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
631 }
632
633 let mut carry = Limb::ZERO;
635 for c in buf[..limb_digits].iter().copied() {
636 carry = Limb(carry.0 * Word::from(radix) + Word::from(c));
637 }
638 for limb in out.limbs_mut().iter_mut() {
641 (*limb, carry) = limb.carrying_mul_add(limb_max, carry, Limb::ZERO);
642 }
643 if carry.0 != 0 && !out.push_limb(carry) {
645 return Err(DecodeError::InputSize);
646 }
647
648 buf_pos = 0;
649 buf[..limb_digits].fill(0);
650 }
651
652 Ok(())
653}
654
655#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
658fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
659 src: &str,
660 radix: u8,
661 out: &mut D,
662) -> Result<(), DecodeError> {
663 debug_assert!(radix == 2 || radix == 4 || radix == 16);
664
665 let digits = radix_preprocess_str(src)?;
666 let shift = radix.trailing_zeros();
667 let limb_digits = (Limb::BITS / shift) as usize;
668 let mut buf = [0u8; Limb::BITS as _];
669 let mut buf_pos = 0;
670 let mut digits_pos = digits.len();
671
672 while digits_pos > 0 {
673 loop {
675 digits_pos -= 1;
676
677 let digit = match digits[digits_pos] {
678 b @ b'0'..=b'9' => b - b'0',
679 b @ b'a'..=b'z' => b + 10 - b'a',
680 b @ b'A'..=b'Z' => b + 10 - b'A',
681 b'_' => {
682 continue;
684 }
685 _ => radix,
686 };
687 if digit >= radix {
688 return Err(DecodeError::InvalidDigit);
689 }
690 buf[buf_pos] = digit;
691 buf_pos += 1;
692
693 if digits_pos == 0 || buf_pos == limb_digits {
694 break;
695 }
696 }
697
698 if buf_pos > 0 {
699 let mut w: Word = 0;
701 for c in buf[..buf_pos].iter().rev().copied() {
702 w = (w << shift) | Word::from(c);
703 }
704 if !out.push_limb(Limb(w)) {
706 return Err(DecodeError::InputSize);
707 }
708
709 buf_pos = 0;
710 buf[..limb_digits].fill(0);
711 }
712 }
713
714 Ok(())
715}
716
717#[cfg(feature = "alloc")]
723pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
724 let mut array_buf = [Limb::ZERO; 128];
725 let mut vec_buf = Vec::new();
726 let limb_count = limbs.len();
727 let buf = if limb_count <= array_buf.len() {
728 array_buf[..limb_count].copy_from_slice(limbs);
729 &mut array_buf[..limb_count]
730 } else {
731 vec_buf.extend_from_slice(limbs);
732 &mut vec_buf[..limb_count]
733 };
734 radix_encode_limbs_mut_to_string(radix, UintRef::new_mut(buf))
735}
736
737#[cfg(feature = "alloc")]
744pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut UintRef) -> String {
745 assert!(
746 (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
747 "unsupported radix"
748 );
749
750 let mut out;
751 if radix.is_power_of_two() {
752 let size = bitlen::from_limbs(limbs.nlimbs()).div_ceil(radix.trailing_zeros()) as usize;
753 out = vec![0u8; size];
754 radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
755 } else {
756 let params = RadixDivisionParams::for_radix(radix);
757 let size = params.encoded_size(limbs.nlimbs());
758 out = vec![0u8; size];
759 params.encode_limbs(limbs, &mut out[..]);
760 }
761 let size = out.len();
762 let mut skip = 0;
763 while skip + 1 < size && out[skip] == b'0' {
764 skip += 1;
765 }
766 if skip > 0 {
767 out.copy_within(skip..size, 0);
768 out.truncate(size - skip);
769 }
770 String::from_utf8(out).expect("utf-8 decoding error")
771}
772
773#[cfg(feature = "alloc")]
778#[allow(clippy::cast_possible_truncation, reason = "needs triage")]
779#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
780fn radix_encode_limbs_by_shifting(radix: u32, limbs: &UintRef, out: &mut [u8]) {
781 debug_assert!(radix.is_power_of_two());
782 debug_assert!(!out.is_empty());
783
784 let radix_bits = radix.trailing_zeros();
785 let mask = (radix - 1) as u8;
786 let mut out_idx = out.len();
787 let mut digits: WideWord = 0;
788 let mut digits_bits = 0;
789 let mut digit;
790
791 for limb in limbs.iter().chain([&Limb::ZERO]) {
792 digits_bits += Limb::BITS;
793 digits |= WideWord::from(limb.0) << (digits_bits % Limb::BITS);
794 for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
795 out_idx -= 1;
796 (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
797 out[out_idx] = if digit < 10 {
798 b'0' + digit
799 } else {
800 b'a' + (digit - 10)
801 };
802 digits_bits -= radix_bits;
803 }
804 }
805
806 out[0..out_idx].fill(b'0');
807}
808
809#[cfg(feature = "alloc")]
811#[derive(Debug, Clone, Copy)]
812pub(crate) struct RadixDivisionParams {
813 radix: u32,
814 digits_per_limb: usize,
815 bits_per_limb: u32,
816 recip_limb: Reciprocal,
817 digits_large: usize,
818 div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
819 recip_large: Reciprocal,
820 shift_large: u32,
821}
822
823#[cfg(feature = "alloc")]
824impl RadixDivisionParams {
825 #[allow(trivial_numeric_casts)]
827 const ALL: [Self; 31] = {
828 let mut res = [Self {
829 radix: 0,
830 digits_per_limb: 0,
831 bits_per_limb: 0,
832 recip_limb: Reciprocal::default(),
833 digits_large: 0,
834 div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
835 shift_large: 0,
836 recip_large: Reciprocal::default(),
837 }; 31];
838 let mut radix: u32 = 3;
839 let mut i: usize = 0;
840 while radix <= RADIX_ENCODING_MAX {
841 if radix.is_power_of_two() {
842 radix += 1;
843 continue;
844 }
845 let digits_limb = Word::MAX.ilog(radix as Word);
846 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
847 let bits_limb = Limb::BITS - div_limb.as_ref().leading_zeros() - 1;
848 let (div_large, digits_large, shift_large) =
849 radix_large_divisor(radix, div_limb, digits_limb as usize);
850 let recip_large = Reciprocal::new(
851 div_large[RADIX_ENCODING_LIMBS_LARGE - 1]
852 .to_nz()
853 .expect_copied("zero divisor"),
854 );
855 res[i] = Self {
856 radix,
857 digits_per_limb: digits_limb as usize,
858 bits_per_limb: bits_limb,
859 recip_limb: Reciprocal::new(div_limb),
860 digits_large,
861 div_large,
862 shift_large,
863 recip_large,
864 };
865 radix += 1;
866 i += 1;
867 }
868 res
869 };
870
871 #[allow(trivial_numeric_casts)]
872 pub const fn for_radix(radix: u32) -> Self {
873 assert!(
874 !(radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX),
875 "invalid radix for division"
876 );
877 let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
878 assert!(ret.radix == radix, "radix lookup failure");
879 ret
880 }
881
882 pub const fn encoded_size(&self, limb_count: usize) -> usize {
884 limb_count * (self.digits_per_limb + 1)
886 }
887
888 #[allow(clippy::cast_possible_truncation)]
892 pub fn encode_limbs(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
893 let mut out_idx = out.len();
894 let mut remain = Uint::<RADIX_ENCODING_LIMBS_LARGE>::ZERO;
895
896 while out_idx > 0 {
897 let (digits, next_idx) = if limbs.nlimbs() >= RADIX_ENCODING_THRESHOLD_LARGE {
898 let div_large = UintRef::new(&self.div_large);
899 let remain_mut = remain.as_mut_uint_ref();
900
901 let limbs_hi = limbs.shl_assign_limb_vartime(self.shift_large);
903 let rem_high = limbs.div_rem_large_shifted::<true>(
904 limbs_hi,
905 div_large,
906 RADIX_ENCODING_LIMBS_LARGE as u32,
907 self.recip_large,
908 );
909 let limbs_rem;
910 (limbs_rem, limbs) = limbs.split_at_mut(RADIX_ENCODING_LIMBS_LARGE - 1);
913
914 remain_mut
916 .leading_mut(RADIX_ENCODING_LIMBS_LARGE - 1)
917 .copy_from(limbs_rem);
918 remain_mut.limbs[RADIX_ENCODING_LIMBS_LARGE - 1] = rem_high;
919 remain_mut.shr_assign_limb_vartime(self.shift_large);
920
921 (remain_mut, out_idx.saturating_sub(self.digits_large))
922 } else {
923 (core::mem::replace(&mut limbs, UintRef::new_mut(&mut [])), 0)
924 };
925
926 self.encode_limbs_small(digits, &mut out[next_idx..out_idx]);
928 out_idx = next_idx;
929 }
930 }
931
932 #[allow(trivial_numeric_casts, reason = "needs triage")]
933 #[allow(clippy::cast_possible_truncation, reason = "needs triage")]
934 #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
935 fn encode_limbs_small(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
936 const DIGITS: &[u8; 36] = b"0123456789abcdefghijklmnopqrstuvwxyz";
937
938 let radix = Word::from(self.radix);
939 let mut out_idx = out.len();
940 let mut bits_acc = 0;
941 let (mut digit, mut digits_word);
942
943 while out_idx > 0 {
944 if limbs.is_empty() {
945 out[0..out_idx].fill(b'0');
946 break;
947 }
948
949 let limbs_hi = limbs.shl_assign_limb_vartime(self.recip_limb.shift());
951 digits_word = limbs
952 .div_rem_limb_with_reciprocal_shifted(limbs_hi, &self.recip_limb)
953 .0;
954
955 bits_acc += self.bits_per_limb;
957 if bits_acc >= Limb::BITS {
958 bits_acc -= Limb::BITS;
959 limbs = limbs.leading_mut(limbs.nlimbs().saturating_sub(1));
960 }
961
962 for _ in 0..self.digits_per_limb.min(out_idx) {
964 out_idx -= 1;
965 (digits_word, digit) = (digits_word / radix, (digits_word % radix) as usize);
966 out[out_idx] = DIGITS[digit];
967 }
968 }
969 }
970}
971
972#[cfg(feature = "alloc")]
977#[allow(trivial_numeric_casts)]
978const fn radix_large_divisor<const LIMBS: usize>(
979 radix: u32,
980 div_limb: NonZero<Limb>,
981 digits_limb: usize,
982) -> ([Limb; LIMBS], usize, u32) {
983 let mut out = [Limb::ZERO; LIMBS];
984 let mut digits_large = digits_limb;
985 let mut top = 1;
986 out[0] = div_limb.get_copy();
987 while top < LIMBS {
989 let mut carry = Limb::ZERO;
990 let mut j = 0;
991 while j < top {
992 (out[j], carry) = out[j].carrying_mul_add(div_limb.get_copy(), carry, Limb::ZERO);
993 j += 1;
994 }
995 if carry.0 != 0 {
996 out[top] = carry;
997 top += 1;
998 }
999 digits_large += digits_limb;
1000 }
1001 let mut out_test = out;
1003 loop {
1004 let mut carry = Limb::ZERO;
1005 let mut j = 0;
1006 while j < LIMBS {
1007 (out_test[j], carry) = out[j].carrying_mul_add(Limb(radix as Word), carry, Limb::ZERO);
1008 j += 1;
1009 }
1010 if carry.0 == 0 {
1011 out = out_test;
1012 digits_large += 1;
1013 } else {
1014 break;
1015 }
1016 }
1017
1018 let out_mut = UintRef::new_mut(&mut out);
1019 let shift = out_mut.leading_zeros();
1020 out_mut.shl_assign_limb_vartime(shift);
1021 (out, digits_large, shift)
1022}
1023
1024#[cfg(test)]
1025mod tests {
1026 use crate::{DecodeError, EncodedUint, Limb, U64, U128};
1027 use hex_literal::hex;
1028
1029 #[cfg(feature = "alloc")]
1030 use {
1031 super::{radix_encode_limbs_to_string, radix_large_divisor},
1032 crate::{NonZero, Uint, Word},
1033 alloc::format,
1034 };
1035
1036 cpubits::cpubits! {
1037 32 => {
1038 use crate::U64 as UintEx;
1039
1040 #[test]
1041 fn from_be_slice() {
1042 let bytes = hex!("0011223344556677");
1043 let n = UintEx::from_be_slice(&bytes);
1044 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1045 }
1046
1047 #[test]
1048 fn from_le_slice() {
1049 let bytes = hex!("7766554433221100");
1050 let n = UintEx::from_le_slice(&bytes);
1051 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1052 }
1053
1054 #[test]
1055 fn from_be_hex() {
1056 let n = UintEx::from_be_hex("0011223344556677");
1057 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1058 }
1059
1060 #[test]
1061 fn from_le_hex() {
1062 let n = UintEx::from_le_hex("7766554433221100");
1063 assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1064 }
1065 }
1066 64 => {
1067 use crate::U128 as UintEx;
1068
1069 #[test]
1070 fn from_be_slice() {
1071 let bytes = hex!("00112233445566778899aabbccddeeff");
1072 let n = UintEx::from_be_slice(&bytes);
1073 assert_eq!(
1074 n.as_limbs(),
1075 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1076 );
1077 }
1078
1079 #[test]
1080 fn from_le_slice() {
1081 let bytes = hex!("ffeeddccbbaa99887766554433221100");
1082 let n = UintEx::from_le_slice(&bytes);
1083 assert_eq!(
1084 n.as_limbs(),
1085 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1086 );
1087 }
1088
1089 #[test]
1090 fn from_be_hex() {
1091 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1092 assert_eq!(
1093 n.as_limbs(),
1094 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1095 );
1096 }
1097
1098 #[test]
1099 fn from_le_hex() {
1100 let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
1101 assert_eq!(
1102 n.as_limbs(),
1103 &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1104 );
1105 }
1106 }
1107 }
1108
1109 #[cfg(feature = "alloc")]
1110 #[test]
1111 fn hex_upper() {
1112 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1113 let n = U128::from_be_hex(hex);
1114 assert_eq!(hex, format!("{n:X}"));
1115 }
1116
1117 #[cfg(feature = "alloc")]
1118 #[test]
1119 fn hex_lower() {
1120 let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
1121 let n = U128::from_be_hex(hex);
1122 assert_eq!(hex, format!("{n:x}"));
1123 }
1124
1125 #[cfg(feature = "alloc")]
1126 #[test]
1127 fn fmt_binary() {
1128 let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1129 let n = U128::from_be_hex(hex);
1130 let expect = "\
1131 1010101010101010101010101010101010111011101110111011101110111011\
1132 1100110011001100110011001100110011011101110111011101110111011101";
1133 assert_eq!(expect, format!("{n:b}"));
1134 }
1135
1136 #[test]
1137 fn from_str_radix_disallowed() {
1138 let tests = [
1139 ("", 10, DecodeError::Empty),
1140 ("+", 10, DecodeError::Empty),
1141 ("_", 10, DecodeError::InvalidDigit),
1142 ("0_", 10, DecodeError::InvalidDigit),
1143 ("0_", 10, DecodeError::InvalidDigit),
1144 ("a", 10, DecodeError::InvalidDigit),
1145 (".", 10, DecodeError::InvalidDigit),
1146 (
1147 "99999999999999999999999999999999",
1148 10,
1149 DecodeError::InputSize,
1150 ),
1151 ];
1152 for (input, radix, expect) in tests {
1153 assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
1154 }
1155 }
1156
1157 #[test]
1158 fn from_str_radix_2() {
1159 let buf = &[b'1'; 128];
1160 let radix = U128::from_u64(2);
1161 let radix_max = U128::from_u64(1);
1162 let mut last: Option<U128> = None;
1163 for idx in (1..buf.len()).rev() {
1164 let res = U128::from_str_radix_vartime(
1165 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1166 2,
1167 )
1168 .expect("error decoding");
1169 assert!(!bool::from(res.is_zero()));
1170 if let Some(prev) = last {
1171 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1172 }
1173 last = Some(res);
1174 }
1175 assert_eq!(last, Some(radix_max));
1176 }
1177
1178 #[test]
1179 fn from_str_radix_5() {
1180 let buf = &[b'4'; 55];
1181 let radix = U128::from_u64(5);
1182 let radix_max = U128::from_u64(4);
1183 let mut last: Option<U128> = None;
1184 for idx in (1..buf.len()).rev() {
1185 let res = U128::from_str_radix_vartime(
1186 core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1187 5,
1188 )
1189 .expect("error decoding");
1190 assert!(!bool::from(res.is_zero()));
1191 if let Some(prev) = last {
1192 assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1193 }
1194 last = Some(res);
1195 }
1196 assert_eq!(last, Some(radix_max));
1197 }
1198
1199 #[test]
1200 fn from_str_radix_10() {
1201 let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
1202 let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
1203 assert_eq!(res, U128::MAX);
1204 }
1205
1206 #[cfg(feature = "alloc")]
1207 #[test]
1208 fn from_str_radix_16() {
1209 let hex = "fedcba9876543210fedcba9876543210";
1210 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1211 assert_eq!(hex, format!("{res:x}"));
1212 }
1213
1214 #[cfg(feature = "alloc")]
1215 #[test]
1216 fn encode_radix_8() {
1217 assert_eq!(
1218 &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
1219 "3777777777777777777777777777777777777777777"
1220 );
1221 assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
1222 assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
1223
1224 let hex = "1234567123456765432107654321";
1225 let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
1226 let out = radix_encode_limbs_to_string(8, res.as_limbs());
1227 assert_eq!(&out, hex);
1228 }
1229
1230 #[cfg(feature = "alloc")]
1231 #[test]
1232 fn encode_radix_10() {
1233 assert_eq!(
1234 &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1235 "340282366920938463463374607431768211455"
1236 );
1237 assert_eq!(
1238 &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1239 "0"
1240 );
1241 assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1242 }
1243
1244 #[cfg(feature = "alloc")]
1245 #[test]
1246 fn encode_radix_16() {
1247 let hex = "fedcba9876543210fedcba9876543210";
1248 let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1249 let out = radix_encode_limbs_to_string(16, res.as_limbs());
1250 assert_eq!(&out, hex);
1251 }
1252
1253 #[cfg(all(feature = "rand_core", feature = "alloc"))]
1254 #[test]
1255 fn encode_radix_round_trip() {
1256 use crate::{Random, U256};
1257 use rand_core::SeedableRng;
1258 let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
1259
1260 let rounds = if cfg!(miri) { 10 } else { 100 };
1261 for _ in 0..rounds {
1262 let uint = U256::random_from_rng(&mut rng);
1263 for radix in 2..=36 {
1264 let enc = uint.to_string_radix_vartime(radix);
1265 let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1266 assert_eq!(
1267 res, uint,
1268 "round trip failure: radix {radix} encoded {uint} as {enc}"
1269 );
1270 }
1271 }
1272 }
1273
1274 cpubits::cpubits! {
1275 32 => {
1276 #[test]
1277 fn encode_be_hex() {
1278 let n = UintEx::from_be_hex("0011223344556677");
1279
1280 let bytes = n.to_be_bytes();
1281 assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
1282
1283 #[cfg(feature = "der")]
1284 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
1285 }
1286 }
1287 64 => {
1288 #[test]
1289 fn encode_be_hex() {
1290 let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1291
1292 let bytes = n.to_be_bytes();
1293 assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
1294
1295 #[cfg(feature = "der")]
1296 assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);
1297 }
1298 }
1299 }
1300
1301 #[test]
1302 fn infer_sizes() {
1303 let bytes = b"0011223344556677";
1304
1305 let n = EncodedUint::from(bytes);
1306 assert_eq!(n.as_slice(), bytes);
1307
1308 let n = EncodedUint::from(*bytes);
1309 assert_eq!(n.as_slice(), bytes);
1310
1311 let n: [u8; 16] = EncodedUint::from(bytes).into();
1312 assert_eq!(n.as_slice(), bytes);
1313
1314 let n: [u8; 16] = (&EncodedUint::from(bytes)).into();
1315 assert_eq!(n.as_slice(), bytes);
1316 }
1317
1318 #[allow(clippy::cast_lossless)]
1319 #[allow(trivial_numeric_casts)]
1320 #[cfg(feature = "alloc")]
1321 #[test]
1322 fn test_radix_large_divisor() {
1323 let radix = 5u32;
1324 let digits_limb = Word::MAX.ilog(radix as Word);
1325 let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
1326 let (div_large, _digits_large, _shift_large) =
1327 radix_large_divisor::<4>(radix, div_limb, digits_limb as usize);
1328 assert!(
1329 Uint::new(div_large)
1330 .checked_mul(&Uint::<1>::from_u32(radix))
1331 .is_none()
1332 .to_bool()
1333 );
1334 }
1335}