-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added functionality for
int_format_into
- Loading branch information
1 parent
2c6a12e
commit cdd0823
Showing
2 changed files
with
155 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
use crate::mem::MaybeUninit; | ||
use crate::slice; | ||
use crate::primitive::str; | ||
|
||
/// A minimal buffer implementation containing elements of type | ||
/// `MaybeUninit::<u8>`. | ||
pub struct NumBuffer<const N: usize> { | ||
pub contents: [MaybeUninit::<u8>; N] | ||
} | ||
|
||
impl<const N: usize> NumBuffer<N> { | ||
fn new() -> NumBuffer<N> { | ||
NumBuffer { | ||
contents: [MaybeUninit::<u8>::uninit(); N] | ||
} | ||
} | ||
} | ||
|
||
macro_rules! int_impl_format_into { | ||
// 2 digit decimal look up table | ||
const DEC_DIGITS_LUT: &[u8; 200] = b"\ | ||
0001020304050607080910111213141516171819\ | ||
2021222324252627282930313233343536373839\ | ||
4041424344454647484950515253545556575859\ | ||
6061626364656667686970717273747576777879\ | ||
8081828384858687888990919293949596979899"; | ||
|
||
const NEGATIVE_SIGN: &[u8; 1] = b"-"; | ||
|
||
($($T:ident)*) => { | ||
|
||
$( | ||
#[unstable(feature = "int_format_into", issue = "138215")] | ||
impl $T { | ||
/// Allows users to write an integer (in signed decimal format) into a variable `buf` of | ||
/// type [`NumBuffer`] that is passed by the caller by mutable reference. | ||
/// | ||
/// This function panics if `buf` does not have enough size to store | ||
/// the signed decimal version of the number. | ||
/// | ||
/// # Examples | ||
/// ``` | ||
#[doc = concat!("let n = 32", stringify!($T), ";")] | ||
/// let mut buf = NumBuffer::<3>::new(); | ||
/// | ||
/// assert_eq!(n.format_into(&mut buf), "32"); | ||
/// ``` | ||
/// | ||
fn format_into(self, buf: &mut $crate::NumBuffer) -> &str { | ||
// counting space for negative sign too, if `self` is negative | ||
let sign_offset = if self < 0 {1} else {0}; | ||
let decimal_string_size: usize = self.ilog(10) as usize + 1 + sign_offset; | ||
|
||
// `buf` must have minimum size to store the decimal string version. | ||
if buf.contents.len() < decimal_string_size { | ||
panic!("Not enough buffer size to format into!"); | ||
} | ||
|
||
// Count the number of bytes in `buf` that are not initialized. | ||
let mut offset = buf.contents.len(); | ||
// Consume the least-significant decimals from a working copy. | ||
let mut remain = self; | ||
|
||
// Format per four digits from the lookup table. | ||
// Four digits need a 16-bit $unsigned or wider. | ||
while size_of::<Self>() > 1 && remain > 999.try_into().expect("branch is not hit for types that cannot fit 999 (u8)") { | ||
// SAFETY: All of the decimals fit in buf, since it now is size-checked | ||
// and the while condition ensures at least 4 more decimals. | ||
unsafe { core::hint::assert_unchecked(offset >= 4) } | ||
// SAFETY: The offset counts down from its initial buf.contents.len() | ||
// without underflow due to the previous precondition. | ||
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) } | ||
offset -= 4; | ||
|
||
// pull two pairs | ||
let scale: Self = 1_00_00.try_into().expect("branch is not hit for types that cannot fit 1E4 (u8)"); | ||
let quad = remain % scale; | ||
remain /= scale; | ||
let pair1 = (quad / 100) as usize; | ||
let pair2 = (quad % 100) as usize; | ||
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair1 * 2 + 0]); | ||
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair1 * 2 + 1]); | ||
buf.contents[offset + 2].write(DEC_DIGITS_LUT[pair2 * 2 + 0]); | ||
buf.contents[offset + 3].write(DEC_DIGITS_LUT[pair2 * 2 + 1]); | ||
} | ||
|
||
// Format per two digits from the lookup table. | ||
if remain > 9 { | ||
// SAFETY: All of the decimals fit in buf, since it now is size-checked | ||
// and the while condition ensures at least 2 more decimals. | ||
unsafe { core::hint::assert_unchecked(offset >= 2) } | ||
// SAFETY: The offset counts down from its initial buf.contents.len() | ||
// without underflow due to the previous precondition. | ||
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) } | ||
offset -= 2; | ||
|
||
let pair = (remain % 100) as usize; | ||
remain /= 100; | ||
buf.contents[offset + 0].write(DEC_DIGITS_LUT[pair * 2 + 0]); | ||
buf.contents[offset + 1].write(DEC_DIGITS_LUT[pair * 2 + 1]); | ||
} | ||
|
||
// Format the last remaining digit, if any. | ||
if remain != 0 || self == 0 { | ||
// SAFETY: All of the decimals fit in buf, since it now is size-checked | ||
// and the if condition ensures (at least) 1 more decimals. | ||
unsafe { core::hint::assert_unchecked(offset >= 1) } | ||
// SAFETY: The offset counts down from its initial buf.contents.len() | ||
// without underflow due to the previous precondition. | ||
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) } | ||
offset -= 1; | ||
|
||
// Either the compiler sees that remain < 10, or it prevents | ||
// a boundary check up next. | ||
let last = (remain & 15) as usize; | ||
buf.contents[offset].write(DEC_DIGITS_LUT[last * 2 + 1]); | ||
// not used: remain = 0; | ||
} | ||
|
||
if self < 0 { | ||
// SAFETY: All of the decimals (with the sign) fit in buf, since it now is size-checked | ||
// and the if condition ensures (at least) that the sign can be added. | ||
unsafe { core::hint::assert_unchecked(offset >= 1) } | ||
|
||
// SAFETY: The offset counts down from its initial buf.contents.len() | ||
// without underflow due to the previous precondition. | ||
unsafe { core::hint::assert_unchecked(offset <= buf.contents.len()) } | ||
|
||
// Setting sign for the negative number | ||
offset -= 1; | ||
buf.contents[offset].write(NEGATIVE_SIGN[0]) | ||
} | ||
|
||
// SAFETY: All buf content since offset is set. | ||
let written = unsafe { buf.contents.get_unchecked(offset..) }; | ||
|
||
// SAFETY: Writes use ASCII from the lookup table | ||
// (and `NEGATIVE_SIGN` in case of negative numbers) exclusively. | ||
let as_str = unsafe { | ||
str::from_utf8_unchecked(crate::slice::from_raw_parts( | ||
crate::mem::MaybeUninit::slice_as_ptr(written), | ||
written.len(), | ||
)) | ||
}; | ||
as_str | ||
} | ||
} | ||
)* | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters