Skip to main content

crypto_bigint/uint/
encoding.rs

1//! Const-friendly decoding/encoding operations for [`Uint`].
2
3#[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    /// Decode [`Uint`] from the provided big endian bytes.
30    ///
31    /// # Panics
32    /// If the supplied byte slice is not equal to the byte length of this [`Uint`], i.e.
33    /// [`Limb::BYTES`] * `LIMBS`.
34    #[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    /// Decode [`Uint`] from the provided little endian bytes.
60    ///
61    /// # Panics
62    /// If the supplied byte slice is not equal to the byte length of this [`Uint`], i.e.
63    /// [`Limb::BYTES`] * `LIMBS`.
64    #[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    /// Decode [`Uint`] from the provided big endian bytes, zero padding if necessary, and
90    /// truncating to the least significant bytes in the event the given amount of data exceeds
91    /// `bits_precision`.
92    ///
93    /// # Panics
94    /// If `bits_precision > Self::BITS`.
95    #[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    /// Decode [`Uint`] from the provided little endian bytes, zero padding if necessary, and
109    /// truncating to the least significant bytes in the event the given amount of data exceeds
110    /// `bits_precision`.
111    ///
112    /// # Panics
113    /// If `bits_precision > Self::BITS`.
114    #[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    /// Decode [`Uint`] from the provided slice, using the supplied [`ByteOrder`] to determine the
128    /// endianness.
129    ///
130    /// # Panics
131    /// If the supplied byte slice is not equal to the byte length of this [`Uint`], i.e.
132    /// [`Limb::BYTES`] * `LIMBS`.
133    #[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    /// Decode [`Uint`] from the provided bytes, using the supplied [`ByteOrder`] to determine the
143    /// endianness.
144    ///
145    /// Zero pads if necessary, and truncates to the least significant bytes in the event the given
146    /// amount of data exceeds `bits_precision`.
147    ///
148    /// # Panics
149    /// If `bits_precision > Self::BITS`.
150    #[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    /// Create a new [`Uint`] from the provided big endian hex string.
160    ///
161    /// # Panics
162    /// If the hex is malformed or not zero-padded accordingly for the size.
163    #[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    /// Create a new [`Uint`] from the provided little endian hex string.
197    ///
198    /// # Panics
199    /// If the hex is malformed or not zero-padded accordingly for the size.
200    #[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    /// Create a new [`Uint`] from the provided hex string, using the supplied [`ByteOrder`] to
234    /// determine the endianness.
235    ///
236    /// # Panics
237    /// - if the hex is malformed or not zero-padded accordingly for the size.
238    #[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    /// Serialize this [`Uint`] as big-endian, writing it into the provided
248    /// byte slice.
249    #[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    /// Serialize this [`Uint`] as little-endian, writing it into the provided
266    /// byte slice.
267    #[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    /// Create a new [`Uint`] from a string slice in a given base.
283    ///
284    /// The string may begin with a `+` character, and may use
285    /// underscore characters to separate digits.
286    ///
287    /// # Errors
288    /// - Returns [`DecodeError::InputSize`] if the size of the decoded integer is larger than this
289    ///   type can represent
290    /// - Returns [`DecodeError::InvalidDigit`] if the input value contains non-digit characters or
291    ///   digits outside of the range `0..radix`.
292    ///
293    /// # Panics
294    /// - if `radix` is not in the range from 2 to 36.
295    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    /// Format a [`Uint`] as a string in a given base.
302    ///
303    /// # Panics
304    /// - if `radix` is not in the range from 2 to 36.
305    #[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    /// Serialize as big endian bytes.
313    #[must_use]
314    pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
315        EncodedUint::new_be(self)
316    }
317
318    /// Serialize as little endian bytes.
319    #[must_use]
320    pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
321        EncodedUint::new_le(self)
322    }
323}
324
325/// Common implementation of a truncating big endian decoder which supports partial inputs and
326/// truncates inputs larger than the provided `bits_precision`.
327///
328/// Fills the supplied `limbs` with the decoded `bytes` after truncating to `bits_precision`.
329pub(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
347/// Common implementation of a truncating little endian decoder which supports partial inputs and
348/// truncates inputs larger than the provided `bits_precision`.
349///
350/// Fills the supplied `limbs` with the decoded `bytes` after truncating to `bits_precision`.
351pub(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/// Handle masking for the case that `bits_precision` is not aligned to `Limb::BITS`.
370#[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/// [`Uint`] encoded as bytes.
380// Until const generic expressions are stable, we cannot statically declare a `u8` array
381// of the size `LIMBS * Limb::BYTES`.
382// So instead we use the array of words, and treat it as an array of bytes.
383// It's a little hacky, but it works, because the array is guaranteed to be contiguous.
384#[derive(Copy, Clone, Debug, PartialEq, Eq)]
385pub struct EncodedUint<const LIMBS: usize>([Word; LIMBS]);
386
387#[cfg(feature = "zeroize")]
388impl<const LIMBS: usize> zeroize::Zeroize for EncodedUint<LIMBS> {
389    fn zeroize(&mut self) {
390        self.0.zeroize();
391    }
392}
393
394#[allow(unsafe_code)]
395const fn cast_slice(limbs: &[Word]) -> &[u8] {
396    let new_len = size_of_val(limbs);
397
398    // SAFETY:
399    // - `size_of_val` returns the size of `limbs` in bytes
400    // - We are creating a new slice of the same size, but casting word to bytes, so we don't need
401    //   to worry about alignment because bytes are always aligned
402    unsafe { core::slice::from_raw_parts(limbs.as_ptr() as *mut u8, new_len) }
403}
404
405#[allow(unsafe_code)]
406const fn cast_slice_mut(limbs: &mut [Word]) -> &mut [u8] {
407    let new_len = size_of_val(limbs);
408
409    // SAFETY:
410    // - `size_of_val` returns the size of `limbs` in bytes
411    // - We are creating a new slice of the same size, but casting word to bytes, so we don't need
412    //   to worry about alignment because bytes are always aligned
413    unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), new_len) }
414}
415
416impl<const LIMBS: usize> EncodedUint<LIMBS> {
417    /// Extracts a byte slice containing the entire contents.
418    #[must_use]
419    pub const fn as_slice(&self) -> &[u8] {
420        cast_slice(&self.0)
421    }
422
423    /// Extracts a mutable byte slice containing the entire contents.
424    pub const fn as_mut_slice(&mut self) -> &mut [u8] {
425        cast_slice_mut(&mut self.0)
426    }
427
428    const fn new_le(value: &Uint<LIMBS>) -> Self {
429        let mut buffer = [0; LIMBS];
430        let mut i = 0;
431
432        while i < LIMBS {
433            let src_bytes = &value.limbs[i].0.to_le_bytes();
434
435            // We could cast the whole `buffer` to bytes at once,
436            // but IndexMut does not work in const context.
437            let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
438
439            // `copy_from_slice` can be used here when MSRV moves past 1.87
440            let mut j = 0;
441            while j < Limb::BYTES {
442                dst_bytes[j] = src_bytes[j];
443                j += 1;
444            }
445
446            i += 1;
447        }
448        Self(buffer)
449    }
450
451    const fn new_be(value: &Uint<LIMBS>) -> Self {
452        let mut buffer = [0; LIMBS];
453        let mut i = 0;
454        while i < LIMBS {
455            let src_bytes = &value.limbs[i].0.to_be_bytes();
456
457            // We could cast the whole `buffer` to bytes at once,
458            // but IndexMut does not work in const context.
459            let dst_bytes: &mut [u8] =
460                cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
461
462            // `copy_from_slice` can be used here when MSRV moves past 1.87
463            let mut j = 0;
464            while j < Limb::BYTES {
465                dst_bytes[j] = src_bytes[j];
466                j += 1;
467            }
468
469            i += 1;
470        }
471        Self(buffer)
472    }
473}
474
475impl<const LIMBS: usize> Default for EncodedUint<LIMBS> {
476    fn default() -> Self {
477        Self([0; LIMBS])
478    }
479}
480
481impl<const LIMBS: usize> AsRef<[u8]> for EncodedUint<LIMBS> {
482    fn as_ref(&self) -> &[u8] {
483        self.as_slice()
484    }
485}
486
487impl<const LIMBS: usize> AsMut<[u8]> for EncodedUint<LIMBS> {
488    fn as_mut(&mut self) -> &mut [u8] {
489        self.as_mut_slice()
490    }
491}
492
493impl<const LIMBS: usize> Deref for EncodedUint<LIMBS> {
494    type Target = [u8];
495    fn deref(&self) -> &Self::Target {
496        self.as_ref()
497    }
498}
499
500impl<const BYTES: usize, const LIMBS: usize> From<EncodedUint<LIMBS>> for [u8; BYTES]
501where
502    EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
503{
504    #[inline]
505    fn from(input: EncodedUint<LIMBS>) -> Self {
506        Self::from(&input)
507    }
508}
509
510impl<const BYTES: usize, const LIMBS: usize> From<&EncodedUint<LIMBS>> for [u8; BYTES]
511where
512    EncodedUint<LIMBS>: EncodedSize<Target = [u8; BYTES]>,
513{
514    #[inline]
515    fn from(input: &EncodedUint<LIMBS>) -> Self {
516        let mut output = [0u8; BYTES];
517        output.as_mut_slice().copy_from_slice(input.as_ref());
518        output
519    }
520}
521
522impl<const BYTES: usize, const LIMBS: usize> From<[u8; BYTES]> for EncodedUint<LIMBS>
523where
524    [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
525{
526    #[inline]
527    fn from(input: [u8; BYTES]) -> Self {
528        Self::from(&input)
529    }
530}
531
532impl<const BYTES: usize, const LIMBS: usize> From<&[u8; BYTES]> for EncodedUint<LIMBS>
533where
534    [u8; BYTES]: EncodedSize<Target = EncodedUint<LIMBS>>,
535{
536    #[inline]
537    fn from(input: &[u8; BYTES]) -> Self {
538        let mut output = Self::default();
539        output.as_mut().copy_from_slice(input.as_ref());
540        output
541    }
542}
543
544/// Returned if an object cannot be instantiated from the given byte slice.
545#[derive(Clone, Copy, Debug, PartialEq, Eq)]
546pub struct TryFromSliceError;
547
548impl fmt::Display for TryFromSliceError {
549    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
550        write!(f, "TryFromSliceError")
551    }
552}
553
554impl core::error::Error for TryFromSliceError {}
555
556impl<'a, const LIMBS: usize> TryFrom<&'a [u8]> for EncodedUint<LIMBS> {
557    type Error = TryFromSliceError;
558
559    fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
560        if bytes.len() != Uint::<LIMBS>::BYTES {
561            return Err(TryFromSliceError);
562        }
563        let mut result = Self::default();
564        result.as_mut().copy_from_slice(bytes);
565        Ok(result)
566    }
567}
568
569impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
570    type Repr = EncodedUint<LIMBS>;
571
572    #[inline]
573    fn from_be_bytes(bytes: Self::Repr) -> Self {
574        Self::from_be_slice(bytes.as_ref())
575    }
576
577    #[inline]
578    fn from_le_bytes(bytes: Self::Repr) -> Self {
579        Self::from_le_slice(bytes.as_ref())
580    }
581
582    #[inline]
583    fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
584        Self::from_be_slice_truncated(bytes, bits_precision)
585    }
586
587    #[inline]
588    fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
589        Self::from_le_slice_truncated(bytes, bits_precision)
590    }
591
592    #[inline]
593    fn to_be_bytes(&self) -> Self::Repr {
594        self.to_be_bytes()
595    }
596
597    #[inline]
598    fn to_le_bytes(&self) -> Self::Repr {
599        self.to_le_bytes()
600    }
601}
602
603/// Decode a single nibble of upper or lower hex
604#[inline(always)]
605#[allow(clippy::cast_sign_loss)]
606const fn decode_nibble(src: u8) -> u16 {
607    let byte = src as i16;
608    let mut ret: i16 = -1;
609
610    // 0-9  0x30-0x39
611    // if (byte > 0x2f && byte < 0x3a) ret += byte - 0x30 + 1; // -47
612    ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
613    // A-F  0x41-0x46
614    // if (byte > 0x40 && byte < 0x47) ret += byte - 0x41 + 10 + 1; // -54
615    ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
616    // a-f  0x61-0x66
617    // if (byte > 0x60 && byte < 0x67) ret += byte - 0x61 + 10 + 1; // -86
618    ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
619
620    ret as u16
621}
622
623/// Decode a single byte encoded as two hexadecimal characters.
624/// Second element of the tuple is non-zero if the `bytes` values are not in the valid range
625/// (0-9, a-z, A-Z).
626#[inline(always)]
627#[allow(clippy::cast_possible_truncation)]
628pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
629    let hi = decode_nibble(bytes[0]);
630    let lo = decode_nibble(bytes[1]);
631    let byte = (hi << 4) | lo;
632    let err = byte >> 8;
633    let result = byte as u8;
634    (result, err)
635}
636
637/// Allow decoding of integers into fixed and variable-length types
638pub(crate) trait DecodeByLimb {
639    /// Access the limbs as a mutable slice
640    fn limbs_mut(&mut self) -> &mut [Limb];
641
642    /// Append a new most-significant limb
643    fn push_limb(&mut self, limb: Limb) -> bool;
644}
645
646/// Wrap a `Limb` slice as a target for decoding
647pub(crate) struct SliceDecodeByLimb<'de> {
648    limbs: &'de mut [Limb],
649    len: usize,
650}
651
652impl<'de> SliceDecodeByLimb<'de> {
653    #[inline]
654    pub fn new(limbs: &'de mut [Limb]) -> Self {
655        Self { limbs, len: 0 }
656    }
657}
658
659impl DecodeByLimb for SliceDecodeByLimb<'_> {
660    #[inline]
661    fn push_limb(&mut self, limb: Limb) -> bool {
662        if self.len < self.limbs.len() {
663            self.limbs[self.len] = limb;
664            self.len += 1;
665            true
666        } else {
667            false
668        }
669    }
670
671    #[inline]
672    fn limbs_mut(&mut self) -> &mut [Limb] {
673        &mut self.limbs[..self.len]
674    }
675}
676
677/// Decode an ascii string in base `radix`, writing the result to the `DecodeByLimb` instance `out`.
678///
679/// The input must be a non-empty ascii string, may begin with a `+` character, and may use `_` as a
680/// separator between digits.
681///
682/// # Panics
683/// - if `radix` is not within `RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX`.
684#[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
685pub(crate) fn radix_decode_str<D: DecodeByLimb>(
686    src: &str,
687    radix: u32,
688    out: &mut D,
689) -> Result<(), DecodeError> {
690    assert!(
691        (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
692        "unsupported radix"
693    );
694    if radix == 2 || radix == 4 || radix == 16 {
695        radix_decode_str_aligned_digits(src, radix as u8, out)
696    } else {
697        radix_decode_str_digits(src, radix as u8, out)
698    }
699}
700
701#[inline(always)]
702/// Perform basic validation and pre-processing on a digit string
703fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
704    // Treat the input as ascii bytes
705    let src_b = src.as_bytes();
706    let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
707
708    if digits.is_empty() {
709        // Blank string or plain "+" not allowed
710        Err(DecodeError::Empty)
711    } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
712        // Leading or trailing underscore not allowed
713        Err(DecodeError::InvalidDigit)
714    } else {
715        // Strip leading zeroes to simplify parsing
716        while digits[0] == b'0' || digits[0] == b'_' {
717            digits = &digits[1..];
718            if digits.is_empty() {
719                break;
720            }
721        }
722        Ok(digits)
723    }
724}
725
726/// Decode a string of digits in base `radix`
727#[allow(clippy::cast_possible_truncation)]
728fn radix_decode_str_digits<D: DecodeByLimb>(
729    src: &str,
730    radix: u8,
731    out: &mut D,
732) -> Result<(), DecodeError> {
733    let digits = radix_preprocess_str(src)?;
734    let mut buf = [0u8; Limb::BITS as _];
735    let mut limb_digits = Word::MAX.ilog(radix.into()) as usize;
736    let mut limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
737    let mut digits_pos = 0;
738    let mut buf_pos = 0;
739
740    while digits_pos < digits.len() {
741        // Parse digits from most significant, to fill buffer limb
742        loop {
743            let digit = match digits[digits_pos] {
744                b @ b'0'..=b'9' => b - b'0',
745                b @ b'a'..=b'z' => b + 10 - b'a',
746                b @ b'A'..=b'Z' => b + 10 - b'A',
747                b'_' => {
748                    digits_pos += 1;
749                    continue;
750                }
751                _ => radix,
752            };
753            if digit >= radix {
754                return Err(DecodeError::InvalidDigit);
755            }
756            buf[buf_pos] = digit;
757            buf_pos += 1;
758            digits_pos += 1;
759
760            if digits_pos == digits.len() || buf_pos == limb_digits {
761                break;
762            }
763        }
764
765        // On the final loop, there may be fewer digits to process
766        if buf_pos < limb_digits {
767            limb_digits = buf_pos;
768            limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
769        }
770
771        // Combine the digit bytes into a limb
772        let mut carry = Limb::ZERO;
773        for c in buf[..limb_digits].iter().copied() {
774            carry = Limb(carry.0 * Word::from(radix) + Word::from(c));
775        }
776        // Multiply the existing limbs by `radix` ^ `limb_digits`,
777        // and add the new least-significant limb
778        for limb in out.limbs_mut().iter_mut() {
779            (*limb, carry) = limb.carrying_mul_add(limb_max, carry, Limb::ZERO);
780        }
781        // Append the new carried limb, if any
782        if carry.0 != 0 && !out.push_limb(carry) {
783            return Err(DecodeError::InputSize);
784        }
785
786        buf_pos = 0;
787        buf[..limb_digits].fill(0);
788    }
789
790    Ok(())
791}
792
793/// Decode digits for bases where an integer number of characters
794/// can represent a saturated Limb (specifically 2, 4, and 16).
795#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
796fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
797    src: &str,
798    radix: u8,
799    out: &mut D,
800) -> Result<(), DecodeError> {
801    debug_assert!(radix == 2 || radix == 4 || radix == 16);
802
803    let digits = radix_preprocess_str(src)?;
804    let shift = radix.trailing_zeros();
805    let limb_digits = (Limb::BITS / shift) as usize;
806    let mut buf = [0u8; Limb::BITS as _];
807    let mut buf_pos = 0;
808    let mut digits_pos = digits.len();
809
810    while digits_pos > 0 {
811        // Parse digits from the least significant, to fill the buffer limb
812        loop {
813            digits_pos -= 1;
814
815            let digit = match digits[digits_pos] {
816                b @ b'0'..=b'9' => b - b'0',
817                b @ b'a'..=b'z' => b + 10 - b'a',
818                b @ b'A'..=b'Z' => b + 10 - b'A',
819                b'_' => {
820                    // cannot occur when c == 0
821                    continue;
822                }
823                _ => radix,
824            };
825            if digit >= radix {
826                return Err(DecodeError::InvalidDigit);
827            }
828            buf[buf_pos] = digit;
829            buf_pos += 1;
830
831            if digits_pos == 0 || buf_pos == limb_digits {
832                break;
833            }
834        }
835
836        if buf_pos > 0 {
837            // Combine the digit bytes into a limb
838            let mut w: Word = 0;
839            for c in buf[..buf_pos].iter().rev().copied() {
840                w = (w << shift) | Word::from(c);
841            }
842            // Append the new most-significant limb
843            if !out.push_limb(Limb(w)) {
844                return Err(DecodeError::InputSize);
845            }
846
847            buf_pos = 0;
848            buf[..limb_digits].fill(0);
849        }
850    }
851
852    Ok(())
853}
854
855/// Encode a slice of limbs to a string in base `radix`. The result will have no leading
856/// zeros unless the value itself is zero.
857///
858/// # Panics
859/// - if `radix` is not in the range from 2 to 36.
860#[cfg(feature = "alloc")]
861pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
862    let mut array_buf = [Limb::ZERO; 128];
863    let mut vec_buf = Vec::new();
864    let limb_count = limbs.len();
865    let buf = if limb_count <= array_buf.len() {
866        array_buf[..limb_count].copy_from_slice(limbs);
867        &mut array_buf[..limb_count]
868    } else {
869        vec_buf.extend_from_slice(limbs);
870        &mut vec_buf[..limb_count]
871    };
872    radix_encode_limbs_mut_to_string(radix, UintRef::new_mut(buf))
873}
874
875/// Encode a slice of limbs to a string in base `radix`. The contents of the slice
876/// will be used as a working buffer. The result will have no leading zeros unless
877/// the value itself is zero.
878///
879/// # Panics
880/// - if `radix` is not in the range from 2 to 36.
881#[cfg(feature = "alloc")]
882pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut UintRef) -> String {
883    assert!(
884        (RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix),
885        "unsupported radix"
886    );
887
888    let mut out;
889    if radix.is_power_of_two() {
890        let size = bitlen::from_limbs(limbs.nlimbs()).div_ceil(radix.trailing_zeros()) as usize;
891        out = vec![0u8; size];
892        radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
893    } else {
894        let params = RadixDivisionParams::for_radix(radix);
895        let size = params.encoded_size(limbs.nlimbs());
896        out = vec![0u8; size];
897        params.encode_limbs(limbs, &mut out[..]);
898    }
899    let size = out.len();
900    let mut skip = 0;
901    while skip + 1 < size && out[skip] == b'0' {
902        skip += 1;
903    }
904    if skip > 0 {
905        out.copy_within(skip..size, 0);
906        out.truncate(size - skip);
907    }
908    String::from_utf8(out).expect("utf-8 decoding error")
909}
910
911/// For `radix` values which are a power of two, encode the mutable limb slice to
912/// the output buffer as ASCII characters in base `radix`. Leading zeros are added to
913/// fill `out`. The slice `limbs` is used as a working buffer. Output will be truncated
914/// if the provided buffer is too small.
915#[cfg(feature = "alloc")]
916#[allow(clippy::cast_possible_truncation, reason = "needs triage")]
917#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
918fn radix_encode_limbs_by_shifting(radix: u32, limbs: &UintRef, out: &mut [u8]) {
919    debug_assert!(radix.is_power_of_two());
920    debug_assert!(!out.is_empty());
921
922    let radix_bits = radix.trailing_zeros();
923    let mask = (radix - 1) as u8;
924    let mut out_idx = out.len();
925    let mut digits: WideWord = 0;
926    let mut digits_bits = 0;
927    let mut digit;
928
929    for limb in limbs.iter().chain([&Limb::ZERO]) {
930        digits_bits += Limb::BITS;
931        digits |= WideWord::from(limb.0) << (digits_bits % Limb::BITS);
932        for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
933            out_idx -= 1;
934            (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
935            out[out_idx] = if digit < 10 {
936                b'0' + digit
937            } else {
938                b'a' + (digit - 10)
939            };
940            digits_bits -= radix_bits;
941        }
942    }
943
944    out[0..out_idx].fill(b'0');
945}
946
947/// Parameter set used to perform radix encoding by division.
948#[cfg(feature = "alloc")]
949#[derive(Debug, Clone, Copy)]
950pub(crate) struct RadixDivisionParams {
951    radix: u32,
952    digits_per_limb: usize,
953    bits_per_limb: u32,
954    recip_limb: Reciprocal,
955    digits_large: usize,
956    div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
957    recip_large: Reciprocal,
958    shift_large: u32,
959}
960
961#[cfg(feature = "alloc")]
962impl RadixDivisionParams {
963    // Generate all valid parameters ahead of time
964    #[allow(trivial_numeric_casts)]
965    const ALL: [Self; 31] = {
966        let mut res = [Self {
967            radix: 0,
968            digits_per_limb: 0,
969            bits_per_limb: 0,
970            recip_limb: Reciprocal::default(),
971            digits_large: 0,
972            div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
973            shift_large: 0,
974            recip_large: Reciprocal::default(),
975        }; 31];
976        let mut radix: u32 = 3;
977        let mut i: usize = 0;
978        while radix <= RADIX_ENCODING_MAX {
979            if radix.is_power_of_two() {
980                radix += 1;
981                continue;
982            }
983            let digits_limb = Word::MAX.ilog(radix as Word);
984            let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
985            let bits_limb = Limb::BITS - div_limb.as_ref().leading_zeros() - 1;
986            let (div_large, digits_large, shift_large) =
987                radix_large_divisor(radix, div_limb, digits_limb as usize);
988            let recip_large = Reciprocal::new(
989                div_large[RADIX_ENCODING_LIMBS_LARGE - 1]
990                    .to_nz()
991                    .expect_copied("zero divisor"),
992            );
993            res[i] = Self {
994                radix,
995                digits_per_limb: digits_limb as usize,
996                bits_per_limb: bits_limb,
997                recip_limb: Reciprocal::new(div_limb),
998                digits_large,
999                div_large,
1000                shift_large,
1001                recip_large,
1002            };
1003            radix += 1;
1004            i += 1;
1005        }
1006        res
1007    };
1008
1009    #[allow(trivial_numeric_casts)]
1010    pub const fn for_radix(radix: u32) -> Self {
1011        assert!(
1012            !(radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX),
1013            "invalid radix for division"
1014        );
1015        let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
1016        assert!(ret.radix == radix, "radix lookup failure");
1017        ret
1018    }
1019
1020    /// Get the minimum size of the required output buffer for encoding a set of limbs.
1021    pub const fn encoded_size(&self, limb_count: usize) -> usize {
1022        // a slightly pessimistic estimate
1023        limb_count * (self.digits_per_limb + 1)
1024    }
1025
1026    /// Encode the mutable limb slice to the output buffer as ASCII characters in base
1027    /// `radix`. Leading zeros are added to fill `out`. The slice `limbs` is used as a
1028    /// working buffer. Output will be truncated if the provided buffer is too small.
1029    #[allow(clippy::cast_possible_truncation)]
1030    pub fn encode_limbs(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1031        let mut out_idx = out.len();
1032        let mut remain = Uint::<RADIX_ENCODING_LIMBS_LARGE>::ZERO;
1033
1034        while out_idx > 0 {
1035            let (digits, next_idx) = if limbs.nlimbs() >= RADIX_ENCODING_THRESHOLD_LARGE {
1036                let div_large = UintRef::new(&self.div_large);
1037                let remain_mut = remain.as_mut_uint_ref();
1038
1039                // Divide by the large divisor
1040                let limbs_hi = limbs.shl_assign_limb_vartime(self.shift_large);
1041                let rem_high = limbs.div_rem_large_shifted::<true>(
1042                    limbs_hi,
1043                    div_large,
1044                    RADIX_ENCODING_LIMBS_LARGE as u32,
1045                    self.recip_large,
1046                );
1047                let limbs_rem;
1048                // At this point, the limbs at and above RADIX_ENCODING_LIMBS_LARGE represent
1049                // the quotient, and the limbs below (plus rem_high) represent the remainder
1050                (limbs_rem, limbs) = limbs.split_at_mut(RADIX_ENCODING_LIMBS_LARGE - 1);
1051
1052                // Copy out the remainder
1053                remain_mut
1054                    .leading_mut(RADIX_ENCODING_LIMBS_LARGE - 1)
1055                    .copy_from(limbs_rem);
1056                remain_mut.limbs[RADIX_ENCODING_LIMBS_LARGE - 1] = rem_high;
1057                remain_mut.shr_assign_limb_vartime(self.shift_large);
1058
1059                (remain_mut, out_idx.saturating_sub(self.digits_large))
1060            } else {
1061                (core::mem::replace(&mut limbs, UintRef::new_mut(&mut [])), 0)
1062            };
1063
1064            // Encode the next batch of digits
1065            self.encode_limbs_small(digits, &mut out[next_idx..out_idx]);
1066            out_idx = next_idx;
1067        }
1068    }
1069
1070    #[allow(trivial_numeric_casts, reason = "needs triage")]
1071    #[allow(clippy::cast_possible_truncation, reason = "needs triage")]
1072    #[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
1073    fn encode_limbs_small(&self, mut limbs: &mut UintRef, out: &mut [u8]) {
1074        const DIGITS: &[u8; 36] = b"0123456789abcdefghijklmnopqrstuvwxyz";
1075
1076        let radix = Word::from(self.radix);
1077        let mut out_idx = out.len();
1078        let mut bits_acc = 0;
1079        let (mut digit, mut digits_word);
1080
1081        while out_idx > 0 {
1082            if limbs.is_empty() {
1083                out[0..out_idx].fill(b'0');
1084                break;
1085            }
1086
1087            // The remainder represents a digit in base `radix ** digits_per_limb`
1088            let limbs_hi = limbs.shl_assign_limb_vartime(self.recip_limb.shift());
1089            digits_word = limbs
1090                .div_rem_limb_with_reciprocal_shifted(limbs_hi, &self.recip_limb)
1091                .0;
1092
1093            // Reduce the length of the input as we consume a limb's worth of bits (conservatively)
1094            bits_acc += self.bits_per_limb;
1095            if bits_acc >= Limb::BITS {
1096                bits_acc -= Limb::BITS;
1097                limbs = limbs.leading_mut(limbs.nlimbs().saturating_sub(1));
1098            }
1099
1100            // Output the individual digits
1101            for _ in 0..self.digits_per_limb.min(out_idx) {
1102                out_idx -= 1;
1103                (digits_word, digit) = (digits_word / radix, (digits_word % radix) as usize);
1104                out[out_idx] = DIGITS[digit];
1105            }
1106        }
1107    }
1108}
1109
1110/// Compute the maximum radix divisor for a number of limbs.
1111/// Returns a pair of the large divisor value and the number of digits,
1112/// such that `divisor = radix ** digits`. The value `div_limb` is the
1113/// largest power of `radix` that can fit within a limb.
1114#[cfg(feature = "alloc")]
1115#[allow(trivial_numeric_casts)]
1116const fn radix_large_divisor<const LIMBS: usize>(
1117    radix: u32,
1118    div_limb: NonZero<Limb>,
1119    digits_limb: usize,
1120) -> ([Limb; LIMBS], usize, u32) {
1121    let mut out = [Limb::ZERO; LIMBS];
1122    let mut digits_large = digits_limb;
1123    let mut top = 1;
1124    out[0] = div_limb.get_copy();
1125    // Calculate largest power of div_limb (itself a power of radix)
1126    while top < LIMBS {
1127        let mut carry = Limb::ZERO;
1128        let mut j = 0;
1129        while j < top {
1130            (out[j], carry) = out[j].carrying_mul_add(div_limb.get_copy(), carry, Limb::ZERO);
1131            j += 1;
1132        }
1133        if carry.0 != 0 {
1134            out[top] = carry;
1135            top += 1;
1136        }
1137        digits_large += digits_limb;
1138    }
1139    // Multiply by radix while we can do so without overflowing
1140    let mut out_test = out;
1141    loop {
1142        let mut carry = Limb::ZERO;
1143        let mut j = 0;
1144        while j < LIMBS {
1145            (out_test[j], carry) = out[j].carrying_mul_add(Limb(radix as Word), carry, Limb::ZERO);
1146            j += 1;
1147        }
1148        if carry.0 == 0 {
1149            out = out_test;
1150            digits_large += 1;
1151        } else {
1152            break;
1153        }
1154    }
1155
1156    let out_mut = UintRef::new_mut(&mut out);
1157    let shift = out_mut.leading_zeros();
1158    out_mut.shl_assign_limb_vartime(shift);
1159    (out, digits_large, shift)
1160}
1161
1162#[cfg(test)]
1163mod tests {
1164    use crate::{DecodeError, EncodedUint, Limb, U64, U128};
1165    use hex_literal::hex;
1166
1167    #[cfg(feature = "alloc")]
1168    use {
1169        super::{radix_encode_limbs_to_string, radix_large_divisor},
1170        crate::{NonZero, Uint, Word},
1171        alloc::format,
1172    };
1173
1174    cpubits::cpubits! {
1175        32 => {
1176            use crate::U64 as UintEx;
1177
1178            #[test]
1179            fn from_be_slice() {
1180                let bytes = hex!("0011223344556677");
1181                let n = UintEx::from_be_slice(&bytes);
1182                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1183            }
1184
1185            #[test]
1186            fn from_le_slice() {
1187                let bytes = hex!("7766554433221100");
1188                let n = UintEx::from_le_slice(&bytes);
1189                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1190            }
1191
1192            #[test]
1193            fn from_be_slice_truncated() {
1194                let bytes = hex!("0011223344556677");
1195                let n = UintEx::from_be_slice_truncated(&bytes, 64);
1196                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1197            }
1198
1199            #[test]
1200            fn from_le_slice_truncated() {
1201                let bytes = hex!("7766554433221100");
1202                let n = UintEx::from_le_slice_truncated(&bytes, 64);
1203                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1204            }
1205
1206            #[test]
1207            fn from_be_hex() {
1208                let n = UintEx::from_be_hex("0011223344556677");
1209                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1210            }
1211
1212            #[test]
1213            fn from_le_hex() {
1214                let n = UintEx::from_le_hex("7766554433221100");
1215                assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
1216            }
1217        }
1218        64 => {
1219            use crate::U128 as UintEx;
1220
1221            #[test]
1222            fn from_be_slice() {
1223                let bytes = hex!("00112233445566778899aabbccddeeff");
1224                let n = UintEx::from_be_slice(&bytes);
1225                assert_eq!(
1226                    n.as_limbs(),
1227                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1228                );
1229            }
1230
1231            #[test]
1232            fn from_le_slice() {
1233                let bytes = hex!("ffeeddccbbaa99887766554433221100");
1234                let n = UintEx::from_le_slice(&bytes);
1235                assert_eq!(
1236                    n.as_limbs(),
1237                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1238                );
1239            }
1240
1241            #[test]
1242            fn from_be_slice_truncated() {
1243                let bytes = hex!("112233445566778899aabbccddeeff");
1244                let n = UintEx::from_be_slice_truncated(&bytes, 128);
1245                assert_eq!(
1246                    n.as_limbs(),
1247                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1248                );
1249            }
1250
1251            #[test]
1252            fn from_le_slice_truncated() {
1253                let bytes = hex!("ffeeddccbbaa998877665544332211");
1254                let n = UintEx::from_le_slice_truncated(&bytes, 128);
1255                assert_eq!(
1256                    n.as_limbs(),
1257                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1258                );
1259            }
1260
1261            #[test]
1262            fn from_be_hex() {
1263                let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1264                assert_eq!(
1265                    n.as_limbs(),
1266                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1267                );
1268            }
1269
1270            #[test]
1271            fn from_le_hex() {
1272                let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
1273                assert_eq!(
1274                    n.as_limbs(),
1275                    &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
1276                );
1277            }
1278        }
1279    }
1280
1281    #[test]
1282    fn fill_limbs_from_be_slice_truncated_empty() {
1283        let mut limbs = [];
1284        assert!(super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 0).is_ok());
1285        assert_eq!(
1286            super::fill_limbs_from_be_slice_truncated(b"", &mut limbs, 1),
1287            Err(DecodeError::Precision)
1288        );
1289    }
1290
1291    #[test]
1292    fn fill_limbs_from_le_slice_truncated_empty() {
1293        let mut limbs = [];
1294        assert!(super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 0).is_ok());
1295        assert_eq!(
1296            super::fill_limbs_from_le_slice_truncated(b"", &mut limbs, 1),
1297            Err(DecodeError::Precision)
1298        );
1299    }
1300
1301    #[cfg(feature = "alloc")]
1302    #[test]
1303    fn hex_upper() {
1304        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1305        let n = U128::from_be_hex(hex);
1306        assert_eq!(hex, format!("{n:X}"));
1307    }
1308
1309    #[cfg(feature = "alloc")]
1310    #[test]
1311    fn hex_lower() {
1312        let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
1313        let n = U128::from_be_hex(hex);
1314        assert_eq!(hex, format!("{n:x}"));
1315    }
1316
1317    #[cfg(feature = "alloc")]
1318    #[test]
1319    fn fmt_binary() {
1320        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
1321        let n = U128::from_be_hex(hex);
1322        let expect = "\
1323            1010101010101010101010101010101010111011101110111011101110111011\
1324            1100110011001100110011001100110011011101110111011101110111011101";
1325        assert_eq!(expect, format!("{n:b}"));
1326    }
1327
1328    #[test]
1329    fn from_str_radix_disallowed() {
1330        let tests = [
1331            ("", 10, DecodeError::Empty),
1332            ("+", 10, DecodeError::Empty),
1333            ("_", 10, DecodeError::InvalidDigit),
1334            ("0_", 10, DecodeError::InvalidDigit),
1335            ("0_", 10, DecodeError::InvalidDigit),
1336            ("a", 10, DecodeError::InvalidDigit),
1337            (".", 10, DecodeError::InvalidDigit),
1338            (
1339                "99999999999999999999999999999999",
1340                10,
1341                DecodeError::InputSize,
1342            ),
1343        ];
1344        for (input, radix, expect) in tests {
1345            assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
1346        }
1347    }
1348
1349    #[test]
1350    fn from_str_radix_2() {
1351        let buf = &[b'1'; 128];
1352        let radix = U128::from_u64(2);
1353        let radix_max = U128::from_u64(1);
1354        let mut last: Option<U128> = None;
1355        for idx in (1..buf.len()).rev() {
1356            let res = U128::from_str_radix_vartime(
1357                core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1358                2,
1359            )
1360            .expect("error decoding");
1361            assert!(!bool::from(res.is_zero()));
1362            if let Some(prev) = last {
1363                assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1364            }
1365            last = Some(res);
1366        }
1367        assert_eq!(last, Some(radix_max));
1368    }
1369
1370    #[test]
1371    fn from_str_radix_5() {
1372        let buf = &[b'4'; 55];
1373        let radix = U128::from_u64(5);
1374        let radix_max = U128::from_u64(4);
1375        let mut last: Option<U128> = None;
1376        for idx in (1..buf.len()).rev() {
1377            let res = U128::from_str_radix_vartime(
1378                core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
1379                5,
1380            )
1381            .expect("error decoding");
1382            assert!(!bool::from(res.is_zero()));
1383            if let Some(prev) = last {
1384                assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
1385            }
1386            last = Some(res);
1387        }
1388        assert_eq!(last, Some(radix_max));
1389    }
1390
1391    #[test]
1392    fn from_str_radix_10() {
1393        let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
1394        let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
1395        assert_eq!(res, U128::MAX);
1396    }
1397
1398    #[cfg(feature = "alloc")]
1399    #[test]
1400    fn from_str_radix_16() {
1401        let hex = "fedcba9876543210fedcba9876543210";
1402        let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1403        assert_eq!(hex, format!("{res:x}"));
1404    }
1405
1406    #[cfg(feature = "alloc")]
1407    #[test]
1408    fn encode_radix_8() {
1409        assert_eq!(
1410            &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
1411            "3777777777777777777777777777777777777777777"
1412        );
1413        assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
1414        assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
1415
1416        let hex = "1234567123456765432107654321";
1417        let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
1418        let out = radix_encode_limbs_to_string(8, res.as_limbs());
1419        assert_eq!(&out, hex);
1420    }
1421
1422    #[cfg(feature = "alloc")]
1423    #[test]
1424    fn encode_radix_10() {
1425        assert_eq!(
1426            &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1427            "340282366920938463463374607431768211455"
1428        );
1429        assert_eq!(
1430            &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1431            "0"
1432        );
1433        assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1434    }
1435
1436    #[cfg(feature = "alloc")]
1437    #[test]
1438    fn encode_radix_16() {
1439        let hex = "fedcba9876543210fedcba9876543210";
1440        let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1441        let out = radix_encode_limbs_to_string(16, res.as_limbs());
1442        assert_eq!(&out, hex);
1443    }
1444
1445    #[cfg(all(feature = "rand_core", feature = "alloc"))]
1446    #[test]
1447    fn encode_radix_round_trip() {
1448        use crate::{Random, U256};
1449        use rand_core::SeedableRng;
1450        let mut rng = chacha20::ChaCha8Rng::seed_from_u64(1);
1451
1452        let rounds = if cfg!(miri) { 10 } else { 100 };
1453        for _ in 0..rounds {
1454            let uint = U256::random_from_rng(&mut rng);
1455            for radix in 2..=36 {
1456                let enc = uint.to_string_radix_vartime(radix);
1457                let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1458                assert_eq!(
1459                    res, uint,
1460                    "round trip failure: radix {radix} encoded {uint} as {enc}"
1461                );
1462            }
1463        }
1464    }
1465
1466    cpubits::cpubits! {
1467        32 => {
1468            #[test]
1469            fn encode_be_hex() {
1470                let n = UintEx::from_be_hex("0011223344556677");
1471
1472                let bytes = n.to_be_bytes();
1473                assert_eq!(bytes.as_ref(), hex!("0011223344556677"));
1474
1475                #[cfg(feature = "der")]
1476                assert_eq!(super::der::count_der_be_bytes(&n.limbs), 7);
1477            }
1478        }
1479        64 => {
1480            #[test]
1481            fn encode_be_hex() {
1482                let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
1483
1484                let bytes = n.to_be_bytes();
1485                assert_eq!(bytes.as_ref(), hex!("00112233445566778899aabbccddeeff"));
1486
1487                #[cfg(feature = "der")]
1488                assert_eq!(super::der::count_der_be_bytes(&n.limbs), 15);
1489            }
1490        }
1491    }
1492
1493    #[test]
1494    fn infer_sizes() {
1495        let bytes = b"0011223344556677";
1496
1497        let n = EncodedUint::from(bytes);
1498        assert_eq!(n.as_slice(), bytes);
1499
1500        let n = EncodedUint::from(*bytes);
1501        assert_eq!(n.as_slice(), bytes);
1502
1503        let n: [u8; 16] = EncodedUint::from(bytes).into();
1504        assert_eq!(n.as_slice(), bytes);
1505
1506        let n: [u8; 16] = (&EncodedUint::from(bytes)).into();
1507        assert_eq!(n.as_slice(), bytes);
1508    }
1509
1510    #[allow(clippy::cast_lossless)]
1511    #[allow(trivial_numeric_casts)]
1512    #[cfg(feature = "alloc")]
1513    #[test]
1514    fn test_radix_large_divisor() {
1515        let radix = 5u32;
1516        let digits_limb = Word::MAX.ilog(radix as Word);
1517        let div_limb = NonZero::new_unchecked(Limb((radix as Word).pow(digits_limb)));
1518        let (div_large, _digits_large, _shift_large) =
1519            radix_large_divisor::<4>(radix, div_limb, digits_limb as usize);
1520        assert!(
1521            Uint::new(div_large)
1522                .checked_mul(&Uint::<1>::from_u32(radix))
1523                .is_none()
1524                .to_bool()
1525        );
1526    }
1527}