Skip to content
This repository has been archived by the owner on Jul 11, 2019. It is now read-only.

Commit

Permalink
Numtoa Optimization
Browse files Browse the repository at this point in the history
- This update will ~double the performance of base 10 numtoa integer conversions.
  • Loading branch information
mmstick committed Jan 17, 2017
1 parent 0a0980e commit d71d0f6
Showing 1 changed file with 118 additions and 21 deletions.
139 changes: 118 additions & 21 deletions src/misc/numtoa.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
use std::ptr::swap;

/// Converts a number into a string representation, storing the conversion into a mutable byte slice.
pub trait NumToA<T> {
/// Given a base for encoding and a mutable byte slice, write the number into the byte slice and return the
/// amount of bytes that were written.
///
/// # Panics
/// If the supplied buffer is smaller than the number of bytes needed to write the integer, this will panic.
fn numtoa(self, base: T, string: &mut [u8]) -> usize;
}

// A lookup table to prevent the need for conditional branching
// The value of the remainder of each step will be used as the index
const LOOKUP: &'static [u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

/// Because the integer to string conversion writes the representation in reverse, this will correct it.
fn reverse(string: &mut [u8], length: usize) {
let mut start = 0isize;
Expand All @@ -15,16 +29,42 @@ fn reverse(string: &mut [u8], length: usize) {
}
}

/// Converts a number into a string representation, storing the conversion into a mutable byte slice.
pub trait NumToA<T> {
/// Given a base for encoding and mutable byte slice, write the number into the byte slice and return the
/// amount of bytes that were written.
fn numtoa(self, base: T, string: &mut [u8]) -> usize;
}
macro_rules! base_10 {
($number:ident, $index:ident, $string:ident) => {
// Decode four characters at the same time
while $number > 9999 {
let rem = $number % 10000;
$string[$index+3] = LOOKUP[(rem / 1000) as usize];
$string[$index+2] = LOOKUP[(rem % 1000 / 100) as usize];
$string[$index+1] = LOOKUP[(rem % 100 / 10) as usize];
$string[$index] = LOOKUP[(rem % 10) as usize];
$index += 4;
$number /= 10000;
}

// A lookup table to prevent the need for conditional branching
// The value of the remainder of each step will be used as the index
const LOOKUP: &'static [u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if $number > 999 {
let rem = $number % 1000;
$string[$index+3] = LOOKUP[($number / 1000) as usize];
$string[$index+2] = LOOKUP[(rem / 100) as usize];
$string[$index+1] = LOOKUP[(rem % 100 / 10) as usize];
$string[$index] = LOOKUP[(rem % 10) as usize];
$index += 4;
} else if $number > 99 {
let rem = $number % 100;
$string[$index+2] = LOOKUP[($number / 100) as usize];
$string[$index+1] = LOOKUP[(rem / 10) as usize];
$string[$index] = LOOKUP[(rem % 10) as usize];
$index += 3;
} else if $number > 9 {
$string[$index+1] = LOOKUP[($number / 10) as usize];
$string[$index] = LOOKUP[($number % 10) as usize];
$index += 2;
} else {
$string[$index] = LOOKUP[$number as usize];
$index += 1;
}
}
}

macro_rules! impl_unsized_numtoa_for {
($t:ty) => {
Expand All @@ -36,11 +76,16 @@ macro_rules! impl_unsized_numtoa_for {
}

let mut index = 0;
while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;

if base == 10 {
base_10!(self, index, string);
} else {
while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;
}
}

reverse(string, index);
Expand All @@ -65,11 +110,15 @@ macro_rules! impl_sized_numtoa_for {
return 1;
}

while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;
if base == 10 {
base_10!(self, index, string);
} else {
while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;
}
}

if is_negative {
Expand All @@ -85,13 +134,61 @@ macro_rules! impl_sized_numtoa_for {
}
}

impl_sized_numtoa_for!(i8);
impl_sized_numtoa_for!(i16);
impl_sized_numtoa_for!(i32);
impl_sized_numtoa_for!(i64);
impl_sized_numtoa_for!(isize);
impl_unsized_numtoa_for!(u8);
impl_unsized_numtoa_for!(u16);
impl_unsized_numtoa_for!(u32);
impl_unsized_numtoa_for!(u64);
impl_unsized_numtoa_for!(usize);

impl NumToA<i8> for i8 {
fn numtoa(mut self, base: i8, string: &mut [u8]) -> usize {
let mut index = 0;
let mut is_negative = false;

if self < 0 {
is_negative = true;
self = self.abs();
} else if self == 0 {
string[0] = b'0';
return 1;
}

while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;
}

if is_negative {
string[index] = b'-';
index += 1;
}

reverse(string, index);
index
}
}

impl NumToA<u8> for u8 {
fn numtoa(mut self, base: u8, string: &mut [u8]) -> usize {
if self == 0 {
string[0] = b'0';
return 1;
}

let mut index = 0;
while self != 0 {
let rem = self % base;
string[index] = LOOKUP[rem as usize];
index += 1;
self /= base;
}

reverse(string, index);
index
}
}

0 comments on commit d71d0f6

Please sign in to comment.