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 9use crate::backend::fd::{AsFd, AsRawFd}; 10use crate::ffi::CStr; 11#[cfg(feature = "std")] 12use core::fmt; 13use core::fmt::Write; 14use itoa::{Buffer, Integer}; 15#[cfg(feature = "std")] 16use std::ffi::OsStr; 17#[cfg(feature = "std")] 18#[cfg(unix)] 19use std::os::unix::ffi::OsStrExt; 20#[cfg(feature = "std")] 21#[cfg(target_os = "wasi")] 22use std::os::wasi::ffi::OsStrExt; 23#[cfg(feature = "std")] 24use 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)] 44pub 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 51impl DecInt { 52 /// Construct a new path component from an integer. 53 #[inline] 54 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] 66 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] 72 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] 80 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] 91 pub fn as_bytes(&self) -> &[u8] { 92 &self.buf[..self.len] 93 } 94} 95 96struct DecIntWriter(DecInt); 97 98impl core::fmt::Write for DecIntWriter { 99 #[inline] 100 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")] 113impl AsRef<Path> for DecInt { 114 #[inline] 115 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")] 122impl fmt::Debug for DecInt { 123 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 124 self.as_str().fmt(fmt) 125 } 126} 127