Skip to main content

itoa/
lib.rs

1//! [![github]](https://github.com/dtolnay/itoa) [![crates-io]](https://crates.io/crates/itoa) [![docs-rs]](https://docs.rs/itoa)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! This crate provides a fast conversion of integer primitives to decimal
10//! strings. The implementation comes straight from [libcore] but avoids the
11//! performance penalty of going through [`core::fmt::Formatter`].
12//!
13//! See also [`zmij`] for printing floating point primitives.
14//!
15//! [libcore]: https://github.com/rust-lang/rust/blob/1.92.0/library/core/src/fmt/num.rs#L190-L253
16//! [`zmij`]: https://github.com/dtolnay/zmij
17//!
18//! # Example
19//!
20//! ```
21//! fn main() {
22//!     let mut buffer = itoa::Buffer::new();
23//!     let printed = buffer.format(128u64);
24//!     assert_eq!(printed, "128");
25//! }
26//! ```
27//!
28//! # Performance
29//!
30//! The [itoa-benchmark] compares this library and other Rust integer formatting
31//! implementations across a range of integer sizes. The vertical axis in this
32//! chart shows nanoseconds taken by a single execution of
33//! `itoa::Buffer::new().format(value)` so a lower result indicates a faster
34//! library.
35//!
36//! [itoa-benchmark]: https://github.com/dtolnay/itoa-benchmark
37//!
38//! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/itoa-benchmark.png)
39
40#![doc(html_root_url = "https://docs.rs/itoa/1.0.18")]
41#![no_std]
42#![allow(
43    clippy::cast_lossless,
44    clippy::cast_possible_truncation,
45    clippy::cast_sign_loss,
46    clippy::expl_impl_clone_on_copy,
47    clippy::identity_op,
48    clippy::items_after_statements,
49    clippy::must_use_candidate,
50    clippy::needless_doctest_main,
51    clippy::unreadable_literal
52)]
53
54mod u128_ext;
55
56use core::hint;
57use core::mem::{self, MaybeUninit};
58use core::str;
59#[cfg(feature = "no-panic")]
60use no_panic::no_panic;
61
62/// A correctly sized stack allocation for the formatted integer to be written
63/// into.
64///
65/// # Example
66///
67/// ```
68/// let mut buffer = itoa::Buffer::new();
69/// let printed = buffer.format(1234);
70/// assert_eq!(printed, "1234");
71/// ```
72pub struct Buffer {
73    bytes: [MaybeUninit<u8>; i128::MAX_STR_LEN],
74}
75
76impl Default for Buffer {
77    #[inline]
78    fn default() -> Buffer {
79        Buffer::new()
80    }
81}
82
83impl Copy for Buffer {}
84
85#[allow(clippy::non_canonical_clone_impl)]
86impl Clone for Buffer {
87    #[inline]
88    fn clone(&self) -> Self {
89        Buffer::new()
90    }
91}
92
93impl Buffer {
94    /// This is a cheap operation; you don't need to worry about reusing buffers
95    /// for efficiency.
96    #[inline]
97    #[cfg_attr(feature = "no-panic", no_panic)]
98    pub fn new() -> Buffer {
99        let bytes = [MaybeUninit::<u8>::uninit(); i128::MAX_STR_LEN];
100        Buffer { bytes }
101    }
102
103    /// Print an integer into this buffer and return a reference to its string
104    /// representation within the buffer.
105    #[cfg_attr(feature = "no-panic", no_panic)]
106    pub fn format<I: Integer>(&mut self, i: I) -> &str {
107        let buf_ptr = self.bytes.as_mut_ptr().cast::<I::Buffer>();
108        let string = i.write(unsafe { &mut *buf_ptr });
109        if string.len() > I::MAX_STR_LEN {
110            unsafe { hint::unreachable_unchecked() };
111        }
112        string
113    }
114}
115
116/// An integer that can be written into an [`itoa::Buffer`][Buffer].
117///
118/// This trait is sealed and cannot be implemented for types outside of itoa.
119pub trait Integer: private::Sealed {
120    /// The maximum length of string that formatting an integer of this type can
121    /// produce on the current target platform.
122    const MAX_STR_LEN: usize;
123}
124
125// Seal to prevent downstream implementations of the Integer trait.
126mod private {
127    #[doc(hidden)]
128    pub trait Sealed: Copy {
129        #[doc(hidden)]
130        type Buffer: 'static;
131        fn write(self, buf: &mut Self::Buffer) -> &str;
132    }
133}
134
135macro_rules! impl_Integer {
136    ($Signed:ident, $Unsigned:ident) => {
137        const _: () = {
138            assert!($Signed::MIN < 0, "need signed");
139            assert!($Unsigned::MIN == 0, "need unsigned");
140            assert!($Signed::BITS == $Unsigned::BITS, "need counterparts");
141        };
142
143        impl Integer for $Unsigned {
144            const MAX_STR_LEN: usize = $Unsigned::MAX.ilog10() as usize + 1;
145        }
146
147        impl private::Sealed for $Unsigned {
148            type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
149
150            #[inline]
151            #[cfg_attr(feature = "no-panic", no_panic)]
152            fn write(self, buf: &mut Self::Buffer) -> &str {
153                let offset = Unsigned::fmt(self, buf);
154                // SAFETY: Starting from `offset`, all elements of the slice have been set.
155                unsafe { slice_buffer_to_str(buf, offset) }
156            }
157        }
158
159        impl Integer for $Signed {
160            const MAX_STR_LEN: usize = $Signed::MAX.ilog10() as usize + 2;
161        }
162
163        impl private::Sealed for $Signed {
164            type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
165
166            #[inline]
167            #[cfg_attr(feature = "no-panic", no_panic)]
168            fn write(self, buf: &mut Self::Buffer) -> &str {
169                let mut offset = Self::MAX_STR_LEN - $Unsigned::MAX_STR_LEN;
170                offset += Unsigned::fmt(
171                    self.unsigned_abs(),
172                    (&mut buf[offset..]).try_into().unwrap(),
173                );
174                if self < 0 {
175                    offset -= 1;
176                    buf[offset].write(b'-');
177                }
178                // SAFETY: Starting from `offset`, all elements of the slice have been set.
179                unsafe { slice_buffer_to_str(buf, offset) }
180            }
181        }
182    };
183}
184
185const _: () =
    {
        if !(i8::MIN < 0) {
            { ::core::panicking::panic_fmt(format_args!("need signed")); }
        };
        if !(u8::MIN == 0) {
            { ::core::panicking::panic_fmt(format_args!("need unsigned")); }
        };
        if !(i8::BITS == u8::BITS) {
            {
                ::core::panicking::panic_fmt(format_args!("need counterparts"));
            }
        };
    };
