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