1 //! Efficient decimal integer formatting. 2 //! 3 //! # Safety 4 //! 5 //! This uses `CStr::from_bytes_with_nul_unchecked` and 6 //! `str::from_utf8_unchecked`on the buffer that it filled itself. 7 #![allow(unsafe_code)] 8 9 use crate::backend::fd::{AsFd, AsRawFd}; 10 use crate::ffi::CStr; 11 #[cfg(feature = "std")] 12 use core::fmt; 13 use core::fmt::Write; 14 use itoa::{Buffer, Integer}; 15 #[cfg(feature = "std")] 16 use std::ffi::OsStr; 17 #[cfg(feature = "std")] 18 #[cfg(unix)] 19 use std::os::unix::ffi::OsStrExt; 20 #[cfg(feature = "std")] 21 #[cfg(target_os = "wasi")] 22 use std::os::wasi::ffi::OsStrExt; 23 #[cfg(feature = "std")] 24 use std::path::Path; 25 26 /// Format an integer into a decimal `Path` component, without constructing a 27 /// temporary `PathBuf` or `String`. 28 /// 29 /// This is used for opening paths such as `/proc/self/fd/<fd>` on Linux. 30 /// 31 /// # Example 32 /// 33 /// ```rust 34 /// # #[cfg(feature = "path")] 35 /// use rustix::path::DecInt; 36 /// 37 /// # #[cfg(feature = "path")] 38 /// assert_eq!( 39 /// format!("hello {}", DecInt::new(9876).as_ref().display()), 40 /// "hello 9876" 41 /// ); 42 /// ``` 43 #[derive(Clone)] 44 pub struct DecInt { 45 // 20 `u8`s is enough to hold the decimal ASCII representation of any 46 // `u64`, and we add one for a NUL terminator for `as_c_str`. 47 buf: [u8; 20 + 1], 48 len: usize, 49 } 50 51 impl DecInt { 52 /// Construct a new path component from an integer. 53 #[inline] new<Int: Integer>(i: Int) -> Self54 pub fn new<Int: Integer>(i: Int) -> Self { 55 let mut me = DecIntWriter(Self { 56 buf: [0; 20 + 1], 57 len: 0, 58 }); 59 let mut buf = Buffer::new(); 60 me.write_str(buf.format(i)).unwrap(); 61 me.0 62 } 63 64 /// Construct a new path component from a file descriptor. 65 #[inline] from_fd<Fd: AsFd>(fd: Fd) -> Self66 pub fn from_fd<Fd: AsFd>(fd: Fd) -> Self { 67 Self::new(fd.as_fd().as_raw_fd()) 68 } 69 70 /// Return the raw byte buffer as a `&str`. 71 #[inline] as_str(&self) -> &str72 pub fn as_str(&self) -> &str { 73 // Safety: `DecInt` always holds a formatted decimal number, so it's 74 // always valid UTF-8. 75 unsafe { core::str::from_utf8_unchecked(self.as_bytes()) } 76 } 77 78 /// Return the raw byte buffer as a `&CStr`. 79 #[inline] as_c_str(&self) -> &CStr80 pub fn as_c_str(&self) -> &CStr { 81 let bytes_with_nul = &self.buf[..=self.len]; 82 debug_assert!(CStr::from_bytes_with_nul(bytes_with_nul).is_ok()); 83 84 // Safety: `self.buf` holds a single decimal ASCII representation and 85 // at least one extra NUL byte. 86 unsafe { CStr::from_bytes_with_nul_unchecked(bytes_with_nul) } 87 } 88 89 /// Return the raw byte buffer. 90 #[inline] as_bytes(&self) -> &[u8]91 pub fn as_bytes(&self) -> &[u8] { 92 &self.buf[..self.len] 93 } 94 } 95 96 struct DecIntWriter(DecInt); 97 98 impl core::fmt::Write for DecIntWriter { 99 #[inline] write_str(&mut self, s: &str) -> core::fmt::Result100 fn write_str(&mut self, s: &str) -> core::fmt::Result { 101 match self.0.buf.get_mut(self.0.len..self.0.len + s.len()) { 102 Some(slice) => { 103 slice.copy_from_slice(s.as_bytes()); 104 self.0.len += s.len(); 105 Ok(()) 106 } 107 None => Err(core::fmt::Error), 108 } 109 } 110 } 111 112 #[cfg(feature = "std")] 113 impl AsRef<Path> for DecInt { 114 #[inline] as_ref(&self) -> &Path115 fn as_ref(&self) -> &Path { 116 let as_os_str: &OsStr = OsStrExt::from_bytes(&self.buf[..self.len]); 117 Path::new(as_os_str) 118 } 119 } 120 121 #[cfg(feature = "std")] 122 impl fmt::Debug for DecInt { fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result123 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 124 self.as_str().fmt(fmt) 125 } 126 } 127