const_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 [`ryu`] for printing floating point primitives.
14//!
15//! [libcore]: https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L201-L254
16//! [`ryu`]: https://github.com/dtolnay/ryu
17//!
18//! # Example
19//!
20//! ```
21//! fn main() {
22//!     let mut buffer = const_itoa::Buffer::new();
23//!     let printed = buffer.format(128u64);
24//!     assert_eq!(printed, "128");
25//! }
26//! ```
27//!
28//! # Performance (lower is better)
29//!
30//! ![performance](https://raw.githubusercontent.com/dtolnay/itoa/master/performance.png)
31
32#![doc(html_root_url = "https://docs.rs/itoa/1.0.15")]
33#![no_std]
34#![allow(
35    clippy::cast_lossless,
36    clippy::cast_possible_truncation,
37    clippy::cast_possible_wrap,
38    clippy::cast_sign_loss,
39    clippy::expl_impl_clone_on_copy,
40    clippy::must_use_candidate,
41    clippy::needless_doctest_main,
42    clippy::unreadable_literal
43)]
44
45mod udiv128;
46
47use core::hint;
48use core::mem::MaybeUninit;
49use core::{ptr, slice, str};
50#[cfg(feature = "no-panic")]
51use no_panic::no_panic;
52
53/// A correctly sized stack allocation for the formatted integer to be written
54/// into.
55///
56/// # Example
57///
58/// ```
59/// let mut buffer = const_itoa::Buffer::new();
60/// let printed = buffer.format(1234);
61/// assert_eq!(printed, "1234");
62/// ```
63pub struct Buffer {
64    bytes: [MaybeUninit<u8>; i128::MAX_STR_LEN],
65}
66
67impl Default for Buffer {
68    #[inline]
69    fn default() -> Buffer {
70        Buffer::new()
71    }
72}
73
74impl Copy for Buffer {}
75
76#[allow(clippy::non_canonical_clone_impl)]
77impl Clone for Buffer {
78    #[inline]
79    fn clone(&self) -> Self {
80        Buffer::new()
81    }
82}
83
84impl Buffer {
85    /// This is a cheap operation; you don't need to worry about reusing buffers
86    /// for efficiency.
87    #[inline]
88    #[cfg_attr(feature = "no-panic", no_panic)]
89    pub const fn new() -> Buffer {
90        let bytes = [MaybeUninit::<u8>::uninit(); i128::MAX_STR_LEN];
91        Buffer { bytes }
92    }
93
94    /// Print an integer into this buffer and return a reference to its string
95    /// representation within the buffer.
96    #[cfg_attr(feature = "no-panic", no_panic)]
97    pub fn format<I: Integer>(&mut self, i: I) -> &str {
98        #[expect(clippy::ptr_as_ptr)]
99        #[expect(clippy::borrow_as_ptr)]
100        let string = i.write(unsafe {
101            &mut *(&mut self.bytes as *mut [MaybeUninit<u8>; i128::MAX_STR_LEN]
102                as *mut <I as private::Sealed>::Buffer)
103        });
104        if string.len() > I::MAX_STR_LEN {
105            unsafe { hint::unreachable_unchecked() };
106        }
107        string
108    }
109}
110
111/// An integer that can be written into an [`const_itoa::Buffer`][Buffer].
112///
113/// This trait is sealed and cannot be implemented for types outside of itoa.
114pub trait Integer: private::Sealed {
115    /// The maximum length of string that formatting an integer of this type can
116    /// produce on the current target platform.
117    const MAX_STR_LEN: usize;
118}
119
120/// Const version of `T: Integer`
121struct ConstInteger<T>(T);
122
123// Seal to prevent downstream implementations of the Integer trait.
124mod private {
125    #[doc(hidden)]
126    pub trait Sealed: Copy {
127        #[doc(hidden)]
128        type Buffer: 'static;
129        fn write(self, buf: &mut Self::Buffer) -> &str;
130    }
131}
132
133const DEC_DIGITS_LUT: [u8; 200] = *b"\
134      0001020304050607080910111213141516171819\
135      2021222324252627282930313233343536373839\
136      4041424344454647484950515253545556575859\
137      6061626364656667686970717273747576777879\
138      8081828384858687888990919293949596979899";
139
140// Adaptation of the original implementation at
141// https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
142macro_rules! impl_Integer {
143    ($t:ty[len = $max_len:expr] as $large_unsigned:ty) => {
144        impl Integer for $t {
145            const MAX_STR_LEN: usize = $max_len;
146        }
147
148        impl private::Sealed for $t {
149            type Buffer = [MaybeUninit<u8>; $max_len];
150
151            #[inline]
152            #[cfg_attr(feature = "no-panic", no_panic)]
153            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
154                ConstInteger(self).write(buf)
155            }
156        }
157
158        impl ConstInteger<$t> {
159            #[allow(unused_comparisons)]
160            #[inline]
161            pub const fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
162                let Self(this) = self;
163                let is_nonnegative = this >= 0;
164                let mut n = if is_nonnegative {
165                    this as $large_unsigned
166                } else {
167                    // Convert negative number to positive by summing 1 to its two's complement.
168                    (!(this as $large_unsigned)).wrapping_add(1)
169                };
170                let mut curr = buf.len();
171                #[expect(clippy::ptr_as_ptr)]
172                let buf_ptr = buf.as_mut_ptr() as *mut u8;
173                let lut_ptr = DEC_DIGITS_LUT.as_ptr();
174
175                // Render 4 digits at a time.
176                while n >= 10000 {
177                    let rem = n % 10000;
178                    n /= 10000;
179
180                    let d1 = ((rem / 100) << 1) as usize;
181                    let d2 = ((rem % 100) << 1) as usize;
182                    curr -= 4;
183                    unsafe {
184                        ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
185                        ptr::copy_nonoverlapping(lut_ptr.add(d2), buf_ptr.add(curr + 2), 2);
186                    }
187                }
188
189                // Render 2 more digits, if >2 digits.
190                if n >= 100 {
191                    let d1 = ((n % 100) << 1) as usize;
192                    n /= 100;
193                    curr -= 2;
194                    unsafe {
195                        ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
196                    }
197                }
198
199                // Render last 1 or 2 digits.
200                if n < 10 {
201                    curr -= 1;
202                    unsafe {
203                        *buf_ptr.add(curr) = (n as u8) + b'0';
204                    }
205                } else {
206                    let d1 = (n << 1) as usize;
207                    curr -= 2;
208                    unsafe {
209                        ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr), 2);
210                    }
211                }
212
213                if !is_nonnegative {
214                    curr -= 1;
215                    unsafe {
216                        *buf_ptr.add(curr) = b'-';
217                    }
218                }
219
220                let len = buf.len() - curr;
221                let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
222                unsafe { str::from_utf8_unchecked(bytes) }
223            }
224        }
225    };
226}
227
228impl Integer for i8 {
    const MAX_STR_LEN: usize = 4;
}
impl private::Sealed for i8 {
    type Buffer = [MaybeUninit<u8>; 4];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 4]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<i8> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 4]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(i8[len = 4] as u32);
