Skip to content

Instantly share code, notes, and snippets.

@Lohann
Last active May 14, 2026 02:28
Show Gist options
  • Select an option

  • Save Lohann/0b78bbce7adc475bb03c2edc29dca19b to your computer and use it in GitHub Desktop.

Select an option

Save Lohann/0b78bbce7adc475bb03c2edc29dca19b to your computer and use it in GitHub Desktop.
Rust itoa
// 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