impl Integer for u8 {
    const MAX_STR_LEN: usize = u8::MAX.ilog10() as usize + 1;
}
impl private::Sealed for u8 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let offset = Unsigned::fmt(self, buf);
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}
impl Integer for i8 {
    const MAX_STR_LEN: usize = i8::MAX.ilog10() as usize + 2;
}
impl private::Sealed for i8 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let mut offset = Self::MAX_STR_LEN - u8::MAX_STR_LEN;
        offset +=
            Unsigned::fmt(self.unsigned_abs(),
                (&mut buf[offset..]).try_into().unwrap());
        if self < 0 { offset -= 1; buf[offset].write(b'-'); }
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}impl_Integer!(i8, u8);
186const _: () =
    {
        if !(i16::MIN < 0) {
            { ::core::panicking::panic_fmt(format_args!("need signed")); }
        };
        if !(u16::MIN == 0) {
            { ::core::panicking::panic_fmt(format_args!("need unsigned")); }
        };
        if !(i16::BITS == u16::BITS) {
            {
                ::core::panicking::panic_fmt(format_args!("need counterparts"));
            }
        };
    };
impl Integer for u16 {
    const MAX_STR_LEN: usize = u16::MAX.ilog10() as usize + 1;
}
impl private::Sealed for u16 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let offset = Unsigned::fmt(self, buf);
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}
impl Integer for i16 {
    const MAX_STR_LEN: usize = i16::MAX.ilog10() as usize + 2;
}
impl private::Sealed for i16 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let mut offset = Self::MAX_STR_LEN - u16::MAX_STR_LEN;
        offset +=
            Unsigned::fmt(self.unsigned_abs(),
                (&mut buf[offset..]).try_into().unwrap());
        if self < 0 { offset -= 1; buf[offset].write(b'-'); }
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}impl_Integer!(i16, u16);
187const _: () =
    {
        if !(i32::MIN < 0) {
            { ::core::panicking::panic_fmt(format_args!("need signed")); }
        };
        if !(u32::MIN == 0) {
            { ::core::panicking::panic_fmt(format_args!("need unsigned")); }
        };
        if !(i32::BITS == u32::BITS) {
            {
                ::core::panicking::panic_fmt(format_args!("need counterparts"));
            }
        };
    };
