dashu-int 0.6.0

Arbitrary-precision integer math library for Rust, balancing ergonomics and efficiency. Provides UBig and IBig (unsigned and signed), with small values inlined on the stack. Efficient arithmetic, modular arithmetic, and number theory (pow, ilog, gcd, gcd_ext); two's-complement bit operations; parsing and formatting in base 2-36; optional serde, rand, num-traits, rkyv, and zeroize.
Documentation
//! Buffered raw digit -> ASCII conversion.

use crate::{arch, math, radix::DigitCase};
use core::{convert::TryInto, fmt, str};

/// Minimum buffer length.
const BUFFER_LEN_MIN: usize = 32;

const BUFFER_LEN: usize = math::round_up_usize(BUFFER_LEN_MIN, arch::digits::DIGIT_CHUNK_LEN);

/// DigitWriter allows writing raw digits and turns them into ASCII.
pub struct DigitWriter<'a> {
    buffer: [u8; BUFFER_LEN],
    buffer_len: usize,
    digit_case: DigitCase,
    writer: &'a mut dyn fmt::Write,
}

impl<'a> DigitWriter<'a> {
    pub fn new(writer: &'a mut dyn fmt::Write, digit_case: DigitCase) -> DigitWriter<'a> {
        DigitWriter {
            buffer: [0; BUFFER_LEN],
            buffer_len: 0,
            digit_case,
            writer,
        }
    }

    /// buf must contain values 0-35, or 0-9 if digit_case is NoLetters.
    pub fn write(&mut self, mut buf: &[u8]) -> fmt::Result {
        while !buf.is_empty() {
            let len = buf.len().min(BUFFER_LEN - self.buffer_len);
            let (buf_chunk, buf_remainder) = buf.split_at(len);
            buf = buf_remainder;
            self.buffer[self.buffer_len..self.buffer_len + len].copy_from_slice(buf_chunk);
            self.buffer_len += len;
            if self.buffer_len == BUFFER_LEN {
                self.flush()?;
            }
        }
        Ok(())
    }

    pub fn write_digit(&mut self, digit: u8) -> fmt::Result {
        self.buffer[self.buffer_len] = digit;
        self.buffer_len += 1;
        if self.buffer_len == BUFFER_LEN {
            self.flush()?;
        }
        Ok(())
    }

    /// Must call flush to make sure all the data is written.
    pub fn flush(&mut self) -> fmt::Result {
        let buffer_len_rounded = math::round_up(self.buffer_len, arch::digits::DIGIT_CHUNK_LEN);
        self.buffer[self.buffer_len..buffer_len_rounded].fill(0);
        for chunk in
            self.buffer[..buffer_len_rounded].chunks_exact_mut(arch::digits::DIGIT_CHUNK_LEN)
        {
            arch::digits::digit_chunk_raw_to_ascii(chunk.try_into().unwrap(), self.digit_case);
        }
        let b = &self.buffer[..self.buffer_len];
        // SAFETY: the buffer contains only ASCII characters 0-9, a-z, A-Z.
        let s = unsafe { str::from_utf8_unchecked(b) };
        self.writer.write_str(s)?;
        self.buffer_len = 0;
        Ok(())
    }
}