Skip to main content

crypto_bigint/uint/
encoding.rs

1//! Const-friendly decoding/encoding operations for [`Uint`].
2
3#[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    /// Create a new [`Uint`] from the provided big endian bytes.
27    ///
28    /// # Panics
29    /// - If the supplied byte slice is the incorrect size
30    #[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    /// Create a new [`Uint`] from the provided big endian hex string.
55    ///
56    /// # Panics
57    /// - if the hex is malformed or not zero-padded accordingly for the size.
58    #[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    /// Create a new [`Uint`] from the provided little endian bytes.
91    ///
92    /// # Panics
93    /// - If the supplied byte slice is the incorrect size
94    #[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    /// Create a new [`Uint`] from the provided little endian hex string.
119    ///
120    /// # Panics
121    /// - if the hex is malformed or not zero-padded accordingly for the size.
122    #[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    /// Serialize this [`Uint`] as big-endian, writing it into the provided
155    /// byte slice.
156    #[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    /// Serialize this [`Uint`] as little-endian, writing it into the provided
173    /// byte slice.
174    #[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    /// Create a new [`Uint`] from a string slice in a given base.
190    ///
191    /// The string may begin with a `+` character, and may use
192    /// underscore characters to separate digits.
193    ///
194    /// # Errors
195    /// - Returns [`DecodeError::InputSize`] if the size of the decoded integer is larger than this
196    ///   type can represent
197    /// - Returns [`DecodeError::InvalidDigit`] if the input value contains non-digit characters or
198    ///   digits outside of the range `0..radix`.
199    ///
200    /// # Panics
201    /// - if `radix` is not in the range from 2 to 36.
202    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    /// Format a [`Uint`] as a string in a given base.
209    ///
210    /// # Panics
211    /// - if `radix` is not in the range from 2 to 36.
212    #[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    /// Serialize as big endian bytes.
220    #[must_use]
221    pub const fn to_be_bytes(&self) -> EncodedUint<LIMBS> {
222        EncodedUint::new_be(self)
223    }
224
225    /// Serialize as little endian bytes.
226    #[must_use]
227    pub const fn to_le_bytes(&self) -> EncodedUint<LIMBS> {
228        EncodedUint::new_le(self)
229    }
230}
231
232/// [`Uint`] encoded as bytes.
233// Until const generic expressions are stable, we cannot statically declare a `u8` array
234// of the size `LIMBS * Limb::BYTES`.
235// So instead we use the array of words, and treat it as an array of bytes.
236// It's a little hacky, but it works, because the array is guaranteed to be contiguous.
237#[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    // SAFETY:
245    // - `size_of_val` returns the size of `limbs` in bytes
246    // - We are creating a new slice of the same size, but casting word to bytes, so we don't need
247    //   to worry about alignment because bytes are always aligned
248    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    // SAFETY:
256    // - `size_of_val` returns the size of `limbs` in bytes
257    // - We are creating a new slice of the same size, but casting word to bytes, so we don't need
258    //   to worry about alignment because bytes are always aligned
259    unsafe { core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), new_len) }
260}
261
262impl<const LIMBS: usize> EncodedUint<LIMBS> {
263    /// Extracts a byte slice containing the entire contents.
264    #[must_use]
265    pub const fn as_slice(&self) -> &[u8] {
266        cast_slice(&self.0)
267    }
268
269    /// Extracts a mutable byte slice containing the entire contents.
270    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            // We could cast the whole `buffer` to bytes at once,
282            // but IndexMut does not work in const context.
283            let dst_bytes: &mut [u8] = cast_slice_mut(core::slice::from_mut(&mut buffer[i]));
284
285            // `copy_from_slice` can be used here when MSRV moves past 1.87
286            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            // We could cast the whole `buffer` to bytes at once,
304            // but IndexMut does not work in const context.
305            let dst_bytes: &mut [u8] =
306                cast_slice_mut(core::slice::from_mut(&mut buffer[LIMBS - 1 - i]));
307
308            // `copy_from_slice` can be used here when MSRV moves past 1.87
309            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/// Returned if an object cannot be instantiated from the given byte slice.
391#[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/// Decode a single nibble of upper or lower hex
440#[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    // 0-9  0x30-0x39
447    // if (byte > 0x2f && byte < 0x3a) ret += byte - 0x30 + 1; // -47
448    ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
449    // A-F  0x41-0x46
450    // if (byte > 0x40 && byte < 0x47) ret += byte - 0x41 + 10 + 1; // -54
451    ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
452    // a-f  0x61-0x66
453    // if (byte > 0x60 && byte < 0x67) ret += byte - 0x61 + 10 + 1; // -86
454    ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
455
456    ret as u16
457}
458
459/// Decode a single byte encoded as two hexadecimal characters.
460/// Second element of the tuple is non-zero if the `bytes` values are not in the valid range
461/// (0-9, a-z, A-Z).
462#[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
473/// Allow decoding of integers into fixed and variable-length types
474pub(crate) trait DecodeByLimb {
475    /// Access the limbs as a mutable slice
476    fn limbs_mut(&mut self) -> &mut [Limb];
477
478    /// Append a new most-significant limb
479    fn push_limb(&mut self, limb: Limb) -> bool;
480}
481
482/// Wrap a `Limb` slice as a target for decoding
483pub(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/// Decode an ascii string in base `radix`, writing the result to the `DecodeByLimb` instance `out`.
514///
515/// The input must be a non-empty ascii string, may begin with a `+` character, and may use `_` as a
516/// separator between digits.
517///
518/// # Panics
519/// - if `radix` is not within `RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX`.
520#[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)]
538/// Perform basic validation and pre-processing on a digit string
539fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
540    // Treat the input as ascii bytes
541    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        // Blank string or plain "+" not allowed
546        Err(DecodeError::Empty)
547    } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
548        // Leading or trailing underscore not allowed
549        Err(DecodeError::InvalidDigit)
550    } else {
551        // Strip leading zeroes to simplify parsing
552        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/// Decode a string of digits in base `radix`
563#[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        // Parse digits from most significant, to fill buffer limb
578        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        // On the final loop, there may be fewer digits to process
602        if buf_pos < limb_digits {
603            limb_digits = buf_pos;
604            limb_max = Limb(Word::pow(radix.into(), limb_digits as _));
605        }
606
607        // Combine the digit bytes into a limb
608        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        // Multiply the existing limbs by `radix` ^ `limb_digits`,
613        // and add the new least-significant limb
614        for limb in out.limbs_mut().iter_mut() {
615            (*limb, carry) = limb.carrying_mul_add(limb_max, carry, Limb::ZERO);
616        }
617        // Append the new carried limb, if any
618        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/// Decode digits for bases where an integer number of characters
630/// can represent a saturated Limb (specifically 2, 4, and 16).
631#[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        // Parse digits from the least significant, to fill the buffer limb
648        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                    // cannot occur when c == 0
657                    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            // Combine the digit bytes into a limb
674            let mut w: Word = 0;
675            for c in buf[..buf_pos].iter().rev().copied() {
676                w = (w << shift) | Word::from(c);
677            }
678            // Append the new most-significant limb
679            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/// Encode a slice of limbs to a string in base `radix`. The result will have no leading
692/// zeros unless the value itself is zero.
693///
694/// # Panics
695/// - if `radix` is not in the range from 2 to 36.
696#[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/// Encode a slice of limbs to a string in base `radix`. The contents of the slice
712/// will be used as a working buffer. The result will have no leading zeros unless
713/// the value itself is zero.
714///
715/// # Panics
716/// - if `radix` is not in the range from 2 to 36.
717#[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/// For `radix` values which are a power of two, encode the mutable limb slice to
749/// the output buffer as ASCII characters in base `radix`. Leading zeros are added to
750/// fill `out`. The slice `limbs` is used as a working buffer. Output will be truncated
751/// if the provided buffer is too small.
752#[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/// Parameter set used to perform radix encoding by division.
785#[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    // Generate all valid parameters ahead of time
801    #[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    /// Get the minimum size of the required output buffer for encoding a set of limbs.
858    pub const fn encoded_size(&self, limb_count: usize) -> usize {
859        // a slightly pessimistic estimate
860        limb_count * (self.digits_per_limb + 1)
861    }
862
863    /// Encode the mutable limb slice to the output buffer as ASCII characters in base
864    /// `radix`. Leading zeros are added to fill `out`. The slice `limbs` is used as a
865    /// working buffer. Output will be truncated if the provided buffer is too small.
866    #[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                // Divide by the large divisor
877                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                // At this point, the limbs at and above RADIX_ENCODING_LIMBS_LARGE represent
887                // the quotient, and the limbs below (plus rem_high) represent the remainder
888                (limbs_rem, limbs) = limbs.split_at_mut(RADIX_ENCODING_LIMBS_LARGE - 1);
889
890                // Copy out the remainder
891                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            // Encode the next batch of digits
903            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            // The remainder represents a digit in base `radix ** digits_per_limb`
926            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            // Reduce the length of the input as we consume a limb's worth of bits (conservatively)
932            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            // Output the individual digits
939            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/// Compute the maximum radix divisor for a number of limbs.
949/// Returns a pair of the large divisor value and the number of digits,
950/// such that `divisor = radix ** digits`. The value `div_limb` is the
951/// largest power of `radix` that can fit within a limb.
952#[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    // Calculate largest power of div_limb (itself a power of radix)
964    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    // Multiply by radix while we can do so without overflowing
978    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}