#![cfg(feature="std")]
use {
alloc::string::ToString,
core::fmt::{self, Debug, Formatter},
std::io::Write,
crate::IoResult,
super::{Inner, Number},
};
#[cfg(any(
target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64",
target_pointer_width = "128",
))]
const BUF_SIZE: usize = 40;
#[cfg(not(any(
target_pointer_width = "8", target_pointer_width = "16", target_pointer_width = "32", target_pointer_width = "64",
target_pointer_width = "128",
)))]
const BUF_SIZE: usize = core::mem::size_of::<usize>() * 3;
const BUF_LAST_IDX: usize = BUF_SIZE - 1;
pub struct NumberWriter {
buf: [u8; BUF_SIZE],
}
const I8_MIN_VALUE: &[u8] = b"-128";
const I16_MIN_VALUE: &[u8] = b"-32768";
const I32_MIN_VALUE: &[u8] = b"-2147483648";
const I64_MIN_VALUE: &[u8] = b"-9223372036854775808";
const I128_MIN_VALUE: &[u8] = b"-170141183460469231731687303715884105728";
macro_rules! write_unsigned { ($buf: expr, $n: expr, $negative: expr, $stream: ident) => {{
let mut n = $n;
let mut i = BUF_LAST_IDX;
loop {
$buf[i] = (n % 10) as u8 + b'0';
n /= 10;
match n {
0 => {
if $negative {
i -= 1;
$buf[i] = b'-';
}
return $stream.write_all(&$buf[i..]);
},
_ => i -= 1,
};
}
}}}
macro_rules! write_signed { ($buf: expr, $ty: ty, $min_value: ident, $n: ident, $stream: ident) => {{
if $n == <$ty>::min_value() {
return $stream.write_all($min_value);
}
let negative = $n.is_negative();
write_unsigned!($buf, $n.abs(), negative, $stream)
}}}
impl NumberWriter {
pub fn new() -> Self {
Self {
buf: [0; BUF_SIZE],
}
}
pub fn write<W>(&mut self, n: &Number, stream: &mut W) -> IoResult<()> where W: Write {
match n.inner {
Inner::I8(i) => write_signed!(self.buf, i8, I8_MIN_VALUE, i, stream),
Inner::I16(i) => write_signed!(self.buf, i16, I16_MIN_VALUE, i, stream),
Inner::I32(i) => write_signed!(self.buf, i32, I32_MIN_VALUE, i, stream),
Inner::I64(i) => write_signed!(self.buf, i64, I64_MIN_VALUE, i, stream),
Inner::I128(i) => write_signed!(self.buf, i128, I128_MIN_VALUE, i, stream),
#[cfg(target_pointer_width = "8")]
Inner::ISize(i) => {
let i = i as i8;
write_signed!(self.buf, i8, I8_MIN_VALUE, i, stream)
},
#[cfg(target_pointer_width = "16")]
Inner::ISize(i) => {
let i = i as i16;
write_signed!(self.buf, i16, I16_MIN_VALUE, i, stream)
},
#[cfg(target_pointer_width = "32")]
Inner::ISize(i) => {
let i = i as i32;
write_signed!(self.buf, i32, I32_MIN_VALUE, i, stream)
},
#[cfg(target_pointer_width = "64")]
Inner::ISize(i) => {
let i = i as i64;
write_signed!(self.buf, i64, I64_MIN_VALUE, i, stream)
},
#[cfg(target_pointer_width = "128")]
Inner::ISize(i) => {
let i = i as i128;
write_signed!(self.buf, i128, I128_MIN_VALUE, i, stream)
},
Inner::U8(u) => write_unsigned!(self.buf, u, false, stream),
Inner::U16(u) => write_unsigned!(self.buf, u, false, stream),
Inner::U32(u) => write_unsigned!(self.buf, u, false, stream),
Inner::U64(u) => write_unsigned!(self.buf, u, false, stream),
Inner::U128(u) => write_unsigned!(self.buf, u, false, stream),
Inner::USize(u) => write_unsigned!(self.buf, u, false, stream),
Inner::F32(f) => stream.write_all(f.to_string().as_bytes()),
Inner::F64(f) => stream.write_all(f.to_string().as_bytes()),
}
}
}
impl Debug for NumberWriter {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "NumberWriter {{ buf: {:?} }}", &self.buf[..])
}
}
#[test]
fn tests() -> IoResult<()> {
use alloc::vec::Vec;
assert!(BUF_SIZE >= u128::max_value().to_string().len());
assert!(BUF_SIZE >= i128::min_value().to_string().len());
assert!(BUF_SIZE >= usize::max_value().to_string().len());
assert!(BUF_SIZE >= isize::min_value().to_string().len());
let mut buf = Vec::with_capacity(BUF_SIZE);
let mut nw = NumberWriter::new();
for i in 0..99 {
buf.clear();
let n = Number::from(u64::max_value() - i as u64);
nw.write(&n, &mut buf)?;
assert_eq!(n.to_string().as_bytes(), buf.as_slice());
buf.clear();
let n = Number::from(u128::max_value() - i as u128);
nw.write(&n, &mut buf)?;
assert_eq!(n.to_string().as_bytes(), buf.as_slice());
buf.clear();
let n = Number::from(i64::min_value() + i as i64);
nw.write(&n, &mut buf)?;
assert_eq!(n.to_string().as_bytes(), buf.as_slice());
buf.clear();
let n = Number::from(i128::min_value() + i as i128);
nw.write(&n, &mut buf)?;
assert_eq!(n.to_string().as_bytes(), buf.as_slice());
}
Ok(())
}