229impl Integer for u8 {
    const MAX_STR_LEN: usize = 3;
}
impl private::Sealed for u8 {
    type Buffer = [MaybeUninit<u8>; 3];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 3]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<u8> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 3]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(u8[len = 3] as u32);
230impl Integer for i16 {
    const MAX_STR_LEN: usize = 6;
}
impl private::Sealed for i16 {
    type Buffer = [MaybeUninit<u8>; 6];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 6]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<i16> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 6]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(i16[len = 6] as u32);
231impl Integer for u16 {
    const MAX_STR_LEN: usize = 5;
}
impl private::Sealed for u16 {
    type Buffer = [MaybeUninit<u8>; 5];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 5]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<u16> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 5]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(u16[len = 5] as u32);
232impl Integer for i32 {
    const MAX_STR_LEN: usize = 11;
}
impl private::Sealed for i32 {
    type Buffer = [MaybeUninit<u8>; 11];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 11]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<i32> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 11]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(i32[len = 11] as u32);
233impl Integer for u32 {
    const MAX_STR_LEN: usize = 10;
}
impl private::Sealed for u32 {
    type Buffer = [MaybeUninit<u8>; 10];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 10]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<u32> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 10]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u32
            } else { (!(this as u32)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(u32[len = 10] as u32);
234impl Integer for i64 {
    const MAX_STR_LEN: usize = 20;
}
impl private::Sealed for i64 {
    type Buffer = [MaybeUninit<u8>; 20];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 20]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<i64> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 20]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u64
            } else { (!(this as u64)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(i64[len = 20] as u64);
235impl Integer for u64 {
    const MAX_STR_LEN: usize = 20;
}
impl private::Sealed for u64 {
    type Buffer = [MaybeUninit<u8>; 20];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 20]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<u64> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 20]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let mut n =
            if is_nonnegative {
                this as u64
            } else { (!(this as u64)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let lut_ptr = DEC_DIGITS_LUT.as_ptr();
        while n >= 10000 {
            let rem = n % 10000;
            n /= 10000;
            let d1 = ((rem / 100) << 1) as usize;
            let d2 = ((rem % 100) << 1) as usize;
            curr -= 4;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
                ptr::copy_nonoverlapping(lut_ptr.add(d2),
                    buf_ptr.add(curr + 2), 2);
            }
        }
        if n >= 100 {
            let d1 = ((n % 100) << 1) as usize;
            n /= 100;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if n < 10 {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
        } else {
            let d1 = (n << 1) as usize;
            curr -= 2;
            unsafe {
                ptr::copy_nonoverlapping(lut_ptr.add(d1), buf_ptr.add(curr),
                    2);
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer!(u64[len = 20] as u64);
236
237macro_rules! impl_Integer_size {
238    ($t:ty as $primitive:ident #[cfg(target_pointer_width = $width:literal)]) => {
239        #[cfg(target_pointer_width = $width)]
240        impl Integer for $t {
241            const MAX_STR_LEN: usize = <$primitive as Integer>::MAX_STR_LEN;
242        }
243
244        #[cfg(target_pointer_width = $width)]
245        impl private::Sealed for $t {
246            type Buffer = <$primitive as private::Sealed>::Buffer;
247
248            #[inline]
249            #[cfg_attr(feature = "no-panic", no_panic)]
250            fn write(self, buf: &mut Self::Buffer) -> &str {
251                (self as $primitive).write(buf)
252            }
253        }
254
255        #[cfg(target_pointer_width = $width)]
256        impl ConstInteger<$t> {
257            #[inline]
258            const fn write(
259                self,
260                buf: &mut [MaybeUninit<u8>; <$t as Integer>::MAX_STR_LEN],
261            ) -> &str {
262                ConstInteger(self.0 as $primitive).write(buf)
263            }
264        }
265    };
266}
267
268impl_Integer_size!(isize as i16 #[cfg(target_pointer_width = "16")]);
269impl_Integer_size!(usize as u16 #[cfg(target_pointer_width = "16")]);
270impl_Integer_size!(isize as i32 #[cfg(target_pointer_width = "32")]);
271impl_Integer_size!(usize as u32 #[cfg(target_pointer_width = "32")]);
272impl 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 ConstInteger<isize> {
    #[inline]
    const fn write(self,
        buf: &mut [MaybeUninit<u8>; <isize as Integer>::MAX_STR_LEN])
        -> &str {
        ConstInteger(self.0 as i64).write(buf)
    }
}impl_Integer_size!(isize as i64 #[cfg(target_pointer_width = "64")]);
273impl 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 ConstInteger<usize> {
    #[inline]
    const fn write(self,
        buf: &mut [MaybeUninit<u8>; <usize as Integer>::MAX_STR_LEN])
        -> &str {
        ConstInteger(self.0 as u64).write(buf)
    }
}impl_Integer_size!(usize as u64 #[cfg(target_pointer_width = "64")]);
274
275macro_rules! impl_Integer128 {
276    ($t:ty[len = $max_len:expr]) => {
277        impl Integer for $t {
278            const MAX_STR_LEN: usize = $max_len;
279        }
280
281        impl private::Sealed for $t {
282            type Buffer = [MaybeUninit<u8>; $max_len];
283
284            #[inline]
285            #[cfg_attr(feature = "no-panic", no_panic)]
286            fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
287                ConstInteger(self).write(buf)
288            }
289        }
290
291        impl ConstInteger<$t> {
292            #[allow(unused_comparisons)]
293            #[inline]
294            #[cfg_attr(feature = "no-panic", no_panic)]
295            pub const fn write(self, buf: &mut [MaybeUninit<u8>; $max_len]) -> &str {
296                let Self(this) = self;
297                let is_nonnegative = this >= 0;
298                let n = if is_nonnegative {
299                    this as u128
300                } else {
301                    // Convert negative number to positive by summing 1 to its two's complement.
302                    (!(this as u128)).wrapping_add(1)
303                };
304                let mut curr = buf.len();
305                #[expect(clippy::ptr_as_ptr)]
306                let buf_ptr = buf.as_mut_ptr() as *mut u8;
307
308                // Divide by 10^19 which is the highest power less than 2^64.
309                let (n, rem) = udiv128::udivmod_1e19(n);
310                #[expect(clippy::ptr_as_ptr)]
311                let buf1 = unsafe {
312                    buf_ptr.add(curr - u64::MAX_STR_LEN) as *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
313                };
314                curr -= ConstInteger(rem).write(unsafe { &mut *buf1 }).len();
315
316                if n != 0 {
317                    // Memset the base10 leading zeros of rem.
318                    let target = buf.len() - 19;
319                    unsafe {
320                        ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
321                    }
322                    curr = target;
323
324                    // Divide by 10^19 again.
325                    let (n, rem) = udiv128::udivmod_1e19(n);
326                    #[expect(clippy::ptr_as_ptr)]
327                    let buf2 = unsafe {
328                        buf_ptr.add(curr - u64::MAX_STR_LEN)
329                            as *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
330                    };
331                    curr -= ConstInteger(rem).write(unsafe { &mut *buf2 }).len();
332
333                    if n != 0 {
334                        // Memset the leading zeros.
335                        let target = buf.len() - 38;
336                        unsafe {
337                            ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
338                        }
339                        curr = target;
340
341                        // There is at most one digit left
342                        // because u128::MAX / 10^19 / 10^19 is 3.
343                        curr -= 1;
344                        unsafe {
345                            *buf_ptr.add(curr) = (n as u8) + b'0';
346                        }
347                    }
348                }
349
350                if !is_nonnegative {
351                    curr -= 1;
352                    unsafe {
353                        *buf_ptr.add(curr) = b'-';
354                    }
355                }
356
357                let len = buf.len() - curr;
358                let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
359                unsafe { str::from_utf8_unchecked(bytes) }
360            }
361        }
362    };
363}
364
365impl Integer for i128 {
    const MAX_STR_LEN: usize = 40;
}
impl private::Sealed for i128 {
    type Buffer = [MaybeUninit<u8>; 40];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 40]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<i128> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 40]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let n =
            if is_nonnegative {
                this as u128
            } else { (!(this as u128)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let (n, rem) = udiv128::udivmod_1e19(n);
        #[expect(clippy :: ptr_as_ptr)]
        let buf1 =
            unsafe {
                buf_ptr.add(curr - u64::MAX_STR_LEN) as
                    *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
            };
        curr -= ConstInteger(rem).write(unsafe { &mut *buf1 }).len();
        if n != 0 {
            let target = buf.len() - 19;
            unsafe {
                ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
            }
            curr = target;
            let (n, rem) = udiv128::udivmod_1e19(n);
            #[expect(clippy :: ptr_as_ptr)]
            let buf2 =
                unsafe {
                    buf_ptr.add(curr - u64::MAX_STR_LEN) as
                        *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
                };
            curr -= ConstInteger(rem).write(unsafe { &mut *buf2 }).len();
            if n != 0 {
                let target = buf.len() - 38;
                unsafe {
                    ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
                }
                curr = target;
                curr -= 1;
                unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer128!(i128[len = 40]);
366impl Integer for u128 {
    const MAX_STR_LEN: usize = 39;
}
impl private::Sealed for u128 {
    type Buffer = [MaybeUninit<u8>; 39];
    #[inline]
    fn write(self, buf: &mut [MaybeUninit<u8>; 39]) -> &str {
        ConstInteger(self).write(buf)
    }
}
impl ConstInteger<u128> {
    #[allow(unused_comparisons)]
    #[inline]
    pub const fn write(self, buf: &mut [MaybeUninit<u8>; 39]) -> &str {
        let Self(this) = self;
        let is_nonnegative = this >= 0;
        let n =
            if is_nonnegative {
                this as u128
            } else { (!(this as u128)).wrapping_add(1) };
        let mut curr = buf.len();
        #[expect(clippy :: ptr_as_ptr)]
        let buf_ptr = buf.as_mut_ptr() as *mut u8;
        let (n, rem) = udiv128::udivmod_1e19(n);
        #[expect(clippy :: ptr_as_ptr)]
        let buf1 =
            unsafe {
                buf_ptr.add(curr - u64::MAX_STR_LEN) as
                    *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
            };
        curr -= ConstInteger(rem).write(unsafe { &mut *buf1 }).len();
        if n != 0 {
            let target = buf.len() - 19;
            unsafe {
                ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
            }
            curr = target;
            let (n, rem) = udiv128::udivmod_1e19(n);
            #[expect(clippy :: ptr_as_ptr)]
            let buf2 =
                unsafe {
                    buf_ptr.add(curr - u64::MAX_STR_LEN) as
                        *mut [MaybeUninit<u8>; u64::MAX_STR_LEN]
                };
            curr -= ConstInteger(rem).write(unsafe { &mut *buf2 }).len();
            if n != 0 {
                let target = buf.len() - 38;
                unsafe {
                    ptr::write_bytes(buf_ptr.add(target), b'0', curr - target);
                }
                curr = target;
                curr -= 1;
                unsafe { *buf_ptr.add(curr) = (n as u8) + b'0'; }
            }
        }
        if !is_nonnegative {
            curr -= 1;
            unsafe { *buf_ptr.add(curr) = b'-'; }
        }
        let len = buf.len() - curr;
        let bytes = unsafe { slice::from_raw_parts(buf_ptr.add(curr), len) };
        unsafe { str::from_utf8_unchecked(bytes) }
    }
}impl_Integer128!(u128[len = 39]);
367
368/// Const version of [`Buffer::format()`].
369///
370/// ```
371/// let mut buffer = const_itoa::Buffer::new();
372/// let printed = buffer.format(1234i16);
373/// assert_eq!(printed, "1234");
374///
375/// # const { macro_rules! assert_eq {($a:expr,$b:expr)=>{{mod __b {pub const VAL: &[u8] = $b.as_bytes();}assert!(matches!($a.as_bytes(), __b::VAL))}}}
376/// let mut buffer = const_itoa::Buffer::new();
377/// let printed = const_itoa::Format(&mut buffer, 1234u16).call_once();
378/// assert_eq!(printed, "1234");
379///
380/// let mut buffer = [std::mem::MaybeUninit::<u8>::uninit(); <i128 as const_itoa::Integer>::MAX_STR_LEN];
381/// let printed = const_itoa::Format(&mut buffer, -1234i128).call_once();
382/// assert_eq!(printed, "-1234");
383/// # }
384/// ```
385pub struct Format<'a, B, T>(pub &'a mut B, pub T);
386
387macro_rules! impl_fns {
388    (
389        type $T:ident = each_of!$each_of:tt;
390
391        $($rest:tt)*
392    ) => {
393        impl_fns! { @impl $T $each_of {$($rest)*} }
394    };
395    (@impl $T:ident [$($Ty:ty),* $(,)?] $impl_body:tt) => {$(
396        const _: () = {
397            type $T = $Ty;
398            const _: () = $impl_body;
399        };
400    )*};
401}
402const _: () =
    {
        type I = u128;
        const _: () =
            {
                impl<'a>
                    Format<'a, [MaybeUninit<u8>; <I as Integer>::MAX_STR_LEN],
                    I> {
                    pub const fn call_once(self) -> &'a str {
                        let Self(buf, i) = self;
                        ConstInteger(i).write(buf)
                    }
                }
                impl<'a> Format<'a, Buffer, I> {
                    #[doc = r" Const version of [`Buffer::format()`]."]
                    pub const fn call_once(self) -> &'a str {
                        let Self(buf, i) = self;
                        let i = ConstInteger(i);
                        #[expect(clippy::ptr_as_ptr)]
                        #[expect(clippy::borrow_as_ptr)]
                        let string =
                            i.write(unsafe {
                                    &mut *(&mut buf.bytes as
                                                        *mut [MaybeUninit<u8>; i128::MAX_STR_LEN] as
                                                    *mut <I as private::Sealed>::Buffer)
                                });
                        if string.len() > I::MAX_STR_LEN {
                            unsafe { hint::unreachable_unchecked() };
                        }
                        string
                    }
                }
            };
    };impl_fns!(
403    type I = each_of![
404        i8, u8, i16, u16, i32, u32, i64, u64, //
405        isize, usize, //
406        i128, u128, //
407    ];
408
409    impl<'a> Format<'a, [MaybeUninit<u8>; <I as Integer>::MAX_STR_LEN], I> {
410        pub const fn call_once(self) -> &'a str {
411            let Self(buf, i) = self;
412
413            ConstInteger(i).write(buf)
414        }
415    }
416
417    impl<'a> Format<'a, Buffer, I> {
418        /// Const version of [`Buffer::format()`].
419        pub const fn call_once(self) -> &'a str {
420            let Self(buf, i) = self;
421            let i = ConstInteger(i);
422
423            #[expect(clippy::ptr_as_ptr)]
424            #[expect(clippy::borrow_as_ptr)]
425            let string = i.write(unsafe {
426                &mut *(&mut buf.bytes as *mut [MaybeUninit<u8>; i128::MAX_STR_LEN]
427                    as *mut <I as private::Sealed>::Buffer)
428            });
429            if string.len() > I::MAX_STR_LEN {
430                unsafe { hint::unreachable_unchecked() };
431            }
432            string
433        }
434    }
435);