diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 897222747f5e6..793b9edd787bc 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -22,7 +22,7 @@ use slice; use str; mod float; -mod num; +pub(crate) mod num; mod builders; #[unstable(feature = "fmt_flags_align", issue = "27726")] diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index c8218172583d6..7d070fede49c1 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -25,6 +25,7 @@ trait Int: PartialEq + PartialOrd + Div + Rem + Sub + Copy { fn zero() -> Self; fn from_u8(u: u8) -> Self; + fn from_u32(u: u32) -> Self; fn to_u8(&self) -> u8; fn to_u16(&self) -> u16; fn to_u32(&self) -> u32; @@ -36,6 +37,7 @@ macro_rules! doit { ($($t:ident)*) => ($(impl Int for $t { fn zero() -> $t { 0 } fn from_u8(u: u8) -> $t { u as $t } + fn from_u32(u: u32) -> $t { u as $t } fn to_u8(&self) -> u8 { *self as u8 } fn to_u16(&self) -> u16 { *self as u16 } fn to_u32(&self) -> u32 { *self as u32 } @@ -200,60 +202,156 @@ macro_rules! impl_Display { #[allow(unused_comparisons)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let is_nonnegative = *self >= 0; - let mut n = if is_nonnegative { + let n = if is_nonnegative { self.$conv_fn() } else { // convert the negative num to positive by summing 1 to it's 2 complement (!self.$conv_fn()).wrapping_add(1) }; - let mut buf: [u8; 39] = unsafe { mem::uninitialized() }; + unsafe { + let mut buf: [u8; i128::MAX_STR_LEN] = mem::uninitialized(); + f.pad_integral(is_nonnegative, "", n.to_str_unchecked(&mut buf, false)) + } + } + })+); +} + +macro_rules! impl_unsigned_to_str { + ($($t:ident),*) => ($( + impl UnsignedToStr for $t { + fn to_str(self, buf: &mut [u8]) -> &mut str { + assert!(buf.len() >= $t::MAX_STR_LEN, concat!( + "A buffer of length ", stringify!($t), "::MAX_STR_LEN or more is required." + )); + unsafe { + self.to_str_unchecked(buf, false) + } + } + + /// `buf` must be large enough + unsafe fn to_str_unchecked(self, buf: &mut [u8], minus_sign: bool) -> &mut str { let mut curr = buf.len() as isize; let buf_ptr = buf.as_mut_ptr(); let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + let mut n = self; - unsafe { - // need at least 16 bits for the 4-characters-at-a-time to work. - if ::mem::size_of::<$t>() >= 2 { - // eagerly decode 4 characters at a time - while n >= 10000 { - let rem = (n % 10000) as isize; - n /= 10000; - - let d1 = (rem / 100) << 1; - let d2 = (rem % 100) << 1; - curr -= 4; - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); - ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); - } - } - - // if we reach here numbers are <= 9999, so at most 4 chars long - let mut n = n as isize; // possibly reduce 64bit math + // need at least 16 bits for the 4-characters-at-a-time to work. + if ::mem::size_of::<$t>() >= 2 { + // eagerly decode 4 characters at a time + #[allow(unused_comparisons, overflowing_literals)] + while n >= 10000 { + let rem = (n % 10000) as isize; + n /= 10000; - // decode 2 more chars, if > 2 chars - if n >= 100 { - let d1 = (n % 100) << 1; - n /= 100; - curr -= 2; + let d1 = (rem / 100) << 1; + let d2 = (rem % 100) << 1; + curr -= 4; ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2), buf_ptr.offset(curr + 2), 2); } + } - // decode last 1 or 2 chars - if n < 10 { - curr -= 1; - *buf_ptr.offset(curr) = (n as u8) + b'0'; + // if we reach here numbers are <= 9999, so at most 4 chars long + let mut n = n as isize; // possibly reduce 64bit math + + // decode 2 more chars, if > 2 chars + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + + // decode last 1 or 2 chars + if n < 10 { + curr -= 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + } + + if minus_sign { + curr -= 1; + *buf_ptr.offset(curr) = b'-'; + } + + str::from_utf8_unchecked_mut( + slice::from_raw_parts_mut(buf_ptr.offset(curr), buf.len() - curr as usize) + ) + } + + fn to_str_radix(self, buf: &mut [u8], radix: u32, uppercase: bool, minus_sign: bool) + -> &mut str { + assert!(2 <= radix && radix <= 36, "radix must be between 2 and 36 inclusive"); + let radix = $t::from_u32(radix); + let mut curr = buf.len(); + macro_rules! next { + () => { + match curr.checked_sub(1) { + Some(next) => curr = next, + None => panic!( + "A buffer of length {} is too small to represent {} in base {}", + buf.len(), self, radix + ) + } + } + } + let mut n = self; + loop { + next!(); + let digit = (n % radix).to_u8(); + buf[curr] = digit + if digit < 10 { + b'0' + } else if uppercase { + b'A' - 10 } else { - let d1 = n << 1; - curr -= 2; - ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2); + b'a' - 10 + }; + n /= radix; + if n == 0 { + if minus_sign { + next!(); + buf[curr] = b'-' + } + return unsafe { + str::from_utf8_unchecked_mut(&mut buf[curr..]) + } } } + } + })*); +} - let buf_slice = unsafe { - str::from_utf8_unchecked( - slice::from_raw_parts(buf_ptr.offset(curr), buf.len() - curr as usize)) +macro_rules! impl_signed_to_str { + ($($t:ident $conv_fn: ident),*) => ($( + impl SignedToStr for $t { + fn to_str(self, buf: &mut [u8]) -> &mut str { + assert!(buf.len() >= $t::MAX_STR_LEN, concat!( + "A buffer of length ", stringify!($t), "::MAX_STR_LEN or more is required." + )); + let is_negative = self < 0; + let n = if is_negative { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + } else { + self.$conv_fn() }; - f.pad_integral(is_nonnegative, "", buf_slice) + unsafe { + n.to_str_unchecked(buf, is_negative) + } + } + + fn to_str_radix(self, buf: &mut [u8], radix: u32, uppercase: bool) -> &mut str { + let is_negative = self < 0; + let n = if is_negative { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + } else { + self.$conv_fn() + }; + n.to_str_radix(buf, radix, uppercase, is_negative) } })*); } @@ -267,3 +365,18 @@ impl_Display!(isize, usize: to_u16); impl_Display!(isize, usize: to_u32); #[cfg(target_pointer_width = "64")] impl_Display!(isize, usize: to_u64); + +impl_unsigned_to_str!(u8, u16, u32, u64, u128); +impl_signed_to_str!(i8 to_u8, i16 to_u16, i32 to_u32, i64 to_u64, i128 to_u128); + +pub(crate) trait UnsignedToStr { + fn to_str(self, buf: &mut [u8]) -> &mut str; + unsafe fn to_str_unchecked(self, buf: &mut [u8], minus_sign: bool) -> &mut str; + fn to_str_radix(self, buf: &mut [u8], radix: u32, uppercase: bool, minus_sign: bool) + -> &mut str; +} + +pub(crate) trait SignedToStr { + fn to_str(self, buf: &mut [u8]) -> &mut str; + fn to_str_radix(self, buf: &mut [u8], radix: u32, uppercase: bool) -> &mut str; +} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 1230066e2b33b..bc6cd7b25ab8a 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -99,6 +99,7 @@ pub mod diy_float; // `Int` + `SignedInt` implemented for signed integers macro_rules! int_impl { ($SelfT:ty, $ActualT:ident, $UnsignedT:ty, $BITS:expr, + MAX_STR_LEN = $MAX_STR_LEN: expr, $add_with_overflow:path, $sub_with_overflow:path, $mul_with_overflow:path) => { @@ -150,6 +151,101 @@ macro_rules! int_impl { from_str_radix(src, radix) } + /// Write the representation in a given base in a pre-allocated buffer. + /// + /// Digits are a subset (depending on `radix`) of `0-9A-Z`. + /// + /// The returned slice starts with a minus sign for negative values + /// but contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `radix` is smaller than 2 or larger than 36, + /// or if `buffer` is too small. + /// As a conservative upper bound, `&mut [u8; 128]` is always large enough. + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!((i16::min_value() + 1).to_uppercase_str_radix(&mut [0; 5], 16), "-7FFF") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_uppercase_str_radix(self, buffer: &mut [u8], radix: u32) -> &mut str { + fmt::num::SignedToStr::to_str_radix(self as $ActualT, buffer, radix, true) + } + + /// Write the representation in a given base in a pre-allocated buffer. + /// + /// Digits are a subset (depending on `radix`) of `0-9a-z`. + /// + /// The returned slice starts with a minus sign for negative values + /// but contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `radix` is smaller than 2 or larger than 36, + /// or if `buffer` is too small. + /// As a conservative upper bound, `&mut [u8; 128]` is always large enough. + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!((i16::min_value() + 1).to_lowercase_str_radix(&mut [0; 5], 16), "-7fff") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_lowercase_str_radix(self, buffer: &mut [u8], radix: u32) -> &mut str { + fmt::num::SignedToStr::to_str_radix(self as $ActualT, buffer, radix, false) + } + + /// Writes the decimal representation in a pre-allocated buffer. + /// + /// The returned slice starts with a minus sign for negative values + /// but contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `buffer` is smaller than [`MAX_STR_LEN`]. + /// + /// [`MAX_STR_LEN`]: #associatedconstant.MAX_STR_LEN + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!(i16::min_value().to_str(&mut [0; i16::MAX_STR_LEN]), "-32768") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_str(self, buffer: &mut [u8]) -> &mut str { + fmt::num::SignedToStr::to_str(self as $ActualT, buffer) + } + + /// The maximum length of the decimal representation of a value of this type. + /// This is intended to be used together with [`to_str`]. + /// + /// [`MAX_STR_LEN`]: #method.to_str + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub const MAX_STR_LEN: usize = $MAX_STR_LEN; + /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -1200,6 +1296,7 @@ macro_rules! int_impl { #[lang = "i8"] impl i8 { int_impl! { i8, i8, u8, 8, + MAX_STR_LEN = 4, // i8::min_value().to_string().len() intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1208,6 +1305,7 @@ impl i8 { #[lang = "i16"] impl i16 { int_impl! { i16, i16, u16, 16, + MAX_STR_LEN = 6, // i16::min_value().to_string().len() intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1216,6 +1314,7 @@ impl i16 { #[lang = "i32"] impl i32 { int_impl! { i32, i32, u32, 32, + MAX_STR_LEN = 11, // i32::min_value().to_string().len() intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1224,6 +1323,7 @@ impl i32 { #[lang = "i64"] impl i64 { int_impl! { i64, i64, u64, 64, + MAX_STR_LEN = 20, // i64::min_value().to_string().len() intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1232,6 +1332,7 @@ impl i64 { #[lang = "i128"] impl i128 { int_impl! { i128, i128, u128, 128, + MAX_STR_LEN = 40, // i128::min_value().to_string().len() intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1241,6 +1342,7 @@ impl i128 { #[lang = "isize"] impl isize { int_impl! { isize, i16, u16, 16, + MAX_STR_LEN = i16::MAX_STR_LEN, intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1250,6 +1352,7 @@ impl isize { #[lang = "isize"] impl isize { int_impl! { isize, i32, u32, 32, + MAX_STR_LEN = i32::MAX_STR_LEN, intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1259,6 +1362,7 @@ impl isize { #[lang = "isize"] impl isize { int_impl! { isize, i64, u64, 64, + MAX_STR_LEN = i64::MAX_STR_LEN, intrinsics::add_with_overflow, intrinsics::sub_with_overflow, intrinsics::mul_with_overflow } @@ -1267,6 +1371,7 @@ impl isize { // `Int` + `UnsignedInt` implemented for unsigned integers macro_rules! uint_impl { ($SelfT:ty, $ActualT:ty, $BITS:expr, + MAX_STR_LEN = $MAX_STR_LEN: expr, $ctpop:path, $ctlz:path, $ctlz_nonzero:path, @@ -1315,6 +1420,98 @@ macro_rules! uint_impl { from_str_radix(src, radix) } + /// Write the representation in a given base in a pre-allocated buffer. + /// + /// Digits are a subset (depending on `radix`) of `0-9A-Z`. + /// + /// The returned slice contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `radix` is smaller than 2 or larger than 36, + /// or if `buffer` is too small. + /// As a conservative upper bound, `&mut [u8; 128]` is always large enough. + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!((std::char::MAX as u32).to_uppercase_str_radix(&mut [0; 8], 16), "10FFFF") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_uppercase_str_radix(self, buffer: &mut [u8], radix: u32) -> &mut str { + fmt::num::UnsignedToStr::to_str_radix(self as $ActualT, buffer, radix, true, false) + } + + /// Write the representation in a given base in a pre-allocated buffer. + /// + /// Digits are a subset (depending on `radix`) of `0-9a-z`. + /// + /// The returned slice contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `radix` is smaller than 2 or larger than 36, + /// or if `buffer` is too small. + /// As a conservative upper bound, `&mut [u8; 128]` is always large enough. + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!((std::char::MAX as u32).to_lowercase_str_radix(&mut [0; 8], 16), "10ffff") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_lowercase_str_radix(self, buffer: &mut [u8], radix: u32) -> &mut str { + fmt::num::UnsignedToStr::to_str_radix(self as $ActualT, buffer, radix, false, false) + } + + /// Writes the decimal representation in a pre-allocated buffer. + /// + /// The returned slice contains no leading zero or plus sign, + /// and is aligned to the *end* of `buffer`. + /// + /// # Panics + /// + /// This function will panic if `buffer` is smaller than [`MAX_STR_LEN`]. + /// + /// [`MAX_STR_LEN`]: #associatedconstant.MAX_STR_LEN + /// + /// # Safety + /// + /// `buffer` may be uninitialized. + /// + /// # Examples + /// + /// ``` + /// #![feature(int_to_str)] + /// + /// assert_eq!(0xFFFF_u32.to_str(&mut [0; u32::MAX_STR_LEN]), "65535") + /// ``` + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub fn to_str(self, buffer: &mut [u8]) -> &mut str { + fmt::num::UnsignedToStr::to_str(self as $ActualT, buffer) + } + + /// The maximum length of the decimal representation of a value of this type. + /// This is intended to be used together with [`to_str`]. + /// + /// [`MAX_STR_LEN`]: #method.to_str + #[unstable(feature = "int_to_str", issue = /* FIXME */ "0")] + pub const MAX_STR_LEN: usize = $MAX_STR_LEN; + /// Returns the number of ones in the binary representation of `self`. /// /// # Examples @@ -2251,6 +2448,7 @@ macro_rules! uint_impl { #[lang = "u8"] impl u8 { uint_impl! { u8, u8, 8, + MAX_STR_LEN = 3, // u8::min_value().to_string().len() intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2805,6 +3003,7 @@ impl u8 { #[lang = "u16"] impl u16 { uint_impl! { u16, u16, 16, + MAX_STR_LEN = 5, // u16::min_value().to_string().len() intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2818,6 +3017,7 @@ impl u16 { #[lang = "u32"] impl u32 { uint_impl! { u32, u32, 32, + MAX_STR_LEN = 10, // u32::min_value().to_string().len() intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2831,6 +3031,7 @@ impl u32 { #[lang = "u64"] impl u64 { uint_impl! { u64, u64, 64, + MAX_STR_LEN = 20, // u64::min_value().to_string().len() intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2844,6 +3045,7 @@ impl u64 { #[lang = "u128"] impl u128 { uint_impl! { u128, u128, 128, + MAX_STR_LEN = 40, // u128::min_value().to_string().len() intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2858,6 +3060,7 @@ impl u128 { #[lang = "usize"] impl usize { uint_impl! { usize, u16, 16, + MAX_STR_LEN = u16::MAX_STR_LEN, intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2871,6 +3074,7 @@ impl usize { #[lang = "usize"] impl usize { uint_impl! { usize, u32, 32, + MAX_STR_LEN = u32::MAX_STR_LEN, intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero, @@ -2885,6 +3089,7 @@ impl usize { #[lang = "usize"] impl usize { uint_impl! { usize, u64, 64, + MAX_STR_LEN = u64::MAX_STR_LEN, intrinsics::ctpop, intrinsics::ctlz, intrinsics::ctlz_nonzero,