Skip to content

Instantly share code, notes, and snippets.

@DutchGhost
Created September 21, 2018 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DutchGhost/9f4488f19c3555426c15c28bd854e037 to your computer and use it in GitHub Desktop.
Save DutchGhost/9f4488f19c3555426c15c28bd854e037 to your computer and use it in GitHub Desktop.
Atoi with exact_chunks
#![feature(exact_chunks)]
const POW10_U64: [u64; 20] = [
10_000_000_000_000_000_000,
1_000_000_000_000_000_000,
100_000_000_000_000_000,
10_000_000_000_000_000,
1_000_000_000_000_000,
100_000_000_000_000,
10_000_000_000_000,
1_000_000_000_000,
100_000_000_000,
10_000_000_000,
1_000_000_000,
100_000_000,
10_000_000,
1_000_000,
100_000,
10_000,
1_000,
100,
10,
1,
];
const ASCII_TO_INT_FACTOR: u8 = 48;
macro_rules! try_parse_byte {
($byte:expr, $const_table:ident, $offset:expr) => {{
let d = u64::from($byte.wrapping_sub(ASCII_TO_INT_FACTOR));
//if the digit is greater than 9, something went terribly horribly wrong.
//return an Err(())
if d > 9 {
return Err(());
}
d * unsafe { $const_table.get_unchecked($offset) }
}};
}
/*
Place ourselves into the correct position (const_table.len() - bytes.len() ),
Then for each byte, convert it to an integer, and multiply by the corresponding power of 10. (This is unrolled 4 times)
If converting the byte to an integer fails, return an Err(())
Notice that the bytes get converted from the `largest` to the `smallest` (e.g: while converting "543", '5' is converted first)
*/
#[inline(always)]
fn bytes_to_int(bytes: &[u8]) -> Result<u64, ()> {
let mut result: u64 = 0;
let mut chunks = bytes.exact_chunks(4);
let mut idx: usize = POW10_U64.len() - bytes.len();
// The main loop, unrolled 4 times, using a match statement on the slice for each Iteration.
for chunk in chunks.by_ref() {
match chunk {
[a, b, c, d] => {
let r1 = try_parse_byte!(a, POW10_U64, idx + 0);
let r2 = try_parse_byte!(b, POW10_U64, idx + 1);
let r3 = try_parse_byte!(c, POW10_U64, idx + 2);
let r4 = try_parse_byte!(d, POW10_U64, idx + 3);
idx += 4;
result = result.wrapping_add(r1 + r2 + r3 + r4);
}
_ => unreachable!(),
}
}
// Fixuploop with .remainder()
for (offset, byte) in chunks.remainder().iter().enumerate() {
let r = try_parse_byte!(byte, POW10_U64, idx + offset);
result = result.wrapping_add(r);
}
Ok(result)
}
#[inline(always)]
fn atoi<T: AsRef<[u8]>>(bytes: T) -> Result<u64, ()> {
bytes_to_int(bytes.as_ref())
}
fn main() {
assert_eq!(atoi("1234567"), Ok(1234567));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment