From 3491c48a3a6374e0c6751b07a8b344ef97487158 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 30 Oct 2024 23:01:40 -0400 Subject: [PATCH] Fix DecInt::new panicking on integers greater than 64 bits and optimize out any panics in DecInt::new --- src/path/dec_int.rs | 50 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/path/dec_int.rs b/src/path/dec_int.rs index 1e9dc1a4f..5326402c2 100644 --- a/src/path/dec_int.rs +++ b/src/path/dec_int.rs @@ -8,7 +8,8 @@ use crate::backend::fd::{AsFd, AsRawFd}; use crate::ffi::CStr; -use core::mem::MaybeUninit; +use core::any::TypeId; +use core::mem::{self, MaybeUninit}; use itoa::{Buffer, Integer}; #[cfg(all(feature = "std", unix))] use std::os::unix::ffi::OsStrExt; @@ -36,25 +37,50 @@ use {core::fmt, std::ffi::OsStr, std::path::Path}; /// ``` #[derive(Clone)] pub struct DecInt { - // 20 `u8`s is enough to hold the decimal ASCII representation of any - // `u64`, and we add one for a NUL terminator for `as_c_str`. - buf: [MaybeUninit; 20 + 1], + // Enough to hold an i64 and NUL terminator. + buf: [MaybeUninit; "-9223372036854775808\0".len()], len: usize, } impl DecInt { /// Construct a new path component from an integer. #[inline] - pub fn new(i: Int) -> Self { - let mut buf = [MaybeUninit::uninit(); 20 + 1]; + pub fn new(i: Int) -> Self { + let mut buf = [MaybeUninit::uninit(); 21]; let mut str_buf = Buffer::new(); let str_buf = str_buf.format(i); - buf[..str_buf.len()].copy_from_slice(unsafe { - // SAFETY: you can always go from init to uninit - core::mem::transmute::<&[u8], &[MaybeUninit]>(str_buf.as_bytes()) - }); - buf[str_buf.len()] = MaybeUninit::new(0); + { + let max_buf_size = { + let bits = match TypeId::of::() { + id if [TypeId::of::(), TypeId::of::()].contains(&id) => u8::BITS, + id if [TypeId::of::() , TypeId::of::()].contains(&id) => u16::BITS, + id if [TypeId::of::() , TypeId::of::()].contains(&id) => u32::BITS, + id if [TypeId::of::() , TypeId::of::()].contains(&id) => u64::BITS, + id if [TypeId::of::() , TypeId::of::()].contains(&id) => u128::BITS, + id if [TypeId::of::() , TypeId::of::()].contains(&id) => usize::BITS, + _ => unreachable!(), + }; + match bits { + 8 => "-128".len(), + 16 => "-32768".len(), + 32 => "-2147483648".len(), + 64 => "-9223372036854775808".len(), + 128 => "-170141183460469231731687303715884105728".len(), + _ => unreachable!(), + } + }; + if str_buf.len() > max_buf_size { + unsafe { core::hint::unreachable_unchecked() } + } + assert!(str_buf.len() < buf.len(), "{} unsupported.", core::any::type_name::()); + + buf[..str_buf.len()].copy_from_slice(unsafe { + // SAFETY: you can always go from init to uninit + mem::transmute::<&[u8], &[MaybeUninit]>(str_buf.as_bytes()) + }); + buf[str_buf.len()] = MaybeUninit::new(0); + } Self { buf, @@ -92,7 +118,7 @@ impl DecInt { pub fn as_bytes_with_nul(&self) -> &[u8] { let init = &self.buf[..=self.len]; // SAFETY: we're guaranteed to have initialized len+1 bytes. - unsafe { core::mem::transmute::<&[MaybeUninit], &[u8]>(init) } + unsafe { mem::transmute::<&[MaybeUninit], &[u8]>(init) } } /// Return the raw byte buffer.