move bulk of display code into two non-inlined functions

This commit is contained in:
Trevor Spiteri 2018-08-12 20:21:19 +02:00
parent 6998d667b1
commit 72d7f61337
3 changed files with 186 additions and 120 deletions

View File

@ -17,8 +17,9 @@ use core::cmp::Ordering;
use core::fmt::{
Binary, Debug, Display, Formatter, LowerHex, Octal, Result as FmtResult, UpperHex,
};
use core::mem;
use core::str;
use typenum::Unsigned;
use frac::Unsigned;
use FixedHelper;
use {
@ -26,11 +27,10 @@ use {
FixedU8,
};
trait Radix2: Copy {
const BITS: u8;
fn radix() -> u8;
fn prefix() -> &'static str;
fn digit(val: u8) -> u8;
trait Radix2 {
fn digit_bits(&self) -> u32;
fn prefix(&self) -> &'static str;
fn encode_digits(&self, digits: &mut [u8]);
}
macro_rules! radix2 {
@ -38,20 +38,21 @@ macro_rules! radix2 {
#[derive(Clone, Copy)]
struct $Radix;
impl Radix2 for $Radix {
const BITS: u8 = $bits;
#[inline(always)]
fn radix() -> u8 {
1 << <$Radix as Radix2>::BITS
}
#[inline(always)]
fn prefix() -> &'static str {
#[inline]
fn digit_bits(&self) -> u32 { $bits }
#[inline]
fn prefix(&self) -> &'static str {
$prefix
}
#[inline]
fn digit(val: u8) -> u8 {
match val {
$($range => val + $inc,)+
_ => unreachable!(),
fn encode_digits(&self, digits: &mut [u8]) {
for digit in digits.iter_mut() {
*digit += match *digit {
$($range => $inc,)+
_ => continue,
};
}
}
}
@ -63,15 +64,64 @@ radix2! { Oct(3, "0o"), 0..=7 => b'0' }
radix2! { LowHex(4, "0x"), 0..=9 => b'0', 10..=15 => b'a' - 10 }
radix2! { UpHex(4, "0x"), 0..=9 => b'0', 10..=15 => b'A' - 10 }
fn fmt_radix2<Frac: Unsigned, F, R>(num: F, _radix: R, fmt: &mut Formatter) -> FmtResult
trait FmtRadix2Helper {
fn int_frac_bits() -> u32;
fn is_zero(&self) -> bool;
fn take_int_digit(&mut self, digit_bits: u32) -> u8;
fn take_frac_digit(&mut self, digit_bits: u32) -> u8;
}
macro_rules! fmt_radix2_helper {
($Inner:ty) => {
impl FmtRadix2Helper for $Inner {
#[inline]
fn int_frac_bits() -> u32 {
mem::size_of::<$Inner>() as u32 * 8
}
#[inline]
fn is_zero(&self) -> bool {
*self == 0
}
#[inline]
fn take_int_digit(&mut self, digit_bits: u32) -> u8 {
let mask = (1 << digit_bits) - 1;
let ret = (*self & mask) as u8;
*self >>= digit_bits;
ret
}
#[inline]
fn take_frac_digit(&mut self, digit_bits: u32) -> u8 {
let int_frac_bits = <$Inner as FmtRadix2Helper>::int_frac_bits();
let rem_bits = int_frac_bits - digit_bits;
let mask = !0 << rem_bits;
let ret = ((*self & mask) >> rem_bits) as u8;
*self <<= digit_bits;
ret
}
}
};
}
fmt_radix2_helper!(u8);
fmt_radix2_helper!(u16);
fmt_radix2_helper!(u32);
fmt_radix2_helper!(u64);
fmt_radix2_helper!(u128);
fn fmt_radix2_helper<F>(
frac_bits: u32,
(is_neg, mut int, mut frac): (bool, F, F),
radix: &dyn Radix2,
fmt: &mut Formatter,
) -> FmtResult
where
F: FixedHelper<Frac>,
R: Radix2,
F: FmtRadix2Helper,
{
let digit_bits: u32 = R::BITS.into();
let frac_bits = Frac::to_u32();
let int_bits = F::bits() - frac_bits;
let (is_neg, mut int, mut frac) = num.parts();
let int_bits = F::int_frac_bits() - frac_bits;
let digit_bits: u32 = radix.digit_bits();
// 128 binary digits, one radix point, one leading zero
let mut buf: [u8; 130] = [0; 130];
let max_int_digits = (int_bits + digit_bits - 1) / digit_bits;
@ -85,9 +135,9 @@ where
} else {
int_start = max_int_digits;
for r in buf[0..max_int_digits as usize].iter_mut().rev() {
*r = R::digit(F::take_int_digit(&mut int, digit_bits));
*r = int.take_int_digit(digit_bits);
int_start -= 1;
if F::part_is_zero(&int) {
if int.is_zero() {
break;
}
}
@ -100,11 +150,23 @@ where
} else {
end = frac_start + frac_digits;
for r in buf[frac_start as usize..end as usize].iter_mut() {
*r = R::digit(F::take_frac_digit(&mut frac, digit_bits));
*r = frac.take_frac_digit(digit_bits);
}
}
let buf = str::from_utf8(&buf[int_start as usize..end as usize]).unwrap();
fmt.pad_integral(!is_neg, R::prefix(), buf)
let used_buf = &mut buf[int_start as usize..end as usize];
radix.encode_digits(used_buf);
let str_buf = str::from_utf8(used_buf).unwrap();
fmt.pad_integral(!is_neg, radix.prefix(), str_buf)
}
#[inline]
fn fmt_radix2<Frac: Unsigned, F, Inner, R>(num: F, radix: R, fmt: &mut Formatter) -> FmtResult
where
F: FixedHelper<Frac, Inner = Inner>,
Inner: FmtRadix2Helper,
R: Radix2,
{
fmt_radix2_helper(Frac::to_u32(), num.parts(), &radix, fmt)
}
macro_rules! impl_fmt {
@ -191,13 +253,65 @@ fn dec_frac_digits(frac_bits: u32) -> u32 {
digits
}
fn fmt_dec<Frac: Unsigned, F>(num: F, fmt: &mut Formatter) -> FmtResult
trait FmtDecHelper {
fn int_frac_bits() -> u32;
fn is_zero(&self) -> bool;
fn cmp_half(&self) -> Ordering;
fn take_int_digit(&mut self) -> u8;
fn take_frac_digit(&mut self) -> u8;
}
macro_rules! fmt_dec_helper {
($Inner:ty) => {
impl FmtDecHelper for $Inner {
#[inline]
fn int_frac_bits() -> u32 {
mem::size_of::<$Inner>() as u32 * 8
}
#[inline]
fn is_zero(&self) -> bool {
*self == 0
}
#[inline]
fn cmp_half(&self) -> Ordering {
self.cmp(&!(!0 >> 1))
}
#[inline]
fn take_int_digit(&mut self) -> u8 {
let ret = (*self % 10) as u8;
*self /= 10;
ret
}
#[inline]
fn take_frac_digit(&mut self) -> u8 {
let next = self.wrapping_mul(10);
let ret = ((*self - next / 10) / (!0 / 10)) as u8;
*self = next;
ret
}
}
};
}
fmt_dec_helper!(u8);
fmt_dec_helper!(u16);
fmt_dec_helper!(u32);
fmt_dec_helper!(u64);
fmt_dec_helper!(u128);
fn fmt_dec_helper<F>(
frac_bits: u32,
(is_neg, mut int, mut frac): (bool, F, F),
fmt: &mut Formatter,
) -> FmtResult
where
F: FixedHelper<Frac>,
F: FmtDecHelper,
{
let frac_bits = Frac::to_u32();
let int_bits = F::bits() - frac_bits;
let (is_neg, mut int, mut frac) = num.parts();
let int_bits = F::int_frac_bits() - frac_bits;
// 40 int digits
// + 128 frac digits
// + 1 dec point,
@ -230,9 +344,9 @@ where
buf[int_start as usize] = b'.';
frac_start = int_start + 1;
for r in buf[1..int_start as usize].iter_mut().rev() {
*r = b'0' + F::take_int_dec_digit(&mut int);
*r = b'0' + int.take_int_digit();
int_start -= 1;
if F::part_is_zero(&int) {
if int.is_zero() {
break;
}
}
@ -243,10 +357,10 @@ where
} else {
end = frac_start + frac_digits;
for r in buf[frac_start as usize..end as usize].iter_mut() {
*r = b'0' + F::take_frac_dec_digit(&mut frac);
*r = b'0' + frac.take_frac_digit();
}
// check for rounding up
let round_up = match F::part_cmp_half(&frac) {
let round_up = match frac.cmp_half() {
Ordering::Less => false,
Ordering::Greater => true,
Ordering::Equal => {
@ -277,13 +391,22 @@ where
fmt.pad_integral(!is_neg, "", buf)
}
#[inline]
fn fmt_dec<Frac: Unsigned, F, Inner>(num: F, fmt: &mut Formatter) -> FmtResult
where
F: FixedHelper<Frac, Inner = Inner>,
Inner: FmtDecHelper,
{
fmt_dec_helper(Frac::to_u32(), num.parts(), fmt)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use *;
#[test]
fn hex() {
use typenum::U7 as Frac;
use frac::U7 as Frac;
let frac = Frac::to_u32();
for i in 0..(1 << frac) {
let p = 0x1234_5678_9abc_def0u64 ^ i as u64;
@ -303,7 +426,7 @@ mod tests {
#[test]
fn dec() {
use typenum::U7 as Frac;
use frac::U7 as Frac;
let frac = Frac::to_u32();
for i in 0..(1 << frac) {
let bits = !0u32 ^ i;

View File

@ -13,87 +13,30 @@
// <https://www.apache.org/licenses/LICENSE-2.0> and
// <https://opensource.org/licenses/MIT>.
use core::cmp::Ordering;
use core::mem;
use typenum::Unsigned;
use frac::Unsigned;
use {
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
FixedU8,
};
pub(crate) trait FixedHelper<Frac: Unsigned>: Sized {
type Part;
type Inner;
#[inline]
fn int_frac_bits() -> u32 {
mem::size_of::<Self::Inner>() as u32 * 8
}
fn one() -> Option<Self>;
fn minus_one() -> Option<Self>;
fn parts(self) -> (bool, Self::Part, Self::Part);
#[inline(always)]
fn bits() -> u32 {
mem::size_of::<Self::Part>() as u32 * 8
}
fn take_int_digit(int_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_frac_digit(frac_part: &mut Self::Part, digit_bits: u32) -> u8;
fn take_int_dec_digit(int_part: &mut Self::Part) -> u8;
fn take_frac_dec_digit(int_part: &mut Self::Part) -> u8;
fn part_is_zero(part: &Self::Part) -> bool;
fn part_cmp_half(part: &Self::Part) -> Ordering;
}
macro_rules! fixed_num_common {
($Fixed:ident($Part:ty); $($rem:tt)+) => {
impl<Frac: Unsigned> FixedHelper<Frac> for $Fixed<Frac> {
type Part = $Part;
$($rem)+
#[inline]
fn take_int_digit(int_part: &mut $Part, digit_bits: u32) -> u8 {
let mask = (1 << digit_bits) - 1;
let ret = (*int_part & mask) as u8;
*int_part >>= digit_bits;
ret
}
#[inline]
fn take_frac_digit(frac_part: &mut $Part, digit_bits: u32) -> u8 {
let rem_bits = <$Fixed<Frac> as FixedHelper<Frac>>::bits() - digit_bits;
let mask = !0 << rem_bits;
let ret = ((*frac_part & mask) >> rem_bits) as u8;
*frac_part <<= digit_bits;
ret
}
#[inline]
fn take_int_dec_digit(int_part: &mut $Part) -> u8 {
let ret = (*int_part % 10) as u8;
*int_part /= 10;
ret
}
#[inline]
fn take_frac_dec_digit(frac_part: &mut $Part) -> u8 {
let next = frac_part.wrapping_mul(10);
let ret = ((*frac_part - next / 10) / (!0 / 10)) as u8;
*frac_part = next;
ret
}
#[inline]
fn part_is_zero(part: &$Part) -> bool {
*part == 0
}
#[inline]
fn part_cmp_half(part: &$Part) -> Ordering {
part.cmp(&!(!0 >> 1))
}
}
};
fn parts(self) -> (bool, Self::Inner, Self::Inner);
}
macro_rules! fixed_num_unsigned {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
($Fixed:ident($Inner:ty)) => {
impl<Frac: Unsigned> FixedHelper<Frac> for $Fixed<Frac> {
type Inner = $Inner;
#[inline]
fn one() -> Option<Self> {
@ -112,7 +55,7 @@ macro_rules! fixed_num_unsigned {
}
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
fn parts(self) -> (bool, $Inner, $Inner) {
let bits = self.to_bits();
let int_bits = <$Fixed<Frac>>::int_bits();
let frac_bits = <$Fixed<Frac>>::frac_bits();
@ -125,9 +68,9 @@ macro_rules! fixed_num_unsigned {
}
macro_rules! fixed_num_signed {
($Fixed:ident($Part:ty)) => {
fixed_num_common! {
$Fixed($Part);
($Fixed:ident($Inner:ty)) => {
impl<Frac: Unsigned> FixedHelper<Frac> for $Fixed<Frac> {
type Inner = $Inner;
#[inline]
fn one() -> Option<Self> {
@ -152,13 +95,13 @@ macro_rules! fixed_num_signed {
}
#[inline]
fn parts(self) -> (bool, $Part, $Part) {
let bits = self.to_bits().wrapping_abs() as $Part;
fn parts(self) -> (bool, $Inner, $Inner) {
let bits = self.to_bits().wrapping_abs() as $Inner;
let int_bits = <$Fixed<Frac>>::int_bits();
let frac_bits = <$Fixed<Frac>>::frac_bits();
let int_part = if int_bits == 0 { 0 } else { bits >> frac_bits };
let frac_part = if frac_bits == 0 { 0 } else { bits << int_bits };
(self.to_bits() < 0, int_part,frac_part)
(self.to_bits() < 0, int_part, frac_part)
}
}
};

View File

@ -370,7 +370,7 @@ macro_rules! fixed {
/// Returns the number of integer bits.
#[inline]
pub fn int_bits() -> u32 {
<$Fixed<Frac> as FixedHelper<Frac>>::bits() - <$Fixed<Frac>>::frac_bits()
<$Fixed<Frac> as FixedHelper<Frac>>::int_frac_bits() - <$Fixed<Frac>>::frac_bits()
}
/// Returns the number of fractional bits.
@ -387,8 +387,8 @@ macro_rules! fixed {
),
#[inline]
pub fn from_bits(v: $Inner) -> $Fixed<Frac> {
let bits = <$Fixed<Frac> as FixedHelper<Frac>>::bits();
assert!(Frac::to_u32() <= bits, "`Frac` too large");
let int_frac_bits = <$Fixed<Frac> as FixedHelper<Frac>>::int_frac_bits();
assert!(Frac::to_u32() <= int_frac_bits, "`Frac` too large");
$Fixed((v, PhantomData))
}
}
@ -1250,7 +1250,7 @@ mod tests {
#[test]
fn rounding() {
use typenum::{U16, U32};
use frac::{U16, U32};
type I0F32 = FixedI32<U32>;