1use crate::ToStr;
2
3use core::{ptr};
4use core::str::from_utf8_unchecked;
5
6static DEC_DIGITS: &[u8; 200] = b"0001020304050607080910111213141516171819\
8 2021222324252627282930313233343536373839\
9 4041424344454647484950515253545556575859\
10 6061626364656667686970717273747576777879\
11 8081828384858687888990919293949596979899";
12static 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'];
13const PTR_PREFIX: [u8; 2] = [b'0', b'x'];
14
15const fn size_of_val<T>(_: &T) -> usize {
16 core::mem::size_of::<T>()
17}
18
19unsafe fn write_u8_to_buf(mut num: u8, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
20 let digits_ptr = DEC_DIGITS.as_ptr();
21
22 if num >= 100 {
23 let index = (num as isize % 100) << 1;
24 num /= 100;
25
26 cursor -= 3;
27 ptr::write(buffer_ptr.offset(cursor), *digits_ptr + num);
28 ptr::copy_nonoverlapping(digits_ptr.offset(index), buffer_ptr.offset(cursor + 1), 2);
29 } else if num <= 9 {
30 cursor -= 1;
31 ptr::write(buffer_ptr.offset(cursor), *digits_ptr + num);
32 } else {
33 let index = num as isize * 2;
34
35 cursor -= 2;
36 ptr::copy_nonoverlapping(digits_ptr.offset(index), buffer_ptr.offset(cursor), 2);
37 }
38
39 cursor
40}
41
42unsafe fn write_u64_to_buf(mut num: u64, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
43 let digits_ptr = DEC_DIGITS.as_ptr();
44
45 while num >= 10000 {
46 let rem = (num % 10000) as isize;
47 num /= 10000;
48
49 let index1 = (rem / 100) << 1;
50 let index2 = (rem % 100) << 1;
51 cursor -= 4;
52 ptr::copy_nonoverlapping(digits_ptr.offset(index1), buffer_ptr.offset(cursor), 2);
53 ptr::copy_nonoverlapping(digits_ptr.offset(index2), buffer_ptr.offset(cursor + 2), 2);
54 }
55
56 if num >= 100 {
57 let index = (num as isize % 100) << 1;
58 num /= 100;
59
60 cursor -= 2;
61 ptr::copy_nonoverlapping(digits_ptr.offset(index), buffer_ptr.offset(cursor), 2);
62 }
63
64 if num < 10 {
65 cursor -= 1;
66 ptr::write(buffer_ptr.offset(cursor), *digits_ptr + num as u8);
67 } else {
68 let index = num as isize * 2;
69
70 cursor -= 2;
71 ptr::copy_nonoverlapping(digits_ptr.offset(index), buffer_ptr.offset(cursor), 2);
72 }
73
74 cursor
75}
76
77#[inline]
79pub fn udivmod_1e19(num: &mut u128) -> u64 {
80 const DIV: u64 = 10_000_000_000_000_000_000;
81
82 let high = (*num >> 64) as u64;
83 if high == 0 {
84 let low = *num as u64;
85 *num = (low / DIV) as u128;
86 return low % DIV;
87 }
88
89 let sr = 65 - high.leading_zeros();
90
91 let mut q: u128 = *num << (128 - sr);
92 let mut r: u128 = *num >> sr;
93 let mut carry: u64 = 0;
94
95 let mut i = 0;
96 while i < sr {
97 i += 1;
98
99 r = (r << 1) | (q >> 127);
100 q = (q << 1) | carry as u128;
101
102 let s = (DIV as u128).wrapping_sub(r).wrapping_sub(1) as i128 >> 127;
103 carry = (s & 1) as u64;
104 r -= (DIV as u128) & s as u128;
105 }
106
107 *num = (q << 1) | carry as u128;
108 r as u64
109}
110
111unsafe fn write_u128_to_buf(mut num: u128, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
112 const U64_TEXT_SIZE: isize = u64::TEXT_SIZE as isize;
113 const U64_TEXT_MAX_WRITTEN: isize = u64::TEXT_SIZE as isize - 1;
114 let digits_ptr = DEC_DIGITS.as_ptr();
115
116 let mut offset = cursor - u64::TEXT_SIZE as isize;
117 let mut written = U64_TEXT_SIZE - write_u64_to_buf(udivmod_1e19(&mut num), buffer_ptr.offset(offset), U64_TEXT_SIZE);
118
119 cursor -= written;
120
121 if num != 0 {
122 ptr::write_bytes(buffer_ptr.offset(cursor), *digits_ptr, (U64_TEXT_MAX_WRITTEN - written) as usize);
123
124 offset = cursor - u64::TEXT_SIZE as isize;
125 written = U64_TEXT_SIZE - write_u64_to_buf(udivmod_1e19(&mut num), buffer_ptr.offset(offset), U64_TEXT_SIZE);
126
127 cursor -= written;
128
129 if num != 0 {
130 ptr::write_bytes(buffer_ptr.offset(cursor), *digits_ptr, (U64_TEXT_MAX_WRITTEN - written) as usize);
131
132 cursor -= 1;
135 *buffer_ptr.offset(cursor) = (num as u8) + b'0';
136 }
137 }
138
139 cursor
140}
141
142unsafe fn write_hex_to_buf(mut num: usize, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
143 const BASE: usize = 4;
144 const BASE_DIGIT: usize = (1 << BASE) - 1;
145 let digits_ptr = HEX_DIGITS.as_ptr();
146
147 loop {
148 let digit = num & BASE_DIGIT;
149 cursor -= 1;
150 ptr::write(buffer_ptr.offset(cursor), *digits_ptr.add(digit));
151 num >>= BASE;
152
153 if num == 0 {
154 break;
155 }
156 }
157
158 cursor
159}
160
161#[inline(always)]
162unsafe fn write_ptr_to_buf(num: usize, buffer_ptr: *mut u8, mut cursor: isize) -> isize {
163 const PTR_PREFIX_SIZE: usize = size_of_val(&PTR_PREFIX);
164 cursor = write_hex_to_buf(num, buffer_ptr, cursor);
165 cursor -= PTR_PREFIX_SIZE as isize;
166
167 ptr::copy_nonoverlapping(PTR_PREFIX.as_ptr(), buffer_ptr.offset(cursor), PTR_PREFIX_SIZE);
168 cursor
169}
170
171macro_rules! impl_unsigned {
172 ($t:ty: $max:expr; $conv:ident as $cv_t:ident) => {
173 unsafe impl ToStr for $t {
174 const TEXT_SIZE: usize = $max;
175
176 #[inline]
177 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
178 debug_assert!(buffer.len() >= Self::TEXT_SIZE);
179
180 unsafe {
181 let offset = $conv(*self as $cv_t, buffer.as_mut_ptr(), buffer.len() as isize) as usize;
182 from_utf8_unchecked(&buffer[offset..])
183 }
184 }
185 }
186 }
187}
188
189impl_unsigned!(u8: 3; write_u8_to_buf as u8);
190impl_unsigned!(u16: 5; write_u64_to_buf as u64);
191impl_unsigned!(u32: 10; write_u64_to_buf as u64);
192impl_unsigned!(u64: 20; write_u64_to_buf as u64);
193#[cfg(target_pointer_width = "16")]
194impl_unsigned!(usize: 5; write_u64_to_buf as u64);
195#[cfg(target_pointer_width = "32")]
196impl_unsigned!(usize: 10; write_u64_to_buf as u64);
197#[cfg(target_pointer_width = "64")]
198impl_unsigned!(usize: 20; write_u64_to_buf as u64);
199impl_unsigned!(u128: 39; write_u128_to_buf as u128);
200
201macro_rules! impl_signed {
202 ($t:ty as $st:ty where $conv:ident as $cv_t:ty) => {
203 unsafe impl ToStr for $t {
204 const TEXT_SIZE: usize = <$st>::TEXT_SIZE + 1;
205
206 #[inline]
207 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
208 if self.is_negative() {
209 debug_assert!(buffer.len() >= Self::TEXT_SIZE);
210
211 let abs = (0 as $st).wrapping_sub(*self as $st);
212 unsafe {
213 let offset = $conv(abs as $cv_t, buffer.as_mut_ptr(), buffer.len() as isize) - 1;
214 ptr::write(buffer.as_mut_ptr().offset(offset), b'-');
215 from_utf8_unchecked(&mut buffer[offset as usize..])
216 }
217
218 } else {
219 ToStr::to_str(&(*self as $st), buffer)
220 }
221 }
222 }
223 }
224}
225
226impl_signed!(i8 as u8 where write_u8_to_buf as u8);
227impl_signed!(i16 as u16 where write_u64_to_buf as u64);
228impl_signed!(i32 as u32 where write_u64_to_buf as u64);
229impl_signed!(i64 as u64 where write_u64_to_buf as u64);
230#[cfg(target_pointer_width = "16")]
231impl_signed!(isize as u16 where write_u64_to_buf as u64);
232#[cfg(target_pointer_width = "32")]
233impl_signed!(isize as u32 where write_u64_to_buf as u64);
234#[cfg(target_pointer_width = "64")]
235impl_signed!(isize as u64 where write_u64_to_buf as u64);
236impl_signed!(i128 as u128 where write_u128_to_buf as u128);
237
238unsafe impl<T> ToStr for *const T {
239 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
240
241 #[inline]
242 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
243 debug_assert!(buffer.len() >= Self::TEXT_SIZE);
244
245 unsafe {
246 let offset = write_ptr_to_buf(*self as usize, buffer.as_mut_ptr(), buffer.len() as isize) as usize;
247 from_utf8_unchecked(&buffer[offset..])
248 }
249 }
250}
251
252unsafe impl<T> ToStr for *mut T {
253 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
254
255 #[inline(always)]
256 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
257 (*self as *const T).to_str(buffer)
258 }
259}
260
261unsafe impl<T> ToStr for core::sync::atomic::AtomicPtr<T> {
262 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
263
264 #[inline(always)]
265 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
266 self.load(core::sync::atomic::Ordering::Acquire).to_str(buffer)
267 }
268}
269
270unsafe impl<T> ToStr for ptr::NonNull<T> {
271 const TEXT_SIZE: usize = usize::TEXT_SIZE + 2;
272
273 #[inline(always)]
274 fn to_str<'a>(&self, buffer: &'a mut [u8]) -> &'a str {
275 self.as_ptr().to_str(buffer)
276 }
277}