1use crate::ToStr;
2
3use core::{num, ptr};
4
5static DEC_DIGITS: &[u8; 200] = b"0001020304050607080910111213141516171819\
7 2021222324252627282930313233343536373839\
8 4041424344454647484950515253545556575859\
9 6061626364656667686970717273747576777879\
10 8081828384858687888990919293949596979899";
11static HEX_DIGITS: [u8; 16] = [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f'];
12const PTR_PREFIX: [u8; 2] = [b'0', b'x'];
13
14const fn size_of_val<T>(_: &T) -> usize {
15 core::mem::size_of::<T>()
16}
17
18macro_rules! write_digit {
19 ($buffer_ptr:ident[$cursor:ident] = $digit:expr) => {
20 $cursor -= 1;
21 unsafe {
22 *$buffer_ptr.offset($cursor) = ($digit as u8) + b'0';
23 }
24 }
25}
26
27macro_rules! write_two_digits {
28 ($buffer_ptr:ident[$cursor:ident] = $digits_ptr:ident[$digits_offset:expr]) => {
29
30 $cursor -= 1;
31 unsafe {
32 *$buffer_ptr.offset($cursor) = *$digits_ptr.offset($digits_offset + 1);
33 }
34
35 $cursor -= 1;
36 unsafe {
37 *$buffer_ptr.offset($cursor) = *$digits_ptr.offset($digits_offset);
38 }
39 };
40}
41
42pub(crate) const unsafe fn write_u8_to_buf(mut num: u8, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
43 let digits_ptr = DEC_DIGITS.as_ptr();
44
45 if num >= 100 {
46 let index = (num as isize % 100) << 1;
47 num /= 100;
48
49 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index]);
50 write_digit!(buffer_ptr[cursor] = num);
51 } else if num <= 9 {
52 write_digit!(buffer_ptr[cursor] = num);
53 } else {
54 let index = num as isize * 2;
55
56 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index]);
57 }
58
59 cursor
60}
61
62pub(crate) const unsafe fn write_u64_to_buf(mut num: u64, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
63 let digits_ptr = DEC_DIGITS.as_ptr();
64
65 while num >= 10000 {
66 let rem = (num % 10000) as isize;
67 num /= 10000;
68
69 let index1 = (rem / 100) << 1;
70 let index2 = (rem % 100) << 1;
71 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index2]);
72 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index1]);
73 }
74
75 if num >= 100 {
76 let index = (num as isize % 100) << 1;
77 num /= 100;
78
79 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index]);
80 }
81
82 if num < 10 {
83 write_digit!(buffer_ptr[cursor] = num);
84 } else {
85 let index = num as isize * 2;
86
87 write_two_digits!(buffer_ptr[cursor] = digits_ptr[index]);
88 }
89
90 cursor
91}
92
93#[inline(always)]
97const fn udivmod_1e19(num: &mut u128) -> u64 {
98 const DIV: u64 = 10_000_000_000_000_000_000;
99
100 #[inline(always)]
101 const fn u128_mulhi(x: u128, y: u128) -> u128 {
102 let x_lo = x as u64;
103 let x_hi = (x >> 64) as u64;
104 let y_lo = y as u64;
105 let y_hi = (y >> 64) as u64;
106
107 let carry = (x_lo as u128 * y_lo as u128) >> 64;
109 let m = x_lo as u128 * y_hi as u128 + carry;
110 let high1 = m >> 64;
111
112 let m_lo = m as u64;
113 let high2 = (x_hi as u128 * y_lo as u128 + m_lo as u128) >> 64;
114
115 x_hi as u128 * y_hi as u128 + high1 + high2
116 }
117
118 let quot = if *num < 1 << 83 {
119 ((*num >> 19) as u64 / (DIV >> 19)) as u128
120 } else {
121 u128_mulhi(*num, 156927543384667019095894735580191660403) >> 62
122 };
123
124 let rem = (*num - quot * DIV as u128) as u64;
125 *num = quot;
126
127 rem
128}
129
130#[inline]
131pub(crate) const unsafe fn write_u128_to_buf(mut num: u128, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
132 const U64_TEXT_MAX_WRITTEN: isize = u64::TEXT_SIZE as isize - 1;
133
134 if num <= u64::MAX as u128 {
135 unsafe {
137 return write_u64_to_buf(num as u64, buffer_ptr, cursor)
138 }
139 }
140
141 let first64 = udivmod_1e19(&mut num);
142 if num <= u64::MAX as u128 {
143 unsafe {
145 ptr::write_bytes(buffer_ptr.offset(cursor - U64_TEXT_MAX_WRITTEN), b'0', U64_TEXT_MAX_WRITTEN as _);
146 }
147
148 unsafe {
149 write_u64_to_buf(first64, buffer_ptr, cursor);
150 }
151 unsafe {
153 write_u64_to_buf(num as u64, buffer_ptr, cursor - U64_TEXT_MAX_WRITTEN)
154 }
155 } else {
156 let second64 = udivmod_1e19(&mut num);
157
158 unsafe {
160 ptr::write_bytes(buffer_ptr.offset(cursor - U64_TEXT_MAX_WRITTEN * 2), b'0', (U64_TEXT_MAX_WRITTEN * 2) as _);
161 }
162
163 unsafe {
164 write_u64_to_buf(first64, buffer_ptr, cursor);
165 }
166
167 cursor -= U64_TEXT_MAX_WRITTEN;
168 let written_cursor = unsafe {
169 write_u64_to_buf(second64, buffer_ptr, cursor)
170 };
171
172 if num != 0 {
173 cursor -= U64_TEXT_MAX_WRITTEN;
174 write_digit!(buffer_ptr[cursor] = num);
177 cursor
178 } else {
179 written_cursor
180 }
181 }
182}
183
184const unsafe fn write_hex_to_buf(mut num: usize, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
185 const BASE: usize = 4;
186 const BASE_DIGIT: usize = (1 << BASE) - 1;
187 let digits_ptr = HEX_DIGITS.as_ptr();
188
189 loop {
190 let digit = num & BASE_DIGIT;
191 cursor -= 1;
192 unsafe {
193 ptr::write(buffer_ptr.offset(cursor), *digits_ptr.add(digit));
194 }
195 num >>= BASE;
196
197 if num == 0 {
198 break;
199 }
200 }
201
202 cursor
203}
204
205#[inline(always)]
206pub(crate) const unsafe fn write_ptr_to_buf(num: usize, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
207 const PTR_PREFIX_SIZE: usize = size_of_val(&PTR_PREFIX);
208 cursor = unsafe {
209 write_hex_to_buf(num, buffer_ptr, cursor)
210 };
211 cursor -= PTR_PREFIX_SIZE as isize;
212
213 unsafe {
214 ptr::copy_nonoverlapping(PTR_PREFIX.as_ptr(), buffer_ptr.offset(cursor), PTR_PREFIX_SIZE);
215 }
216
217 cursor
218}
219
220macro_rules! impl_unsigned {
221 ($t:ident: $max:expr; $conv:ident($($cv_t:tt)*)) => {
222 #[inline]
223 pub(crate) const fn $t(num: $t, buffer: &'_ mut [core::mem::MaybeUninit<u8>]) -> &'_ str {
224 debug_assert!(buffer.len() >= <$t as crate::ToStr>::TEXT_SIZE);
225 unsafe {
226 let offset = super::$conv(num $($cv_t)*, buffer.as_mut_ptr() as *mut u8, buffer.len() as isize);
227 let slice = core::slice::from_raw_parts(buffer.as_ptr().offset(offset) as *const u8, buffer.len() - offset as usize);
228 core::str::from_utf8_unchecked(slice)
229 }
230 }
231
232 unsafe impl crate::ToStr for $t {
233 const TEXT_SIZE: usize = $max;
234
235 #[inline(always)]
236 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
237 let buffer = unsafe {
238 core::mem::transmute::<&'a mut [u8], &'a mut [core::mem::MaybeUninit<u8>]>(buffer)
239 };
240 $t(*self, buffer)
241 }
242 }
243 }
244}
245
246pub mod unsigned {
247 impl_unsigned!(u8: 3; write_u8_to_buf(as u8));
248 impl_unsigned!(u16: 5; write_u64_to_buf(as u64));
249 impl_unsigned!(u32: 10; write_u64_to_buf(as u64));
250 impl_unsigned!(u64: 20; write_u64_to_buf(as u64));
251 impl_unsigned!(u128: 39; write_u128_to_buf(as u128));
252
253 pub(crate) const fn usize(num: usize, buffer: &'_ mut [core::mem::MaybeUninit<u8>]) -> &'_ str {
254 debug_assert!(buffer.len() >= <usize as crate::ToStr>::TEXT_SIZE);
255 unsafe {
256 let offset = super::write_u64_to_buf(num as _, buffer.as_mut_ptr() as *mut u8, buffer.len() as isize);
257 let slice = core::slice::from_raw_parts(buffer.as_ptr().offset(offset) as *const u8, buffer.len() - offset as usize);
258 core::str::from_utf8_unchecked(slice)
259 }
260 }
261}
262
263unsafe impl ToStr for usize {
264 #[cfg(target_pointer_width = "16")]
265 const TEXT_SIZE: usize = <u16 as ToStr>::TEXT_SIZE;
266 #[cfg(target_pointer_width = "32")]
267 const TEXT_SIZE: usize = <u32 as ToStr>::TEXT_SIZE;
268 #[cfg(target_pointer_width = "64")]
269 const TEXT_SIZE: usize = <u64 as ToStr>::TEXT_SIZE;
270
271 #[inline]
272 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
273 let buffer = unsafe {
274 core::mem::transmute::<&'a mut [u8], &'a mut [core::mem::MaybeUninit<u8>]>(buffer)
275 };
276 unsigned::usize(*self, buffer)
277 }
278}
279
280macro_rules! impl_signed {
281 ($t:ident as $st:ident where $conv:ident as $cv_t:ty) => {
282 #[inline]
283 pub(crate) const fn $t(num: $t, buffer: &'_ mut [core::mem::MaybeUninit<u8>]) -> &'_ str {
284 if num.is_negative() {
285 debug_assert!(buffer.len() >= <$t as crate::ToStr>::TEXT_SIZE);
286
287 let abs = (0 as $st).wrapping_sub(num as $st);
288 unsafe {
289 let offset = super::$conv(abs as $cv_t, buffer.as_mut_ptr() as *mut u8, buffer.len() as isize) - 1;
290 core::ptr::write(buffer.as_mut_ptr().offset(offset), core::mem::MaybeUninit::new(b'-'));
291 let slice = core::slice::from_raw_parts(buffer.as_ptr().offset(offset) as *const u8, buffer.len() - offset as usize);
292 core::str::from_utf8_unchecked(slice)
293 }
294
295 } else {
296 crate::numeric::unsigned::$st(num as $st, buffer)
297 }
298 }
299
300 unsafe impl crate::ToStr for $t {
301 const TEXT_SIZE: usize = <$st>::TEXT_SIZE + 1;
302
303 #[inline(always)]
304 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
305 let buffer = unsafe {
306 core::mem::transmute::<&'a mut [u8], &'a mut [core::mem::MaybeUninit<u8>]>(buffer)
307 };
308 $t(*self, buffer)
309 }
310 }
311 }
312}
313
314pub mod signed {
315 impl_signed!(i8 as u8 where write_u8_to_buf as u8);
316 impl_signed!(i16 as u16 where write_u64_to_buf as u64);
317 impl_signed!(i32 as u32 where write_u64_to_buf as u64);
318 impl_signed!(i64 as u64 where write_u64_to_buf as u64);
319 impl_signed!(i128 as u128 where write_u128_to_buf as u128);
320
321 #[inline]
322 pub(crate) const fn isize(num: isize, buffer: &'_ mut [core::mem::MaybeUninit<u8>]) -> &'_ str {
323 if num.is_negative() {
324 debug_assert!(buffer.len() >= <isize as crate::ToStr>::TEXT_SIZE);
325
326 #[cfg(target_pointer_width = "16")]
327 let abs = 0i16.wrapping_sub(num as i16);
328 #[cfg(target_pointer_width = "32")]
329 let abs = 0i32.wrapping_sub(num as i32);
330 #[cfg(target_pointer_width = "64")]
331 let abs = 0i64.wrapping_sub(num as i64);
332
333 unsafe {
334 let offset = super::write_u64_to_buf(abs as _, buffer.as_mut_ptr() as *mut u8, buffer.len() as isize) - 1;
335 core::ptr::write(buffer.as_mut_ptr().offset(offset), core::mem::MaybeUninit::new(b'-'));
336 let slice = core::slice::from_raw_parts(buffer.as_ptr().offset(offset) as *const u8, buffer.len() - offset as usize);
337 core::str::from_utf8_unchecked(slice)
338 }
339 } else {
340 super::unsigned::usize(num as _, buffer)
341 }
342 }
343}
344
345unsafe impl ToStr for isize {
346 #[cfg(target_pointer_width = "16")]
347 const TEXT_SIZE: usize = <i16 as ToStr>::TEXT_SIZE;
348 #[cfg(target_pointer_width = "32")]
349 const TEXT_SIZE: usize = <i32 as ToStr>::TEXT_SIZE;
350 #[cfg(target_pointer_width = "64")]
351 const TEXT_SIZE: usize = <i64 as ToStr>::TEXT_SIZE;
352
353 #[inline(always)]
354 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
355
356 let buffer = unsafe {
357 core::mem::transmute::<&'a mut [u8], &'a mut [core::mem::MaybeUninit<u8>]>(buffer)
358 };
359 signed::isize(*self, buffer)
360 }
361}
362
363unsafe impl<T> ToStr for *const T {
364 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
365
366 #[inline]
367 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
368 debug_assert!(buffer.len() >= Self::TEXT_SIZE);
369
370 unsafe {
371 let offset = write_ptr_to_buf(*self as usize, buffer.as_mut_ptr(), buffer.len() as isize) as usize;
372 core::str::from_utf8_unchecked(&buffer[offset..])
373 }
374 }
375}
376
377unsafe impl<T> ToStr for *mut T {
378 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
379
380 #[inline(always)]
381 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
382 (*self as *const T).to_str(buffer)
383 }
384}
385
386unsafe impl<T> ToStr for core::sync::atomic::AtomicPtr<T> {
387 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
388
389 #[inline(always)]
390 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
391 self.load(core::sync::atomic::Ordering::Acquire).to_str(buffer)
392 }
393}
394
395unsafe impl<T> ToStr for ptr::NonNull<T> {
396 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
397
398 #[inline(always)]
399 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
400 self.as_ptr().to_str(buffer)
401 }
402}
403
404macro_rules! impl_non_zero_repr {
405 ($($t:ty: $repr:ty);* $(;)?) => {
406 $(
407 unsafe impl ToStr for $t {
408 const TEXT_SIZE: usize = {
409 assert!(core::mem::size_of::<$t>() == core::mem::size_of::<$repr>(), "NonZero type doesn't match Repr type");
410 <$repr as ToStr>::TEXT_SIZE
411 };
412
413 #[inline(always)]
414 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
415 ToStr::to_str(&(*self).get(), buffer)
416 }
417 }
418 )*
419 }
420}
421
422impl_non_zero_repr!(
423 num::NonZeroU8: u8;
424 num::NonZeroU16: u16;
425 num::NonZeroU32: u32;
426 num::NonZeroU64: u64;
427 num::NonZeroU128: u128;
428 num::NonZeroUsize: usize;
429
430 num::NonZeroI8: i8;
431 num::NonZeroI16: i16;
432 num::NonZeroI32: i32;
433 num::NonZeroI64: i64;
434 num::NonZeroI128: i128;
435 num::NonZeroIsize: isize;
436);