impl Integer for u32 {
    const MAX_STR_LEN: usize = u32::MAX.ilog10() as usize + 1;
}
impl private::Sealed for u32 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let offset = Unsigned::fmt(self, buf);
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}
impl Integer for i32 {
    const MAX_STR_LEN: usize = i32::MAX.ilog10() as usize + 2;
}
impl private::Sealed for i32 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let mut offset = Self::MAX_STR_LEN - u32::MAX_STR_LEN;
        offset +=
            Unsigned::fmt(self.unsigned_abs(),
                (&mut buf[offset..]).try_into().unwrap());
        if self < 0 { offset -= 1; buf[offset].write(b'-'); }
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}impl_Integer!(i32, u32);
188const _: () =
    {
        if !(i64::MIN < 0) {
            { ::core::panicking::panic_fmt(format_args!("need signed")); }
        };
        if !(u64::MIN == 0) {
            { ::core::panicking::panic_fmt(format_args!("need unsigned")); }
        };
        if !(i64::BITS == u64::BITS) {
            {
                ::core::panicking::panic_fmt(format_args!("need counterparts"));
            }
        };
    };
impl Integer for u64 {
    const MAX_STR_LEN: usize = u64::MAX.ilog10() as usize + 1;
}
impl private::Sealed for u64 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let offset = Unsigned::fmt(self, buf);
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}
impl Integer for i64 {
    const MAX_STR_LEN: usize = i64::MAX.ilog10() as usize + 2;
}
impl private::Sealed for i64 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let mut offset = Self::MAX_STR_LEN - u64::MAX_STR_LEN;
        offset +=
            Unsigned::fmt(self.unsigned_abs(),
                (&mut buf[offset..]).try_into().unwrap());
        if self < 0 { offset -= 1; buf[offset].write(b'-'); }
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}impl_Integer!(i64, u64);
189const _: () =
    {
        if !(i128::MIN < 0) {
            { ::core::panicking::panic_fmt(format_args!("need signed")); }
        };
        if !(u128::MIN == 0) {
            { ::core::panicking::panic_fmt(format_args!("need unsigned")); }
        };
        if !(i128::BITS == u128::BITS) {
            {
                ::core::panicking::panic_fmt(format_args!("need counterparts"));
            }
        };
    };
