I'd like to convert a usize
value into the corresponding chars ascii bytes.
For example, for the value 50usize
, I'd want to obtain [53, 48]
.
fn usize_to_ascii_bytes(input: usize) -> Vec<u8> {
...
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn usize_to_ascii_bytes_test() {
assert_eq!(vec![53], usize_to_ascii_bytes(5));
assert_eq!(vec![53, 48], usize_to_ascii_bytes(50));
}
}
Context: I'm doing the Redis CodeCrafters course, and for a GET request, I need to write the length of the value to the output stream; value being a vec of u8. I know I could convert that value's .len()
to a string then obtain u8 in ascii, but I'm looking for the most effective way of doing this, minimizing conversions and copies, in the most idiomatic way in Rust.
You can convert each digit one by one, starting from the last one, and then reverse the Vec:
fn usize_to_ascii_bytes(mut input: usize) -> Vec<u8> {
let mut vec = Vec::new();
loop {
vec.push(b'0' + (input % 10) as u8);
input /= 10;
if input == 0 {
break;
}
}
vec.reverse();
vec
}
Although I think a simple implementation should be just as fast since you have to allocate a Vec anyways:
fn usize_to_ascii_bytes_simple(input: usize) -> Vec<u8> {
input.to_string().into()
}
I benchmarked the two functions and the string one turned out to be consistently slightly faster for the inputs I tested, possibly because Rust's usize to string conversion uses a better algorithm than one digit at a time.
use criterion::{black_box, criterion_group, criterion_main, Criterion};
criterion_main!(benches);
criterion_group!(benches, bench_main);
fn usize_to_ascii_bytes(mut input: usize) -> Vec<u8> {
let mut vec = Vec::new();
loop {
vec.push(b'0' + (input % 10) as u8);
input /= 10;
if input == 0 {
break;
}
}
vec.reverse();
vec
}
fn usize_to_ascii_bytes_simple(input: usize) -> Vec<u8> {
input.to_string().into()
}
fn bench_main(c: &mut Criterion) {
let inputs = [0, 123, 456789, 101112131415];
let mut group = c.benchmark_group("usize_to_ascii");
group.bench_function("loop", |b| {
b.iter(|| {
for &input in &inputs {
black_box(usize_to_ascii_bytes(input));
}
})
});
group.bench_function("string", |b| {
b.iter(|| {
for &input in &inputs {
black_box(usize_to_ascii_bytes_simple(input));
}
})
});
}
usize_to_ascii/loop time: [315.93 ns 321.52 ns 327.95 ns]
usize_to_ascii/string time: [284.08 ns 288.74 ns 294.24 ns]