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
6#[cfg(feature = "rlp")]
7mod rlp;
8
9#[cfg(feature = "alloc")]
10use alloc::{string::String, vec::Vec};
11
12use super::Uint;
13use crate::{DecodeError, Limb, Word};
14
15#[cfg(feature = "alloc")]
16use super::boxed::div::div_rem_vartime_in_place;
17#[cfg(feature = "alloc")]
18use super::div_limb::{div2by1, Reciprocal};
19#[cfg(feature = "alloc")]
20use crate::{NonZero, WideWord};
21
22#[cfg(feature = "hybrid-array")]
23use crate::Encoding;
24
25#[cfg(feature = "alloc")]
26const RADIX_ENCODING_LIMBS_LARGE: usize = 32;
27
28const RADIX_ENCODING_MIN: u32 = 2;
29const RADIX_ENCODING_MAX: u32 = 36;
30
31impl<const LIMBS: usize> Uint<LIMBS> {
32    /// Create a new [`Uint`] from the provided big endian bytes.
33    pub const fn from_be_slice(bytes: &[u8]) -> Self {
34        assert!(
35            bytes.len() == Limb::BYTES * LIMBS,
36            "bytes are not the expected size"
37        );
38
39        let mut res = [Limb::ZERO; LIMBS];
40        let mut buf = [0u8; Limb::BYTES];
41        let mut i = 0;
42
43        while i < LIMBS {
44            let mut j = 0;
45            while j < Limb::BYTES {
46                buf[j] = bytes[i * Limb::BYTES + j];
47                j += 1;
48            }
49            res[LIMBS - i - 1] = Limb(Word::from_be_bytes(buf));
50            i += 1;
51        }
52
53        Uint::new(res)
54    }
55
56    /// Create a new [`Uint`] from the provided big endian hex string.
57    ///
58    /// Panics if the hex is malformed or not zero-padded accordingly for the size.
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    pub const fn from_le_slice(bytes: &[u8]) -> Self {
92        assert!(
93            bytes.len() == Limb::BYTES * LIMBS,
94            "bytes are not the expected size"
95        );
96
97        let mut res = [Limb::ZERO; LIMBS];
98        let mut buf = [0u8; Limb::BYTES];
99        let mut i = 0;
100
101        while i < LIMBS {
102            let mut j = 0;
103            while j < Limb::BYTES {
104                buf[j] = bytes[i * Limb::BYTES + j];
105                j += 1;
106            }
107            res[i] = Limb(Word::from_le_bytes(buf));
108            i += 1;
109        }
110
111        Uint::new(res)
112    }
113
114    /// Create a new [`Uint`] from the provided little endian hex string.
115    ///
116    /// Panics if the hex is malformed or not zero-padded accordingly for the size.
117    pub const fn from_le_hex(hex: &str) -> Self {
118        let bytes = hex.as_bytes();
119
120        assert!(
121            bytes.len() == Limb::BYTES * LIMBS * 2,
122            "bytes are not the expected size"
123        );
124
125        let mut res = [Limb::ZERO; LIMBS];
126        let mut buf = [0u8; Limb::BYTES];
127        let mut i = 0;
128        let mut err = 0;
129
130        while i < LIMBS {
131            let mut j = 0;
132            while j < Limb::BYTES {
133                let offset = (i * Limb::BYTES + j) * 2;
134                let (result, byte_err) = decode_hex_byte([bytes[offset], bytes[offset + 1]]);
135                err |= byte_err;
136                buf[j] = result;
137                j += 1;
138            }
139            res[i] = Limb(Word::from_le_bytes(buf));
140            i += 1;
141        }
142
143        assert!(err == 0, "invalid hex byte");
144
145        Uint::new(res)
146    }
147
148    /// Serialize this [`Uint`] as big-endian, writing it into the provided
149    /// byte slice.
150    #[cfg(feature = "hybrid-array")]
151    #[inline]
152    pub(crate) fn write_be_bytes(&self, out: &mut [u8]) {
153        debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
154
155        for (src, dst) in self
156            .limbs
157            .iter()
158            .rev()
159            .cloned()
160            .zip(out.chunks_exact_mut(Limb::BYTES))
161        {
162            dst.copy_from_slice(&src.to_be_bytes());
163        }
164    }
165
166    /// Serialize this [`Uint`] as little-endian, writing it into the provided
167    /// byte slice.
168    #[cfg(feature = "hybrid-array")]
169    #[inline]
170    pub(crate) fn write_le_bytes(&self, out: &mut [u8]) {
171        debug_assert_eq!(out.len(), Limb::BYTES * LIMBS);
172
173        for (src, dst) in self
174            .limbs
175            .iter()
176            .cloned()
177            .zip(out.chunks_exact_mut(Limb::BYTES))
178        {
179            dst.copy_from_slice(&src.to_le_bytes());
180        }
181    }
182
183    /// Create a new [`Uint`] from a string slice in a given base.
184    ///
185    /// The string may begin with a `+` character, and may use
186    /// underscore characters to separate digits.
187    ///
188    /// If the input value contains non-digit characters or digits outside of the range `0..radix`
189    /// this function will return [`DecodeError::InvalidDigit`].
190    /// If the size of the decoded integer is larger than this type can represent,
191    /// this function will return [`DecodeError::InputSize`].
192    /// Panics if `radix` is not in the range from 2 to 36.
193    pub fn from_str_radix_vartime(src: &str, radix: u32) -> Result<Self, DecodeError> {
194        let mut slf = Self::ZERO;
195        radix_decode_str(src, radix, &mut SliceDecodeByLimb::new(&mut slf.limbs))?;
196        Ok(slf)
197    }
198
199    /// Format a [`Uint`] as a string in a given base.
200    ///
201    /// Panics if `radix` is not in the range from 2 to 36.
202    #[cfg(feature = "alloc")]
203    pub fn to_string_radix_vartime(&self, radix: u32) -> String {
204        let mut buf = *self;
205        radix_encode_limbs_mut_to_string(radix, buf.as_limbs_mut())
206    }
207}
208
209/// Encode a [`Uint`] to a big endian byte array of the given size.
210pub(crate) const fn uint_to_be_bytes<const LIMBS: usize, const BYTES: usize>(
211    uint: &Uint<LIMBS>,
212) -> [u8; BYTES] {
213    if BYTES != LIMBS * Limb::BYTES {
214        panic!("BYTES != LIMBS * Limb::BYTES");
215    }
216
217    let mut ret = [0u8; BYTES];
218    let mut i = 0;
219
220    while i < LIMBS {
221        let limb_bytes = uint.limbs[LIMBS - i - 1].0.to_be_bytes();
222        let mut j = 0;
223
224        while j < Limb::BYTES {
225            ret[i * Limb::BYTES + j] = limb_bytes[j];
226            j += 1;
227        }
228
229        i += 1;
230    }
231
232    ret
233}
234
235/// Encode a [`Uint`] to a little endian byte array of the given size.
236pub(crate) const fn uint_to_le_bytes<const LIMBS: usize, const BYTES: usize>(
237    uint: &Uint<LIMBS>,
238) -> [u8; BYTES] {
239    if BYTES != LIMBS * Limb::BYTES {
240        panic!("BYTES != LIMBS * Limb::BYTES");
241    }
242
243    let mut ret = [0u8; BYTES];
244    let mut i = 0;
245
246    while i < LIMBS {
247        let limb_bytes = uint.limbs[i].0.to_le_bytes();
248        let mut j = 0;
249
250        while j < Limb::BYTES {
251            ret[i * Limb::BYTES + j] = limb_bytes[j];
252            j += 1;
253        }
254
255        i += 1;
256    }
257
258    ret
259}
260
261/// Decode a single nibble of upper or lower hex
262#[inline(always)]
263const fn decode_nibble(src: u8) -> u16 {
264    let byte = src as i16;
265    let mut ret: i16 = -1;
266
267    // 0-9  0x30-0x39
268    // if (byte > 0x2f && byte < 0x3a) ret += byte - 0x30 + 1; // -47
269    ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47);
270    // A-F  0x41-0x46
271    // if (byte > 0x40 && byte < 0x47) ret += byte - 0x41 + 10 + 1; // -54
272    ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54);
273    // a-f  0x61-0x66
274    // if (byte > 0x60 && byte < 0x67) ret += byte - 0x61 + 10 + 1; // -86
275    ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86);
276
277    ret as u16
278}
279
280/// Decode a single byte encoded as two hexadecimal characters.
281/// Second element of the tuple is non-zero if the `bytes` values are not in the valid range
282/// (0-9, a-z, A-Z).
283#[inline(always)]
284pub(crate) const fn decode_hex_byte(bytes: [u8; 2]) -> (u8, u16) {
285    let hi = decode_nibble(bytes[0]);
286    let lo = decode_nibble(bytes[1]);
287    let byte = (hi << 4) | lo;
288    let err = byte >> 8;
289    let result = byte as u8;
290    (result, err)
291}
292
293/// Allow decoding of integers into fixed and variable-length types
294pub(crate) trait DecodeByLimb {
295    /// Access the limbs as a mutable slice
296    fn limbs_mut(&mut self) -> &mut [Limb];
297
298    /// Append a new most-significant limb
299    fn push_limb(&mut self, limb: Limb) -> bool;
300}
301
302/// Wrap a `Limb`` slice as a target for decoding
303pub(crate) struct SliceDecodeByLimb<'de> {
304    limbs: &'de mut [Limb],
305    len: usize,
306}
307
308impl<'de> SliceDecodeByLimb<'de> {
309    #[inline]
310    pub fn new(limbs: &'de mut [Limb]) -> Self {
311        Self { limbs, len: 0 }
312    }
313}
314
315impl DecodeByLimb for SliceDecodeByLimb<'_> {
316    #[inline]
317    fn push_limb(&mut self, limb: Limb) -> bool {
318        if self.len < self.limbs.len() {
319            self.limbs[self.len] = limb;
320            self.len += 1;
321            true
322        } else {
323            false
324        }
325    }
326
327    #[inline]
328    fn limbs_mut(&mut self) -> &mut [Limb] {
329        &mut self.limbs[..self.len]
330    }
331}
332
333/// Decode an ascii string in base `radix`, writing the result
334/// to the `DecodeByLimb` instance `out`.
335/// The input must be a non-empty ascii string, may begin with a `+`
336/// character, and may use `_` as a separator between digits.
337pub(crate) fn radix_decode_str<D: DecodeByLimb>(
338    src: &str,
339    radix: u32,
340    out: &mut D,
341) -> Result<(), DecodeError> {
342    if !(RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix) {
343        panic!("unsupported radix");
344    }
345    if radix == 2 || radix == 4 || radix == 16 {
346        radix_decode_str_aligned_digits(src, radix as u8, out)
347    } else {
348        radix_decode_str_digits(src, radix as u8, out)
349    }
350}
351
352#[inline(always)]
353/// Perform basic validation and pre-processing on a digit string
354fn radix_preprocess_str(src: &str) -> Result<&[u8], DecodeError> {
355    // Treat the input as ascii bytes
356    let src_b = src.as_bytes();
357    let mut digits = src_b.strip_prefix(b"+").unwrap_or(src_b);
358
359    if digits.is_empty() {
360        // Blank string or plain "+" not allowed
361        Err(DecodeError::Empty)
362    } else if digits.starts_with(b"_") || digits.ends_with(b"_") {
363        // Leading or trailing underscore not allowed
364        Err(DecodeError::InvalidDigit)
365    } else {
366        // Strip leading zeroes to simplify parsing
367        while digits[0] == b'0' || digits[0] == b'_' {
368            digits = &digits[1..];
369            if digits.is_empty() {
370                break;
371            }
372        }
373        Ok(digits)
374    }
375}
376
377/// Decode a string of digits in base `radix`
378fn radix_decode_str_digits<D: DecodeByLimb>(
379    src: &str,
380    radix: u8,
381    out: &mut D,
382) -> Result<(), DecodeError> {
383    let digits = radix_preprocess_str(src)?;
384    let mut buf = [0u8; Limb::BITS as _];
385    let mut limb_digits = Word::MAX.ilog(radix as _) as usize;
386    let mut limb_max = Limb(Word::pow(radix as _, limb_digits as _));
387    let mut digits_pos = 0;
388    let mut buf_pos = 0;
389
390    while digits_pos < digits.len() {
391        // Parse digits from most significant, to fill buffer limb
392        loop {
393            let digit = match digits[digits_pos] {
394                b @ b'0'..=b'9' => b - b'0',
395                b @ b'a'..=b'z' => b + 10 - b'a',
396                b @ b'A'..=b'Z' => b + 10 - b'A',
397                b'_' => {
398                    digits_pos += 1;
399                    continue;
400                }
401                _ => radix,
402            };
403            if digit >= radix {
404                return Err(DecodeError::InvalidDigit);
405            }
406            buf[buf_pos] = digit;
407            buf_pos += 1;
408            digits_pos += 1;
409
410            if digits_pos == digits.len() || buf_pos == limb_digits {
411                break;
412            }
413        }
414
415        // On the final loop, there may be fewer digits to process
416        if buf_pos < limb_digits {
417            limb_digits = buf_pos;
418            limb_max = Limb(Word::pow(radix as _, limb_digits as _));
419        }
420
421        // Combine the digit bytes into a limb
422        let mut carry = Limb::ZERO;
423        for c in buf[..limb_digits].iter().copied() {
424            carry = Limb(carry.0 * (radix as Word) + (c as Word));
425        }
426        // Multiply the existing limbs by `radix` ^ `limb_digits`,
427        // and add the new least-significant limb
428        for limb in out.limbs_mut().iter_mut() {
429            (*limb, carry) = Limb::ZERO.mac(*limb, limb_max, carry);
430        }
431        // Append the new carried limb, if any
432        if carry.0 != 0 && !out.push_limb(carry) {
433            return Err(DecodeError::InputSize);
434        }
435
436        buf_pos = 0;
437        buf[..limb_digits].fill(0);
438    }
439
440    Ok(())
441}
442
443/// Decode digits for bases where an integer number of characters
444/// can represent a saturated Limb (specifically 2, 4, and 16).
445fn radix_decode_str_aligned_digits<D: DecodeByLimb>(
446    src: &str,
447    radix: u8,
448    out: &mut D,
449) -> Result<(), DecodeError> {
450    debug_assert!(radix == 2 || radix == 4 || radix == 16);
451
452    let digits = radix_preprocess_str(src)?;
453    let shift = radix.trailing_zeros();
454    let limb_digits = (Limb::BITS / shift) as usize;
455    let mut buf = [0u8; Limb::BITS as _];
456    let mut buf_pos = 0;
457    let mut digits_pos = digits.len();
458
459    while digits_pos > 0 {
460        // Parse digits from the least significant, to fill the buffer limb
461        loop {
462            digits_pos -= 1;
463
464            let digit = match digits[digits_pos] {
465                b @ b'0'..=b'9' => b - b'0',
466                b @ b'a'..=b'z' => b + 10 - b'a',
467                b @ b'A'..=b'Z' => b + 10 - b'A',
468                b'_' => {
469                    // cannot occur when c == 0
470                    continue;
471                }
472                _ => radix,
473            };
474            if digit >= radix {
475                return Err(DecodeError::InvalidDigit);
476            }
477            buf[buf_pos] = digit;
478            buf_pos += 1;
479
480            if digits_pos == 0 || buf_pos == limb_digits {
481                break;
482            }
483        }
484
485        if buf_pos > 0 {
486            // Combine the digit bytes into a limb
487            let mut w: Word = 0;
488            for c in buf[..buf_pos].iter().rev().copied() {
489                w = (w << shift) | (c as Word);
490            }
491            // Append the new most-significant limb
492            if !out.push_limb(Limb(w)) {
493                return Err(DecodeError::InputSize);
494            }
495
496            buf_pos = 0;
497            buf[..limb_digits].fill(0);
498        }
499    }
500
501    Ok(())
502}
503
504/// Encode a slice of limbs to a string in base `radix`. The result will have no leading
505/// zeros unless the value itself is zero.
506/// Panics if `radix` is not in the range from 2 to 36.
507#[cfg(feature = "alloc")]
508pub(crate) fn radix_encode_limbs_to_string(radix: u32, limbs: &[Limb]) -> String {
509    let mut array_buf = [Limb::ZERO; 128];
510    let mut vec_buf = Vec::new();
511    let limb_count = limbs.len();
512    let buf = if limb_count <= array_buf.len() {
513        array_buf[..limb_count].copy_from_slice(limbs);
514        &mut array_buf[..limb_count]
515    } else {
516        vec_buf.extend_from_slice(limbs);
517        &mut vec_buf[..limb_count]
518    };
519    radix_encode_limbs_mut_to_string(radix, buf)
520}
521
522/// Encode a slice of limbs to a string in base `radix`. The contents of the slice
523/// will be used as a working buffer. The result will have no leading zeros unless
524/// the value itself is zero.
525/// Panics if `radix` is not in the range from 2 to 36.
526#[cfg(feature = "alloc")]
527pub(crate) fn radix_encode_limbs_mut_to_string(radix: u32, limbs: &mut [Limb]) -> String {
528    if !(RADIX_ENCODING_MIN..=RADIX_ENCODING_MAX).contains(&radix) {
529        panic!("unsupported radix");
530    }
531
532    let mut out;
533    if radix.is_power_of_two() {
534        let bits = radix.trailing_zeros() as usize;
535        let size = (limbs.len() * Limb::BITS as usize).div_ceil(bits);
536        out = vec![0u8; size];
537        radix_encode_limbs_by_shifting(radix, limbs, &mut out[..]);
538    } else {
539        let params = RadixDivisionParams::for_radix(radix);
540        let size = params.encoded_size(limbs.len());
541        out = vec![0u8; size];
542        params.encode_limbs(limbs, &mut out[..]);
543    }
544    let size = out.len();
545    let mut skip = 0;
546    while skip + 1 < size && out[skip] == b'0' {
547        skip += 1;
548    }
549    if skip > 0 {
550        out.copy_within(skip..size, 0);
551        out.truncate(size - skip);
552    }
553    String::from_utf8(out).expect("utf-8 decoding error")
554}
555
556/// For `radix` values which are a power of two, encode the mutable limb slice to
557/// the output buffer as ASCII characters in base `radix`. Leading zeros are added to
558/// fill `out`. The slice `limbs` is used as a working buffer. Output will be truncated
559/// if the provided buffer is too small.
560#[cfg(feature = "alloc")]
561fn radix_encode_limbs_by_shifting(radix: u32, limbs: &mut [Limb], out: &mut [u8]) {
562    debug_assert!(radix.is_power_of_two());
563    debug_assert!(!out.is_empty());
564
565    let radix_bits = radix.trailing_zeros();
566    let mask = (radix - 1) as u8;
567    let mut out_idx = out.len();
568    let mut digits: WideWord = 0;
569    let mut digits_bits = 0;
570    let mut digit;
571
572    for limb in limbs.iter().chain([&Limb::ZERO]) {
573        digits_bits += Limb::BITS;
574        digits |= (limb.0 as WideWord) << (digits_bits % Limb::BITS);
575        for _ in 0..((digits_bits / radix_bits) as usize).min(out_idx) {
576            out_idx -= 1;
577            (digit, digits) = ((digits as u8) & mask, digits >> radix_bits);
578            out[out_idx] = if digit < 10 {
579                b'0' + digit
580            } else {
581                b'a' + (digit - 10)
582            };
583            digits_bits -= radix_bits;
584        }
585    }
586
587    out[0..out_idx].fill(b'0');
588}
589
590/// Parameter set used to perform radix encoding by division.
591#[cfg(feature = "alloc")]
592#[derive(Debug, Clone, Copy)]
593pub(crate) struct RadixDivisionParams {
594    radix: u32,
595    digits_limb: usize,
596    reciprocal: Reciprocal,
597    digits_large: usize,
598    div_large: [Limb; RADIX_ENCODING_LIMBS_LARGE],
599}
600
601#[cfg(feature = "alloc")]
602impl RadixDivisionParams {
603    // Generate all valid parameters ahead of time
604    #[allow(trivial_numeric_casts)]
605    const ALL: [Self; 31] = {
606        let mut res = [Self {
607            radix: 0,
608            digits_limb: 0,
609            reciprocal: Reciprocal::default(),
610            digits_large: 0,
611            div_large: [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE],
612        }; 31];
613        let mut radix: u32 = 3;
614        let mut i: usize = 0;
615        while radix <= RADIX_ENCODING_MAX {
616            if radix.is_power_of_two() {
617                radix += 1;
618                continue;
619            }
620            let digits_limb = Word::MAX.ilog(radix as Word);
621            let div_limb = NonZero(Limb((radix as Word).pow(digits_limb)));
622            let (div_large, digits_large) =
623                radix_large_divisor(radix, div_limb, digits_limb as usize);
624            res[i] = Self {
625                radix,
626                digits_limb: digits_limb as usize,
627                reciprocal: Reciprocal::new(div_limb),
628                digits_large,
629                div_large,
630            };
631            radix += 1;
632            i += 1;
633        }
634        res
635    };
636
637    #[allow(trivial_numeric_casts)]
638    pub const fn for_radix(radix: u32) -> Self {
639        if radix < RADIX_ENCODING_MIN || radix > RADIX_ENCODING_MAX {
640            panic!("invalid radix for division");
641        }
642        let ret = Self::ALL[(radix + radix.leading_zeros() - 33) as usize];
643        if ret.radix != radix {
644            panic!("radix lookup failure");
645        }
646        ret
647    }
648
649    /// Get the minimum size of the required output buffer for encoding a set of limbs.
650    pub const fn encoded_size(&self, limb_count: usize) -> usize {
651        // a slightly pessimistic estimate
652        limb_count * (self.digits_limb + 1)
653    }
654
655    /// Encode the mutable limb slice to the output buffer as ASCII characters in base
656    /// `radix`. Leading zeros are added to fill `out`. The slice `limbs` is used as a
657    /// working buffer. Output will be truncated if the provided buffer is too small.
658    #[allow(trivial_numeric_casts)]
659    fn encode_limbs(&self, limbs: &mut [Limb], out: &mut [u8]) {
660        debug_assert!(!limbs.is_empty());
661
662        let radix = self.radix as Word;
663        let div_limb = self.reciprocal.divisor().0;
664        let mut limb_count = limbs.len();
665        let mut out_idx = out.len();
666
667        if limb_count > RADIX_ENCODING_LIMBS_LARGE {
668            // Divide by the large divisor and recurse on the encoding of the digits
669            let mut remain;
670            while limb_count >= RADIX_ENCODING_LIMBS_LARGE {
671                remain = self.div_large;
672                div_rem_vartime_in_place(&mut limbs[..limb_count], &mut remain);
673                limb_count = limb_count + 1 - RADIX_ENCODING_LIMBS_LARGE;
674                if limbs[limb_count - 1] == Limb::ZERO {
675                    limb_count -= 1;
676                }
677                let next_idx = out_idx.saturating_sub(self.digits_large);
678                self.encode_limbs(&mut remain, &mut out[next_idx..out_idx]);
679                out_idx = next_idx;
680            }
681        }
682
683        let lshift = self.reciprocal.shift();
684        let rshift = (Limb::BITS - lshift) % Limb::BITS;
685        let mut hi = Limb::ZERO;
686        let mut digits_word;
687        let mut digit;
688
689        loop {
690            digits_word = if limb_count > 0 {
691                let mut carry = Limb::ZERO;
692
693                // If required by the reciprocal, left shift the buffer, placing the
694                // overflow into `hi`.
695                if lshift > 0 {
696                    for limb in limbs[..limb_count].iter_mut() {
697                        (*limb, carry) = ((*limb << lshift) | carry, *limb >> rshift);
698                    }
699                    carry |= hi << lshift;
700                } else {
701                    carry = hi;
702                }
703
704                // Divide in place by `radix ** digits_per_limb`
705                for limb in limbs[..limb_count].iter_mut().rev() {
706                    (limb.0, carry.0) = div2by1(carry.0, limb.0, &self.reciprocal);
707                }
708                if limbs[limb_count - 1] << lshift < div_limb {
709                    hi = limbs[limb_count - 1];
710                    limb_count -= 1;
711                } else {
712                    hi = Limb::ZERO
713                }
714
715                // The remainder represents a digit in base `radix ** digits_per_limb`
716                carry.0 >> lshift
717            } else {
718                // Use up the remainder in `hi`, and on any further loops continue with `0` if necessary
719                let res = hi.0;
720                hi = Limb::ZERO;
721                res
722            };
723
724            // Output the individual digits
725            for _ in 0..self.digits_limb.min(out_idx) {
726                out_idx -= 1;
727                (digits_word, digit) = (digits_word / radix, (digits_word % radix) as u8);
728                out[out_idx] = if digit < 10 {
729                    b'0' + digit
730                } else {
731                    b'a' + (digit - 10)
732                };
733            }
734
735            // Finished when the buffer is full
736            if out_idx == 0 {
737                break;
738            }
739        }
740    }
741}
742
743/// Compute the maximum radix divisor for a number of limbs.
744/// Returns a pair of the large divisor value and the number of digits,
745/// such that `divisor = radix ** digits`. The value `div_limb` is the
746/// largest power of `radix` that can fit within a limb.
747#[cfg(feature = "alloc")]
748#[allow(trivial_numeric_casts)]
749const fn radix_large_divisor(
750    radix: u32,
751    div_limb: NonZero<Limb>,
752    digits_limb: usize,
753) -> ([Limb; RADIX_ENCODING_LIMBS_LARGE], usize) {
754    let mut out = [Limb::ZERO; RADIX_ENCODING_LIMBS_LARGE];
755    let mut digits_large = digits_limb;
756    let mut top = 1;
757    out[0] = div_limb.0;
758    // Calculate largest power of div_limb (itself a power of radix)
759    while top < RADIX_ENCODING_LIMBS_LARGE {
760        let mut carry = Limb::ZERO;
761        let mut j = 0;
762        while j < top {
763            (out[j], carry) = Limb::ZERO.mac(out[j], div_limb.0, carry);
764            j += 1;
765        }
766        if carry.0 != 0 {
767            out[top] = carry;
768            top += 1;
769        }
770        digits_large += digits_limb;
771    }
772    // Multiply by radix while we can do so without overflowing
773    let mut out_test = out;
774    loop {
775        let mut carry = Limb::ZERO;
776        let mut j = 0;
777        while j < RADIX_ENCODING_LIMBS_LARGE {
778            (out_test[j], carry) = Limb::ZERO.mac(out[j], Limb(radix as Word), carry);
779            j += 1;
780        }
781        if carry.0 == 0 {
782            out = out_test;
783            digits_large += 1;
784        } else {
785            break;
786        }
787    }
788    (out, digits_large)
789}
790
791#[cfg(test)]
792mod tests {
793    use crate::{DecodeError, Limb, Zero, U128, U64};
794    use hex_literal::hex;
795
796    #[cfg(feature = "alloc")]
797    use alloc::format;
798
799    #[cfg(target_pointer_width = "32")]
800    use crate::U64 as UintEx;
801
802    #[cfg(target_pointer_width = "64")]
803    use crate::U128 as UintEx;
804
805    #[cfg(feature = "alloc")]
806    use super::radix_encode_limbs_to_string;
807
808    #[test]
809    #[cfg(target_pointer_width = "32")]
810    fn from_be_slice() {
811        let bytes = hex!("0011223344556677");
812        let n = UintEx::from_be_slice(&bytes);
813        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
814    }
815
816    #[test]
817    #[cfg(target_pointer_width = "64")]
818    fn from_be_slice() {
819        let bytes = hex!("00112233445566778899aabbccddeeff");
820        let n = UintEx::from_be_slice(&bytes);
821        assert_eq!(
822            n.as_limbs(),
823            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
824        );
825    }
826
827    #[test]
828    #[cfg(target_pointer_width = "32")]
829    fn from_le_slice() {
830        let bytes = hex!("7766554433221100");
831        let n = UintEx::from_le_slice(&bytes);
832        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
833    }
834
835    #[test]
836    #[cfg(target_pointer_width = "64")]
837    fn from_le_slice() {
838        let bytes = hex!("ffeeddccbbaa99887766554433221100");
839        let n = UintEx::from_le_slice(&bytes);
840        assert_eq!(
841            n.as_limbs(),
842            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
843        );
844    }
845
846    #[test]
847    #[cfg(target_pointer_width = "32")]
848    fn from_be_hex() {
849        let n = UintEx::from_be_hex("0011223344556677");
850        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
851    }
852
853    #[test]
854    #[cfg(target_pointer_width = "64")]
855    fn from_be_hex() {
856        let n = UintEx::from_be_hex("00112233445566778899aabbccddeeff");
857        assert_eq!(
858            n.as_limbs(),
859            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
860        );
861    }
862
863    #[test]
864    #[cfg(target_pointer_width = "32")]
865    fn from_le_hex() {
866        let n = UintEx::from_le_hex("7766554433221100");
867        assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
868    }
869
870    #[test]
871    #[cfg(target_pointer_width = "64")]
872    fn from_le_hex() {
873        let n = UintEx::from_le_hex("ffeeddccbbaa99887766554433221100");
874        assert_eq!(
875            n.as_limbs(),
876            &[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
877        );
878    }
879
880    #[cfg(feature = "alloc")]
881    #[test]
882    fn hex_upper() {
883        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
884        let n = U128::from_be_hex(hex);
885        assert_eq!(hex, format!("{:X}", n));
886    }
887
888    #[cfg(feature = "alloc")]
889    #[test]
890    fn hex_lower() {
891        let hex = "aaaaaaaabbbbbbbbccccccccdddddddd";
892        let n = U128::from_be_hex(hex);
893        assert_eq!(hex, format!("{:x}", n));
894    }
895
896    #[cfg(feature = "alloc")]
897    #[test]
898    fn fmt_binary() {
899        let hex = "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD";
900        let n = U128::from_be_hex(hex);
901        let expect = "\
902            1010101010101010101010101010101010111011101110111011101110111011\
903            1100110011001100110011001100110011011101110111011101110111011101";
904        assert_eq!(expect, format!("{:b}", n));
905    }
906
907    #[test]
908    fn from_str_radix_disallowed() {
909        let tests = [
910            ("", 10, DecodeError::Empty),
911            ("+", 10, DecodeError::Empty),
912            ("_", 10, DecodeError::InvalidDigit),
913            ("0_", 10, DecodeError::InvalidDigit),
914            ("0_", 10, DecodeError::InvalidDigit),
915            ("a", 10, DecodeError::InvalidDigit),
916            (".", 10, DecodeError::InvalidDigit),
917            (
918                "99999999999999999999999999999999",
919                10,
920                DecodeError::InputSize,
921            ),
922        ];
923        for (input, radix, expect) in tests {
924            assert_eq!(U64::from_str_radix_vartime(input, radix), Err(expect));
925        }
926    }
927
928    #[test]
929    fn from_str_radix_2() {
930        let buf = &[b'1'; 128];
931        let radix = U128::from_u64(2);
932        let radix_max = U128::from_u64(1);
933        let mut last: Option<U128> = None;
934        for idx in (1..buf.len()).rev() {
935            let res = U128::from_str_radix_vartime(
936                core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
937                2,
938            )
939            .expect("error decoding");
940            assert!(!bool::from(res.is_zero()));
941            if let Some(prev) = last {
942                assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
943            }
944            last = Some(res);
945        }
946        assert_eq!(last, Some(radix_max));
947    }
948
949    #[test]
950    fn from_str_radix_5() {
951        let buf = &[b'4'; 55];
952        let radix = U128::from_u64(5);
953        let radix_max = U128::from_u64(4);
954        let mut last: Option<U128> = None;
955        for idx in (1..buf.len()).rev() {
956            let res = U128::from_str_radix_vartime(
957                core::str::from_utf8(&buf[..idx]).expect("utf-8 error"),
958                5,
959            )
960            .expect("error decoding");
961            assert!(!bool::from(res.is_zero()));
962            if let Some(prev) = last {
963                assert_eq!(res.saturating_mul(&radix).saturating_add(&radix_max), prev);
964            }
965            last = Some(res);
966        }
967        assert_eq!(last, Some(radix_max));
968    }
969
970    #[test]
971    fn from_str_radix_10() {
972        let dec = "+340_282_366_920_938_463_463_374_607_431_768_211_455";
973        let res = U128::from_str_radix_vartime(dec, 10).expect("error decoding");
974        assert_eq!(res, U128::MAX);
975    }
976
977    #[cfg(feature = "alloc")]
978    #[test]
979    fn from_str_radix_16() {
980        let hex = "fedcba9876543210fedcba9876543210";
981        let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
982        assert_eq!(hex, format!("{res:x}"));
983    }
984
985    #[cfg(feature = "alloc")]
986    #[test]
987    fn encode_radix_8() {
988        assert_eq!(
989            &radix_encode_limbs_to_string(8, U128::MAX.as_limbs()),
990            "3777777777777777777777777777777777777777777"
991        );
992        assert_eq!(&radix_encode_limbs_to_string(8, U128::ZERO.as_limbs()), "0");
993        assert_eq!(&radix_encode_limbs_to_string(8, U128::ONE.as_limbs()), "1");
994
995        let hex = "1234567123456765432107654321";
996        let res = U128::from_str_radix_vartime(hex, 8).expect("error decoding");
997        let out = radix_encode_limbs_to_string(8, res.as_limbs());
998        assert_eq!(&out, hex);
999    }
1000
1001    #[cfg(feature = "alloc")]
1002    #[test]
1003    fn encode_radix_10() {
1004        assert_eq!(
1005            &radix_encode_limbs_to_string(10, U128::MAX.as_limbs()),
1006            "340282366920938463463374607431768211455"
1007        );
1008        assert_eq!(
1009            &radix_encode_limbs_to_string(10, U128::ZERO.as_limbs()),
1010            "0"
1011        );
1012        assert_eq!(&radix_encode_limbs_to_string(10, U128::ONE.as_limbs()), "1");
1013    }
1014
1015    #[cfg(feature = "alloc")]
1016    #[test]
1017    fn encode_radix_16() {
1018        let hex = "fedcba9876543210fedcba9876543210";
1019        let res = U128::from_str_radix_vartime(hex, 16).expect("error decoding");
1020        let out = radix_encode_limbs_to_string(16, res.as_limbs());
1021        assert_eq!(&out, hex);
1022    }
1023
1024    #[cfg(all(feature = "rand", feature = "alloc"))]
1025    #[test]
1026    fn encode_radix_round_trip() {
1027        use crate::{Random, U256};
1028        use rand_core::SeedableRng;
1029        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(1);
1030
1031        for _ in 0..100 {
1032            let uint = U256::random(&mut rng);
1033            for radix in 2..=36 {
1034                let enc = uint.to_string_radix_vartime(radix);
1035                let res = U256::from_str_radix_vartime(&enc, radix).expect("decoding error");
1036                assert_eq!(
1037                    res, uint,
1038                    "round trip failure: radix {radix} encoded {uint} as {enc}"
1039                );
1040            }
1041        }
1042    }
1043}