impl Integer for u128 {
    const MAX_STR_LEN: usize = u128::MAX.ilog10() as usize + 1;
}
impl private::Sealed for u128 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let offset = Unsigned::fmt(self, buf);
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}
impl Integer for i128 {
    const MAX_STR_LEN: usize = i128::MAX.ilog10() as usize + 2;
}
impl private::Sealed for i128 {
    type Buffer = [MaybeUninit<u8>; Self::MAX_STR_LEN];
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        let mut offset = Self::MAX_STR_LEN - u128::MAX_STR_LEN;
        offset +=
            Unsigned::fmt(self.unsigned_abs(),
                (&mut buf[offset..]).try_into().unwrap());
        if self < 0 { offset -= 1; buf[offset].write(b'-'); }
        unsafe { slice_buffer_to_str(buf, offset) }
    }
}impl_Integer!(i128, u128);
190
191macro_rules! impl_Integer_size {
192    ($t:ty as $primitive:ident #[cfg(target_pointer_width = $width:literal)]) => {
193        #[cfg(target_pointer_width = $width)]
194        impl Integer for $t {
195            const MAX_STR_LEN: usize = <$primitive as Integer>::MAX_STR_LEN;
196        }
197
198        #[cfg(target_pointer_width = $width)]
199        impl private::Sealed for $t {
200            type Buffer = <$primitive as private::Sealed>::Buffer;
201
202            #[inline]
203            #[cfg_attr(feature = "no-panic", no_panic)]
204            fn write(self, buf: &mut Self::Buffer) -> &str {
205                (self as $primitive).write(buf)
206            }
207        }
208    };
209}
210
211impl_Integer_size!(isize as i16 #[cfg(target_pointer_width = "16")]);
212impl_Integer_size!(usize as u16 #[cfg(target_pointer_width = "16")]);
213impl_Integer_size!(isize as i32 #[cfg(target_pointer_width = "32")]);
214impl_Integer_size!(usize as u32 #[cfg(target_pointer_width = "32")]);
215impl Integer for isize {
    const MAX_STR_LEN: usize = <i64 as Integer>::MAX_STR_LEN;
}
impl private::Sealed for isize {
    type Buffer = <i64 as private::Sealed>::Buffer;
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        (self as i64).write(buf)
    }
}impl_Integer_size!(isize as i64 #[cfg(target_pointer_width = "64")]);
216impl Integer for usize {
    const MAX_STR_LEN: usize = <u64 as Integer>::MAX_STR_LEN;
}
impl private::Sealed for usize {
    type Buffer = <u64 as private::Sealed>::Buffer;
    #[inline]
    fn write(self, buf: &mut Self::Buffer) -> &str {
        (self as u64).write(buf)
    }
}impl_Integer_size!(usize as u64 #[cfg(target_pointer_width = "64")]);
217
218#[repr(C, align(2))]
219struct DecimalPairs([u8; 200]);
220
221// The string of all two-digit numbers in range 00..99 is used as a lookup table.
222static DECIMAL_PAIRS: DecimalPairs = DecimalPairs(
223    *b"0001020304050607080910111213141516171819\
224       2021222324252627282930313233343536373839\
225       4041424344454647484950515253545556575859\
226       6061626364656667686970717273747576777879\
227       8081828384858687888990919293949596979899",
228);
229
230// Returns {value / 100, value % 100} correct for values of up to 4 digits.
231fn divmod100(value: u32) -> (u32, u32) {
232    if true {
    if !(value < 10_000) {
        ::core::panicking::panic("assertion failed: value < 10_000")
    };
};debug_assert!(value < 10_000);
233    const EXP: u32 = 19; // 19 is faster or equal to 12 even for 3 digits.
234    const SIG: u32 = (1 << EXP) / 100 + 1;
235    let div = (value * SIG) >> EXP; // value / 100
236    (div, value - div * 100)
237}
238
239/// This function converts a slice of ascii characters into a `&str` starting
240/// from `offset`.
241///
242/// # Safety
243///
244/// `buf` content starting from `offset` index MUST BE initialized and MUST BE
245/// ascii characters.
246#[cfg_attr(feature = "no-panic", no_panic)]
247unsafe fn slice_buffer_to_str(buf: &[MaybeUninit<u8>], offset: usize) -> &str {
248    // SAFETY: `offset` is always included between 0 and `buf`'s length.
249    let written = unsafe { buf.get_unchecked(offset..) };
250    // SAFETY: (`assume_init_ref`) All buf content since offset is set.
251    // SAFETY: (`from_utf8_unchecked`) Writes use ASCII from the lookup table exclusively.
252    unsafe { str::from_utf8_unchecked(&*(written as *const [MaybeUninit<u8>] as *const [u8])) }
253}
254
255trait Unsigned: Integer {
256    fn fmt(self, buf: &mut Self::Buffer) -> usize;
257}
258
259macro_rules! impl_Unsigned {
260    ($Unsigned:ident) => {
261        impl Unsigned for $Unsigned {
262            #[cfg_attr(feature = "no-panic", no_panic)]
263            fn fmt(self, buf: &mut Self::Buffer) -> usize {
264                // Count the number of bytes in buf that are not initialized.
265                let mut offset = buf.len();
266                // Consume the least-significant decimals from a working copy.
267                let mut remain = self;
268
269                // Format per four digits from the lookup table.
270                // Four digits need a 16-bit $Unsigned or wider.
271                while mem::size_of::<Self>() > 1
272                    && remain
273                        > 999
274                            .try_into()
275                            .expect("branch is not hit for types that cannot fit 999 (u8)")
276                {
277                    offset -= 4;
278
279                    // pull two pairs
280                    let scale: Self = 1_00_00
281                        .try_into()
282                        .expect("branch is not hit for types that cannot fit 1E4 (u8)");
283                    let quad = remain % scale;
284                    remain /= scale;
285                    let (pair1, pair2) = divmod100(quad as u32);
286                    unsafe {
287                        buf[offset + 0]
288                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0));
289                        buf[offset + 1]
290                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1));
291                        buf[offset + 2]
292                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0));
293                        buf[offset + 3]
294                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1));
295                    }
296                }
297
298                // Format per two digits from the lookup table.
299                if remain > 9 {
300                    offset -= 2;
301
302                    let (last, pair) = divmod100(remain as u32);
303                    remain = last as Self;
304                    unsafe {
305                        buf[offset + 0]
306                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 0));
307                        buf[offset + 1]
308                            .write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 1));
309                    }
310                }
311
312                // Format the last remaining digit, if any.
313                if remain != 0 || self == 0 {
314                    offset -= 1;
315
316                    // Either the compiler sees that remain < 10, or it prevents
317                    // a boundary check up next.
318                    let last = remain as u8 & 15;
319                    buf[offset].write(b'0' + last);
320                    // not used: remain = 0;
321                }
322
323                offset
324            }
325        }
326    };
327}
328
329impl Unsigned for u8 {
    fn fmt(self, buf: &mut Self::Buffer) -> usize {
        let mut offset = buf.len();
        let mut remain = self;
        while mem::size_of::<Self>() > 1 &&
                remain >
                    999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)")
            {
            offset -= 4;
            let scale: Self =
                1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
            let quad = remain % scale;
            remain /= scale;
            let (pair1, pair2) = divmod100(quad as u32);
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                1));
                buf[offset +
                            2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                0));
                buf[offset +
                            3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                1));
            }
        }
        if remain > 9 {
            offset -= 2;
            let (last, pair) = divmod100(remain as u32);
            remain = last as Self;
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                1));
            }
        }
        if remain != 0 || self == 0 {
            offset -= 1;
            let last = remain as u8 & 15;
            buf[offset].write(b'0' + last);
        }
        offset
    }
}impl_Unsigned!(u8);
330impl Unsigned for u16 {
    fn fmt(self, buf: &mut Self::Buffer) -> usize {
        let mut offset = buf.len();
        let mut remain = self;
        while mem::size_of::<Self>() > 1 &&
                remain >
                    999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)")
            {
            offset -= 4;
            let scale: Self =
                1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
            let quad = remain % scale;
            remain /= scale;
            let (pair1, pair2) = divmod100(quad as u32);
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                1));
                buf[offset +
                            2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                0));
                buf[offset +
                            3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                1));
            }
        }
        if remain > 9 {
            offset -= 2;
            let (last, pair) = divmod100(remain as u32);
            remain = last as Self;
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                1));
            }
        }
        if remain != 0 || self == 0 {
            offset -= 1;
            let last = remain as u8 & 15;
            buf[offset].write(b'0' + last);
        }
        offset
    }
}impl_Unsigned!(u16);
331impl Unsigned for u32 {
    fn fmt(self, buf: &mut Self::Buffer) -> usize {
        let mut offset = buf.len();
        let mut remain = self;
        while mem::size_of::<Self>() > 1 &&
                remain >
                    999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)")
            {
            offset -= 4;
            let scale: Self =
                1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
            let quad = remain % scale;
            remain /= scale;
            let (pair1, pair2) = divmod100(quad as u32);
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                1));
                buf[offset +
                            2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                0));
                buf[offset +
                            3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                1));
            }
        }
        if remain > 9 {
            offset -= 2;
            let (last, pair) = divmod100(remain as u32);
            remain = last as Self;
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                1));
            }
        }
        if remain != 0 || self == 0 {
            offset -= 1;
            let last = remain as u8 & 15;
            buf[offset].write(b'0' + last);
        }
        offset
    }
}impl_Unsigned!(u32);
332impl Unsigned for u64 {
    fn fmt(self, buf: &mut Self::Buffer) -> usize {
        let mut offset = buf.len();
        let mut remain = self;
        while mem::size_of::<Self>() > 1 &&
                remain >
                    999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)")
            {
            offset -= 4;
            let scale: Self =
                1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)");
            let quad = remain % scale;
            remain /= scale;
            let (pair1, pair2) = divmod100(quad as u32);
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 +
                                1));
                buf[offset +
                            2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                0));
                buf[offset +
                            3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 +
                                1));
            }
        }
        if remain > 9 {
            offset -= 2;
            let (last, pair) = divmod100(remain as u32);
            remain = last as Self;
            unsafe {
                buf[offset +
                            0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                0));
                buf[offset +
                            1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 +
                                1));
            }
        }
        if remain != 0 || self == 0 {
            offset -= 1;
            let last = remain as u8 & 15;
            buf[offset].write(b'0' + last);
        }
        offset
    }
}impl_Unsigned!(u64);
333
334impl Unsigned for u128 {
335    #[cfg_attr(feature = "no-panic", no_panic)]
336    fn fmt(self, buf: &mut Self::Buffer) -> usize {
337        // Optimize common-case zero, which would also need special treatment due to
338        // its "leading" zero.
339        if self == 0 {
340            let offset = buf.len() - 1;
341            buf[offset].write(b'0');
342            return offset;
343        }
344        // Take the 16 least-significant decimals.
345        let (quot_1e16, mod_1e16) = div_rem_1e16(self);
346        let (mut remain, mut offset) = if quot_1e16 == 0 {
347            (mod_1e16, u128::MAX_STR_LEN)
348        } else {
349            // Write digits at buf[23..39].
350            enc_16lsd::<{ u128::MAX_STR_LEN - 16 }>(buf, mod_1e16);
351
352            // Take another 16 decimals.
353            let (quot2, mod2) = div_rem_1e16(quot_1e16);
354            if quot2 == 0 {
355                (mod2, u128::MAX_STR_LEN - 16)
356            } else {
357                // Write digits at buf[7..23].
358                enc_16lsd::<{ u128::MAX_STR_LEN - 32 }>(buf, mod2);
359                // Quot2 has at most 7 decimals remaining after two 1e16 divisions.
360                (quot2 as u64, u128::MAX_STR_LEN - 32)
361            }
362        };
363
364        // Format per four digits from the lookup table.
365        while remain > 999 {
366            offset -= 4;
367
368            // pull two pairs
369            let quad = remain % 1_00_00;
370            remain /= 1_00_00;
371            let (pair1, pair2) = divmod100(quad as u32);
372            unsafe {
373                buf[offset + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0));
374                buf[offset + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1));
375                buf[offset + 2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0));
376                buf[offset + 3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1));
377            }
378        }
379
380        // Format per two digits from the lookup table.
381        if remain > 9 {
382            offset -= 2;
383
384            let (last, pair) = divmod100(remain as u32);
385            remain = last as u64;
386            unsafe {
387                buf[offset + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 0));
388                buf[offset + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair as usize * 2 + 1));
389            }
390        }
391
392        // Format the last remaining digit, if any.
393        if remain != 0 {
394            offset -= 1;
395
396            // Either the compiler sees that remain < 10, or it prevents
397            // a boundary check up next.
398            let last = remain as u8 & 15;
399            buf[offset].write(b'0' + last);
400            // not used: remain = 0;
401        }
402        offset
403    }
404}
405
406// Encodes the 16 least-significant decimals of n into `buf[OFFSET..OFFSET + 16]`.
407#[cfg_attr(feature = "no-panic", no_panic)]
408fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], n: u64) {
409    // Consume the least-significant decimals from a working copy.
410    let mut remain = n;
411
412    // Format per four digits from the lookup table.
413    for quad_index in (1..4).rev() {
414        // pull two pairs
415        let quad = remain % 1_00_00;
416        remain /= 1_00_00;
417        let (pair1, pair2) = divmod100(quad as u32);
418        unsafe {
419            buf[quad_index * 4 + OFFSET + 0]
420                .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0));
421            buf[quad_index * 4 + OFFSET + 1]
422                .write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1));
423            buf[quad_index * 4 + OFFSET + 2]
424                .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0));
425            buf[quad_index * 4 + OFFSET + 3]
426                .write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1));
427        }
428    }
429
430    // final two pairs
431    let (pair1, pair2) = divmod100(remain as u32);
432    unsafe {
433        buf[OFFSET + 0].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 0));
434        buf[OFFSET + 1].write(*DECIMAL_PAIRS.0.get_unchecked(pair1 as usize * 2 + 1));
435        buf[OFFSET + 2].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 0));
436        buf[OFFSET + 3].write(*DECIMAL_PAIRS.0.get_unchecked(pair2 as usize * 2 + 1));
437    }
438}
439
440// Euclidean division plus remainder with constant 1E16 basically consumes 16
441// decimals from n.
442//
443// The integer division algorithm is based on the following paper:
444//
445//   T. Granlund and P. Montgomery, “Division by Invariant Integers Using Multiplication”
446//   in Proc. of the SIGPLAN94 Conference on Programming Language Design and
447//   Implementation, 1994, pp. 61–72
448//
449#[cfg_attr(feature = "no-panic", no_panic)]
450fn div_rem_1e16(n: u128) -> (u128, u64) {
451    const D: u128 = 1_0000_0000_0000_0000;
452    // The check inlines well with the caller flow.
453    if n < D {
454        return (0, n as u64);
455    }
456
457    // These constant values are computed with the CHOOSE_MULTIPLIER procedure
458    // from the Granlund & Montgomery paper, using N=128, prec=128 and d=1E16.
459    const M_HIGH: u128 = 76624777043294442917917351357515459181;
460    const SH_POST: u8 = 51;
461
462    // n.widening_mul(M_HIGH).1 >> SH_POST
463    let quot = u128_ext::mulhi(n, M_HIGH) >> SH_POST;
464    let rem = n - quot * D;
465    (quot, rem as u64)
466}