#![allow(missing_docs)]
pub use self::ExponentFormat::*;
pub use self::SignificantDigits::*;
pub use self::SignFormat::*;
use char;
use char::CharExt;
use fmt;
use iter::{IteratorExt, range};
use num::{cast, Float, ToPrimitive};
use num::FpCategory as Fp;
use ops::{FnOnce, Index};
use result::Result::Ok;
use slice::{self, SliceExt};
use str::{self, StrExt};
pub enum ExponentFormat {
ExpNone,
ExpDec
}
pub enum SignificantDigits {
DigMax(uint),
DigExact(uint)
}
pub enum SignFormat {
SignNeg
}
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
pub fn float_to_str_bytes_common<T: Float, U, F>(
num: T,
radix: uint,
negative_zero: bool,
sign: SignFormat,
digits: SignificantDigits,
exp_format: ExponentFormat,
exp_upper: bool,
f: F
) -> U where
F: FnOnce(&str) -> U,
{
assert!(2 <= radix && radix <= 36);
match exp_format {
ExpDec if radix >= DIGIT_E_RADIX => panic!("float_to_str_bytes_common: radix {} incompatible with \
use of 'e' as decimal exponent", radix),
_ => ()
}
let _0: T = Float::zero();
let _1: T = Float::one();
match num.classify() {
Fp::Nan => return f("NaN"),
Fp::Infinite if num > _0 => {
return f("inf");
}
Fp::Infinite if num < _0 => {
return f("-inf");
}
_ => {}
}
let neg = num < _0 || (negative_zero && _1 / num == Float::neg_infinity());
let mut buf = [0u8; 1536];
let mut end = 0;
let radix_gen: T = cast(radix as int).unwrap();
let (num, exp) = match exp_format {
ExpNone => (num, 0i32),
ExpDec if num == _0 => (num, 0i32),
ExpDec => {
let (exp, exp_base) = match exp_format {
ExpDec => (num.abs().log10().floor(), cast::<f64, T>(10.0f64).unwrap()),
ExpNone => panic!("unreachable"),
};
(num / exp_base.powf(exp), cast::<T, i32>(exp).unwrap())
}
};
let mut deccum = num.trunc();
loop {
let current_digit = (deccum % radix_gen).abs();
deccum = deccum / radix_gen;
deccum = deccum.trunc();
let c = char::from_digit(current_digit.to_int().unwrap() as uint, radix);
buf[end] = c.unwrap() as u8;
end += 1;
if deccum == _0 { break; }
}
let (limit_digits, digit_count, exact) = match digits {
DigMax(count) => (true, count + 1, false),
DigExact(count) => (true, count + 1, true)
};
match sign {
SignNeg if neg => {
buf[end] = b'-';
end += 1;
}
_ => ()
}
buf.slice_to_mut(end).reverse();
let start_fractional_digits = end;
deccum = num.fract();
if deccum != _0 || (limit_digits && exact && digit_count > 0) {
buf[end] = b'.';
end += 1;
let mut dig = 0u;
while (!limit_digits && deccum != _0)
|| (limit_digits && dig < digit_count && (
exact
|| (!exact && deccum != _0)
)
) {
deccum = deccum * radix_gen;
let current_digit = deccum.trunc().abs();
let c = char::from_digit(current_digit.to_int().unwrap() as uint,
radix);
buf[end] = c.unwrap() as u8;
end += 1;
deccum = deccum.fract();
dig += 1u;
}
if limit_digits && dig == digit_count {
let ascii2value = |&: chr: u8| {
(chr as char).to_digit(radix).unwrap()
};
let value2ascii = |&: val: uint| {
char::from_digit(val, radix).unwrap() as u8
};
let extra_digit = ascii2value(buf[end - 1]);
end -= 1;
if extra_digit >= radix / 2 { let mut i: int = end as int - 1;
loop {
if i < 0
|| buf[i as uint] == b'-'
|| buf[i as uint] == b'+' {
for j in range(i as uint + 1, end).rev() {
buf[j + 1] = buf[j];
}
buf[(i + 1) as uint] = value2ascii(1);
end += 1;
break;
}
if buf[i as uint] == b'.' { i -= 1; continue; }
let current_digit = ascii2value(buf[i as uint]);
if current_digit < (radix - 1) {
buf[i as uint] = value2ascii(current_digit+1);
break;
} else {
buf[i as uint] = value2ascii(0);
i -= 1;
}
}
}
}
}
if !exact {
let buf_max_i = end - 1;
let mut i = buf_max_i;
while i > start_fractional_digits && buf[i] == b'0' {
i -= 1;
}
if i >= start_fractional_digits {
if buf[i] == b'.' { i -= 1 }
if i < buf_max_i {
end = i + 1;
}
}
} else {
let max_i = end - 1;
if buf[max_i] == b'.' {
end = max_i;
}
}
match exp_format {
ExpNone => {},
_ => {
buf[end] = match exp_format {
ExpDec if exp_upper => 'E',
ExpDec if !exp_upper => 'e',
_ => panic!("unreachable"),
} as u8;
end += 1;
struct Filler<'a> {
buf: &'a mut [u8],
end: &'a mut uint,
}
impl<'a> fmt::Writer for Filler<'a> {
fn write_str(&mut self, s: &str) -> fmt::Result {
slice::bytes::copy_memory(self.buf.slice_from_mut(*self.end),
s.as_bytes());
*self.end += s.len();
Ok(())
}
}
let mut filler = Filler { buf: &mut buf, end: &mut end };
match sign {
SignNeg => {
let _ = fmt::write(&mut filler, format_args!("{:-}", exp));
}
}
}
}
f(unsafe { str::from_utf8_unchecked(buf.index(&(0..end))) })
}