1#![no_std]
77use core::mem::size_of;
78use core::str;
79
80pub trait NumToA<T> {
82 fn numtoa(self, base: T, string: &mut [u8]) -> &[u8];
111
112 fn numtoa_str(self, base: T, buf: &mut [u8]) -> &str;
114}
115
116const LOOKUP: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
119
120const DEC_LOOKUP: &[u8; 200] = b"0001020304050607080910111213141516171819\
122 2021222324252627282930313233343536373839\
123 4041424344454647484950515253545556575859\
124 6061626364656667686970717273747576777879\
125 8081828384858687888990919293949596979899";
126
127macro_rules! base_10 {
128 ($number:ident, $index:ident, $string:ident) => {
129 while $number > 9999 {
131 let rem = ($number % 10000) as u16;
132 let (frst, scnd) = ((rem / 100) * 2, (rem % 100) * 2);
133 $string[$index-3..$index-1].copy_from_slice(&DEC_LOOKUP[frst as usize..frst as usize+2]);
134 $string[$index-1..$index+1].copy_from_slice(&DEC_LOOKUP[scnd as usize..scnd as usize+2]);
135 $index = $index.wrapping_sub(4);
136 $number /= 10000;
137 }
138
139 if $number > 999 {
140 let (frst, scnd) = (($number / 100) * 2, ($number % 100) * 2);
141 $string[$index-3..$index-1].copy_from_slice(&DEC_LOOKUP[frst as usize..frst as usize+2]);
142 $string[$index-1..$index+1].copy_from_slice(&DEC_LOOKUP[scnd as usize..scnd as usize+2]);
143 $index = $index.wrapping_sub(4);
144 } else if $number > 99 {
145 let section = ($number as u16 / 10) * 2;
146 $string[$index-2..$index].copy_from_slice(&DEC_LOOKUP[section as usize..section as usize+2]);
147 $string[$index] = LOOKUP[($number % 10) as usize];
148 $index = $index.wrapping_sub(3);
149 } else if $number > 9 {
150 $number *= 2;
151 $string[$index-1..$index+1].copy_from_slice(&DEC_LOOKUP[$number as usize..$number as usize+2]);
152 $index = $index.wrapping_sub(2);
153 } else {
154 $string[$index] = LOOKUP[$number as usize];
155 $index = $index.wrapping_sub(1);
156 }
157 }
158}
159
160macro_rules! impl_unsized_numtoa_for {
161 ($t:ty) => {
162 impl NumToA<$t> for $t {
163 fn numtoa(mut self, base: $t, string: &mut [u8]) -> &[u8] {
164 if cfg!(debug_assertions) {
166 if base == 10 {
167 match size_of::<$t>() {
168 2 => debug_assert!(string.len() >= 5, "u16 base 10 conversions require at least 5 bytes"),
169 4 => debug_assert!(string.len() >= 10, "u32 base 10 conversions require at least 10 bytes"),
170 8 => debug_assert!(string.len() >= 20, "u64 base 10 conversions require at least 20 bytes"),
171 16 => debug_assert!(string.len() >= 39, "u128 base 10 conversions require at least 39 bytes"),
172 _ => unreachable!()
173 }
174 }
175 }
176
177 let mut index = string.len() - 1;
178 if self == 0 {
179 string[index] = b'0';
180 return &string[index..];
181 }
182
183 if base == 10 {
184 base_10!(self, index, string);
186 } else {
187 while self != 0 {
188 let rem = self % base;
189 string[index] = LOOKUP[rem as usize];
190 index = index.wrapping_sub(1);
191 self /= base;
192 }
193 }
194
195 &string[index.wrapping_add(1)..]
196 }
197
198
199 fn numtoa_str(self, base: $t, buf: &mut [u8]) -> &str {
200 unsafe { str::from_utf8_unchecked(self.numtoa(base, buf)) }
201 }
202 }
203 }
204}
205
206macro_rules! impl_sized_numtoa_for {
207 ($t:ty) => {
208 impl NumToA<$t> for $t {
209 fn numtoa(mut self, base: $t, string: &mut [u8]) -> &[u8] {
210 if cfg!(debug_assertions) {
211 if base == 10 {
212 match size_of::<$t>() {
213 2 => debug_assert!(string.len() >= 6, "i16 base 10 conversions require at least 6 bytes"),
214 4 => debug_assert!(string.len() >= 11, "i32 base 10 conversions require at least 11 bytes"),
215 8 => debug_assert!(string.len() >= 19, "i64 base 10 conversions require at least 19 bytes"),
216 16 => debug_assert!(string.len() >= 39, "i128 base 10 conversions require at least 39 bytes"),
217 _ => unreachable!()
218 }
219 }
220 }
221
222 let mut index = string.len() - 1;
223 let mut is_negative = false;
224
225 if self < 0 {
226 is_negative = true;
227 self = match self.checked_abs() {
228 Some(value) => value,
229 None => {
230 let value = <$t>::max_value();
231 string[index] = LOOKUP[((value % base + 1) % base) as usize];
232 index -= 1;
233 value / base + ((value % base == base - 1) as $t)
234 }
235 };
236 } else if self == 0 {
237 string[index] = b'0';
238 return &string[index..];
239 }
240
241 if base == 10 {
242 base_10!(self, index, string);
244 } else {
245 while self != 0 {
246 let rem = self % base;
247 string[index] = LOOKUP[rem as usize];
248 index = index.wrapping_sub(1);
249 self /= base;
250 }
251 }
252
253 if is_negative {
254 string[index] = b'-';
255 index = index.wrapping_sub(1);
256 }
257
258 &string[index.wrapping_add(1)..]
259 }
260
261
262 fn numtoa_str(self, base: $t, buf: &mut [u8]) -> &str {
263 unsafe { str::from_utf8_unchecked(self.numtoa(base, buf)) }
264 }
265 }
266 }
267}
268
269impl_sized_numtoa_for!(i16);
270impl_sized_numtoa_for!(i32);
271impl_sized_numtoa_for!(i64);
272impl_sized_numtoa_for!(i128);
273impl_sized_numtoa_for!(isize);
274impl_unsized_numtoa_for!(u16);
275impl_unsized_numtoa_for!(u32);
276impl_unsized_numtoa_for!(u64);
277impl_unsized_numtoa_for!(u128);
278impl_unsized_numtoa_for!(usize);
279
280impl NumToA<i8> for i8 {
281 fn numtoa(mut self, base: i8, string: &mut [u8]) -> &[u8] {
282 if cfg!(debug_assertions) {
283 if base == 10 {
284 debug_assert!(string.len() >= 4, "i8 conversions need at least 4 bytes");
285 }
286 }
287
288 let mut index = string.len() - 1;
289 let mut is_negative = false;
290
291 if self < 0 {
292 is_negative = true;
293 self = match self.checked_abs() {
294 Some(value) => value,
295 None => {
296 let value = <i8>::max_value();
297 string[index] = LOOKUP[((value % base + 1) % base) as usize];
298 index -= 1;
299 value / base + ((value % base == base - 1) as i8)
300 }
301 };
302 } else if self == 0 {
303 string[index] = b'0';
304 return &string[index..];
305 }
306
307 if base == 10 {
308 if self > 99 {
309 let section = (self / 10) * 2;
310 string[index-2..index].copy_from_slice(&DEC_LOOKUP[section as usize..section as usize+2]);
311 string[index] = LOOKUP[(self % 10) as usize];
312 index = index.wrapping_sub(3);
313 } else if self > 9 {
314 let idx = self as usize * 2;
315 string[index-1..index+1].copy_from_slice(&DEC_LOOKUP[idx..idx+2]);
316 index = index.wrapping_sub(2);
317 } else {
318 string[index] = LOOKUP[self as usize];
319 index = index.wrapping_sub(1);
320 }
321 } else {
322 while self != 0 {
323 let rem = self % base;
324 string[index] = LOOKUP[rem as usize];
325 index = index.wrapping_sub(1);
326 self /= base;
327 }
328 }
329
330 if is_negative {
331 string[index] = b'-';
332 index = index.wrapping_sub(1);
333 }
334
335 &string[index.wrapping_add(1)..]
336 }
337
338 fn numtoa_str(self, base: Self, buf: &mut [u8]) -> &str {
339 unsafe { str::from_utf8_unchecked(self.numtoa(base, buf)) }
340 }
341}
342
343impl NumToA<u8> for u8 {
344 fn numtoa(mut self, base: u8, string: &mut [u8]) -> &[u8] {
345 if cfg!(debug_assertions) {
346 if base == 10 {
347 debug_assert!(string.len() >= 3, "u8 conversions need at least 3 bytes");
348 }
349 }
350
351 let mut index = string.len() - 1;
352 if self == 0 {
353 string[index] = b'0';
354 return &string[index..];
355 }
356
357 if base == 10 {
358 if self > 99 {
359 let section = (self / 10) * 2;
360 string[index-2..index].copy_from_slice(&DEC_LOOKUP[section as usize..section as usize+2]);
361 string[index] = LOOKUP[(self % 10) as usize];
362 index = index.wrapping_sub(3);
363 } else if self > 9 {
364 self *= 2;
365 string[index-1..index+1].copy_from_slice(&DEC_LOOKUP[self as usize..self as usize+2]);
366 index = index.wrapping_sub(2);
367 } else {
368 string[index] = LOOKUP[self as usize];
369 index = index.wrapping_sub(1);
370 }
371 } else {
372 while self != 0 {
373 let rem = self % base;
374 string[index] = LOOKUP[rem as usize];
375 index = index.wrapping_sub(1);
376 self /= base;
377 }
378 }
379
380 &string[index.wrapping_add(1)..]
381 }
382
383 fn numtoa_str(self, base: Self, buf: &mut [u8]) -> &str {
384 unsafe { str::from_utf8_unchecked(self.numtoa(base, buf)) }
385 }
386}
387
388#[test]
389fn str_convenience() {
390 let mut buffer = [0u8; 20];
391 assert_eq!("256123", 256123.numtoa_str(10, &mut buffer));
392}
393
394#[test]
395#[should_panic]
396fn base10_u8_array_too_small() {
397 let mut buffer = [0u8; 2];
398 let _ = 0u8.numtoa(10, &mut buffer);
399}
400
401#[test]
402fn base10_u8_array_just_right() {
403 let mut buffer = [0u8; 3];
404 let _ = 0u8.numtoa(10, &mut buffer);
405}
406
407#[test]
408#[should_panic]
409fn base10_i8_array_too_small() {
410 let mut buffer = [0u8; 3];
411 let _ = 0i8.numtoa(10, &mut buffer);
412}
413
414#[test]
415fn base10_i8_array_just_right() {
416 let mut buffer = [0u8; 4];
417 assert_eq!((-127i8).numtoa(10, &mut buffer), b"-127");
418}
419
420#[test]
421#[should_panic]
422fn base10_i16_array_too_small() {
423 let mut buffer = [0u8; 5];
424 let _ = 0i16.numtoa(10, &mut buffer);
425}
426
427#[test]
428fn base10_i16_array_just_right() {
429 let mut buffer = [0u8; 6];
430 assert_eq!((-12768i16).numtoa(10, &mut buffer), b"-12768");
431}
432
433#[test]
434#[should_panic]
435fn base10_u16_array_too_small() {
436 let mut buffer = [0u8; 4];
437 let _ = 0u16.numtoa(10, &mut buffer);
438}
439
440#[test]
441fn base10_u16_array_just_right() {
442 let mut buffer = [0u8; 5];
443 let _ = 0u16.numtoa(10, &mut buffer);
444}
445
446#[test]
447#[should_panic]
448fn base10_i32_array_too_small() {
449 let mut buffer = [0u8; 10];
450 let _ = 0i32.numtoa(10, &mut buffer);
451}
452
453#[test]
454fn base10_i32_array_just_right() {
455 let mut buffer = [0u8; 11];
456 let _ = 0i32.numtoa(10, &mut buffer);
457}
458
459#[test]
460#[should_panic]
461fn base10_u32_array_too_small() {
462 let mut buffer = [0u8; 9];
463 let _ = 0u32.numtoa(10, &mut buffer);
464}
465
466#[test]
467fn base10_u32_array_just_right() {
468 let mut buffer = [0u8; 10];
469 let _ = 0u32.numtoa(10, &mut buffer);
470}
471
472#[test]
473#[should_panic]
474fn base10_i64_array_too_small() {
475 let mut buffer = [0u8; 18];
476 let _ = 0i64.numtoa(10, &mut buffer);
477}
478
479#[test]
480fn base10_i64_array_just_right() {
481 let mut buffer = [0u8; 19];
482 let _ = 0i64.numtoa(10, &mut buffer);
483}
484
485#[test]
486#[should_panic]
487fn base10_u64_array_too_small() {
488 let mut buffer = [0u8; 19];
489 let _ = 0u64.numtoa(10, &mut buffer);
490}
491
492#[test]
493fn base10_u64_array_just_right() {
494 let mut buffer = [0u8; 20];
495 let _ = 0u64.numtoa(10, &mut buffer);
496}
497
498#[test]
499fn base10_i8_all() {
500 let mut buffer = [0u8; 4];
501 for i in i8::MIN..i8::MAX {
502 let _ = i.numtoa(10, &mut buffer);
503 }
504}
505
506#[test]
507fn base10_u8_all() {
508 let mut buffer = [0u8; 3];
509 for i in u8::MIN..u8::MAX {
510 let _ = i.numtoa(10, &mut buffer);
511 }
512}
513
514#[test]
515#[should_panic]
516fn base10_i128_array_too_small() {
517 let mut buffer = [0u8; 38];
518 let _ = 0i128.numtoa(10, &mut buffer);
519}
520
521#[test]
522fn base10_i128_array_just_right() {
523 let mut buffer = [0u8; 39];
524 let _ = 0i128.numtoa(10, &mut buffer);
525}
526
527#[test]
528#[should_panic]
529fn base10_u128_array_too_small() {
530 let mut buffer = [0u8; 38];
531 let _ = 0u128.numtoa(10, &mut buffer);
532}
533
534#[test]
535fn base10_u128_array_just_right() {
536 let mut buffer = [0u8; 39];
537 let _ = 0u128.numtoa(10, &mut buffer);
538}
539
540#[test]
541fn base8_min_signed_number() {
542 let mut buffer = [0u8; 50];
543 assert_eq!((-128i8).numtoa(8, &mut buffer), b"-200");
544 assert_eq!((-32768i16).numtoa(8, &mut buffer), b"-100000");
545 assert_eq!((-2147483648i32).numtoa(8, &mut buffer), b"-20000000000");
546 assert_eq!((-9223372036854775808i64).numtoa(8, &mut buffer), b"-1000000000000000000000");
547 assert_eq!((i128::MIN).numtoa(8, &mut buffer), b"-2000000000000000000000000000000000000000000");
548}
549
550#[test]
551fn base16_min_signed_number() {
552 let mut buffer = [0u8; 40];
553 assert_eq!((-128i8).numtoa(16, &mut buffer), b"-80");
554 assert_eq!((-32768i16).numtoa(16, &mut buffer), b"-8000");
555 assert_eq!((-2147483648i32).numtoa(16, &mut buffer), b"-80000000");
556 assert_eq!((-9223372036854775808i64).numtoa(16, &mut buffer), b"-8000000000000000");
557 assert_eq!((i128::MIN).numtoa(16, &mut buffer), b"-80000000000000000000000000000000");
558}