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