Last active
May 14, 2026 02:28
-
-
Save Lohann/0b78bbce7adc475bb03c2edc29dca19b to your computer and use it in GitHub Desktop.
Rust itoa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Implementation of itoa (integer to ASCII), which converts an 64-bit to decimal string. | |
| // | |
| // Rust's `format!("{num}")` requires heap `alloc` and bloats the code with panic messages, | |
| // I needed something simpler for support basic tracing in Wasm targets. | |
| // This implementation focus on smaller binary instead raw performance, ideal for | |
| // no_std environements like wasm32-unknown-unknown or embeeded. | |
| // | |
| // @author Lohann Paterno Coutinho Ferreira <[email protected]> | |
| #[unsafe(no_mangle)] | |
| pub unsafe fn itoa(mut x: u64, ptr: *mut u8, mut len: u32) -> u32 { | |
| // For better performance, avoid branches by assembling the solution | |
| // we get two possible bit patterns above the low 17 bits, | |
| // depending on whether val is below or above the threshold. | |
| const C1: u64 = 0b011_00000000000000000 - 10; // 393206 | |
| const C2: u64 = 0b100_00000000000000000 - 100; // 524188 | |
| const C3: u64 = 0b111_00000000000000000 - 1000; // 916504 | |
| const C4: u64 = 0b100_00000000000000000 - 10000; // 514288 | |
| // Compute the number of digits of `x` | |
| // digits = 1 + log10(x); | |
| let digits = { | |
| let mut digits = 1u32; | |
| let mut val = x; | |
| if val >= 10_000_000_000 { | |
| val /= 10_000_000_000; | |
| digits += 10; | |
| } | |
| if val >= 100_000 { | |
| val /= 100_000; | |
| digits += 5; | |
| } | |
| // Value of top bits: | |
| // +c1 +c2 1&2 +c3 +c4 3&4 ^ | |
| // 0..=9 010 011 010 110 011 010 000 = 0 | |
| // 10..=99 011 011 011 110 011 010 001 = 1 | |
| // 100..=999 011 100 000 110 011 010 010 = 2 | |
| // 1000..=9999 011 100 000 111 011 011 011 = 3 | |
| // 10000..=99999 011 100 000 111 100 100 100 = 4 | |
| val = (((val + C1) & (val + C2)) ^ ((val + C3) & (val + C4))) >> 17; | |
| digits + val as u32 | |
| }; | |
| // find where the fist digit starts by computing: | |
| // len = min(digits, len) | |
| len = (len ^ digits) & 0u32.wrapping_sub(u32::from(len < digits)); | |
| len ^= digits; | |
| let mut end = ptr.add(len as usize); | |
| // return if the buffer size is zero. | |
| if end.addr() <= ptr.addr() { | |
| return digits; | |
| } | |
| // skip least significant digits when `len < digits` | |
| while len < digits { | |
| x = x.div_euclid(10); | |
| len += 1; | |
| } | |
| // write digit by digit to buffer. | |
| while end.addr() > ptr.addr() { | |
| end = end.sub(1); | |
| *end = b'0'.wrapping_add((x % 10) as u8); | |
| x = x.div_euclid(10); | |
| } | |
| digits | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment