From 629e8c36c41820af6d0f8e68509a1086e347e27d Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 7 Sep 2018 20:31:25 +0200 Subject: [PATCH] feat: Provide a safe API to write integers to a buffer Closes #19 --- src/lib.rs | 68 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8517921..ff6b4d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,14 +26,44 @@ use core::{fmt, mem, ptr, slice, str}; /// Write integer to an `io::Write`. #[cfg(feature = "std")] #[inline] -pub fn write(wr: W, value: V) -> io::Result { - value.write(wr) +pub fn write(mut wr: W, value: V) -> io::Result { + let mut buf = Buffer::new(); + let s = buf.format(value); + wr.write_all(s.as_bytes())?; + Ok(s.len()) } /// Write integer to an `fmt::Write`. #[inline] -pub fn fmt(wr: W, value: V) -> fmt::Result { - value.fmt(wr) +pub fn fmt(mut wr: W, value: V) -> fmt::Result { + let mut buf = Buffer::new(); + wr.write_str(buf.format(value)) +} + +/// A safe API for formatting integers to text. +#[derive(Copy, Clone)] +pub struct Buffer { + bytes: [u8; I128_MAX_LEN], +} + +impl Default for Buffer { + fn default() -> Buffer { + Buffer::new() + } +} + +impl Buffer { + /// This is a cheap operation; you don't need to worry about reusing buffers + /// for efficiency. + pub fn new() -> Buffer { + Buffer { bytes: unsafe { mem::uninitialized() } } + } + + /// Print an integer into this buffer and return a reference to its string representation + /// within the buffer + pub fn format(&mut self, i: I) -> &str { + i.write(self) + } } // Seal to prevent downstream implementations of the Integer trait. @@ -47,12 +77,7 @@ mod private { pub trait Integer: private::Sealed { // Not public API. #[doc(hidden)] - #[cfg(feature = "std")] - fn write(self, W) -> io::Result; - - // Not public API. - #[doc(hidden)] - fn fmt(self, W) -> fmt::Result; + fn write<'a>(self, buf: &'a mut Buffer) -> &'a str; } trait IntegerPrivate { @@ -71,20 +96,16 @@ const DEC_DIGITS_LUT: &'static[u8] = macro_rules! impl_IntegerCommon { ($max_len:expr, $t:ident) => { impl Integer for $t { - #[cfg(feature = "std")] - #[inline] - fn write(self, mut wr: W) -> io::Result { - 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(self, mut wr: W) -> fmt::Result { - 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) }) + fn write<'a>(self, buf: &'a mut Buffer) -> &'a str { + unsafe { + debug_assert!($max_len <= I128_MAX_LEN); + let buf = mem::transmute::<&mut [u8; I128_MAX_LEN], &mut [u8; $max_len]>( + &mut buf.bytes + ); + let bytes = self.write_to(buf); + str::from_utf8_unchecked(bytes) + } } } @@ -254,7 +275,6 @@ macro_rules! impl_Integer128 { #[cfg(all(feature = "i128"))] const U128_MAX_LEN: usize = 39; -#[cfg(all(feature = "i128"))] const I128_MAX_LEN: usize = 40; #[cfg(all(feature = "i128"))]