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