Skip to content

Commit

Permalink
Merge pull request #16 from Object905/master
Browse files Browse the repository at this point in the history
Use different buffer size for every integer type
  • Loading branch information
dtolnay authored Mar 22, 2018
2 parents ab5b81e + e6f1c2e commit ce0dc04
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 23 deletions.
74 changes: 51 additions & 23 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ pub trait Integer: private::Sealed {
fn fmt<W: fmt::Write>(self, W) -> fmt::Result;
}

trait IntegerPrivate {
fn write_to(self, buf: &mut [u8; MAX_LEN]) -> &[u8];
trait IntegerPrivate<B> {
fn write_to(self, buf: &mut B) -> &[u8];
}

const DEC_DIGITS_LUT: &'static[u8] =
Expand All @@ -66,23 +66,23 @@ const DEC_DIGITS_LUT: &'static[u8] =
6061626364656667686970717273747576777879\
8081828384858687888990919293949596979899";

const MAX_LEN: usize = 40; // i128::MIN (including minus sign)

// Adaptation of the original implementation at
// https://github.com/rust-lang/rust/blob/b8214dc6c6fc20d0a660fb5700dca9ebf51ebe89/src/libcore/fmt/num.rs#L188-L266
macro_rules! impl_IntegerCommon {
($t:ident) => {
($max_len:expr, $t:ident) => {
impl Integer for $t {
#[cfg(feature = "std")]
#[inline]
fn write<W: io::Write>(self, mut wr: W) -> io::Result<usize> {
let mut buf = unsafe { mem::uninitialized() };
let mut buf: [u8; $max_len] = unsafe { mem::uninitialized() };
let bytes = self.write_to(&mut buf);
try!(wr.write_all(bytes));
Ok(bytes.len())
}

#[inline]
fn fmt<W: fmt::Write>(self, mut wr: W) -> fmt::Result {
let mut buf = unsafe { mem::uninitialized() };
let mut buf: [u8; $max_len] = unsafe { mem::uninitialized() };
let bytes = self.write_to(&mut buf);
wr.write_str(unsafe { str::from_utf8_unchecked(bytes) })
}
Expand All @@ -93,12 +93,13 @@ macro_rules! impl_IntegerCommon {
}

macro_rules! impl_Integer {
($($t:ident),* as $conv_fn:ident) => {$(
impl_IntegerCommon!($t);
($($max_len:expr => $t:ident),* as $conv_fn:ident) => {$(
impl_IntegerCommon!($max_len, $t);

impl IntegerPrivate for $t {
impl IntegerPrivate<[u8; $max_len]> for $t {
#[allow(unused_comparisons)]
fn write_to(self, buf: &mut [u8; MAX_LEN]) -> &[u8] {
#[inline]
fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
let is_nonnegative = self >= 0;
let mut n = if is_nonnegative {
self as $conv_fn
Expand Down Expand Up @@ -160,23 +161,44 @@ macro_rules! impl_Integer {
)*};
}

impl_Integer!(i8, u8, i16, u16, i32, u32 as u32);
impl_Integer!(i64, u64 as u64);
const I8_MAX_LEN: usize = 4;
const U8_MAX_LEN: usize = 3;
const I16_MAX_LEN: usize = 6;
const U16_MAX_LEN: usize = 5;
const I32_MAX_LEN: usize = 11;
const U32_MAX_LEN: usize = 10;
const I64_MAX_LEN: usize = 20;
const U64_MAX_LEN: usize = 20;

impl_Integer!(
I8_MAX_LEN => i8,
U8_MAX_LEN => u8,
I16_MAX_LEN => i16,
U16_MAX_LEN => u16,
I32_MAX_LEN => i32,
U32_MAX_LEN => u32
as u32);

impl_Integer!(I64_MAX_LEN => i64, U64_MAX_LEN => u64 as u64);

#[cfg(target_pointer_width = "16")]
impl_Integer!(isize, usize as u16);
impl_Integer!(I16_MAX_LEN => isize, U16_MAX_LEN => usize as u16);

#[cfg(target_pointer_width = "32")]
impl_Integer!(isize, usize as u32);
impl_Integer!(I32_MAX_LEN => isize, U32_MAX_LEN => usize as u32);

#[cfg(target_pointer_width = "64")]
impl_Integer!(isize, usize as u64);
impl_Integer!(I64_MAX_LEN => isize, U64_MAX_LEN => usize as u64);

#[cfg(all(feature = "i128"))]
macro_rules! impl_Integer128 {
($($t:ident),*) => {$(
impl_IntegerCommon!($t);
($($max_len:expr => $t:ident),*) => {$(
impl_IntegerCommon!($max_len, $t);

impl IntegerPrivate for $t {
impl IntegerPrivate<[u8; $max_len]> for $t {
#[allow(unused_comparisons)]
fn write_to(self, buf: &mut [u8; MAX_LEN]) -> &[u8] {
#[inline]
fn write_to(self, buf: &mut [u8; $max_len]) -> &[u8] {
let is_nonnegative = self >= 0;
let n = if is_nonnegative {
self as u128
Expand All @@ -190,7 +212,8 @@ macro_rules! impl_Integer128 {
unsafe {
// Divide by 10^19 which is the highest power less than 2^64.
let (n, rem) = udiv128::udivmod_1e19(n);
curr -= rem.write_to(buf).len() as isize;
let buf1 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
curr -= rem.write_to(&mut *buf1).len() as isize;

if n != 0 {
// Memset the base10 leading zeros of rem.
Expand All @@ -200,7 +223,7 @@ macro_rules! impl_Integer128 {

// Divide by 10^19 again.
let (n, rem) = udiv128::udivmod_1e19(n);
let buf2 = buf_ptr.offset(curr - buf.len() as isize) as *mut _;
let buf2 = buf_ptr.offset(curr - U64_MAX_LEN as isize) as *mut [u8; U64_MAX_LEN];
curr -= rem.write_to(&mut *buf2).len() as isize;

if n != 0 {
Expand Down Expand Up @@ -230,4 +253,9 @@ macro_rules! impl_Integer128 {
}

#[cfg(all(feature = "i128"))]
impl_Integer128!(i128, u128);
const U128_MAX_LEN: usize = 39;
#[cfg(all(feature = "i128"))]
const I128_MAX_LEN: usize = 40;

#[cfg(all(feature = "i128"))]
impl_Integer128!(I128_MAX_LEN => i128, U128_MAX_LEN => u128);
1 change: 1 addition & 0 deletions src/udiv128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// (https://github.com/rust-lang/rust/issues/44545) and to allow function
// inlining which doesn’t happen with the intrinsic.

#[inline]
pub fn udivmod_1e19(n: u128) -> (u128, u64) {
let d = 10_000_000_000_000_000_000_u64; // 10^19

Expand Down

0 comments on commit ce0dc04

Please sign in to comment.